Looking for something to add to my exploratory project, other than the ability to connect at any time and the ability to handle disconnects and reconnects between applications, I thought of allowing components to be written in C rather that Ada and interfacing them to the Ada framework.
At first I was going to use C# but thought better than to start with it after a brief examination of what was necessary to use it in conjunction with code written in other languages. At least with Microsoft C#. I only found that GNAT also had C# after I began to use GNAT C along with the GNAT Ada that I had been using. So I haven't looked into using the GNAT C#.
This post will illustrate some of the interfaces between a C component and the Ada framework as well as an abbreviated C component meant only to use the interfaces.
Additional Ada to Interface to Framework
The Ada framework uses generics to instantiate message topics for use by a particular component. For ease of interfacing with C, an extension of the Ada portion of an application was created to be the interface to C.
The install of a C component is done via the declaration of an Import to specify the C install method in the application's Install procedure that is run when the application launches. This procedure invokes some initialization procedures as well as the Install of each component of the application to cause them to be included in the build. Each such Install procedure then registers itself with the framework and registers for each message topic that it will produce or needs to consume. The message topic Ada packages (these would be C++/C# classes) contain the topic identifier and name as well as the topic attributes and the data format of messages of the particular topic.
For the C interface, the sample component declaration is
procedure Com_C1_Install; -- C component
pragma Import(C, Com_C1_Install, "comC1Install"); -- to install
invoked as
Com_C1_Install;
along with an Ada component
Com_Periodic.Install;
that will produce a topic for the C component to consume. Both of these components will register themselves to be run periodically.
A general Ada interface package was added to allow C components of any application to call the framework to register themselves and to register to produce or consume particular message topics. This package declares the format of the data to be provided and the interface procedures to be invoked by a C component. Without going into too much detail the Ada specification (the visible info) consists of
type Register_C_Component_Params_Type
--| Input parameters to supply with registerCComponent
is record
Name : String(1..30); -- name of component
Initialize : System.Address; -- callback addr for component initialization
Main : System.Address; -- callback address of periodic main method
Stack_Size : Integer; -- 0 to use default
QoS : QoS_Ptr; -- pointer to quality of service data
end record;
type Register_C_Component_Params_Ptr
is access Register_C_Component_Params_Type;
for Register_C_Component_Params_Ptr'storage_size use 0;
type Register_C_Component_Status_Ptr
--| Pointer to where output status is to be stored
is access Character;
for Register_C_Component_Status_Ptr'storage_size use 0;
type Component_Key_Ptr
--| Pointer to where output of participant key is to be stored
is access mC.Itf_Types.Participant_Key_Type;
for Component_Key_Ptr'storage_size use 0;
procedure Register_C_Component
( Params : in Register_C_Component_Params_Ptr;
--| Pointer to input parameters
Status : in Register_C_Component_Status_Ptr;
--| Register status to be returned
Key : in System.Address
--| Location where participant key of component is to be returned
);
pragma Export(C, Register_C_Component, "registerCComponent" );
to register the component and
type Register_C_Topic_Params_Type
--| Input parameters to supply with registerCTopic
is record
Name : String(1..30);
--| Name of topic
Id : Integer;
--| Topic identifier
Participant : mC.Itf_Types.Participant_Key_Type;
--| Participant key of consumer or producer
Queue_Size : Integer;
--| Queue size to use if topic Permanence is Queued and
--| Role is not Producer
Callback : System.Address;
--| Callback address for notify
Verify : System.Address;
--| Callback address for component's verify/filter function
Delivery_Id : Integer;
--| Identifier requested for delivery if 1-of-N consumer
Instances : Integer;
--| Number of instances of 1-of-N topics to be retained
--| for delivery to consumer
Detection : Character;
--| Whether consumer is notified of new message (N) or
--| must poll for it (P)
Role : Character;
--| Whether registering as producer/publisher (P) or
--| consumer/subscriber (C)
end record;
for Register_C_Topic_Params_Type'Alignment use 8;
type Register_C_Topic_Params_Ptr
is access Register_C_Topic_Params_Type;
for Register_C_Topic_Params_Ptr'storage_size use 0;
type Register_C_Topic_Status_Ptr
--| Pointer to where output status is to be stored
is access Character;
for Register_C_Topic_Status_Ptr'storage_size use 0;
procedure Register_C_Topic
( Params : in Register_C_Topic_Params_Ptr;
--| Pointer to input parameters
Status : in Register_C_Topic_Status_Ptr;
--| Register status to be returned
Key : in System.Address
--| Key to be used by component reader or writer to be returned
);
pragma Export(C, Register_C_Topic, "registerCTopic" );
to register to produce or consume a topic. Also
procedure Reader_C_Topic
( Id : in Integer;
--| Topic identifier
Key : in System.Address;
--| Key of component for topic
Ptr : in System.Address
--| Pointer to Data portion of message data buffer
);
pragma Export(C, Reader_C_Topic, "readCTopic" );
to Read a topic. Interface procedures to verify a topic, write a topic, and publish a topic are similar.
These common procedures invoke those necessary for a particular application to actually register the component or topic in another Ada package. This is done to be able to keep the common compilation units in one folder/library and the few that are different for a particular application in its own folder. For this still tiny sample, the Register and Reader are contained in a Com_C package. Since there is, as yet, only one C component for the application and no Verify, Writer or Publish interfaces the tables to select the correct interface procedure are as follows.
package body Com_C is
type Registered_Topic_Count_Type
--| Range of allowed topics for application
is new Integer range 0..1;
type Register_Topic_Type
--| Procedure to register a topic by a component
is access procedure
( Participant : in mC.Itf_Types.Participant_Key_Type;
Detection : in mC.Itf_Types.Message_Detection_Type;
Queue_Size : in mC.Itf_Types.Permanence_Queue_Size_Type;
Role : in mC.Itf_Types.Participant_Role_Type;
Callback : in mC.Itf_Types.Event_Driven_Entry_Point_Type;
Verify : in mC.Message.Verify_Filter_Entry_Point_Type;
Status : out mC.Message.Status_Type;
Key : out mC.Message.Key_Type
);
type Reader_Topic_Type
--| Procedure to obtain the data pointer of an instance of
--| a registered topic
is access procedure
( Key : in mC.Message.Key_Type;
Ptr : out System.Address
);
...
type Registered_Topic_Method_Type
--| Access to procedures for a topic
is record
Register : Register_Topic_Type;
--| To register the topic
Reader : Reader_Topic_Type;
--| To obtain the data address to examine the data of an instance
Verify : Verify_Topic_Type;
--| To obtain the data address to verify/filter the data of an
--| instance during a Read
Writer : Writer_Topic_Type;
--| To obtain the data address into which to write the data of
--| an instance
Publish : Publish_Topic_Type;
--| To publish a topic instance after the buffer has been written
end record;
type Registered_Topic_Methods_Type
is array( 1..Registered_Topic_Count_Type'last )
of Registered_Topic_Method_Type;
type Registered_Topic_Index_Type
--| Index into Method array
is array( mC.Message.Topic_Id_Type )
of Registered_Topic_Count_Type;
Topic
--| Registered topics
: constant Registered_Topic_Index_Type
:= ( mC.Message.Topic_Periodic_Id => 1, -- index into Method to
others => 0 -- obtain procedure access
);
procedure Register_Topic_Periodic
( Participant : in mC.Itf_Types.Participant_Key_Type;
Detection : in mC.Itf_Types.Message_Detection_Type;
Queue_Size : in mC.Itf_Types.Permanence_Queue_Size_Type;
Role : in mC.Itf_Types.Participant_Role_Type;
Callback : in mC.Itf_Types.Event_Driven_Entry_Point_Type;
Verify : in mC.Message.Verify_Filter_Entry_Point_Type;
Status : out mC.Message.Status_Type;
Key : out mC.Message.Key_Type
);
procedure Reader_Topic_Periodic
( Key : in mC.Message.Key_Type;
Ptr : out System.Address
);
Method
--| Register topic methods
: constant Registered_Topic_Methods_Type
:= ( 1 => ( Register => Register_Topic_Periodic'access,
Reader => Reader_Topic_Periodic'access,
Verify => null,
Writer => null,
Publish => null )
);
The procedure called by the common package to register a component is
procedure Register_C_Topic
( Name : in mC.Topic_Name_String_Type;
Ident : in Integer;
Participant : in mC.Itf_Types.Participant_Key_Type;
Detection : in mC.Itf_Types.Message_Detection_Type;
Queue_Size : in mC.Itf_Types.Permanence_Queue_Size_Type;
Role : in mC.Itf_Types.Participant_Role_Type;
Option : in mC.Itf_Types.Participant_Option_Type
:= mC.Itf_Types.Actual;
Delivery_Id : in mC.Itf_Types.Delivery_List_Type
:= mC.Itf_Types.Deliver_All;
Instances : in mC.Itf_Types.Delivery_Instances_Type := 0;
Callback : in mC.Itf_Types.Event_Driven_Entry_Point_Type
:= null;
Verify : in mC.Message.Verify_Filter_Entry_Point_Type := null;
Key : out mC.Message.Key_Type;
Status : out mC.Message.Status_Type
) is
-- ++
--| Logic_Flow:
--| Topic registry "factory" to register the topics requested by
--| C components by this application.
-- --
Ok
--| True if topic has been validated
: Boolean := False;
Topic_Id
--| Input Ident integer typed as a member of the topic id type
: mC.Message.Topic_Id_Type;
for Topic_Id'address use Ident'address;
use type mC.Message.Topic_Id_Type;
use type String_Tools.Comparison_Type;
begin -- Register_C_Topic
--| Logic_Step:
--| Validate the topic identifier and that the name is associated
--| with the identifier and select the topic package and
--| register it.
if Topic_Id = mT.Topic_Periodic.Id then
Ok := String_Tools.Blind_Compare
( Left => String(Name),
Right => mT.Topic_Periodic.Name ) = String_Tools.Equal;
if not Ok then
Key := Null_Key; -- key is unusable for failure
Status := mC.Message.Failure;
end if; -- String_Tools.Blind_Compare fails
else
Key := Null_Key; -- key is unusable for failure
Status := mC.Message.Failure;
end if; -- Ident matches a topic
--| Logic_Step:
--| Execute the register method for the topic.
if Ok and then
Topic(Topic_Id) /= 0 and then
Method(Topic(Topic_Id)).Register /= null
then
Method(Topic(Topic_Id)).Register( Participant => Participant,
Detection => Detection,
Queue_Size => Queue_Size,
Role => Role,
Callback => Callback,
Verify => Verify,
Status => Status,
Key => Key );
else
Key := Null_Key; -- key is unusable for failure
Status := mC.Message.Failure;
end if;
exception
when others =>
Key := Null_Key; -- key is unusable for failure
Status := mC.Message.Failure;
end Register_C_Topic;
The above procedure returns a Key that identifies the topic and component to the common Ada/C interface procedure that returns it, along with the Status as to whether the register succeeded, to the C component. This key must be returned when a Reader, Verify, Writer, or Publish is invoked.
The procedure called by the common package to read a topic for a component is
procedure Topic_Reader
( Ident : in Integer;
Key : in mC.Message.Key_Type;
Ptr : out System.Address
) is
-- ++
--| Logic_Flow:
--| Select reader procedure for topic and run it.
-- --
Data
--| Address of data buffer
: Integer;
Data_Ptr
--| Address of data buffer
: System.Address;
for Data_Ptr'address use Data'address;
Topic_Id
--| Input Ident integer typed as a member of the topic id type
: mC.Message.Topic_Id_Type;
for Topic_Id'address use Ident'address;
begin -- Topic_Reader
--| Logic_Step:
--| Execute the reader method for the topic.
if Topic(Topic_Id) /= 0 and then
Method(Topic(Topic_Id)).Reader /= null
then
Method(Topic(Topic_Id)).Reader( Key => Key,
Ptr => Data_Ptr );
--| Logic_Step:
--| Adjust pointer past message header to the topic data.
if Data > 0 then
Data := Data + mC.Message.Message_Header_Size;
end if;
Ptr := Data_Ptr;
else
end if;
exception
when others =>
Ptr := System.Null_Address;
end Topic_Reader;
Procedures to verify, write, or publish will be similar.
The framework takes care of everything else. For instance, if a C component registers a Main procedure (as it will do in this example) to be executed whenever its periodic interval elapses, the framework retains the callback and invokes it. So no additional interface code is necessary. Similarly, if the C component provides a Verify method, when the component invokes the topic's Reader, the framework will invoke the Valid function of the Ada version of the topic and then the Verify method, via the callback of the component.
Message Topics
For each topic that is to be read or written by a C component, a C version of the topic must be supplied via a header and code. The header will be similar to and yet different from the Ada specification. That is, it will describe the format of the data but will lack the Ada generic. Instead, it will contain the declarations for the Reader, etc methods as well as a method to initialize hidden data such as the topic id and name. For my purposes, these files are named similarly to the Ada version of the topic package. In this sample, mTTopicPeriodic since the Ada package was named mT-Topic_Periodic. (mT- precedes the various topics so that this group of units sort together.)
Likewise, since each topic will have its own data format and reader, etc methods that are not enclosed in a corresponding Ada package (C++/C# class) each such visible name is preceded by the basic name of the topic to differentiate one from another. (Similarly, since I have named Ada components as Com_... to identify them as components, I have named the first C sample component comC1 with file names of comC1Install and comC1Main as replacements for Ada separates. Thus all the files that would be part of a similar Ada package sort together in a folder.)
In this example, the Ada topic preexisted its use in C. The way the framework currently works, a new topic only used by C components would still need a minimal Ada version to interface with the framework. Since it wouldn't be used by Ada code, the data format however would only need to be a blob that resulted in specifying the buffer size necessary for messages and declare a Valid function that only returns True. Then any general validation to be done could be done by the Verify method supplied by the C component callback, something I will try with further examples in implementing a C publishing component.
The C topic description (mTTopicPeriodic.h) and the corresponding Ada topic specification are as follows.
Ada:
package mT.Topic_Periodic is
-- ++
--| Overview:
--| This package is the mechanism for transmitting the Topic_Periodic
--| message to other components.
--| Notes:
--| The request will only be routed to the application components
--| that have registered to treat it.
-- --
Id
: constant mC.Message.Topic_Id_Type := mC.Message.Topic_Periodic_Id;
Name
: constant String(1..14) := "Topic Periodic";
Storage
: constant mC.Message.Buffer_Storage_Allocation_Type
:= mC.Message.Reusable;
Protocol
: constant mC.Itf_Types.Topic_Delivery_Protocol_Type
:= mC.Itf_Types.Publish_Subscribe;
Exchange
: constant mC.Itf_Types.Message_Exchange_Type
:= mC.Itf_Types.No_Exchange;
Permanence
: constant mC.Itf_Types.Topic_Permanence_Type := mC.Itf_Types.MRP;
Kind
: constant mC.Itf_Types.Topic_Kind_Type := mC.Itf_Types.NA;
type Data_Type
--| Data type containing the format of the message
is record
Publishing_Component : mC.Itf_Types.Participant_Key_Type;
--| Sample data that provides the publisher
Reference_Number : Integer;
--| Number that can be changed to check that new value published
Time : Integer;
--| Elapsed time at which sent
end record;
for Data_Type'size use 5*32; -- bits
for Data_Type'alignment use mC.Message.Message_Alignment; -- bytes
type Topic_Type
is record
Header : mC.Message.Message_Header_Type;
Data : Data_Type;
end record;
for Topic_Type'alignment use mC.Message.Message_Alignment; -- bytes
for Topic_Type'size use mC.Message.Message_Header_Size*8+(5*32); -- bits
---------------------------------------------------------------------
function Valid
( Key : in mC.Message.Key_Type
--| Access key
) return Boolean;
--| Validate the Topic Data
-- ++
--| Overview:
--| This function returns TRUE if the Message data passes the
--| validation.
--| Limitations:
--| This function must ONLY be used to validate data following the
--| instantiation of a topic Reader. It must NOT be used after the
--| instantiation of a topic Writer since it will overwrite the
--| Valid flag of the instantiation parameter causing the lack of
--| the assignment of a write buffer pointer to appear to be valid.
-- --
---------------------------------------------------------------------
package Data
--| Accessor to the topic
is new mC.Message.Topic( Id => Id,
Name => Name,
Protocol => Protocol,
Permanence => Permanence,
Exchange => Exchange,
Kind => Kind,
Data_Type => Topic_Type,
Validation => Valid'access );
end mT.Topic_Periodic;
C header:
#ifndef TOPICPERIODIC
#define TOPICPERIODIC
#include <infCtoAda.h> // "spec" for particular top level declarations
// to interface to Ada
typedef struct //dataTypeRec
{ // Data type containing the format of the message
participantKey publishingComponent;
// Participant key of publishing component
Int32 referenceNumber;
// Number that can be changed to check that new value published
Int32 time;
// Elapsed time at which sent
} dataTypeRec; // end struct
typedef dataTypeRec * dataWriterPtr;
// Pointer to the data in the data buffer of the message
// containing the topic
typedef const dataTypeRec * dataReaderPtr;
// Pointer to the data of the data buffer of the message
// containing the topic for use by a Reader or Verify method
typedef struct //topicType
{ messageHeaderType Header;
dataTypeRec Data;
} topicType; // end struct
//---------------------------------------------------------------------
typedef struct // mTTopicPeriodicFormat
{ Int32 id;
TopicNameType name;
Address dataTypeAddr;
} mTTopicPeriodicFormat;
typedef mTTopicPeriodicFormat * mTTopicPeriodic;
void mTTopicPeriodicInit();
// Initialize the internal data
void mTTopicPeriodicInfo(mTTopicPeriodicFormat *);
// Get the topic information
dataReaderPtr mTTopicPeriodicReader(messageKey);
// Topic Reader
#endif // TOPICPERIODIC
The Reader in mTTopicPeriodic.c interfaces with Ada is in the following:
const TopicNameType Name = "Topic Periodic ";
const Int32 topicPeriodicId = 105;
static mTTopicPeriodicFormat mTTopicPeriodicInformation;
// topic information
//-------------------------------------------------------------------
void mTTopicPeriodicInit()
{ // initialize the topic data
mTTopicPeriodicInformation.id = topicPeriodicId;
int i;
for (i = 0; i < 30; i++)
{ mTTopicPeriodicInformation.name[i] = Name[i];
} // end for
mTTopicPeriodicInformation.dataTypeAddr = 0; //null;
}// end method mTTopicPeriodicInit
//-------------------------------------------------------------------
void mTTopicPeriodicInfo(mTTopicPeriodicFormat * info)
{ // return topic data
*info = mTTopicPeriodicInformation;
} // end method mTTopicPeriodicInfo
//-------------------------------------------------------------------
dataReaderPtr mTTopicPeriodicReader(messageKey accessKey)
{ // Topic Reader
dataReaderPtr dataAddress; // address of instance of topic data
readCTopic(topicPeriodicId, &accessKey, &dataAddress);
return dataAddress;
} // end method mTTopicPeriodicReader
Sample C component
The sample C component only does what is necessary. That is, it registers itself via the install method invoked at the time the application is launched and indicates that it can correctly read the topic in the main method that is invoked by the framework when the periodic interval has elapsed. The files of the com1C component are as follows:
com1C.h:
#ifndef COMC1
#define COMC1
#include <infCtoAda.h>
Int32 periodicTopicId;
// Topic identifier of mTTopicPeriodic
messageKey periodicTopicAccessKey;
// Access key to read the topic
// declare component 1's Install procedure to be called by framework
extern void comC1Install(); // Install component
// declare component 1's main entry for use as callback
extern void comC1Main(); // Main procedure of component
#endif // COMC1
comC1Install.c:
#include <stdio.h>
#include <infCtoAda.h> // "spec" for interface between C and Ada
#include <mTTopicPeriodic.h> // "spec" for particular topic
#include <comC1.h> // "spec" for component 1
extern void comC1Install()
// externally callable procedure to install a C based component
// that calls an Ada procedure to register itself
{ // begin
// Obtain address of the component's Main entry point.
void (*addr)(char);
addr = &comC1Main;
// Setup to register the component with the framework.
registerCComponentParams params;
qoService qoS;
qoS.activation = 'P'; // periodic
qoS.instances = 1;
qoS.timeInterval = 500; // milliseconds
qoS.timeCapacity = 2; // milliseconds
int i; // set component name to "comC1"
for (i = 0; i < 30; i++)
{ params.name[i] = ' ';
}
params.name[0] = 'c';
params.name[1] = 'o';
params.name[2] = 'm';
params.name[3] = 'C';
params.name[4] = '1';
params.initialize = 0; // no Initialize method
params.main = (int)(addr); // set Main method callback
params.stackSize = 0; // use default
params.qoS = &qoS; // address of quality of service
// Declare the variables to be set by the framework -
// key must be used later to register the topics
char status;
participantKey key;
// Register the component
registerCComponent( ¶ms, &status, &key );
// Setup to register the mTTopicPeriodic topic with the framework
// to be consumed by the component.
registerCTopicParams topicParams;
messageKey accessKey;
// First, initialize the topic's data.
// Note: The initialize should be in a common Initialize.c to be
// executed prior to the install of the components since
// multiple components could register for the same topic.
mTTopicPeriodicInit();
// Second, retrieve the data.
mTTopicPeriodicFormat info;
mTTopicPeriodicInfo(&info);
periodicTopicId = info.id; // pass topic id for use by comC1Main
// Third, setup the data to pass to the framework to register
// for the topic.
for (i = 0; i < 30; i++) // topic name
{ topicParams.name[i] = info.name[i];
}
topicParams.id = info.id; // topic id
topicParams.participant = key; // participant component key
topicParams.queueSize = 0; // Queue size to use if topic
// Permanence is Queued and Role
// is not Producer
topicParams.callback = 0; // callback address for notify
topicParams.verify = 0; // callback address for particular
// participant component function
// to verify/filter instance of topic
// prior to allowing the Reader to
// to provide a data Ptr
topicParams.deliveryId = 0; // to be supplied if registering as a
// 1-of-N consumer for selective
// delivery
topicParams.instances = 0; // Number of instances of topics to be
// retained for delivery to consumer
topicParams.detection = 'P'; // Periodic component so must poll for
// new instance of topic
topicParams.role = 'C'; // registering as consumer/subscriber
// Register the topic
registerCTopic( &topicParams, &status, &accessKey );
// Save key to comC1.h for use by comC1Main
periodicTopicAccessKey = accessKey;
} // end method comC1Install
comC1Main.c:
Another interface from C to Ada uses snprintf to format the output and then invokes the Ada procedure (via console) that outputs the text to the console and to a disk log file that can be examined after the debug run has finished.
#include <stdio.h>
#include <comC1.h> // "spec" for component 1
#include <infCtoAda.h>
#include <mTTopicPeriodic.h>
extern void comC1Main()
{ // externally callable procedure to act as callback into the
// C based component
// Do console output to illustrate that framework invoked this
// method.
consoleLen = snprintf(consoleBuffer,CONSOLE_MAX,"In comC1Main %u\n",
periodicTopicAccessKey);
console(consoleBuffer,consoleLen);
// Read the latest instance of the topic
dataReaderPtr buffer = mTTopicPeriodicReader(periodicTopicAccessKey);
consoleLen = snprintf(consoleBuffer,CONSOLE_MAX,
"comC1Main after Read %u\n",buffer);
console(consoleBuffer,consoleLen);
if (buffer == 0)
{ // no data for topic
consoleLen = snprintf(consoleBuffer,CONSOLE_MAX,
"comC1Main no data for topic\n");
console(consoleBuffer,consoleLen);
}
else
{ // Display the data read
consoleLen = snprintf(consoleBuffer,CONSOLE_MAX, \
"publishing component %u %u %u\n", \
buffer->publishingComponent.value[0], \
buffer->publishingComponent.value[1], \
buffer->publishingComponent.value[2]);
console(consoleBuffer,consoleLen);
consoleLen = snprintf(consoleBuffer,CONSOLE_MAX, \
"reference number & time %u %u\n", \
buffer->referenceNumber,buffer->time);
console(consoleBuffer,consoleLen);
// The following line was included in a compile attempt to show
// that C wouldn't allow the change to the value in the buffer.
//buffer->publishingComponent.value[0] = 55;
} // end if
} // end method comC1Main
Build .bat files
Mostly I built the Ada code via a GNAT GPS project as well as compiling the .c files one at a time until they compiled and then the following batch file to put the C code into a library and do the bind and link. The second batch file also compiles the Ada code of the project. (The indented line in each batch file is actually an extension of the previous line.)
cd C:\Source\EP\UserApp\Try5
c:\gnatpro\5.04a1\bin\gprmake -d -PApp3.gpr
c:\gnatpro\5.04a1\bin\gnatbind -x C:\Source\EP\ObjectC\App3.ali
c:\gnatpro\5.04a1\bin\gnatlink C:\Source\EP\ObjectC\App3.ali
C:\Source\EP\ObjectC\libapp3.a -o C:\Source\EP\ObjectC\App3.exe
pause
cd C:\Source\EP\UserApp\Try5
c:\gnatpro\5.04a1\bin\gnatmake -s -c -PApp3.gpr
c:\gnatpro\5.04a1\bin\gprmake -d -PApp3.gpr
c:\gnatpro\5.04a1\bin\gnatbind -x C:\Source\EP\ObjectC\App3.ali
c:\gnatpro\5.04a1\bin\gnatlink C:\Source\EP\ObjectC\App3.ali
C:\Source\EP\ObjectC\libapp3.a -o C:\Source\EP\ObjectC\App3.exe
pause
Logged Console Output
The following is a portion of the logged console output illustrating that the C topic Reader as called from comC1Main is able to read the data of the topic. In this output, the C component is run the first time before the Ada component has published the topic. Therefore, the console output shows that no data was returned the first time that the framework caused comC1Main to be executed.
...
In comC1Install Note: The code for this output was removed prior to
In comC1Install 4625632 copying it into the comC1Install sample above.
in mC-C_Itf And most of the following is in the Ada code.
comC1
mC-C_Ift Init 0
mC-C_Itf Main 4625632
P 65536 500 2
Register Component comC1 5 1 5 0 Actual
Process_Priority 10 2
*** Periodic timing interval increased of/to 10 50 ***
mC-C_Itf 1 5 0
returned to comC1Install again
...
Note: The following is the registration of the Ada component
Register Component Com_Periodic 6 1 6 0 Actual
Process_Priority 10 2
*** Periodic timing interval increased of/to 10 50 ***
Com_Periodic 6
...
In comC1Main 1 comC1Main entered
mC-C_Itf Access_Key 209911516 In the Ada code
Com_C Topic_Reader Header 0
Com_C Topic_Reader Data 0
mTTopicPeriodicReader 0 Back in the C code
comC1Main after Read 0 Back in comC1Main
comC1Main no data for topic null pointer to data, topic yet to be produced
Com_Periodic to publish Topic_1 1 2 3 1 5062 Ada component runs
Com_Periodic exit Main and publishes topic
...
In comC1Main 1 comC1Main entered again
mC-C_Itf Access_Key 209911516
Local Topic instance
Com_C Topic_Reader Header 4976512 address of message buffer
Com_C Topic_Reader Data 4976532 address of data within the buffer
mTTopicPeriodicReader 4976532 back in C code with pointer to data
comC1Main after Read 4976532
publishing component 1 2 3 fake component key supplied by publisher
reference number & time 1 5062 first published instance at time 5062
Scheduler Periodic calling Release 5
Com_Periodic to publish Topic_1 1 2 3 2 10062 Ada component runs again
Com_Periodic exit Main