The following is the second portion of the Pseudo Visual
Compiler using Interface to Ada as the OFP - Part 5 post. Please read it first.
New Packages - Framework (continued)
The ReceiveInterface component receives, via its queue,
messages received from remote applications.
It then converts them back to Topic formatted messages and performs
various validations. Currently these
are only that the topic is a valid combination, that the message has the
correct length, and Heartbeat messages are valid. When CRCs are implemented they will be validated as received in
the particular Receive package. Eventually
various validations will be combined and the pipe will be closed if too many
invalid messages are received. (Such as
messages for which there is no consumer.)
For messages other than Heartbeats and the need to request
remote applications to forward any messages that they produce that the local
app has components that want to consume, Delivery is invoked to cause it to be
forwarded to the component or components that registered to consume it.
Other than Heartbeats, the special REGISTER REQUEST and
RESPONSE messages are treated in ReceiveInterface. The REGISTER REQUEST message is created when sufficient
Heartbeats have been received. This
message is created via Library SendRegisterRequest to include, as data, the
non-framework topics that components have registered to consume. This is even if there is already a local
producer of the topic since a remote app might not have a local consumer. When the REGISTER REQUEST message is
received by the ReceiveInterface of the remote application, it is forwarded to
Library to add the topics to those for which there are consumers.
Note: For other REQUEST topics only one consumer is allowed
since it is thought that only one component should be fulfilling the request
and the response is to be returned to the particular requestor. But this can't be enforced over multiple
applications. So if two applications
consume the same REQUEST topic there can be two responses.
Since ReceiveInterface receives byte messages rather than in
the normal Topic format, it uses a different queue – the DisburseBytes
queue. Also, unlike other components,
its Write interface is visible to other packages so that Delivery isn't
necessary. This is because it is a
framework component and only other framework components should be writing to
its queue – namely Receive. User
components cannot register to produce framework topics and would have no need
to deliver byte messages to ReceiveInterface which could only deliver them to
other user components for which the use of Delivery is better suited.
ReceiveInterface.ads
with Itf;
package ReceiveInterface is
-- Install
the ReceiveInterface framework package to treat Receive messages
function
Install
return
Itf.ParticipantKeyType;
-- Write a
message to the DisburseQueue from multiple Receive threads
function
DisburseWrite
( Message :
in Itf.BytesType
) return
Boolean;
end ReceiveInterface;
ReceiveInterface.adb
with Component;
with Delivery;
with DisburseBytes;
with Format;
with Library;
with Remote;
with System;
with Text_IO;
with Topic;
with Unchecked_Conversion;
package body ReceiveInterface is
package
Int_IO is new Text_IO.Integer_IO( Integer );
QueueName :
Itf.V_Short_String_Type
:= ( Count => 19,
Data =>
"ReceiveIntfaceQueue " );
Key :
Itf.ParticipantKeyType := Component.NullKey;
--
Component's key returned from Register
-- List of
messages
type
ReceivedMessageArrayType
is array
(1..10) of Itf.MessageType;
type
ReceivedMessageListTableType
is record
Count : Integer; -- number
of entries
NewlyAdded : Integer; -- number not yet Popped
List : ReceivedMessageArrayType;
end record;
-- Table of
received messages
MsgTable :
ReceivedMessageListTableType;
RequestTopic : Topic.TopicIdType;
package
DisburseQueue
--
Instantiate disburse queue for the component
is new
DisburseBytes( QueueName => QueueName'Address,
Periodic
=> False,
Universal => System.Null_Address,
Forward
=> System.Null_Address );
ReceiveInterfaceName : Itf.V_Medium_String_Type
:= ( Count
=> 16,
Data =>
"ReceiveInterface " );
Result :
Component.RegisterResult;
function
to_Callback is new Unchecked_Conversion
( Source => System.Address,
Target =>
Topic.CallbackType );
procedure
Main -- Threads callback
( T : in
Boolean := False );
-- Install
the ReceiveInterface framework package to treat Receive messages
function
Install
return
Itf.ParticipantKeyType is
Status :
Library.AddStatus;
use type
Component.ComponentStatus;
use type
Library.AddStatus;
begin --
Install
-- Note:
ReceiveInterface is not Transmit but it needs a way to register
-- its queue as well as have its higher
priority assigned.
Result :=
Component.RegisterTransmit
(
Name => ReceiveInterfaceName,
RemoteId => 0, -- not
sending to remote app
Callback =>
to_Callback(Main'Address),
Queue =>
DisburseQueue.Location,
QueueWrite => DisburseWrite'Address );
if
Result.Status = Component.VALID then
DisburseQueue.ProvideWaitEvent( Event => Result.Event );
Key :=
Result.Key;
RequestTopic.Topic := Topic.ANY;
RequestTopic.Ext :=
Topic.FRAMEWORK;
Status
:= Library.RegisterTopic( RequestTopic, Result.Key,
Delivery.CONSUMER,
to_Callback(Main'Address) );
if
Status /= Library.SUCCESS then
Text_IO.Put_Line( "ERROR: Register of Topic failed" );
end if;
end if;
return
Key;
end
Install;
-- The
methods to validate the received message and forward it are below.
-- These
methods execute in the ReceiveInterface thread via the Callback
-- forever
loop started by the event initiated by the signal to end the wait.
procedure
AnnounceError
(
RecdMessage : in Itf.BytesType
) is
Length : Integer :=
RecdMessage.Count;
I : Integer := 0;
ZeroCount
: Integer := 0;
ZeroStart
: Integer := 0;
use type
Itf.Byte;
begin --
AnnounceError
for J in
1..Length loop
if
RecdMessage.Bytes(J) = 0 then
ZeroCount := ZeroCount + 1;
else
ZeroCount := 0;
ZeroStart := J;
end if;
end loop;
while
Length > 0 loop
if I
> ZeroStart + 28 then
exit;
end if;
if
Length >= Integer(Itf.HeaderSize) then
Text_IO.Put("ERROR: ");
Int_IO.Put(Integer(RecdMessage.Bytes(I)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+1)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+2)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+3)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+4)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+5)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+6)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+7)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+8)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+9)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+10)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+11)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+12)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+13)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+14)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+15)));
Int_IO.Put(Integer(RecdMessage.Bytes(I+16)));
Text_IO.Put_Line(" ");
Length := Length - Integer(Itf.HeaderSize);
I :=
I + Integer(Itf.HeaderSize);
else
Length := 0;
end if;
end loop;
end AnnounceError;
-- Copy
message into table
procedure
CopyMessage
( M : in Integer;
RecdMessage : in Itf.BytesType
) is
Index :
Integer;
Size : Itf.Int16;
ReferenceNumber : Itf.Int32;
use type
Itf.Int16;
begin --
CopyMessage
MsgTable.Count := MsgTable.Count + 1;
Index :=
MsgTable.Count;
Size :=
Itf.Int16(RecdMessage.Bytes(M+1));
Size :=
Itf.Int16(256 * Integer(Size) + (Integer(RecdMessage.Bytes(M+2))));
MsgTable.List(Index).Header.CRC := Size;
MsgTable.List(Index).Header.Id.Topic
:= Topic.Id_Type'Val(RecdMessage.Bytes(M+3));
MsgTable.List(Index).Header.Id.Ext :=
Topic.Extender_Type'Val(RecdMessage.Bytes(M+4));
MsgTable.List(Index).Header.From.AppId :=
Itf.Int8(RecdMessage.Bytes(M+5));
MsgTable.List(Index).Header.From.ComId :=
Itf.Int8(RecdMessage.Bytes(M+6));
MsgTable.List(Index).Header.From.SubId :=
Itf.Int8(RecdMessage.Bytes(M+7));
MsgTable.List(Index).Header.To.AppId :=
Itf.Int8(RecdMessage.Bytes(M+8));
MsgTable.List(Index).Header.To.ComId :=
Itf.Int8(RecdMessage.Bytes(M+9));
MsgTable.List(Index).Header.To.SubId :=
Itf.Int8(RecdMessage.Bytes(M+10));
ReferenceNumber := Integer(RecdMessage.Bytes(M+11));
ReferenceNumber := 256 * ReferenceNumber + Integer(RecdMessage.Bytes(M+12));
ReferenceNumber := 256 * ReferenceNumber +
Integer(RecdMessage.Bytes(M+13));
ReferenceNumber := 256 * ReferenceNumber +
Integer(RecdMessage.Bytes(M+14));
MsgTable.List(Index).Header.ReferenceNumber := ReferenceNumber;
Size := Itf.Int16(RecdMessage.Bytes(M+15));
Size :=
256 * Size + Itf.Int16(RecdMessage.Bytes(M+16));
MsgTable.List(Index).Header.Size := Itf.Int16(Size);
MsgTable.List(Index).Data := (others => ' ');
for I in
1..Integer(Size) loop
declare
Pos : Integer :=
Integer(RecdMessage.Bytes(M+I+Integer(Itf.HeaderSize)));
Item
: Character;
for
Item use at Pos'Address;
begin
MsgTable.List(Index).Data(I) := Item;
end;
end loop;
end
CopyMessage;
procedure
ParseRecdMessages
(
RecdMessage : in Itf.BytesType
) is
M : Integer := 0;
Id : Topic.TopicIdType; -- topic;
Size :
Integer;
use type
Itf.Byte;
begin --
ParseRecdMessages
while M
<= RecdMessage.Count loop
if (M + Integer(Itf.HeaderSize)) <=
RecdMessage.Count then -- space for header
Id.Topic := Topic.Id_Type'val(RecdMessage.Bytes(M + 3));
Id.Ext := Topic.Extender_Type'val(RecdMessage.Bytes(M + 4));
if
Library.ValidPairing(Id) then
--
assuming if Topic Id is valid that the remaining data is as well
Size := Integer(RecdMessage.Bytes(M + 15)) * 256; -- 8 bit shift
Size := Size + Integer(RecdMessage.Bytes(M + 16)); -- data size
if
(M + Size + Integer(Itf.HeaderSize)) <= RecdMessage.Count then
-- space for message
CopyMessage(M, RecdMessage);
end
if;
M
:= M + Size + Integer(Itf.HeaderSize);
end
if;
else --
scan for another message
for N
in M..RecdMessage.Count loop
Id.Topic := Topic.Id_Type'val(RecdMessage.Bytes(N));
if
(N+1) >= RecdMessage.Count then
return; -- no space left
end
if;
Id.Ext := Topic.Extender_Type'val(RecdMessage.Bytes(N + 1));
if
Library.ValidPairing(Id) then
M
:= N;
exit; -- inner loop
end
if;
end
loop;
end if;
exit;
-- outer loop
end loop;
end
ParseRecdMessages;
--
Determine if 3 or more consecutive heartbeats have been received and
-- the
Register Request has been acknowledged or the needs to be sent.
procedure
TreatHeartbeatMessage
(
RemoteAppId : in Itf.Int8
) is
Acknowledged : Boolean;
ConsecutiveValid : Integer
:= Remote.ConsecutiveValidHeartbeats(RemoteAppId);
begin --
TreatHeartbeatMessage
Int_IO.Put(ConsecutiveValid);
Text_IO.Put_Line(" ");
if
ConsecutiveValid >= 3 then -- connection established
Remote.SetConnected(RemoteAppId, True);
Acknowledged
:= Remote.RegisterAcknowledged(RemoteAppId);
if not
Acknowledged and then
(ConsecutiveValid mod 3) = 0
then --
where only every 3rd time to allow acknowledged to be set
Library.SendRegisterRequest(RemoteAppId);
end if;
end if;
end
TreatHeartbeatMessage;
-- Validate
any heartbeat message.
-- Notes: A
heartbeat message must identify that it is meant for this
-- application and originated in the
remote application for
-- which this instantiation of the
Receive thread is responsible.
function
HeartbeatMessage
(
RecdMessage : in Itf.MessageType;
RemoteAppId : in Itf.Int8
) return
Boolean is
HeartbeatMessage : Boolean := False;
ConsecutiveValid : Integer
:=
Remote.ConsecutiveValidHeartbeats(RemoteAppId);
begin --
HeartbeatMessage
HeartbeatMessage := Format.DecodeHeartbeatMessage(RecdMessage,
RemoteAppId);
if
HeartbeatMessage then
ConsecutiveValid := ConsecutiveValid + 1;
else
ConsecutiveValid := 0;
end if;
Remote.ConsecutiveValidHeartbeats( RemoteAppId, ConsecutiveValid );
-- Return
whether a Heartbeat message; whether or not valid.
return
HeartbeatMessage;
end
HeartbeatMessage;
--
Non-Heartbeat Messages have to be messages formatted as framework topic
--
messages. Otherwise, they will be
discarded. These topic messages will
-- be
forwarded to the component(s) that has/have registered to consume them.
procedure
ForwardMessage
( Message :
in Itf.MessageType
) is
use type
Topic.Extender_Type;
use type
Topic.Id_Type;
begin --
ForwardMessage
-- Check
if a framework Register message.
if
Message.Header.Id.Topic = Topic.REGISTER then -- Check if acknowledge
if
Message.Header.Id.Ext = Topic.RESPONSE then
Remote.SetRegisterAcknowledged(Message.Header.From.AppId, True);
else --
register Request message
Library.RegisterRemoteTopics(Message.Header.From.AppId, Message);
end if;
else --
Forward other messages
Delivery.Publish(Message);
end if;
end
ForwardMessage;
procedure
Main -- callback
( T : in
Boolean := False
) is
RecdMessage : Itf.BytesType;
begin --
Main
Text_IO.Put_Line("in ReceiveInterface callback");
loop --
forever
DisburseQueue.EventWait; -- wait for event
loop --
until queue is empty
if
DisburseQueue.Unread then
RecdMessage := DisburseQueue.Read;
if
RecdMessage.Count > 0 then
-- message to be converted and treated
if RecdMessage.Count >= Integer(Itf.HeaderSize) then -- message can
have a
declare –- header
TopicId : Topic.TopicIdType;
Valid : Boolean;
begin
TopicId.Topic := Topic.Id_Type'val(RecdMessage.Bytes(3));
TopicId.Ext := Topic.Extender_Type'val(RecdMessage.Bytes(4));
Valid := Library.ValidPairing(TopicId);
if not Valid then
AnnounceError(RecdMessage);
else -- Convert received message(s) to topic messages.
MsgTable.Count := 0;
ParseRecdMessages(recdMessage);
if MsgTable.Count > 0 then
for M in 1..MsgTable.Count loop
declare
Msg : Itf.MessageType;
for Msg use at MsgTable.List(M)'address;
use type Topic.Extender_Type;
use type Topic.Id_Type;
begin
if ((Msg.Header.Id.Topic = Topic.HEARTBEAT)
and then
(Msg.Header.Id.Ext = Topic.FRAMEWORK))
then
if
HeartbeatMessage(Msg,Msg.Header.From.AppId) then
TreatHeartbeatMessage(Msg.Header.From.AppId);
else
Remote.SetConnected(Msg.Header.From.AppId,False);
end if;
else
ForwardMessage(Msg);
end if;
end;
end loop;
end if; -- MsgTable.Count > 0
end if; -- valid pairing
end; -- declare block
end if; -- Length large enough
end
if; -- RecdMessage.Length > 0
else
-- DisburseQueue has no messages to read
exit; -- the inner loop
end
if; -- DisburseQueue.Unread
end
loop; -- until queue empty
end loop;
-- forever
end Main;
-- Write a
message to the DisburseQueue from multiple Receive threads
-- Note:
Doing this write will queue the message in the thread of the
-- particular Receive thread. The forever loop will then have its
-- wait signaled to continue and the queue
can be read in the thread
-- of this component to treat the message
and deliver it.
function
DisburseWrite
( Message :
in Itf.BytesType
) return
Boolean is
begin --
DisburseWrite
return
DisburseQueue.Write(Message);
end
DisburseWrite;
end ReceiveInterface;
Modified Packages from previous posts
The Itf package spec has some additions while the package
body is unchanged.
Itf.ads
with Topic;
package Itf is
type
Int8 is new Integer range -2**7+1..2**7-1;
for
Int8'Size use 8; -- bits
type Int16
is new Integer range -2**15+1..2**15-1;
for
Int16'Size use 16; -- bits
subtype
Int32 is Integer range -2**31+1..2**31-1;
type
Interface_Integer
is range
-(2 ** 31) .. (2 ** 31) - 1;
type
Nat8 is new Natural range 0..2**8-1;
for
Nat8'Size use 8;
type Nat32
is new Natural;
type Byte
-- 8-bit
byte
is mod
2**8;
for
Byte'Size use 8;
type Word
-- 16-bit
word
is mod
2**16;
for
Word'Size use 16;
type
Longword
-- 32-bit
longword
is range
-(2**31) .. 2**31 - 1;
for
Longword'Size use 32;
type
ByteArray
--
Unconstrained array of bytes
is array
(Integer range <>) of Byte;
type ApplicationIdType
is
record
Name :
String(1..10);
Id : Int8;
end record;
type
ApplicationNameType
--|
Character string identifying the hosted function application
is new
String(1..10);
ApplicationId : Itf.ApplicationIdType; -- the local AppId
-- Possible
methods of inter-application communications
type
CommMethodType
is (
NONE, -- Topic added to the library
MS_PIPE, -- Topic already added for the component
TCP_IP -- Topic not added
);
Configuration_App_Path_Max
-- Maximum
length of application path
: constant
Integer := 150;
type
V_Short_String_Type
is record
Count :
Integer;
Data : String(1..20);
end record;
type
V_Medium_String_Type
is record
Count :
Integer;
Data : String(1..50);
end record;
type
V_Long_String_Type
is record
Count :
Integer;
Data : String(1..Configuration_App_Path_Max);
end record;
type
Message_Size_Type
-- Number
of total message bytes in any protocol message
is new
Natural range 0..4096;
subtype
Message_Data_Count_Type
-- Number
of message data bytes in topic protocol message
is
Message_Size_Type
range
Message_Size_Type'first..4096;
type
Message_Buffer_Type
-- Message
buffer for remote messages
is array(
1..Message_Data_Count_Type'last ) of Byte;
Message_Size
--| Maximum
message size; Header and data
: constant
:= 4096; -- bytes
Message_Alignment
-- Byte
boundary at which to align message header and topic buffer
: constant
:= 4;
type
GenericMessageType is private;
--
Identifier of component
type
ParticipantKeyType
is record
AppId :
Int8; -- application identifier
ComId :
Int8; -- component identifier
SubId :
Int8; -- subcomponent identifier
end record;
type
HeaderType
is record
CRC : Int16; -- message CRC
Id : Topic.TopicIdType; -- topic of the message
From :
ParticipantKeyType; -- publishing component
To : ParticipantKeyType; -- consumer component
ReferenceNumber : Int32; --
reference number of message
Size :
Int16; -- size of data
portion of message
end record;
for
HeaderType
use record
CRC at
0 range 0..15;
Id at
2 range 0..15;
From at
4 range 0..23;
To at
7 range 0..23;
ReferenceNumber at 10 range 0..31;
Size at 14 range 0..15;
end record;
HeaderSize
: constant
Int16 := 16;
-- A
message consists of the header data and the actual data of the message
type
MessageType
is record
Header :
HeaderType;
Data
: String(1..4080);
end record;
MessageSize
: constant Integer := 250;
-- A
message as received by NamedPipe to be queued to ReceiveInterface
type
BytesType
is record
Count :
Integer; -- number of bytes in message
Bytes :
Itf.ByteArray(1..MessageSize);
end record;
-- Callback
types of instantiation of NamedPipe
type
ReceiveOpenCallbackType
-- Callback
to open a receive pipe
is access
function
(
RemoteAppId : in Int8
--
Receive pipe to be opened
) return
Boolean;
type
ReceiveCallbackType
-- Callback
to execute receive message to be transmitted
is access
procedure
( Pair : in Int8;
-- Pair
index of receive pipe
Message :
out BytesType
--
Received Message to be returned
);
type
TransmitCallbackType
-- Callback
to execute receive message to be transmitted
is access
procedure
( Message :
in BytesType
--
Message to be transmitted
);
--
Declarations for Forward Table to be used by an instantiation of Disburse
type
ForwardType
-- Callback
to forward message to component message callback
is access
procedure
(
Message : in MessageType );
type
DisburseDataType
is record
TopicId :
Topic.TopicIdType;
Forward :
ForwardType;
end record;
type
DisburseDataArrayType
is
array(1..10) of DisburseDataType;
-- Table of
topics to disburse to their callback
type
DisburseTableType
is record
Count :
Integer;
List : DisburseDataArrayType;
end record;
NullMessage
: MessageType;
procedure Initialize;
private
type
GenericMessageType
-- Message
of any protocol
is array(
1..Message_Size ) of Byte;
for
GenericMessageType'alignment use Message_Alignment;
end Itf;
Component has changes to have special Register methods for
Receive and Transmit components and functions to forward particular Delivery
messages to Write to the queue of the component to take delivery of the
message. The component table and the
types that it needs are visible which should most likely be hidden. The table is used in Threads but the
necessary data could be returned via a function.
Component.ads
with Configuration;
with ExecItf;
with Itf;
with Topic;
with Threads;
with System;
package Component is
MaxUserComponents
-- Maximum
allowed number of user (non-framework) components
: constant
Integer := 8;
MaxComponents
: constant
Integer := 8 + (2 * (Configuration.MaxApplications - 1));
NullKey :
Itf.ParticipantKeyType;
type
ComponentStatus
is ( NONE,
VALID,
DUPLICATE,
INVALID,
INCONSISTENT,
INCOMPLETE
);
type
ComponentKind
is ( USER,
FRAMEWORK,
RECEIVE, -- special framework
component
TRANSMIT -- special framework
component
);
type
ComponentSpecial
is (
NORMAL,
RECEIVE,
RECEIVEINTERFACE,
TRANSMIT
);
type
RegisterResult
is record
Status :
ComponentStatus;
Key : Itf.ParticipantKeyType;
Event : ExecItf.HANDLE;
end record;
type
DisburseWriteCallback
-- Callback
to execute the Write function of a participant component's
-- Disburse
queue
is access
function
( Message :
in Itf.MessageType
--
Message to be written to the queue
) return
Boolean; -- indicates if Write was successful
--
Component data from registration as well as run-time status
type
ComponentDataType
is record
Kind : ComponentKind;
--
Whether user component or a framework component
Name : Itf.V_Medium_String_Type;
--
Component name
Key : Itf.ParticipantKeyType;
--
Component key (application and component identifiers)
RemoteAppId : Itf.Int8;
-- Remote
application for transmit
Period : Integer;
--
Periodic interval in milliseconds; 0 if only message consumer
Priority : Threads.ComponentThreadPriority;
--
Requested priority for component
fMain : Topic.CallbackType;
-- Main
entry point of the component
WaitEvent : ExecItf.HANDLE;
-- Wait
Event associated component
Queue : System.Address;
--
Message queue of the component
QueueWrite :
DisburseWriteCallback;
--
Callback to Write to the component's queue
Special : ComponentSpecial;
--
Special processing
end record;
type
ComponentDataArrayType is array (1..MaxComponents) of ComponentDataType;
-- List of
components
type
ComponentTableType
is record
AllowComponentRegistration : Boolean;
-- True
indicates that components are allowed to register themselves
Count :
Integer;
-- Number
of registered components of the application
List :
ComponentDataArrayType;
--
Registration supplied data concerning the component as well as
--
run-time status data
end record;
--
Component table containing registration data as well as run-time status data
-- Note: I
would like to keep this table hidden from components but I don't
-- know how to structure C# so that
classes of a certain kind (that is,
-- App, Component, Threads, etc) aren't directly
visible to components
-- such as ComPublish.
-- Note:
There must be one creation of a new table.
Only one instance.
ComponentTable : ComponentTableType;
-- true if
Component class has been initialized
ComponentInitialized : Boolean := False;
--
Determine if two components are the same
function
CompareParticipants
( Left : in Itf.ParticipantKeyType;
Right :
in Itf.ParticipantKeyType
) return
Boolean;
-- Forward
Message to Callback of Component
function
DisburseWrite
(
ComponentKey : in Itf.ParticipantKeyType;
--
Component for message delivery
Message : in Itf.MessageType
--
Message to be delivered
) return
Boolean; -- true indicates successful write to queue
-- Forward
Message to instantiation of Transmit package to send to remote app
function
TransmitWrite
(
RemoteAppId : in Itf.Int8;
--
Transmit component to send Message
Message : in Itf.MessageType
--
Message to be delivered
) return
Boolean; -- true indicates successful write to queue
procedure
Initialize;
-- Register
User Component
function
Register
( Name : in Itf.V_Medium_String_Type; -- name
of component
RemoteId : in Itf.Int8 :=
0; -- remote id for transmit
Period : in Integer; -- # of
millisec at which Main() function to cycle
Priority : in
Threads.ComponentThreadPriority; -- Requested priority of thread
Callback : in
Topic.CallbackType; -- Callback function of component
Queue : in System.Address; -- message queue of component
QueueWrite : in System.Address
-- queue Write function address
) return
RegisterResult;
-- Register
Receive Component
function
RegisterReceive
( Name : in Itf.V_Medium_String_Type; -- name of
component
Callback
: in Topic.CallbackType --
Callback function of component
) return
RegisterResult;
-- Register
Transmit Component
function
RegisterTransmit
( Name : in Itf.V_Medium_String_Type; -- name
of component
RemoteId : in Itf.Int8; -- remote app to transmit to
Callback : in
Topic.CallbackType; -- Callback
function of component
Queue : in
System.Address; -- message
queue of component
QueueWrite : in System.Address -- queue Write function address
) return
RegisterResult;
end Component;
Component.adb
with CStrings;
with System;
with Text_IO;
with Unchecked_Conversion;
package body Component is
package
Int_IO is new Text_IO.Integer_IO( Integer );--debug
Kind :
ComponentKind := USER; -- can be overridden by Register of Receive, etc
-- Find the
index into the registered Application table of the currently
-- running
application and return it.
function
ApplicationIndex
return
Itf.Int8 is
Index :
Itf.Int8; -- Index of hosted function application in Application table
use type
Itf.Int8;
begin --
ApplicationIndex
-- Find
index to be used for hosted function application processor
Index :=
Itf.ApplicationId.Id;
if Index
= 0 then
Text_IO.Put_Line("ERROR: Application Index doesn't exist");
end if;
return
index;
end
ApplicationIndex;
function
CompareParticipants
( Left : in Itf.ParticipantKeyType;
Right :
in Itf.ParticipantKeyType
) return
Boolean is
use type
Itf.Int8;
begin --
CompareParticipants
--
Determine if two components are the same
if
((Left.AppId = Right.AppId) and then
(Left.ComId = Right.ComId) and then
(Left.SubId = Right.SubId))
then
return
True;
else
return
False;
end if;
end
CompareParticipants;
function
DisburseWrite
(
ComponentKey : in Itf.ParticipantKeyType;
Message : in Itf.MessageType
) return
Boolean is
begin --
DisburseWrite
for I in
1..ComponentTable.Count loop
if
CompareParticipants(ComponentTable.List(I).Key, ComponentKey) and then
ComponentTable.List(I).QueueWrite /= null
then
return ComponentTable.List(I).QueueWrite(Message);
end if;
end loop;
Text_IO.Put("Failed to find Component for ");
Text_IO.Put_Line(Message.Data(1..2));
return
False; -- no component to which to write the message
end
DisburseWrite;
-- Forward
Message to instantiation of Transmit package to send to remote app
function
TransmitWrite
(
RemoteAppId : in Itf.Int8;
Message : in Itf.MessageType
) return
Boolean is
Success :
Boolean;
use type
Itf.Int8;
begin --
TransmitWrite
if
RemoteAppId /= 0 then
for I
in 1..ComponentTable.Count loop
if
ComponentTable.List(I).RemoteAppId = RemoteAppId then
return ComponentTable.List(I).QueueWrite(Message);
end
if;
end
loop;
return
False; -- no Transmit component to send message to remote app
else --
forward to all Transmit components
for I
in 1..ComponentTable.Count loop
if
ComponentTable.List(I).Kind = TRANSMIT then
Success := ComponentTable.List(I).QueueWrite(Message);
end
if;
end
loop;
return
True;
end if;
end
TransmitWrite;
procedure
Initialize is
begin --
Initialize
NullKey.AppId := 0;
NullKey.ComId := 0;
NullKey.SubId := 0;
ComponentTable.Count := 0;
ComponentTable.AllowComponentRegistration := False;
end Initialize;
-- Look up
the Name in the registered component and return the index of where
-- the data
has been stored. Return zero if the
Name is not in the list.
function
Lookup
( Name :
in Itf.V_Medium_String_Type
) return
Integer is
App : Itf.Int8;
-- Application id
Idx :
Integer; -- Index of component in
registry
CompareName : String(1..Name.Count+1);
begin --
Lookup
App :=
ApplicationIndex;
CompareName(1..Name.Count) := Name.Data(1..Name.Count);
CompareName(Name.Count+1) := ASCII.NUL;
Idx :=
0;
for I in
1..ComponentTable.Count loop
declare
TableName : String(1..ComponentTable.List(I).Name.Count+1);
begin
if
Name.Count = ComponentTable.List(I).Name.Count then
TableName(1..Name.Count) :=
ComponentTable.List(I).Name.Data(1..Name.Count);
TableName(Name.Count+1) := ASCII.NUL;
if
CStrings.Compare( Left =>
CompareName'Address,
Right => TableName'Address,
IgnoreCase => True ) = 0
then
Idx := I;
exit; -- loop
end if;
end
if;
end;
end
loop;
-- Return
the index.
return
Idx;
end Lookup;
--
Increment the identifier of the component key and then return it with
-- the
application identifier as the next available component key.
function
NextComponentKey
return
Itf.ParticipantKeyType is
App : Itf.Int8; -- Index of current
application
ReturnApp
: Itf.ParticipantKeyType;
begin --
NextComponentKey
App :=
ApplicationIndex;
if
ComponentTable.Count < MaxComponents then
ComponentTable.Count := ComponentTable.Count + 1;
ReturnApp.AppId := App;
ReturnApp.ComId :=
Itf.Int8(ComponentTable.Count);
ReturnApp.SubId := 0;
return
ReturnApp;
else
Text_IO.Put_Line("ERROR: More components than can be
accommodated");
return
NullKey;
end if;
end
NextComponentKey;
function
Register
( Name : in Itf.V_Medium_String_Type; -- name
of component
RemoteId : in Itf.Int8 :=
0; -- remote id for transmit
Period : in Integer; -- # of
millisec at which Main function to cycle
Priority : in Threads.ComponentThreadPriority;
-- Requested priority of thread
Callback : in
Topic.CallbackType; -- Callback function of component
Queue : in System.Address;
-- Disburse.QueuePtrType
QueueWrite : in System.Address
-- message queue Write function of component
) return
RegisterResult is
App : Itf.Int8; -- Index of current
application
CIndex : Integer; -- Index of component; 0 if not found
Location
: Integer; -- Location of component in
the registration table
NewKey : Itf.ParticipantKeyType;
-- component key of new component
Result : RegisterResult;
function
to_Write_Ptr is new Unchecked_Conversion
( Source => System.Address,
Target => DisburseWriteCallback
);
use type
Topic.CallbackType;
begin --
Register
Result.Status := NONE; -- unresolved
Result.Key := NullKey;
Result.Event :=
System.Null_Address;
NewKey :=
NullKey;
-- Find
index to be used for application
App :=
ApplicationIndex;
-- Look
up the component in the Component Table
CIndex :=
Lookup(Name);
-- Return
if component has already been registered
if CIndex
> 0 then -- duplicate registration
Result.Status := DUPLICATE;
return
Result;
end if;
-- Return
if component is periodic but without a Main entry point.
if Period
> 0 then
if
Callback = null then
Result.Status := INVALID;
return Result;
end if;
end if;
-- Add
new component to component registration table.
--
-- First obtain the new table location and set
the initial values.
NewKey :=
NextComponentKey;
Location
:= ComponentTable.Count;
ComponentTable.List(Location).Kind := Kind;
ComponentTable.List(Location).Name := Name;
Kind :=
USER; -- clear Kind for next Register
declare
EventName : String(1..Name.Count+1);
package
Int_IO is new Text_IO.Integer_IO( Integer );
function to_Int is new Unchecked_Conversion( Source => ExecItf.HANDLE,
Target =>
Integer );
begin
EventName(1..Name.Count) := Name.Data(1..Name.Count);
EventName(Name.Count+1) := ASCII.NUL; -- terminating NUL
Result.Event := ExecItf.CreateEvent( ManualReset => True,
InitialState =>
False,
Name => EventName'Address );
Text_IO.Put("EventName ");
Text_IO.Put(EventName);
Text_IO.Put(" ");
Int_IO.Put(to_Int(Result.Event));
Text_IO.Put_Line(" ");
end;
ComponentTable.List(Location).Key := NewKey;
ComponentTable.List(Location).RemoteAppId := RemoteId;
ComponentTable.List(Location).Period := Period;
ComponentTable.List(Location).Priority := Priority;
ComponentTable.List(Location).fMain := Callback;
ComponentTable.List(Location).WaitEvent := Result.Event;
ComponentTable.List(Location).Queue := Queue;
ComponentTable.List(Location).QueueWrite := to_Write_Ptr(QueueWrite);
-- Return
status and the assigned component key.
Result.Status := VALID;
Result.Key := NewKey;
return
Result;
end
Register;
-- Register
Receive Component
function
RegisterReceive
( Name : in Itf.V_Medium_String_Type; -- name of
component
Callback
: in Topic.CallbackType --
Callback function of component
) return
RegisterResult is
begin --
RegisterReceive
Kind :=
RECEIVE;
return
Register( Name => Name,
Period => 0,
Priority => Threads.HIGH,
Callback => Callback,
Queue =>
System.Null_Address,
QueueWrite => System.Null_Address );
end
RegisterReceive;
-- Register
Transmit Component
function
RegisterTransmit
( Name : in Itf.V_Medium_String_Type; -- name
of component
RemoteId : in Itf.Int8; -- remote app to transmit to
Callback : in
Topic.CallbackType; -- Callback
function of component
Queue : in
System.Address; -- message
queue of component
QueueWrite : in System.Address -- queue Write function address
) return
RegisterResult is
use type
Itf.Int8;
begin --
RegisterTransmit
if RemoteId = 0 then
Kind :=
FRAMEWORK;
else
Kind :=
TRANSMIT;
end if;
return
Register( Name => Name,
RemoteId => RemoteId,
Period => 0,
Priority => Threads.HIGH,
Callback => Callback,
Queue => Queue,
QueueWrite => QueueWrite );
end
RegisterTransmit;
end Component;
Delivery has some minor changes from the last time
published.
Delivery.ads
with Component;
with Itf;
with Topic;
package Delivery is
type
DistributionType
is (
CONSUMER,
PRODUCER
);
procedure
Initialize;
--
Initialize Delivery package
procedure
Publish
( Message :
in Itf.MessageType );
-- Re-Publish
message received from Remote via ReceiveInterface
procedure
Publish
(
RemoteAppId : in Itf.Int8;
Message : in out
Itf.MessageType );
-- Publish
message to Remote such as Register Request
procedure
Publish
(
TopicId : in Topic.TopicIdType;
ComponentKey : in Itf.ParticipantKeyType;
Message : in String );
-- Publish
local message except for Response message
procedure
Publish
(
TopicId : in Topic.TopicIdType;
ComponentKey : in Itf.ParticipantKeyType;
From : in Itf.ParticipantKeyType;
Message : in String );
-- Publish
local Response message to specify the source of Request message
end Delivery;
Delivery.adb
with Disburse;
with Library;
with Remote;
with System;
with Text_IO;
with Unchecked_Conversion;
package body Delivery is
package
Int_IO is new Text_IO.Integer_IO( Integer ); -- for debug
ReferenceNumber : Itf.Int32; -- ever increasing message reference number
type
QueueCallbackType
-- Callback
to execute a Queue Write
is access
function
( Message :
in Itf.MessageType
) return
Boolean;
function
to_Ptr is new Unchecked_Conversion
( Source => System.Address,
Target => QueueCallbackType );
procedure
Initialize is
begin --
Initialize
ReferenceNumber := 0; -- ever increment for each message
end
Initialize;
procedure
PublishResponseToRequestor
(
TopicId : in Topic.TopicIdType;
Consumers
: in out Library.TopicTableType;
Msg : in Itf.MessageType
) is
Found :
Boolean := False;
use type
System.Address;
begin --
PublishResponseToRequestor
for I in
1..Consumers.Count loop
if
Component.CompareParticipants( Msg.Header.To,
Consumers.List(I).ComponentKey
)
then --
return response to the requestor
Consumers.List(I).ReferenceNumber := 0;
Found
:= Component.DisburseWrite( Consumers.List(I).ComponentKey,
Msg );
end if;
end loop;
if not
Found then
Text_IO.Put_Line("ERROR: Delivery couldn't find requestor for
response");
end if;
end
PublishResponseToRequestor;
-- Remote
messages are to be ignored if the From and To components are
-- the same
since this would transmit the message back to the remote app.
-- Remote
messages are only to be forwarded to the To component and not
-- to all
the components of the consumers list since separate messages
-- are sent
by the remote component for each local consumer.
function
Ignore
(
Message : in Itf.MessageType;
ComponentKey : in Itf.ParticipantKeyType
) return
Boolean is
Equal :
Boolean;
use type
Itf.Int8;
begin --
Ignore
Equal :=
Component.CompareParticipants
( Message.Header.From,
Message.Header.To);
if Equal
and then Message.Header.To.AppId /= Itf.ApplicationId.Id then
-- same
from and to component and remote message
return
True;
end if;
if
Message.Header.From.AppId /= Itf.ApplicationId.Id then
--
remote message; check if consumer component is 'to' participant
if not
Component.CompareParticipants
( Message.Header.To, ComponentKey )
then
return True;
end if;
end if;
return False;
end Ignore;
-- Remote
messages are to be ignored if the From and To components are
-- the same
since this would transmit the message back to the remote app.
-- Remote
messages are only to be forwarded to the To component and not
-- to all
the components of the consumers list since separate messages
-- are sent
by the remote component for each local consumer.
function
Ignore
( To : in Itf.ParticipantKeyType;
From : in Itf.ParticipantKeyType;
ComponentKey : in Itf.ParticipantKeyType
) return
Boolean is
Equal :
Boolean;
use type
Itf.Int8;
begin --
Ignore
Equal :=
Component.CompareParticipants(From, To);
if Equal
and then To.AppId /= Itf.ApplicationId.Id then
-- same
from and to component and remote message
return
True;
end if;
if
From.AppId /= Itf.ApplicationId.Id and then -- from is remote
ComponentKey.AppId = Itf.ApplicationId.Id -- component is local
then --
remote message; check if consumer component is 'to' participant
if not
Component.CompareParticipants(To, ComponentKey) then
return True;
end if;
end if;
return
False;
end Ignore;
procedure
Publish
( Message :
in Itf.MessageType
) is
Consumers
: Library.TopicTableType;
Found : Boolean;
use type
Itf.Int8;
use type
System.Address;
use type
Topic.Extender_Type;
use type
Topic.Id_Type;
begin --
Publish
-- Get
the set of consumers of the topic
Consumers
:= Library.TopicConsumers(Message.Header.Id);
if
Message.Header.Id.Ext = Topic.REQUEST then
--
forward the request topic to its consumer
for I
in 1..Consumers.Count loop
if
Message.Header.Id.Topic = Consumers.List(I).Id.Topic then
--
the only possible consumer of the request topic
Consumers.List(I).Requestor := Message.Header.From;
Consumers.List(I).ReferenceNumber := Message.Header.ReferenceNumber;
Found := Component.DisburseWrite( Consumers.List(I).ComponentKey,
Message );
if
not Found then
Text_IO.Put
("ERROR: remote Request Delivery couldn't find queue for consumer
");
Int_IO.Put(Integer(Message.Header.From.AppId));
Text_IO.Put(" ");
Int_IO.Put(Integer(Message.Header.From.ComId));
Text_IO.Put(" ");
Int_IO.Put(Integer(Message.Header.To.AppId));
Text_IO.Put(" ");
Int_IO.Put(Integer(Message.Header.To.ComId));
Text_IO.Put(" Topic ");
Int_IO.Put(Integer(Topic.Id_Type'pos(Message.Header.Id.Topic)));
Text_IO.Put(" ");
Int_IO.Put(Integer(Topic.Extender_Type'pos(Message.Header.Id.Ext)));
Text_IO.Put_Line(" ");
else
return; -- can only be one consumer
end
if;
end
if;
end
loop;
elsif
Message.Header.Id.Ext = Topic.RESPONSE then
--
forward the response topic to the request publisher
for I
in 1..Consumers.Count loop
if
Message.Header.Id.Topic = Consumers.List(I).Id.Topic and then
Component.CompareParticipants(Consumers.List(I).ComponentKey,
Message.Header.To)
then
-- found the publisher of the Request
Found := Component.DisburseWrite( Consumers.List(I).ComponentKey,
Message );
if
not Found then
Text_IO.Put_Line(
"ERROR: Remote Response Delivery couldn't find queue for
consumer");
end
if;
Exit; -- loop
end
if;
end
loop;
else --
Default topic - forward to possible multiple consumers
for I
in 1..Consumers.Count loop
if
Message.Header.Id.Topic = Topic.HEARTBEAT then
null;
end
if;
--
Avoid sending topic back to the remote app that transmitted it to
--
this app or forwarding a remote message that is to be delivered to
-- a
different component.
if
Consumers.List(I).Id.Topic = Message.Header.Id.Topic and then
Ignore(Message.Header.To, Message.Header.From,
Consumers.List(I).ComponentKey)
then
null;
else
-- Deliver message to local application by copying to its queue
Consumers.List(I).Requestor := Message.Header.From;
Consumers.List(I).ReferenceNumber := 0;
if
Consumers.List(I).ComponentKey.AppId = Itf.ApplicationId.Id then
Found := Component.DisburseWrite( Consumers.List(I).ComponentKey,
Message );
if not Found then
Text_IO.Put_Line("ERROR: Remote default Delivery couldn't find
queue for consumer");
Int_IO.Put(Integer(Message.Header.From.AppId));
Text_IO.Put(" ");
Int_IO.Put(Integer(Message.Header.From.ComId));
Text_IO.Put(" ");
Int_IO.Put(Integer(Message.Header.To.AppId));
Text_IO.Put(" ");
Int_IO.Put(Integer(Message.Header.To.ComId));
Text_IO.Put(" Topic ");
Int_IO.Put(Integer(Topic.Id_Type'pos(Message.Header.Id.Topic)));
Text_IO.Put(" ");
Int_IO.Put(Integer(Topic.Extender_Type'pos(Message.Header.Id.Ext)));
Text_IO.Put_Line(" ");
end if;
end
if;
end
if; -- Ignore
end
loop;
end if;
end
Publish; -- (from remote)
procedure
Publish
(
RemoteAppId : in Itf.Int8;
Message : in out Itf.MessageType
) is
Found :
Boolean;
begin --
Publish
--
Forward Message to instance of Transmit to send to the Remote App
Found :=
Component.TransmitWrite( RemoteAppId,
Message );
end
Publish;
procedure
Publish
(
TopicId : in Topic.TopicIdType;
ComponentKey : in Itf.ParticipantKeyType;
Message : in String
) is
begin --
Publish
--
forward for treatment
Publish(TopicId, ComponentKey, Component.NullKey, Message);
end
Publish;
procedure
Publish
(
TopicId : in Topic.TopicIdType;
ComponentKey : in Itf.ParticipantKeyType;
From : in Itf.ParticipantKeyType;
Message : in String
) is
Consumers
: Library.TopicTableType;
Found : Boolean;
Length : Integer := 0;
RequestConsumers : Library.TopicTableType;
RequestTopic : Topic.TopicIdType;
Msg :
Itf.MessageType;
use type
Itf.Int8;
use type
Itf.Int16;
use type
System.Address;
use type
Topic.Extender_Type;
begin --
Publish
--
Increment the reference number associated with all new messages
ReferenceNumber := ReferenceNumber + 1;
--
Initialize an instance of a message
Msg.Header.CRC := 0;
Msg.Header.Id := TopicId;
Msg.Header.From
:= ComponentKey;
Msg.Header.To := From;
Msg.Header.ReferenceNumber := ReferenceNumber;
Found :=
False;
for I in
1..Itf.Int16(Message'Length) loop
Length
:= Integer(I);
Msg.Data(Length) := Message(Length);
Msg.Header.Size := I; -- in case there is no NUL
if
Message(Length) = ASCII.NUL then
Found
:= True;
Msg.Header.Size := I-1;
exit;
-- loop
end if;
end loop;
if not
Found then -- need to add trailing NUL in case message sent to C#
Msg.Data(Length+1) := ASCII.NUL;
end if;
-- Get
the set of consumers of the topic
Consumers
:= Library.TopicConsumers(TopicId);
RequestTopic := TopicId;
if
TopicId.Ext = Topic.RESPONSE then -- the message has to be delivered
-- to the particular requestor
-- Get
the consumer of the request topic
RequestTopic.Ext := Topic.REQUEST;
RequestConsumers := Library.TopicConsumers(RequestTopic);
if
Component.CompareParticipants(Msg.Header.To, Component.NullKey) then
Text_IO.Put_Line("ERROR: No 'To' address for Response");
return;
end if;
if
Msg.Header.To.AppId /= Itf.ApplicationId.Id then
--
send to remote application
Publish(Msg.Header.To.AppId,
Msg);
return;
end if;
PublishResponseToRequestor(TopicId, Consumers, Msg);
elsif
TopicId.Ext = Topic.REQUEST then -- only one consumer possible
if
Consumers.Count > 0 then
--
forward request to the lone consumer of request topic
Msg.Header.To := Consumers.List(1).ComponentKey;
Consumers.List(1).Requestor := ComponentKey;
Consumers.List(1).ReferenceNumber := ReferenceNumber;
if
Msg.Header.To.AppId /= Itf.ApplicationId.Id then
--
send to remote app
Publish(Msg.Header.To.AppId, Msg);
else
-- forward to local consumer
Found := Component.DisburseWrite( Consumers.List(1).ComponentKey,
Msg );
if
not Found then
Text_IO.Put_Line("ERROR: Delivery didn't have queue for
request");
end
if;
end
if;
else
Text_IO.Put_Line("ERROR: Delivery couldn't find consumer for
request");
end if;
else --
the published topic has to be the Default - can be multiple consumers
for I
in 1..Consumers.Count loop
Msg.Header.To := Consumers.List(I).ComponentKey;
--
Avoid sending topic back to the remote app that transmitted it to
-- this app or forwarding a remote message
that is to be delivered to
-- a
different component.
if
Ignore(Msg, Consumers.List(I).ComponentKey) then
null; -- ignore
else
-- publish to local or remote component
if
Msg.Header.To.AppId /= Itf.ApplicationId.Id then
-- Deliver message to remote application
Publish(Msg.Header.To.AppId, Msg);
else -- Deliver message to local application by copying to
-- consumer's queue
Consumers.List(I).Requestor :=
ComponentKey;
Consumers.List(I).ReferenceNumber := 0;
Found := Component.DisburseWrite( Consumers.List(I).ComponentKey,
Msg );
if not Found then
Text_IO.Put_Line(
"ERROR: local default Delivery couldn't find queue for
consumer");
end if;
end
if;
end
if; -- Ignore
end
loop;
end if;
end
Publish;
end Delivery;
Library had the treatment of topics necessary for
communication with remote applications added from the last time it was
published. This is in conjunction with
Format to encode and decode the messages.
Also there is the new ability to know when a Framework topic is allowed
and when it no longer is allowed.
Library.ads
with Component;
with Configuration;
with Delivery;
with Itf;
with Topic;
package Library is
-- A
library of registered message topics with their producer
-- and
consumer components.
-- Possible
results of attempt to register a topic
type
AddStatus
is (
SUCCESS, -- Topic added to the library
DUPLICATE, -- Topic already added for the component
FAILURE, -- Topic not added
NOTALLOWED -- Topic not allowed, such as for second consumer of REQUEST
);
--
Component data from registration as well as run-time status
type
TopicDataType
is record
Id : Topic.TopicIdType;
--
complete topic identifier
ComponentKey :
Itf.ParticipantKeyType;
--
component that produces the topic
Distribution :
Delivery.DistributionType;
--
whether consumed or produced
fEntry :
Topic.CallbackType;
--
callback, if any to consume the messages
Requestor : Itf.ParticipantKeyType;
--
component that produced REQUEST topic
ReferenceNumber : Itf.Int32;
--
reference number of a REQUEST topic
end record;
type
TopicTableArrayType
is
array(1..Configuration.MaxApplications*Component.MaxComponents)
of TopicDataType;
type
TopicTableType
is record
Count :
Integer;
-- Number
of declared topics of the configuration of applications
List : TopicTableArrayType;
end record;
-- Data of
Remote Request topic
type
TopicListDataType
is record
TopicId
: Topic.TopicIdType;
ComponentKey : Itf.ParticipantKeyType;
end record;
-- List of
topics
type
TopicListTableArrayType
is
array(1..25)
of
TopicListDataType;
type
TopicListTableType
is record
Count :
Integer;
List : TopicListTableArrayType;
end record;
procedure
Initialize;
function
RegisterTopic
( Id : in Topic.TopicIdType;
ComponentKey : in Itf.ParticipantKeyType;
Distribution : in Delivery.DistributionType;
fEntry : in Topic.CallbackType
) return
AddStatus;
procedure
RegisterRemoteTopics
(
RemoteAppId : in Itf.Int8;
Message : in Itf.MessageType
);
procedure
RemoveRemoteTopics
(
RemoteAppId : in Itf.Int8
);
-- Send the
Register Request message to the remote app.
procedure
SendRegisterRequest
(
RemoteAppId : in Itf.Int8
);
function
TopicConsumers
( Id : in
Topic.TopicIdType
) return
TopicTableType;
--
Determine if supplied topic is a known pairing.
function
ValidPairing
( Id : in
Topic.TopicIdType
) return
Boolean;
end Library;
Library.adb
with App;
with Format;
with Itf;
with System;
with Text_IO;
with Topic;
package body Library is
package
Int_IO is new Text_IO.Integer_IO( Integer );
-- Library
of topic producers and consumers
TopicTable
: TopicTableType;
type
CallbackDataType
is record
cEntry :
Topic.CallbackType; -- Any entry point to consume the message
end record;
type
CallbackDataArrayType
is
array(1..Component.MaxComponents) of CallbackDataType;
type CallbackTableType
is record
Count :
Integer;
List : CallbackDataArrayType;
end record;
procedure
SendRegisterResponse
(RemoteAppId : in Itf.Int8);
procedure
Initialize is
begin --
Initialize
TopicTable.Count := 0;
end Initialize;
-- Add a
topic with its component, whether producer or consumer, and entry for consumer
function
RegisterTopic
( Id : in Topic.TopicIdType;
ComponentKey : in Itf.ParticipantKeyType;
Distribution : in Delivery.DistributionType;
fEntry : in
Topic.CallbackType
) return
AddStatus is
EntryFound
:
Boolean;
use type
Delivery.DistributionType;
use type
System.Address;
use type
Topic.Extender_Type;
use type
Topic.Id_Type;
begin --
RegisterTopic
--
Determine if supplied topic is a known pairing.
EntryFound := ValidPairing(Id);
if (not
EntryFound) then
return
NOTALLOWED;
end if;
--
Determine if a framework topic. That
is, a user component shouldn't be
--
registering these topics to the Library.
if
(Id.Topic in Topic.NONE..Topic.REGISTER or else
Id.Ext = Topic.FRAMEWORK) and then
not
App.FrameworkTopicsAllowed
then
return
NOTALLOWED;
end if;
--
Determine if topic has already been added to the library.
-- Note:
A REQUEST message of a particular Topic should only be registered
-- for one consumer. Delivery will only route the REQUEST to one
-- consumer. That is, REQUEST messages are paired with a RESPONSE
-- message with only one component to be
designated to produce the
-- RESPONSE. There can be multiple requestors and the response will
-- be delivered to the requesting
component.
EntryFound := False;
for I in
1..TopicTable.Count loop
if
Id.Topic = TopicTable.List(I).Id.Topic then -- topic id already in table
-- Be
sure this new registration isn't for a second request consumer
if
(Id.Ext = TopicTable.List(I).Id.Ext and then
Id.Ext = Topic.REQUEST and then
Distribution = Delivery.CONSUMER)
then
if
not Component.CompareParticipants -- different components
( ComponentKey,
TopicTable.List(I).ComponentKey ) and then
TopicTable.List(I).Distribution = Delivery.CONSUMER -- 2nd Consumer
then
EntryFound := True;
return NOTALLOWED;
end
if;
end
if;
end if;
-- topic in table
end loop;
-- Check
that consumer component has a queue
if
Distribution = Delivery.CONSUMER then
for K
in 1..Component.ComponentTable.Count loop
if
Component.CompareParticipants(
ComponentKey, Component.ComponentTable.List(K).Key)
then
if
Component.ComponentTable.List(K).Queue = System.Null_Address then
return NOTALLOWED;
end
if;
end
if;
end
loop;
end if;
if not
EntryFound then -- add the topic with its component to the table
declare
K :
Integer := TopicTable.Count + 1;
begin
TopicTable.List(K).Id := Id;
TopicTable.List(K).ComponentKey := ComponentKey;
TopicTable.List(K).Distribution := Distribution;
TopicTable.List(K).fEntry := fEntry;
TopicTable.Count := K;
return SUCCESS;
end;
end if;
return
FAILURE;
end
RegisterTopic;
procedure
RegisterRemoteTopics
(
RemoteAppId : in Itf.Int8;
Message : in Itf.MessageType
) is
Index : Integer;
Topics :
Library.TopicListTableType;
use type
Itf.Int8;
begin --
RegisterRemoteTopics
-- Check
if topics from remote app have already been registered.
for I in
1..TopicTable.Count loop
if
TopicTable.List(I).ComponentKey.AppId = RemoteAppId then
Text_IO.Put_Line("RegisterRemoteTopics already in
table");
--
Send Response to the remote app again.
SendRegisterResponse(RemoteAppId);
return; -- since TopicTable already contains entries from remote app
end if;
end loop;
-- Decode
Register Request topic.
Topics :=
Format.DecodeRegisterRequestTopic(Message);
-- Add
the topics from remote app as ones that it consumes.
Index :=
TopicTable.Count + 1;
for I in
1..Topics.Count loop
--
Ignore local consumer being returned in Register Request
if
Topics.List(I).ComponentKey.AppId /= Itf.ApplicationId.Id then
TopicTable.List(Index).Id := Topics.List(I).TopicId;
Text_IO.Put("RegisterRequest topic");
Int_IO.Put(Index);
Int_IO.Put(Topic.Id_Type'pos(TopicTable.List(Index).Id.Topic));
Int_IO.Put(Topic.Extender_Type'pos(TopicTable.List(Index).Id.Ext));
Text_IO.Put(" ");
Int_IO.Put(Index);
Int_IO.Put(Topic.Id_Type'pos(TopicTable.List(Index).Id.Topic));
Int_IO.Put(Topic.Extender_Type'pos(TopicTable.List(Index).Id.Ext));
TopicTable.List(Index).ComponentKey.AppId :=
Topics.List(I).ComponentKey.AppId;
TopicTable.List(Index).ComponentKey.ComId := Topics.List(I).ComponentKey.ComId;
TopicTable.List(Index).ComponentKey.SubId :=
Topics.List(I).ComponentKey.SubId;
TopicTable.List(Index).Distribution := Delivery.CONSUMER;
TopicTable.List(Index).fEntry := null;
TopicTable.List(Index).Requestor.AppId := Itf.Int8(RemoteAppId);
TopicTable.List(Index).Requestor.ComId := 0; -- add for Request message
TopicTable.List(Index).Requestor.SubId := 0; -- sometime
TopicTable.List(Index).ReferenceNumber := 0;
Index
:= Index + 1;
TopicTable.Count := Index;
else
Text_IO.Put("ERROR: Register Request contains local
component");
Int_IO.Put(Integer(Topics.List(I).ComponentKey.AppId));
Int_IO.Put(Integer(Topics.List(I).ComponentKey.ComId));
Text_IO.Put("
");
end if;
end loop;
Text_IO.Put_Line("TopicTable after Decode");
-- Send
Response to the remote app.
SendRegisterResponse(RemoteAppId);
end
RegisterRemoteTopics;
procedure
RemoveRemoteTopics
(
RemoteAppId : in Itf.Int8
) is
NewCount
: Integer := TopicTable.Count;
Index : Integer :=
TopicTable.Count;
NewIndex
: Integer;
use type
Itf.Int8;
begin --
RemoveRemoteTopics
Text_IO.Put("RemoveRemoteTopics count=");
Int_IO.Put(TopicTable.Count);
Text_IO.Put(" RemoteAppId");
Int_IO.Put(Integer(RemoteAppId));
Text_IO.Put_Line(" ");
--
Actually working backwards so will only have topics from another
-- remote
app to move up to replace those of the disconnected app.
for I in
1..TopicTable.Count loop
if
(TopicTable.List(Index).ComponentKey.AppId = RemoteAppId) then
Text_IO.Put("RemoteTopic in Library table");
Int_IO.Put(Topic.Id_Type'pos(TopicTable.List(Index).Id.Topic));
Int_IO.Put(Topic.Extender_Type'pos(TopicTable.List(Index).Id.Ext));
Text_IO.Put_Line(" ");
--
Move up any entries that are after this one
NewIndex := Index;
for J
in Index+1..NewCount loop
TopicTable.List(NewIndex) := TopicTable.List(J);
NewIndex := NewIndex + 1;
end
loop;
NewCount := NewIndex;
end if;
Index
:= Index - 1;
end loop;
TopicTable.Count := NewCount;
Text_IO.Put("TopicTable after Decode");
Int_IO.Put(TopicTable.Count);
Text_IO.Put_Line(" ");
for I in
1..TopicTable.Count loop
Int_IO.Put(I);
Int_IO.Put(Topic.Id_Type'pos(TopicTable.List(Index).Id.Topic));
Int_IO.Put(Topic.Extender_Type'pos(TopicTable.List(Index).Id.Ext));
Int_IO.Put(Integer(TopicTable.List(I).ComponentKey.AppId));
Int_IO.Put(Integer(TopicTable.List(I).ComponentKey.ComId));
Text_IO.Put_Line(" ");
end loop;
end
RemoveRemoteTopics;
-- Send the
Register Request message to the remote app.
This
-- message
is to contain the topics of the local app for which
-- there
are consumers so that the remote app will forward
-- any of
those topics that it publishes.
procedure
SendRegisterRequest
(
RemoteAppId : in Itf.Int8
) is
Message :
Itf.MessageType;
TopicConsumers : TopicTableType;
use type
Delivery.DistributionType;
use type
Itf.Int8;
use type
Topic.Extender_Type;
use type
Topic.Id_Type;
begin --
SendRegisterRequest
-- Build
table of all non-framework topics that have local consumers.
TopicConsumers.Count := 0;
for I in
1..TopicTable.Count loop
if
TopicTable.List(I).Id.Topic /= Topic.REGISTER and then
TopicTable.List(I).Id.Ext /= Topic.FRAMEWORK
then
if
TopicTable.List(I).Distribution = Delivery.CONSUMER and then
TopicTable.List(I).ComponentKey.AppId = Itf.ApplicationId.Id
then
Text_IO.Put("RegisterRequest");
Int_IO.Put(Integer(TopicTable.List(I).ComponentKey.AppId));
Int_IO.Put(Topic.Id_Type'pos(TopicTable.List(I).Id.Topic));
Int_IO.Put(Topic.Extender_Type'pos(TopicTable.List(I).Id.Ext));
Text_IO.Put_Line(" ");
TopicConsumers.Count := TopicConsumers.Count + 1;
TopicConsumers.List(TopicConsumers.Count) := TopicTable.List(I);
end
if;
end if;
end loop;
-- Build
Register Request topic of these topics.
Message
:= Format.RegisterRequestTopic(RemoteAppId, TopicConsumers);
Text_IO.Put_Line("Publish of Register Request");
Delivery.Publish(RemoteAppId,
Message);
-- if
this works then Format doesn't really need to fill in header.
-- or do
a new Publish for this.
end
SendRegisterRequest;
procedure
SendRegisterResponse
(
RemoteAppId : in Itf.Int8
) is
ResponseMessage
: Itf.MessageType;
begin -- SendRegisterResponse
ResponseMessage.Header.CRC := 0;
ResponseMessage.Header.Id.Topic := Topic.REGISTER;
ResponseMessage.Header.Id.Ext := Topic.RESPONSE;
ResponseMessage.Header.From := Component.NullKey;
ResponseMessage.Header.From.AppId := Itf.ApplicationId.Id;
ResponseMessage.Header.To := Component.NullKey;
ResponseMessage.Header.To.AppId := RemoteAppId;
ResponseMessage.Header.ReferenceNumber := 0;
ResponseMessage.Header.Size := 0;
ResponseMessage.Data(1) := ' ';
Delivery.Publish( RemoteAppId, ResponseMessage );
end
SendRegisterResponse;
-- Return
list of callback consumers
function
Callbacks
( Id : in
Itf.ParticipantKeyType
) return
CallbackTableType is
EntryPoints : CallbackTableType;
use type
Topic.CallbackType;
begin --
Callbacks
EntryPoints.Count := 0;
for I in
1..TopicTable.Count loop
if
((Component.CompareParticipants(TopicTable.List(I).ComponentKey, Id))
and
then
(TopicTable.List(I).fEntry /= null))
then
EntryPoints.List(EntryPoints.Count).cEntry := TopicTable.List(I).fEntry;
EntryPoints.Count := EntryPoints.Count + 1;
end if;
end loop;
return
EntryPoints;
end
Callbacks;
-- Return
list of consumers of the specified topic
function
TopicConsumers
( Id : in
Topic.TopicIdType
) return
TopicTableType is
--debug
Heartbeat
: Boolean := False;
TopicConsumers : TopicTableType;
use type
Delivery.DistributionType;
use type
Topic.Extender_Type;
use type
Topic.Id_Type;
begin --
TopicConsumers
if
(Id.Topic = Topic.HEARTBEAT) then
Heartbeat := True;
end if;
TopicConsumers.Count := 0;
for I in
1..TopicTable.Count loop
if
((Id.Topic = TopicTable.List(I).Id.Topic) and then
(Id.Ext = TopicTable.List(I).Id.Ext))
then
if
(TopicTable.List(I).Distribution = Delivery.CONSUMER) then
-- if (Heartbeat) then
-- Console.Write("Consume Heartbeat
{0} {1}",
-- TopicTable.List(I).Component.AppId,
-- TopicTable.List(I).Component.ComId);
-- end if;
TopicConsumers.Count := TopicConsumers.Count + 1;
TopicConsumers.List(TopicConsumers.Count) := TopicTable.List(I);
end
if;
end if;
end loop;
return
TopicConsumers;
end
TopicConsumers;
function
ValidPairing
( Id : in
Topic.TopicIdType
) return
Boolean is
use type
Topic.Id_Type;
use type
Topic.Extender_Type;
begin --
ValidPairing
for I in
1..Topic.TopicIds.Count loop
if
((Id.Topic = Topic.TopicIds.List(I).Topic) and then -- then known
(Id.Ext = Topic.TopicIds.List(I).Ext)) then -- topic pairing
return True;
end if;
end loop;
return
False;
end
ValidPairing;
end Library;
Format.ads
with Itf;
with Library;
package Format is
procedure
Initialize;
function
DecodeHeartbeatMessage
(
Message : in Itf.MessageType;
RemoteAppId : in Itf.Int8
) return
Boolean;
function
DecodeRegisterRequestTopic
( Message :
in Itf.MessageType
) return
Library.TopicListTableType;
function
EncodeHeartbeatMessage
(
RemoteAppId : in Itf.Int8
) return
Itf.MessageType;
function
RegisterRequestTopic
(
AppId : in Itf.Int8;
Consumers
: in Library.TopicTableType
) return
Itf.MessageType;
end Format;
Format.adb
with Component;
with CStrings;
with Itf;
with System;
with Text_IO;
with Topic;
package body Format is
package
Int_IO is new Text_IO.Integer_IO( Integer );
HeartbeatIteration : Integer := 0;
procedure
Initialize is
begin --
Initialize
null;
end
Initialize;
function
DecodeHeartbeatMessage
(
Message : in Itf.MessageType;
RemoteAppId
: in Itf.Int8
) return
Boolean is
I :
CStrings.StringOffsetType;
-- Index
into string
J :
CStrings.CompareResultType;
-- 0 if
two strings compare
L :
CStrings.StringOffsetType;
-- Length
of substring
Msg
: CStrings.StringType
:= Message.Data'Address;
use type
Itf.Int16;
use type
Topic.Extender_Type;
use type
Topic.Id_Type;
begin --
DecodeHeartbeatMessage
if
Message.Header.Id.Topic /= Topic.HEARTBEAT or else
Message.Header.Id.Ext /= Topic.FRAMEWORK
then
Text_IO.Put_Line("Heartbeat message Topic invalid ");
return
False;
end if;
--
assuming rest of header is ok
if
Message.Header.Size /= 15 then
Text_IO.Put("Heartbeat message has a size other than 15 ");
Int_IO.Put(Integer(Message.Header.Size));
Text_IO.Put_Line(" ");
return
False;
end if;
-- Find
first delimiter, if any.
I :=
CStrings.IndexOf1(Msg, '|');
L := 0;
if I >
0 then -- delimiter found
-- Is
substring prior to delimiter the message id?
declare
SubString1
--
String where NUL will be located at position corresponding to '|'
:
String(1..I);
Heartbeat : String(1..10);
begin
SubString1 := CStrings.Substring(Msg, 1, I-1); -- string prior to '|'
SubString1(I) := ASCII.NUL;
Heartbeat(1..9) := "Heartbeat";
Heartbeat(10) := ASCII.NUL;
J :=
CStrings.Compare(Substring1'Address, Heartbeat'Address, False);
if J
/= 0 then
-- miscompare
return False; -- not Heartbeat message
end
if;
end;
else
return
False; -- not Heartbeat message
end if;
--
Heartbeat message
L :=
Integer(Message.Header.Size) - I + 1; -- where I is location of the '|'
declare
SubString1 : String(1..L);
--
String where NUL will be located at position corresponding to '|'
Msg1 :
CStrings.StringType := CStrings.AddToAddress(Msg,I);
begin
SubString1 := CStrings.Substring(Msg1, 1, L-1);
SubString1(L) := ASCII.NUL;
I :=
CStrings.IndexOf1(SubString1'Address,'|');
declare
Numeric : String(1..I);
SubString2 : String(1..SubString1'Length-I+1);
Field : Integer;
Result : Boolean;
Msg2 : CStrings.StringType
:= CStrings.AddToAddress(Msg1,I);
begin
Numeric := CStrings.Substring(SubString1'Address, 1, I-1);
Numeric(I) := ASCII.NUL;
L :=
SubString1'Length - I -1;
CStrings.TryParse(Numeric'Address, Numeric'Length, Field, Result);
if
not Result then
return False;
end
if;
if
Field /= Integer(RemoteAppId) then -- 1st field not as expected
return False;
end
if;
--
Get "to" app id
SubString2 := CStrings.SubString(Msg2, 1, L+1);
SubString2(L) := ASCII.NUL;
I :=
CStrings.IndexOf1(SubString2'Address,'|');
Numeric := CStrings.Substring(SubString2'Address, 1, I-1);
Numeric(I) := ASCII.NUL;
L := SubString1'Length - I -1;
CStrings.TryParse(Numeric'Address, Numeric'Length, Field, Result);
if
not Result then
return False;
end
if;
if
Field /= Integer(Itf.ApplicationId.Id) then -- 2nd field not as expected
return False;
end
if;
--
Get Heartbeat Iteration. Otherwise
ignore it for now.
--
declare
--
SubString3 : String(1..SubString2'Length-I);--width with up to | removed
--
Msg3 : CStrings.StringType := CStrings.AddToAddress(Msg2,I);
--
begin
--
SubString3 := CStrings.SubString(Msg3, 1, L+1);
--
end;
end;
end;
return
True;
end
DecodeHeartbeatMessage;
function
EncodeHeartbeatMessage
(
RemoteAppId : in Itf.Int8
) return
Itf.MessageType is
Success :
Boolean;
Id : String(1..2);
Message :
Itf.MessageType; -- message to be returned
Msg :
String(1..25) := (others => ASCII.NUL); -- enough space for Data
for Msg
use at Message.Data'address;
begin --
EncodeHeartbeatMessage
Msg(1..10) := "Heartbeat|";
CStrings.IntegerToString( Integer(Itf.ApplicationId.Id), 1, True, Id,
Success );
Msg(11)
:= Id(1);
Msg(12..12) := "|";
CStrings.IntegerToString( Integer(RemoteAppId), 1, True, Id, Success );
Msg(13)
:= Id(1);
Msg(14..14) := "|";
CStrings.IntegerToString( HeartbeatIteration, 1, True, Id, Success );
Msg(15)
:= Id(1);
Message.Header.CRC := 0;
Message.Header.Id.Topic := Topic.HEARTBEAT;
Message.Header.Id.Ext := Topic.FRAMEWORK;
Message.Header.From.AppId := Itf.ApplicationId.Id;
Message.Header.From.ComId := 0;
Message.Header.From.SubId := 0;
Message.Header.To.AppId := RemoteAppId;
Message.Header.To.ComId := 0;
Message.Header.To.SubId := 0;
Message.Header.ReferenceNumber := 0;
Message.Header.Size := 15;
return
Message;
end
EncodeHeartbeatMessage;
function
DecodeRegisterRequestTopic
( Message :
in Itf.MessageType
) return
Library.TopicListTableType is
Count : Integer;
Size : Integer;
Index :
Integer;
I : Integer;
TopicData
: Library.TopicListTableType;
Msg :
String(1..Integer(Message.Header.Size)+1); -- +1 for trailing NUL
for Msg
use at Message.Data'Address;
begin -- DecodeRegisterRequestTopic
--
Extract size from the message
Count :=
(Integer(Message.Header.Size) + 1) / 5; -- bytes per item
--
Extract topics from the message
TopicData.Count := 0;
Size :=
Integer(Message.Header.Size);
I := 1;
Index :=
1;
while
Size > 0 loop
declare
Id :
Topic.Id_Type;
for
Id use at Msg(I)'Address;
Ext :
Topic.Extender_Type;
for
Ext use at Msg(I+1)'Address;
AId :
Itf.Int8;
for
AId use at Msg(I+2)'Address;
CId :
Itf.Int8;
for
CId use at Msg(I+3)'Address;
SId :
Itf.Int8;
for
SId use at Msg(I+4)'Address;
begin
TopicData.List(Index).TopicId.Topic := Id;
TopicData.List(Index).TopicId.Ext := Ext;
TopicData.List(Index).ComponentKey.AppId :=
AId;
TopicData.List(Index).ComponentKey.ComId := CId;
TopicData.List(Index).ComponentKey.SubId := SId;
end;
Index
:= Index + 1;
TopicData.Count := TopicData.Count + 1;
I := I
+ 5;
Size :=
Size - 5;
end loop;
return
TopicData;
end
DecodeRegisterRequestTopic;
function
RegisterRequestTopic
(
AppId : in Itf.Int8;
Consumers
: in Library.TopicTableType
) return
Itf.MessageType is
Index :
Integer;
Key :
Itf.ParticipantKeyType;
Message :
Itf.MessageType;
Msg :
String(1..200); -- sufficient size
for Msg
use at Message.Data'Address;
use type
Topic.Id_Type;
begin --
RegisterRequestTopic
Key.AppId
:= Itf.ApplicationId.Id;
Key.ComId
:= 0; -- for
Key.SubId
:= 0; -- Framework
Message.Header.CRC := 0;
Message.Header.Id.Topic := Topic.REGISTER;
Message.Header.Id.Ext := Topic.REQUEST;
Message.Header.From := Key;
Key.AppId
:= Itf.Int8(AppId);
Message.Header.To := Key;
Message.Header.ReferenceNumber := 0;
Index :=
1;
for I in
1..Consumers.Count loop
if
Consumers.List(I).Id.Topic /= Topic.ANY and then -- don't
Consumers.List(I).Id.Topic /= Topic.REGISTER and then -- include
Library.ValidPairing(Consumers.List(I).Id)
then
declare
Id
: Topic.Id_Type;
for
Id use at Msg(Index)'Address;
Ext
: Topic.Extender_Type;
for
Ext use at Msg(Index+1)'Address;
AId : Itf.Int8;
for
AId use at Msg(Index+2)'Address;
CId
: Itf.Int8;
for
CId use at Msg(Index+3)'Address;
SId
: Itf.Int8;
for
SId use at Msg(Index+4)'Address;
begin
Id
:= Consumers.List(I).Id.Topic;
Ext
:= Consumers.List(I).Id.Ext;
AId
:= Consumers.List(I).ComponentKey.AppId;
CId
:= Consumers.List(I).ComponentKey.ComId;
SId
:= Consumers.List(I).ComponentKey.SubId;
end;
Index
:= Index + 5;
else
Text_IO.Put_Line("RegisterRequestTopic Invalid Pairing");
end if;
end loop;
Msg(Index) := ASCII.NUL; -- append terminating NUL
Message.Header.Size := Itf.Int16(Index-1);
return
Message;
end
RegisterRequestTopic;
end Format;
Debugging/Testing
While testing/debugging I created a configuration of 4
applications – the maximum allowed at the present time – and added a third
application (a duplicate of the first) to send the OFP KEYPUSH to the second
application. Various text output was
added to attempt to track what was happening.
To capture this output DOS windows were opened and the output was piped
(i.e., the > operator is used) to files for each application. Although frequently the output became intermixed
due to the thread being suspended before the output was finished so output from
another thread would appear and have to be interpreted. Or at least that's my opinion based upon my
years of past experience. (I had
forgotten that Critical Region is how I had locked the thread in the
past.)
Only a small amount of this text output was retained in the
code presented in this post.
This testing succeeded with communications between
applications 1 and 2 and between 3 and 2 while attempting to open a connection
to the non-existent application 4.
Other configurations of 2 applications and three applications were also
used.
In any case, messages were transmitted by one application
and the other application would receive and treat the message. However, the received message wouldn't go
any further and I would need to add text output to find how far the treatment
of the message had gotten to locate where the error was located so that it
could be corrected. The statements to
track down this type of mistake have all been removed.