Saturday, January 21, 2017

Message Service with GtkAda Windows

Now I am turning more attention to driving an A661 protocol Display Application using one of the Message Service (MS) framework applications to enable the display layer window. 

Upon receiving the enable command via an A661 protocol message, the Display Application is now able to display a window for A661 layer 1.  This took a bit of doing.


Previously, my exploratory project (EP) used Microsoft C# as the programming language to implement the Display Application.  I was able to use it for free for study.  It was nice enough to use since it was a visual compiler where widgets could be placed in the window form and had interfaces with Windows that could be looked up online to determine what was needed to respond to mouse clicks, enter text into dialog boxes, refresh the display, etc.

However, Microsoft now wants much bigger bucks than I'm willing to spend to just play around with it.

Therefore, with the new incarnation of the exploratory project that I have dubbed MS I was looking for another language that would interface to Microsoft Windows.  Upon the options was just to use the Mono language that I had used with EP for its Linux version.  There seems to be a Cygwin add-on that is supposed to support Mono running on Windows.  However, after starting downloads, I put it aside.

GNAT GtkAda then became an option.  I had already created a dummy Display Application in Ada where I had implemented a very much partial interface from one of the two MS framework applications.  Just to be able to send the Enable Layer command and have the dummy Display Application respond with the A661 message that the layer was enabled.  Thus using GtkAda, that supports interfaces to Microsoft Windows as well as for Linux, would allow me to keep this preliminary code while adding the ability to actually display something on the console screen.

So to keep busy I have embarked upon such a Display Application that will necessitate incorporating various code or techniques of EP into the MS framework application.  For instance, using the Delivery Identifier of a message topic that has already been built into the MS framework to route a particular A661 message with a particular Widget Id to the application component that has registered to treat the message.

Therefore, the first order of business was to use GtkAda to display a Windows window when the Enable Layer command was received by the Display Application.

GtkAda Learning Experience

It took quite a bit of time (week and half, two weeks) getting started with GtkAda.  I at first let it create a project gpr with Ada and C to include the two C functions that get called to determine if the other application is running using the code from the dummy Display app.  This code is used to determine when to open MS Pipes between the two apps where I use one pipe by one app to transmit to the other where the pipe is opened for receive.  And vice versus for the other app with the other pipe of the pipe pair.

However, the build would then say that the Gtk spec had to be recompiled.  Or with other attempts it would say that the references to C functions were undefined whereas the without GtkAda builds knew how to link them in.  Or, after rewriting a C file in Ada with the references to the Windows functions that are identified in the GNAT installation were undefined – again no such problem with the C functions in the non GtkAda build.  I spent hours and hours on internet searches related to this but the suggestions never solved the problem.

Before removing the C functions completely to just have regular Ada invoking the GtkAda functions of a simple example (that I tried and which ran successfully) I also had combinations where I got
C:\Source\MS\Try12>C:\gnat\2016\bin\gnatbind -x C:\Source\MS\Build11\app11.ali
error: "gtk.ali" not found, "gtk.ads" must be compiled
error message.  Where, of course, gtk.ads can't be recompiled since it is part of the dll files that come with the GtkAda installation.

Anyway, I tried various project gpr file combinations as well as the use of batch files to do the build in make, bind, link steps with none that would build successfully. 

Finally I gave up and removed the use of the C functions meaning that the Display application couldn't first determine whether the MS framework application that it was paired with was running before opening the pipes to it.  That is, just its receive pipe since the dummy Display application hadn't opened the transmit pipe until it was time to send the response to the Enable Layer request back to the framework application.

It still took some fussing to come up with a project file that would successfully build.  However, I finally came up with
with "gtkada";

project App11New is

   for Source_Dirs use ("C:\Source\MS\Try13\srcDisplay\App11", "C:\Source\MS\Utilities");
   for Object_Dir use "C:\Source\MS\Build11";
   for Main use ("C:\Source\MS\Try13\srcDisplay\App11\app11.adb");

   package Builder is
      for Default_Switches ("ada") use ("-s", "-m", "-g", "-k");
   end Builder;

   package Compiler is
      for Default_Switches ("ada") use ("-g");
   end Compiler;

end App11New;
that I run from a batch file that just invokes it with
C:\gnat\2016\bin\gprbuild -P C:\Source\MS\Try13\App11New.gpr

Notice that the project file contains
   for Exec_Dir use "C:\Source\GUI\User_App";
for where to store the exe file.  This is because I had previously found that I had had to copy the GtkAda dll files from their GNAT folder to where I was going to bind and link the executable.  That is, GNAT could find its Ada libraries via the System path but didn't seem to be able to find the GtkAda libraries even though that System path was also defined.  I had found that early on in attempting to build the simple example.

