Exploratory Project C Language Components Revisited
While implementing a sample Delivery Identifier message exchange topic in C I found that both requested identifiers and those that hadn't been requested were delivered to my sample callback method to consume the topic. While thinking about what I needed to fix in the framework interface to correct this problem it occurred to me that I had implemented the entire subject incorrectly.
That is, that I didn't need to have dummy Ada topics for topics that were only going to be used by C language components nor did I need to have the Com_C and mC-C_Itf Ada packages to be the interface to C language components.
To receive messages (instances of topics) from a remote application and forward them to the correct component I early on created a Remote child package of the mC.Message package. When a topic was received and validated it was re-published (that is, published again locally) to be routed to the appropriate component. Thus, for these messages, the topic could be published via a "backdoor" without instantiating an instance of the topic. Such topics were first registered via the transmit/receipt of the remote registration messages via the Remote package via a different framework Register procedure. I'm referring to this as a backdoor since the receive of the message is internal to the scope / envelope of the framework instead of external in a user component.
So it occurred to me that I could do something similar for the topics sent by a C component. Although such topics were local to the application on which the framework was loaded, that such a foreign language topic instance could be interfaced to the framework by another "backdoor".
Therefore, I discarded the previous C language component interface and set about implementing a foreign language interface via a Foreign Ada child package of the mC Message package to parallel the Remote package. The Foreign package is also internal to the framework and entered via the use of the Ada Export pragma.
The Foreign package not only needs special Register and Publish procedures but special internally accessible Reader, Verify, and Writer procedures along with its own procedure to register a foreign language component.
These were added with the use of the Ada Export pragma to supply the correspondence between the Ada name and the C language name used to invoke the framework procedure. Each of these procedures than does some validation of the calling parameters supplied by C and then calls the framework procedure that is also invoked via an instantiation of a topic via Ada.
Voilà. The framework is accessible to C language components without the need to create extra Ada code to provide a minimal Ada version of a C-only topic or modify Ada interface packages when a new foreign language topic is created.
However, to create a C-only topic, the topic identifier still needs to be added to the list of valid identifiers in the mC-Message specification.
When the topic is used by both Ada and C, the topic descriptions must be the same. Same identifier and name, same specification for message exchange, kind, permanence, etc.
During the addition of the treatment of a Delivery Identifier topic, I found there was a need to rework the C topic declarations. That is, I found out that my naming was insufficient to allow unique names for the data format typedefs.
I also decided that the topic attributes shouldn't be visible so changed the topic files to replace the topic Info functions with a Register method. The component that needs a particular topic invokes the Register method of the topic while supplying its component attributes such as whether to be a producer or a consumer of the topic. The particular topic then calls a common mT-Topic Register method while supplying its attributes and passing along those of the component. The common Register then invokes the Ada interface via the Foreign language package. This keeps the topics attributes hidden and means less code in a component Install method to register for a topic.
I now use '_' to indicate where dot notation would be in Ada packages / C++ C# classes if C could have such classes. I use separate .c files for most code files mimicking Ada procedure separates. I use '-' in the .h and .c file names the same as in the GNAT Ada names that separate package names of sub and child packages and package names from procedure names in separates. But '_' is used in the C code where '.' would be used in Ada code since can’t use either ‘.’ or '-' in the C code itself. Otherwise normal C practice is used for names with a leading lowercase letter and with the '_' separator only used between “package” names and function/method names but not in the name itself.
This causes the file names to sort together and identifies what .h file a typedef or variable comes from. Hence, what would be the mT-Topic.ads in Ada is mT-Topic.h in C and an example typedef name of mT-Topic.h is
typedef mT_Topic_RegisterTopicParams * mT_Topic_AttributesPtr;
and referenced in mT-Topic.c as
void mT_Topic_Register(mC_ItfC_SubscriptionAttributesPtr subAttributes,
mT_Topic_AttributesPtr topicAttributes,
char * status,
mC_ItfC_MessageKeyPtr accessKey) {
...
}
what would be
package body mT.Topic is
procedure Register
( Sub_Attributes : in mC.Itf_C.Subscription_Attributes_Ptr;
Topic_Attributes : in mT.Topic.AttributesPtr;
Status : out Character;
Access_Key : out mC.Itf_C.Message_Key_Ptr ) is
begin
...
end Register;
...
end mT.Topic;
in normal Ada naming.
I also completed the interface of C to Ada by implementing the Content Filtered point-to-point method of delivery as well as the previously mentioned Delivery Identifier One-of-N method of delivery where the set of delivery identifiers is specified with the registration of the topic by a consumer component as those, from the entire range of possibilities, that the component will treat and the topic writer has to specify which delivery identifier it will be publishing.
The Content Filtered method requires that the consumer component save the filter conditions specified by each of the requesting components and then create a response to a particular component when its conditions are satisfied. For this, it needs to be able to determine what the component key of the requestor is so that it can supply it when creating a response. This isn't necessary for the other delivery methods since the framework keeps track of the requestor to which to return a Request/Response reply and which components have requested to consume particular Delivery Identifiers.
Sample code to treat a Content Filtered request is as follows:
// Do Content Filtered
mT_TopicContentFiltered_Request_DataReaderPtr
dataContentFilteredPtr =
mT_TopicContentFiltered_Request_Reader
(comC1_TopicContentFiltered_Request_AccessKey,false);
if (dataContentFilteredPtr != 0)
{
// Obtain the requestor.
mC_ItfC_ParticipantKey requestor;
mT_Topic_Source(&comC1_TopicContentFiltered_Request_AccessKey, &requestor);
consoleLen = snprintf(consoleBuffer,CONSOLE_MAX,
"comC1_Main received content filtered request %u %u %u\n",
requestor.value[0], requestor.value[1], requestor.value[2] );
console(consoleBuffer,consoleLen);
// Check if new request from a requestor.
int index = -1;
int r;
for (r = 0; r < contentFilteredRequests.count; r++)
{
if (requestor.value[0] ==
contentFilteredRequests.data[r].requestor.value[0] &&
requestor.value[1] ==
contentFilteredRequests.data[r].requestor.value[1] &&
requestor.value[2] ==
contentFilteredRequests.data[r].requestor.value[2])
{ index = r;
break;
}
} // end for
// Obtain next list location if new requestor,
if (index < 0)
{
if (contentFilteredRequests.count < contentFilterRequestMax)
{
index = contentFilteredRequests.count;
contentFilteredRequests.count++;
}
}
// save content filtered conditions
if (index >= 0)
{ contentFilteredRequests.data[index].requestor = requestor;
contentFilteredRequests.data[index].request[0].condition1 =
dataContentFilteredPtr->condition1;
contentFilteredRequests.data[index].request[0].scope1 =
dataContentFilteredPtr->scope1;
contentFilteredRequests.data[index].request[0].id1 =
dataContentFilteredPtr->id1;
contentFilteredRequests.data[index].request[1].condition1 =
dataContentFilteredPtr->condition2;
contentFilteredRequests.data[index].request[1].scope1 =
dataContentFilteredPtr->scope2;
contentFilteredRequests.data[index].request[1].id1 =
dataContentFilteredPtr->id2;
} // end if
} // end if (dataContentFilteredPtr != 0)
// Check if any of the requests are satisfied, respond to each
// that is satisfied when have data.
if (contentFilteredRequests.count > 0 && dataPeriodic != 0)
{
int r;
for (r = 0; r < contentFilteredRequests.count; r++)
{ // Note: Below is not the best example for treatment of saved content
// filtered conditions and scope hasn't even been considered but
// assumed to be WhileSatisfied
if ( (contentFilteredRequests.data[r].request[0].condition1 == GE) &&
(dataPeriodic->referenceNumber >=
contentFilteredRequests.data[r].request[0].id1)
&& (contentFilteredRequests.data[r].request[1].condition1 == LE) &&
(dataPeriodic->referenceNumber <=
contentFilteredRequests.data[r].request[1].id1) )
{ // send response
mT_TopicContentFiltered_Response_DataWriterPtr dataCFResponse =
mT_TopicContentFiltered_Response_Writer
(comC1_TopicContentFiltered_Response_AccessKey,
&contentFilteredRequests.data[r].requestor); // specification of requestor
if (dataCFResponse != 0)
{
dataCFResponse->referenceNumber = dataPeriodic->referenceNumber;
dataCFResponse->time = dataPeriodic->time;
dataCFResponse->publisher = dataPeriodic->publishingComponent;
consoleLen = snprintf(consoleBuffer,CONSOLE_MAX,
"comC1-Main to publish CF Response %u\n",
dataPeriodic->referenceNumber);
console(consoleBuffer,consoleLen);
mT_TopicContentFiltered_Response_Publish
(comC1_TopicContentFiltered_Response_AccessKey);
} // end if
} // end if
} // end for
} // end if
Logged Console Output
The following is a portion of the logged console output illustrating that the C topic Reader as called from comC1-Main is able to read the data of the topic. There is also output from the comC2-Nto1-Request and the comC2-ContentFiltered-Response methods that are run by the framework upon demand; that is, when their topic is published. In addition there is output from comC1-Verify-mT-TopicPeriodic, comC1-Verify-mT-TopicNto1-Response and comC2-Verify-mT-TopicNto1-Request that is executed by the framework when the component invokes the corresponding Reader.
Com_Periodic to publish Topic_1 1 2 3 1 5484 <-- first Ada component publish
Com_Periodic exit Main
In comC1-Main 1 5007328 <-- periodic C component runs
mT_TopicPeriodic_Reader 1 5007328 <-- read starts
in comC1_Verify_mT_TopicPeriodic <-- that causes verify to be entered
mT_TopicPeriodic_Verify 1 5007328
Foreign Verify Access_Key 243530864
mT_TopicPeriodic_Verify again 5007444
comC1_Verify_mT_TopicPeriodic after Verify 500744
publishing component 1 2 3 <-- values examined
reference number & time 1 5484 <-- in Verify
mT_TopicPeriodic_Reader 1 5007328 243531292
comC1Main after Read 5007444
publishing component 1 2 3 <-- Values examined in Main
reference number & time 1 5484 <-- following Read
mTTopicNto1ResponseReader 257 5008288
mTTopicNto1ResponseReader 257 5008288 243531292
comC1-Main after Response Read 0 <-- no Nto1 Response available
comC1-Main no data for response topic <-- to be read
mTTopicNto1RequestWriter 257 5007744
Topic_Writer Queued 7 7 Topic Nto1 Request
mTTopicNto1RequestWriter again 5007860
comC1-Main after Request Writer 5007860
comC1-Main to publish Request 1 35329 9
mTTopicNto1RequestPublish 257 5007744 <-- publish of first Nto1 Request
comC1_Main Writer for mT_Topic1ofN for 1234
In comC2_Nto1_Request 257
Topic_Writer Del Id 7 7 Topic 1ofN
mTTopicNto1Reader 257 5010272
comC1_Main to publish mT_Topic1ofN for 1234 6529 <-- publish of first del id of 1234
in comC2_Verify_mT_TopicNto1_Request <-- comC2 is notified of Nto1 request
mTTopicNto1RequestVerify 257 5010272 <-- topic and verify is entered
Foreign Verify Access_Key 277085296 <-- as part of reader
mTTopicNto1RequestVerify again 5007860
comC2_Verify_mT_TopicNto1_Request after Verify 50
Request source 3 7 0 <-- indication that verify can
Request ref# time 1 35329 <-- examine values of topic
mTTopicNto1Reader 257 5010272 277085724
comC2_Nto1_Request after Reader 5007860 <-- comC2 reads the Nto1 request topic
Topic_Writer Queued 8 8 Topic ContentFiltered Request
comC2_Nto1_Request after CF Writer 5011444 <-- comC2 writes and publishes
comC2_Nto1_Request publish CF <-- content filtered request
mTTopicNto1ResponseWriter 257 5010656
Topic_Writer Queued 8 8 Topic Nto1 Response
mTTopicNto1ResponseWriter 257 5010656 277085724
comC2_Nto1_Request after Response Writer 5008628 <-- comC2 writes and publishes
mTTopicNto1ResponsePublish 257 5010656 <-- Nto1 Response
In comC2_1ofN <-- comC2 is notified of 1ofN
comC2_1ofN: id 1234 time 6529 publisher 3 7 0 <-- del id of 1234 topic
Com_Periodic to publish Topic_1 1 2 3 2 10484 <-- Ada periodic component runs for
Com_Periodic exit Main <-- second time
In comC1-Main 1 5007328 <-- C comC1 periodic component runs
mT_TopicPeriodic_Reader 1 5007328 <-- for second time
in comC1_Verify_mT_TopicPeriodic
mT_TopicPeriodic_Verify 1 5007328
Foreign Verify Access_Key 243530864
mT_TopicPeriodic_Verify again 5007444
comC1_Verify_mT_TopicPeriodic after Verify 500744
publishing component 1 2 3
reference number & time 2 10484
mT_TopicPeriodic_Reader 1 5007328 243531292
comC1Main after Read 5007444
publishing component 1 2 3
reference number & time 2 10484
mTTopicNto1ResponseReader 257 5008288
in comC1VerifymTTopicNto1Response
mTTopicNto1ResponseVerify 257 5008288
Foreign Verify Access_Key 243530864
mTTopicNto1ResponseVerify again 5008628
comC1_Verify_mT_TopicNto1_Response after Verify 5
publishing component 3 7 0
reference number & time 1 35329
mTTopicNto1ResponseReader 257 5008288 243531292
comC1-Main after Response Read 5008628
comC1-Main Nto1 Response 3 7 0
comC1-Main Nto1 Response 1 35329
mTTopicNto1RequestWriter 257 5007744
Topic_Writer Queued 7 7 Topic Nto1 Request
mTTopicNto1RequestWriter again 5007860
comC1-Main after Request Writer 5007860
comC1-Main to publish Request 2 35334 9
mTTopicNto1RequestPublish 257 5007744
comC1_Main Writer for mT_Topic1ofN for 4321 <-- del id of 4321 to be written
In comC2_Nto1_Request 257
Topic_Writer Del Id 7 7 Topic 1ofN
mTTopicNto1Reader 257 5010272
comC1_Main no data buffer to write mT_Topic_1ofN <-- no data buffer supplied since no
component subscribed for 4321
in comC2_Verify_mT_TopicNto1_Request
comC1_Main received content filtered request 3 8
mTTopicNto1RequestVerify 257 5010272
comC1-Main to check to publish CF Response 2
Foreign Verify Access_Key 277085296
mTTopicNto1RequestVerify again 5007860
comC2_Verify_mT_TopicNto1_Request after Verify 50
Request source 3 7 0
Request ref# time 2 35334
mTTopicNto1Reader 257 5010272 277085724
comC2_Nto1_Request after Reader 5007860
mTTopicNto1ResponseWriter 257 5010656
Topic_Writer Queued 8 8 Topic Nto1 Response
mTTopicNto1ResponseWriter 257 5010656 277085724
comC2_Nto1_Request after Response Writer 5008628
mTTopicNto1ResponsePublish 257 5010656
Com_Periodic to publish Topic_1 1 2 3 3 15484 <-- Ada periodic component runs for
Com_Periodic exit Main <-- third time
In comC1-Main 1 5007328 <-- C comC1 periodic component runs
mT_TopicPeriodic_Reader 1 5007328 <-- for third time
in comC1_Verify_mT_TopicPeriodic
mT_TopicPeriodic_Verify 1 5007328
Foreign Verify Access_Key 243530864
mT_TopicPeriodic_Verify again 5007444
comC1_Verify_mT_TopicPeriodic after Verify 500744
publishing component 1 2 3
reference number & time 3 15484
mT_TopicPeriodic_Reader 1 5007328 243531292
comC1Main after Read 5007444
publishing component 1 2 3
reference number & time 3 15484
mTTopicNto1ResponseReader 257 5008288
in comC1VerifymTTopicNto1Response
mTTopicNto1ResponseVerify 257 5008288
Foreign Verify Access_Key 243530864
mTTopicNto1ResponseVerify again 5008628
comC1_Verify_mT_TopicNto1_Response after Verify 5
publishing component 3 7 0
reference number & time 2 35334
mTTopicNto1ResponseReader 257 5008288 243531292
comC1-Main after Response Read 5008628
comC1-Main Nto1 Response 3 7 0
comC1-Main Nto1 Response 2 35334
mTTopicNto1RequestWriter 257 5007744
Topic_Writer Queued 7 7 Topic Nto1 Request
mTTopicNto1RequestWriter again 5007860
comC1-Main after Request Writer 5007860
comC1-Main to publish Request 3 35339 9
mTTopicNto1RequestPublish 257 5007744
comC1_Main Writer for mT_Topic1ofN for 1234
In comC2_Nto1_Request 257
Topic_Writer Del Id 7 7 Topic 1ofN
mTTopicNto1Reader 257 5010272
comC1_Main to publish mT_Topic1ofN for 1234 6539
in comC2_Verify_mT_TopicNto1_Request
comC1-Main to check to publish CF Response 3 <-- check if content filtered
conditions met
mTTopicNto1RequestVerify 257 5010272
Foreign Verify Access_Key 277085296
mTTopicNto1RequestVerify again 5007860
comC2_Verify_mT_TopicNto1_Request after Verify 50
Request source 3 7 0
Request ref# time 3 35339
mTTopicNto1Reader 257 5010272 277085724
comC2_Nto1_Request after Reader 5007860
mTTopicNto1ResponseWriter 257 5010656
Topic_Writer Queued 8 8 Topic Nto1 Response
mTTopicNto1ResponseWriter 257 5010656 277085724
comC2_Nto1_Request after Response Writer 5008628
mTTopicNto1ResponsePublish 257 5010656
In comC2_1ofN
comC2_1ofN: id 1234 time 6539 publisher 3 7 0
Com_Periodic to publish Topic_1 1 2 3 4 20484 <-- Ada periodic component runs for
Com_Periodic exit Main <-- fourth time
In comC1-Main 1 5007328
mT_TopicPeriodic_Reader 1 5007328
in comC1_Verify_mT_TopicPeriodic
mT_TopicPeriodic_Verify 1 5007328
Foreign Verify Access_Key 243530864
mT_TopicPeriodic_Verify again 5007444
comC1_Verify_mT_TopicPeriodic after Verify 500744
publishing component 1 2 3
reference number & time 4 20484
mT_TopicPeriodic_Reader 1 5007328 243531292
comC1Main after Read 5007444
publishing component 1 2 3
reference number & time 4 20484
mTTopicNto1ResponseReader 257 5008288
in comC1VerifymTTopicNto1Response
mTTopicNto1ResponseVerify 257 5008288
Foreign Verify Access_Key 243530864
mTTopicNto1ResponseVerify again 5008628
comC1_Verify_mT_TopicNto1_Response after Verify 5
publishing component 3 7 0
reference number & time 3 35339
mTTopicNto1ResponseReader 257 5008288 243531292
comC1-Main after Response Read 5008628
comC1-Main Nto1 Response 3 7 0
comC1-Main Nto1 Response 3 35339
mTTopicNto1RequestWriter 257 5007744
Topic_Writer Queued 7 7 Topic Nto1 Request
mTTopicNto1RequestWriter again 5007860
comC1-Main after Request Writer 5007860
comC1-Main to publish Request 4 35344 9
mTTopicNto1RequestPublish 257 5007744
comC1_Main Writer for mT_Topic1ofN for 4321
In comC2_Nto1_Request 257
Topic_Writer Del Id 7 7 Topic 1ofN
mTTopicNto1Reader 257 5010272
comC1_Main no data buffer to write mT_Topic_1ofN
in comC2_Verify_mT_TopicNto1_Request
comC1-Main to check to publish CF Response 4 <-- content filtered conditions
mTTopicNto1RequestVerify 257 5010272
Topic_Writer Queued 7 8 Topic ContentFiltered Response
Foreign Verify Access_Key 277085296
comC1-Main to publish CF Response 4 <-- met for first time
mTTopicNto1RequestVerify again 5007860
comC2_Verify_mT_TopicNto1_Request after Verify 50
Request source 3 7 0
Request ref# time 4 35344
mTTopicNto1Reader 257 5010272 277085724
comC2_Nto1_Request after Reader 5007860
mTTopicNto1ResponseWriter 257 5010656
Topic_Writer Queued 8 8 Topic Nto1 Response
mTTopicNto1ResponseWriter 257 5010656 277085724
comC2_Nto1_Request after Response Writer 5008628
mTTopicNto1ResponsePublish 257 5010656
In comC2_Content_Filtered-Response <-- comC2 notified of content
comC2_ContentFiltered_Response: ref # 4 time 2048 <-- filtered as first time
comC2_ContentFiltered_Response: publisher 1 2 3 <-- conditions met
Com_Periodic to publish Topic_1 1 2 3 5 25484 <-- Ada periodic component runs for
Com_Periodic exit Main <-- fifth time
...