Monday, April 25, 2011

Exploratory Project C Language Use of Verify Callback

Exploratory Project C Language Use of Verify Callback


I have now added the Verify interface between the Ada framework and a C component.

The comC1 C component supplies its Verify method when it registers a topic consumer.  The Verify method for the topic is both for the comC1 component and the particular topic so the method was named comC1VerifymTTopicPeriodic.  The Install method (that is, comC1Install) was modified such that the register for the topic is as follows
  topicParams.verify = (Int32)(&comC1VerifymTTopicPeriodic); // callback address
for notify where the Verify callback address is no longer set to 0 for null.

The Verify method is for the mTTopicPeriodic topic so the interface to obtain the address of the data in the buffer containing the instance of the message topic has been added to that .h and .c file that were provided in the preceding post.  The added interface is
  dataReaderPtr mTTopicPeriodicVerify(messageKey accessKey)
  { // Topic Verify
    dataReaderPtr dataAddress; // address of instance of topic data

    verifyCTopic(topicPeriodicId, &accessKey, (dataAddr*)(&dataAddress));

    return dataAddress;
  } // end method mTTopicPeriodicVerify

The Verify method for the topic, that is the function invoked by the framework when the component attempts to read the topic, is named after the method as comC1VerifymTTopicPeriodic.c so that it sorts in its Windows folder along with the other files for the component. 

The sample verify function itself only displays the data values to illustrate that the data pointer does indeed point to the data in the buffer.  Since, unlike the Reader interface, the framework will not invoke the verify function if there is no topic to be verified, there is no need to check for a null pointer. 

The framework returns the topic key to the verify function via a pointer while the interface back to the framework to obtain the data pointer uses the value.  Therefore, memcpy is used to copy the value pointed to by topicAccessKey to a local value to supply to mTTopicPeriodicVerify.  (Note:  In Ada the .all construct can be used to do this.)

#include <infCtoAda.h>
#include <conversion.h>
#include <mTTopicPeriodic.h>
#include <comC1.h>

Boolean comC1VerifymTTopicPeriodic(messageKeyPtr topicAccessKey)
{ // Verify topic mTTopicPeriodic for use by component comC1

  messageKey key;
  memcpy(&key,topicAccessKey,sizeof(key));

  dataReaderPtr buffer = mTTopicPeriodicVerify(key);

  consoleLen = snprintf(consoleBuffer,CONSOLE_MAX,"verify 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);
//buffer->publishingComponent.value[0] = 55; // attempt to change value
  return TRUE;

} // end method comC1VerifymTTopicPeriodic
where Boolean is defined in the infCtoAda header file as
  typedef Int32 Boolean;
  #define FALSE 0;
  #define TRUE 1;

Nothing further need be done.

Friday, April 22, 2011

Exploratory Project C Language Components

Exploratory Project C Language Components


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( &params, &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