GtkAda Success

In any case, I was then able to build the Display app and by launching it first, the framework app could send the Enable Layer request to the Display app and it would then display a small window to be used for the A661 layer 1.

However, its minimize, full screen, and terminate buttons of the upper right of the standard Windows window didn't work as they had in the simple example.

Doing some further internet searches I found GtkAda function calls to specify the size to be used for the window and to center it.  Including these calls didn't compile - at least the one to specify the starting size.  However, I then found that the more recent GtkAda release that I was using had eliminated it and suggested another.  But the search also turned up a third that I tried.

Thus, always starting the Display app first, the larger window displayed centered on the console screen.

Double checking the simple example it did its calls as
with Gtk.Main, Gtk.Window;

procedure Simple_Application is
   Window : Gtk.Window.Gtk_Window;
begin
   Gtk.Main.Init;
   Gtk.Window.Gtk_New (Window);
   Gtk.Window.Show (Window);
   Gtk.Main.Main;
end Simple_Application;
where I had found that Main enabled a loop to treat events.

In my code I had done the various setups including Gtk.Main.Main at initialization time but had left Gtk.Window.Show until the Enable Layer had been received and the response had been sent.

Changing my initialization sequence to
    -- Initializes GtkAda
    Gtk.Main.Init;

    -- Create the basic window
    Gtk.Window.Gtk_New
    ( Window   => Win,
      The_Type => Gtk.Enums.Window_Toplevel );
   
    Set_Position( Win, Win_Pos_Center );
   
    Set_Default_Size( Win, 500, 300 );

    --  Set a window title
    Gtk.Window.Set_Title
    ( Window => Win,
      Title  => "Layer 1" );
where use statements had been added in the meantime so that the leftover dot notation is no longer needed.

And moving the call to Main to after the Show; that is,
    if Request.Command = A661.Types.UA_Request then

      if Request.Request = A661.Types.Layer_Active then

        Index := Index + A661.Types.A661_Request_Structure_Size;

        -- Send Notify that the layer is active back to the
        –- user application
        Send_Notify_Layer_Active;

        -- Show the window.
        Gtk.Window.Show_All( Win );

        -- Signal handling loop
        Gtk.Main.Main;

        return True;

      elsif ...
eliminated the problem.

The displayed window (prior to resizing it) is


GtkAda To Do

Now I can continue attempting to redo some of the past C# work involving buttons, list boxes, edit boxes, and such along with sending mouse clicks on the widgets back to the A661 user application program (the framework application) and having it send commands back to the display program to hide the clicked button and display a different one or produce a different page, etc thus mimicking aircraft MCDU behavior.


I'll surely discover some other gotchas between the documented GtkAda function calls and changes made in the newer release.

Message Service Update


The latest adaptation of my exploratory project is known as the Message Service. 

This is an update to the previous memo.  It includes the addition of communication with a non-framework based application.  The non-framework based application chosen is an ARINC-661 protocol Display Application.  The changes to the framework based application needed to interface to a non-framework based application will be the updated portion of this memo.  A minimal example of a Display Application was developed to verify that the communications between the two kinds of applications was successfully handled by the changes to the Message Service based application.

The Message Service (MS) is a framework for the delivery of individual messages from a producer component to the consumer components that have registered for the topic of the message.  The delivery will occur within the application of the producer as well as other (remote) applications based upon the Message Service.

The Message Service is a Microsoft Windows PC based application written almost exclusively in Ada.  However, it consigns Operating System (OS) interfaces to particular Ada packages so that it can easily be supported by Linux as well (and has been in the past).

The Message Service uses its own mechanism for the delivery of local messages and Microsoft Pipes for delivery between applications.  (Linux named pipes will support delivery between applications when running under Linux as the OS.  This was implemented in previous versions of the exploratory project so the modifications are known.)  In addition, previous versions also supported the use of TCP/IP for delivery between applications – either hosted by a single PC or multiple PCs.

The OS interfaces are mainly hidden within the Message Service framework and not accessible to the User Components.  Where the term User Components refers to OS threads whose purpose is to support the objective of the application.  This adaptation moved Remote IO to a user component.  To support the move of this feature specific OS interfaces were made visible via the Remote_Itf package.

This adaptation of the exploratory project consigns the actual message delivery between applications to an input/output (IO) user component.  Previously the delivery of topic messages between applications was implemented within the framework.  With this modification of the project architecture other such user components can be developed to interface to non-framework based applications – for instance, A661 display applications.  Or, at least, separate Ada packages can be implemented within the Remote IO component to interface to each particular category of remote application.

