Project

General

Profile

Writing an Ada actor

The real advantage about Ada’s actor programming is the availability of full Narval API :
  • It provides buffers and tasking control, e.g. start and stop the network tasking;
  • Inclusion of new Narval parameters, adding them in the top of the parameters list and trigger actions when a change occurs;
  • Access to the logger messaging system (log4Ada library);
  • Developing event builders with actors having multiple inputs and outputs.

There are two possibilities to write an Ada actor :

  • Full Ada actor : The installation of the Narval source code or the narval-dev debian package is necessary to allow the inheritance from Narval stub (staff). In that way during the compilation of an actor, the Narval API code is also compiled. Nevertheless it could take a longer compilation time.
  • Actor as an Ada shared library : The full Narval API installation is not needed. The shared library provides the same advantages as with a full Ada actor like having access to the full API (buffers, Narval parameters, log4Ada) or developing event builders with actors having multiple inputs and outputs. But there is no control over the actors' tasks.

Writing a full Ada actor

Topology informations

In the topology file one provides the executable name of the actor in the binary_code tag. In this example the executable names are tp_producer, tp_consumer and tp_filter

Here is an example of a topology using the three type of generic actors :

<configuration>
  <producer>
    <name>data_transmitter</name>
    <hostname>localhost</hostname>
    <binary_code>tp_producer</binary_code>
    <output_buffer_name>data1</output_buffer_name>
    <size output_buffer="data1">1000000</size>
    <port output_buffer="data1">fifo</port>
    <log_level>info</log_level>
  </producer>
  <intermediary input_buffers="1" output_buffers="1">
  <name>data_filter</name>
    <hostname>localhost</hostname>
    <binary_code>tp_filter</binary_code>
    <data_source source_port="fifo" source_buffer="data1">data_transmitter</data_source>
    <output_buffer_name>data2</output_buffer_name>
    <size output_buffer="data2">1000000</size>
    <port output_buffer="data2">fifo</port>
    <log_level>info</log_level>
  </intermediary>
  <consumer>
    <name>data_receiver</name>
    <hostname>localhost</hostname>
    <binary_code>tp_consumer</binary_code>
    <data_source source_port="fifo" source_buffer="data2">data_filter</data_source>
    <debug>info</debug>
  </consumer>
</configuration>

See Configuration Files pages for more information.

Loading your subsystem in Narval

You can use this script to launch your actors:

launch mysubsystem localhost
set configuration_file mytopology.xml mysubsystem
set action configure mysubsystem
set action load mysubsystem
set action initialise mysubsystem
set action start mysubsystem

Compiling an Ada actor

Define the files actor_cibles.mk, actor_definitions.mk, misc_actor.cfg and an Ada project file (gpr) to be used by the makefile (be careful about tab and copy/paste, as this file contains makefile syntax):

actor_cibles.mk

.PHONY:misc_actor
misc_actor:
   rm -f misc_actor.cfg
   ln -s $(ACTOR_DIR)/misc_actor.cfg
   $(GNATDIST) -x $(ACTOR_INCLUDES) misc_actor.cfg -Pnarval \
   $(ACTOR_EXE) -laACTOR(AWSLIB) -rdynamic

+actor_definitions.mk+