The User Components are Installed by an application specific Ada package that are invoked directly from the Ada main procedure of the Ada based application.  Otherwise, each application contains common code.  The OS folders that contain the application consist of the common code as well as folders that are specific to the application.  The OS interface code is written to recognize which operating system the application is running under.  If this wasn't so builds could be done by operating system by also having OS specific folders.

The current architecture has the following components/threads (figure Message Service Overall Structure) where the IO component has multiple threads.  The framework has the threads and packages of figure Message Service Framework Structure.

  




The above figure illustrates six User Components along with the Apps package and the IO Framework Friend Component which is actually a component with subcomponents to be illustrated below.  It is considered a friend of the Message Service Framework since it is installed by the Framework rather than the Apps package that is invoked by the Ada main procedure to install the user components of the particular application.  As a friend component it enjoys access to selected IO procedures/functions and uses an OS event to start the framework Support thread and is likewise started by an OS event rather than the framework topics selected by the user components. 

The Apps package is invoked when the OS launches the application.  It reads the Configuration file to obtain the allowed applications with their executable names and whether the application is a Message Service Framework based application or another non-MS application such as an A661 Display application and the communication methods that the application supports.  The Apps package uses this information to build a table including the information.  The Apps package contains an application unique procedure that invokes the Install procedures of the User Components of the application.

The messages between the IO component and the framework are communicated via queues where the address of the message is passed in the queue rather than the message data to avoid unnecessary copying of the message data.  Note: "To Queue" is an abbreviation of "To IO Framework Component" and similarly for "From Queue".  The Start Event causes the thread to recover from an OS Wait and to read the queue for attributes of received messages to be delivered to local user components or transmitted to a remote application.

The messages between the user components are delivered via a framework topic feature.  This mechanism is supported by each user component registering with the framework which topics it will publish and which it wants to have delivered to it.  The framework keeps track of this and when a user component publishes a topic it delivers the message data of the topic to the components that registered to consume it.

An application that isn't based upon the Message Service framework will, of course, have its own mechanism to deliver messages.  Such applications will communicate with MS applications via communication buses such as pipes and TCP/IP and the MS framework will deliver the received message as a topic to the user component that has registered to consume it.  Likewise, topic messages to be transmitted will be converted as necessary for the particular remote application by that application's IO component.



The diagram of the IO Framework Friend Component prior to the inclusion of non-MS framework protocols is above. 

With the inclusion a set of protocol packages has been added similar to the communication method packages (MS_Pipe, etc) with each protocol package treating specifics of the particular protocol to communicate between the MS Framework based application and the remote application for which the communications protocol applies.


The IO Framework Friend Component consists of
1) the Remote IO thread that receives OS wakeup events from the MS Framework to transmit messages to the remote MS Framework based applications,
2) the A661 IO thread that receives OS wakeup events to treat requests and to transmit messages to remote A661 Display applications,
3) a Receive thread for each remote application of the configuration, and
3) the Monitor thread to watch for disconnects between the local application and a remote application.

The Receive threads queue notifications of messages received from remote applications as they are received.  If the received message is from a framework based application, the message is forwarded to the MS Based package for treatment.  Likewise, if the message is from an A661 display application, the message is forwarded to the A661 package for treatment.

The MS Based child package of Remote IO will check whether the received message is a Heartbeat, Remote Register, or user component message and, if one the latter two, queue the message and send the OS wakeup event to the Remote thread of the MS Framework.  When awoken from its Wait, the Remote thread will publish the topic data portion of the received message to the user components that have registered for the topic.

The A661 child package of Remote IO will queue the received message to the A661 IO thread package for treatment.  This A661 IO package (just identified as A661 in the implementation and not to be confused with the A661 child package of Remote IO) has its own set of child packages to be a mini-framework for the treatment of A661 messages.  As such it is incomplete.  That is, in previous versions of the Exploratory Project, I had implemented a more complete (yet still not full-fledged) implementation that interfaced to a C# (Mono for Linux) Display application that could display widgets in a window on the terminal screen.  Since Microsoft has not renewed my access to C# I implemented the limited A661 display application to be used to debug the interface to a MS Framework application in Ada.  This limited version just decodes the trial message and sends the response to illustrate that the concept design works.

When user components publish topics that a remote application has registered to consume them, the OS Framework will deliver the topic message to the Remote component which will then queue it for delivery to the IO Framework Friend Component.  This will send the OS wakeup event of the Remote IO thread.  The top-level Transmit procedure of Remote IO then forwards the message to the MS Based or A661 child package as appropriate for the specialize protocol processing.  The applicable child package will then cause the message to be transmitted via the correct communications driver package. 

This allows user components to treat either MS Based message topics or A661 messages and publish their response.  The response message will then be queued to the "To Remote IO" message queue, Remote IO will be woken up and it will forward the response message to the correct child package to transmit to the specified remote application.

For messages received from MS Based applications, the messages are queued to the "From Remote IO" message queue for delivery directly to the Message Remote package which, in turn, will deliver them to the applicable user component.  See the following diagram. 

For messages received from an A661 display application, the messages are queued to the "To A661" queue for delivery to the A661 for specialized processing.  When and if more completely implemented like that of the previous Exploratory Project, it would look up the user component action procedure that is to treat the A661 widget event and then cause it to be delivered to user component.

Both the Receive threads and Transmit use particular IO driver packages.  For instance, there will be a Receive thread for each supported method of external communications for each remote application of the configuration.  Transmit, on the other hand, will select the supported method of communications and invoke its transmit routine.

The Remote IO thread can also be awakened by the Monitor thread to treat changes in the status of the connection between the local application and a remote application.  Likewise, the dequeued To Remote IO items can indicate errors that should be logged rather than a message to be transmitted.

The Remote_Itf package is a friend interface to particular OS functions and procedures otherwise hidden within the Framework.  It also contains the circular queues and the mechanism for sending the OS wakeup events to either the Remote thread of the Framework or the Remote_IO thread.  The OS event selected depends upon which queue had an added message element.

There are other support procedures, of course, besides those illustrated.

The Message Service Framework is illustrated above.

The User Components interface with the Message Service Framework via the Itf package which, in turn, invokes the Itf package procedures of the Message-Itf package that is a child package of Message. 

If the request is to register the User Component or one of the topics it will produce or consume, the Message-Itf procedure will invoke the Scheduler package to allocate the component or the Message-Registry package to update the topic tables.  Since these requests precede the starting of the threads, each such request will be treated without thread concurrency.  That is, these requests run under the Operating System launch thread where there are no other running threads.

If the request is to obtain a message buffer, publish an instance of a topic (the message buffer), or get any recently published topics that the user component has registered to consume the Message-Itf package buffers the request and sends an OS wakeup event to the Support thread within the Message-Support package.  The Support thread then invokes one of Message-Delivery procedures.  Since the Support thread is the highest priority thread, only one such topic request will be treated at a time.

After all User Components of the particular application have been installed, Scheduler will start the User Component threads as well as the framework threads.  Besides the Support and Remote threads, these include the Event Driven and the three different periodic threads that implement the delivery of topics to the various user components. 

Note: If a user component has installed itself to run as a Periodic thread it will run as one of the Scheduler Periodic threads.  Similarly for Aperiodic and Pseudo Periodic.  If the user component installed itself as an Event Driven thread, it will be run as one of the Scheduler Event Driven threads when its topic is published.  That is, the Scheduler creates a thread farm with an instance of each kind of thread as the user components install themselves.  Any particular User Component thread is a thread from the Scheduler thread farm.

Periodic threads are run at a rate specified by the install.  Aperiodic threads run and then Scheduler Aperiodic delays for the specified interval and then activates the user component entry point once again.  Pseudo Periodic threads wait for their OS start event every time they return from the user component.  Thus they are all similar but have a different mechanism to provide their periodicity

No matter the cause for how a user component is run, the availability of the topic instances (i.e., messages) disappears after the user component returns control to the Scheduler.  Following the return the Scheduler thread invokes a Message Release procedure that releases the message data buffer of the topic that was temporarily allocated to the user component.  So if a component needs to retain particular data from a received message it must copy it from the message.

A user component is provided a pointer to the message data buffer upon its return from its Itf request for a particular topic (or for any newly available topic).  It can then type cast the pointer to the format of the topic's data and examine it without the need to copy it from the data buffer.  When the component is going to publish a topic message it, of course, must copy data values into the data buffer via the supplied pointer.

Each consumer of a particular topic is provided its own data buffer each time the topic is published.  Therefore, a component can attempt to modify the message that it receives but such a modification will not affect the message delivered to another component and will also not be useful.  In this manner the Message Service attempts to avoid excessive copying of data which can effect performance.


This feature also exists for the messages created to be transmitted to other applications or those received from other applications.  Since the Remote and Receive threads do not know in advance which topic will be delivered to the Remote_IO thread for transmit or from a Receive thread for delivery to a local user consumer component, these message buffers are pre-allocated at the maximum size.  As a buffer is needed for transmit or for the next receive, the assigned buffer is marked as no longer available.  To avoid copying only the address of the buffer is passed in the From Remote IO or To Remote IO queues.  After the data has been transmitted by Remote IO or published by Remote for delivery to a local user component a Release command is queued to enable the buffer to once again be marked as available.