ACTOR_INCLUDES=-I$(ACTOR_DIR)
ACTOR_EXE=tp_producer
EXECUTABLES+=$(ACTOR_EXE)
CLEAN+=misc_actor.cfg $(ACTOR_DIR)/*~

ALL+=misc_actor

misc_actor.cfg

configuration misc_actor is
  pragma Starter (None);

  Narval_Naming_Service : Partition := (Narval.Narval_Naming_Registry);
  procedure Init_Narval_Naming_Service is in Narval_Naming_Service;
  for Narval_Naming_Service'Reconnection use Fail_Until_Restart;

  tp_actor : Partition;
  procedure Init_Tp_actor;
  for tp_actor'Termination use Local_Termination;
  for tp_actor'Main use Init_Tp_actor;
  for tp_actor’Reconnection use Fail_Until_Restart;
end misc_actor;

pragma.adc

--  Problem to solve
pragma Warnings (Off, "*declaration of ""Actors"" hides*");
pragma Warnings (Off, "*declaration of ""Name"" hides*");
pragma Warnings (Off, "*declaration of ""Action"" hides*");
--  GNAT Bug work around
pragma Warnings (Off, "*declaration of ""Finalize"" hides*");
pragma Warnings (Off, "*declaration of ""Buffer_Handling"" hides*");
pragma Warnings (Off, "*declaration of ""Parameter_Changed"" hides*");
pragma Warnings (Off, "*declaration of ""On_Stop"" hides*");
pragma Warnings (Off, "*declaration of ""On_Resume"" hides*");
pragma Warnings (Off, "*declaration of ""On_Initialise"" hides*");
pragma Warnings (Off, "*declaration of ""On_Start"" hides*");
pragma Warnings (Off, "*declaration of ""On_Reset"" hides*");
pragma Warnings (Off, "*declaration of ""On_Suspend"" hides*");
pragma Warnings (Off, "*declaration of ""On_Unload"" hides*");
pragma Warnings (Off, "*declaration of ""On_Reset_Com"" hides*");
pragma Warnings (Off, "*declaration of ""Distribute_Work"" hides*");
--  AWS generated code
pragma Warnings (Off, "*declaration hides ""String_Capsule_Type"" at*");
pragma Warnings (Off, "*declaration hides ""String_Array_Type"" at*");

actor.gpr

with "narval_lib_annex_e";
project actor is
 For Source_Dirs use (".");
 package Compiler is
    for Default_Switches ("Ada") use ("-gnatf",
                                     "-O2",
                                     "-gnatwahl",
                                     "-gnato",
                                     "-gnaty",
                                     "-gnat05");
 end Compiler;
 package Builder is
    for Global_Configuration_Pragmas use "pragmas.adc";
 end Builder;
 package Linker is
    for Default_Switches ("Ada") use ("-luuid", "-ldl", "-Wl,--as-needed",
                     "-rdynamic");
 end Linker;
end actor;

Makefile You can find a sample here

CPUS := $(shell getconf _NPROCESSORS_ONLN)

default:
   @echo "available targets :" 
   @echo "    - actor" 
   @echo "    - library" 

.PHONY:actor

actor:
   po_gnatdist -aP$(NARVAL_ADA_PROJECT_PATH) -p -j$(CPUS) \
   misc_actor.cfg -Pactor tp_actor

install:actor
   mv -f tp_actor $(HOME)/bin/

clean:
   rm -f *.o *.so *~ tp_actor
   rm -Rf dsa *.ali

Attention: Be careful about tab and copy/paste, as this file contains makefile syntax

Common API for a full Ada actor: producers, consumers and filters

Ada is an object oriented language and to code an actor with the Narval’s API the actor must inherit and override some of the methods from the class Actor_Active_Type (tagged record type in Ada syntax).

The actor must reside inside the package hierarchy Narval.Actors.Actives.Consumers or Narval.Actors.Actives.Producer or directly in the Narval.Actors.Actives and it must be inherited from the classes Consumer_Type, Producer_Type or directly from Active_Actor_Type if the actor is a filter.

Specify the actor’s code :

init_tp_producer.adb

with Narval.Actors.Actives.Producers.Tp;
with Actor_Body;

procedure Init_Tp_Producer is
  use Narval.Actors.Actives.Producers.Tp;
  A_Tp_Producer : aliased Tp_Producer_Type;
begin
  Actor_Body (A_Tp_Producer'Unchecked_Access);
end Init_Tp_Producer;

narval-actors-actives-producers_tp.ads

package Narval.Actors.Actives.Producer.Tp is
  type Tp_Producer_Type is new Producer_Type with private;
private
  type Tp_Producer_Type is new Producer_Type with record
     Number_Of_Event : Natural := 0;
  end record;
  procedure Buffer_Handling
    (Object : access Tp_Producer_Type);
end Narval.Actors.Actives.Producer.Tp;

Finally, implement the Narval-Actors-Actives-Producers_Tp.adb with the methods specified in the file Narval-Actors-Actives-Producers_Tp.ads. You can specify the following methods:

Mandatory methods

procedure Buffer_Handling 
(Object : access My_Active_Actor_Type; Actor_Name : String) 

procedure to consume or produce some buffers.

Optional methods

The On_Start procedure is called automatically when the acquisition is started. e.g: Change the configuration bits in the electronics to take data

procedure On_Start
(Object : access My_Active_Actor_Type)

The On_Stop procedure is called automatically when you stop the acquisition system. e.g:Change the configuration bits in the electronics to stop the acquisition data.
procedure On_Stop 
(Object : access My_Active_Actor_Type)

procedure On_Reset_com 
(Object : access My_Active_Actor_Type)

The On_Reset procedure will be called once per instance every time the system needs to be returned to a state ready for initialisation.

procedure On_Reset  
(Object : access My_Active_Actor_Type)

The following two procedures are called when the actors pass from the loaded state to the stopped state:

The On_Pause procedure will be called once per instance every time the system pauses data acquisition

procedure On_Pause  
(Object : access My_Active_Actor_Type)

The On_Resume procedure will be called once per instance every time the system
resumes data acquisition after a pause.
procedure On_Resume  
(Object : access My_Active_Actor_Type)

Code Samples

  • Producer-consumer : This code is composed of a producer which filsl a buffer with an integer value corresponding to the number of events produced, and a consumer getting the buffer and printing the integer value on the screen.
  • Producer-filter-consumer : This code is composed of: a producer which fills a buffer with an integer value corresponding to the number of events produced, a filter which gets and multiplies by 2 the integer value from buffer and a consumer getting the modified value and printing it on the screen.

Writing an Ada actor as shared library

To write an actor in C it is necessary to inherit from one or more classes. To write an actor in Ada, it is possible to use interfaces like in java, since Ada 2005 . In order to write a Narval actor as a shared Ada library it is mandatory to inherit from an Actor_Interface and to implement a set of methods. The actor interface is a package external to Narval providing to the actor with an interface free of Narval’s notions.

It is very easy to compile a shared library with this actor interface. Just code all the API interface methods providing control for all the levels on the state machine: On_Start, On_Stop, On_Initialise, Buffer_Handling, etc (the same described for the full Ada actors API). Then provide an empty method when no actions have to be done during the state machine level or declaring it as a null procedure.

Mandatory methods

The Buffer_Handling procedure will be called when an incoming data block is received and requires processing.

procedure Buffer_Handling 
(Object : access My_Active_Actor_Type; 
Actor_Name : String)

The On_Start method is called automatically when the acquisition is started. e.g: Change the configuration bits in the electronics to take data
procedure On_Start 
(Object : access My_Active_Actor_Type)

The On_Stop procedure is called automatically when you stop the acquisition system. e.g: Change the configuration bits in the electronics to stop the acquisition data.
procedure On_Stop 
(Object : access My_Active_Actor_Type)

The On_Initialise procedure will be called once per instance every time an initialisation of the loaded program is required. After the initial On_Initialise procedure call, any further call will follow a On_Reset procedure call.
procedure On_Initialise 
(Active_Actor : access Active_Actor_Type)

procedure On_Reset_Com 
(Active_Actor : access Active_Actor_Type)

The On_Pause procedure will be called once per instance every time the system pauses data acquisition
procedure On_Pause 
(Active_Actor : access Active_Actor_Type)

The On_Resume procedure will be called once per instance every time the system resumes data acquisition after a pause.
procedure On_Resume 
(Active_Actor : access Active_Actor_Type)

The On_Reset procedure will be called once per instance every time the system needs to be returned to a state ready for initialisation.
procedure On_Reset 
(Active_Actor : access Active_Actor_Type)

The On_Unload procedure will be called once per instance every time the system requests exit of the algorithm library code
procedure On_Unload 
(Active_Actor : access Active_Actor_Type)

Aditionally, it is necessary to provide the input and output methods:
procedure Add_Input 
(Object : access Omni_Actor_Type;
Input : Buffers.Buffer_Consume_Access)

procedure Add_Output 
(Object : access Omni_Actor_Type;
Output : Buffers.Buffer_Produce_Access)

Input/Output methods

By using Clear_IO method, it will be possible to clear the input and output list and then to provide
the actor with new outputs or have a different number of inputs.

procedure Clear_IO 
(Object : access Omni_Actor_Type)

It is possible to append parameters to the actor by calling the Append_Parameters method that returns a vector of parameters and polling a set of user defined parameters inside.
procedure Append_Parameters
(Object : access Omni_Actor_Type ;
Vector : in out Parameters.Parameter_Vector_Package.Vector)

Finally, the Set_Logger method which returns a pointer to the logger object enabling to add new user defined log codes to be redirected to the central log system in Narval.

procedure Set_Logger
(Object : access Omni_Actor_Type;
Logger : Log4ada.Loggers.Logger_Class_Access)