1. Description

1.1. Ada for Automation

Ada for Automation (A4A in short) is a framework for designing industrial automation applications using the Ada language.

It makes use of the libmodbus library to allow building a ModbusTCP client or server, or a Modbus RTU master or slave.

It can also use Hilscher communication boards allowing to communicate on field buses like AS-Interface, CANopen, CC-Link, DeviceNet, PROFIBUS, EtherCAT, Ethernet/IP, Modbus TCP, PROFINET, Sercos III, POWERLINK, or VARAN.

With the help of GtkAda, the binding to the Graphic Tool Kit, one can design Graphical User Interfaces.

Thanks to Gnoga, built on top of Simple Components, it is also possible to provide a Web User Interface.

Nice addition is the binding to the Snap7 library which allows to communicate with SIEMENS S7 PLCs using S7 Communication protocol ISO on TCP (RFC1006).

Of course, all the Ada ecosystem is available.

Using Ada bindings, C, C++, Fortran libraries can also be used.

And, since it is Ada, it can be compiled using the same code base to target all major platforms.

1.2. This demo application

This is a demo application featuring:

  • a basic command line interface,

  • a kernel with a Modbus TCP Server (K0),

  • a trivial application mirroring inputs in outputs.

2. Projects diagram

The following picture shows the diagram of projects :

diagram classes

3. License

Those files are included in the Ada for Automation root folder :

The GPL License you should read carefully. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007

GCC RUNTIME LIBRARY EXCEPTION Version 3.1, 31 March 2009

Hence, each source file contains the following header :

--                            Ada for Automation                            --
--                                                                          --
--                   Copyright (C) 2012-2022, Stephane LOS                  --
--                                                                          --
-- This library is free software;  you can redistribute it and/or modify it --
-- under terms of the  GNU General Public License  as published by the Free --
-- Software  Foundation;  either version 3,  or (at your  option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE.                            --
--                                                                          --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception,   --
-- version 3.1, as published by the Free Software Foundation.               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --

4. Building

The provided makefile uses GPRbuild and provides six targets:

  • all : builds the executable,

  • app_doc : creates the documentation of the source code,

  • clean : cleans the space.

Additionally one can generate some documentation using Asciidoctor with :

  • read_me_html : generates the README in HTML format,

  • read_me_pdf : generates the README in PDF format,

  • read_me : generates the README in both formats.

5. Running

Of course, this application is of interest only if a Modbus TCP Client application is talking to it.

Good candidates are a SCADA or a PLC but, if none is at your disposal, you could use one of :

  • 020 a4a-k1-cli,

  • 021 a4a-k1-gui,

  • 022 a4a-k1-wui,

  • your own.

In a console:

Build the application:


Optionally create the documentation:

make app_doc

Run the application:

make run

Use Ctrl+C to exit.

Optionally clean all:

make clean

6. Directories

Where you will find the executable.

The place where GNATdoc would create the documentation.

Build artifacts go here.

Application source files.

7. Application

This is a basic Ada for Automation application which implements a Modbus TCP server.

It has a Command Line Interface and listen to Modbus TCP requests.

7.1. Deployment diagram

diagram deployment

7.2. Activity diagram

The Kernel manages the communication channel and provides an interface to it, namely the package "A4A.Memory.MBTCP_IOSlave".

diagram activity initialization
diagram activity running
diagram activity finalization

7.3. Modbus TCP Server Configuration

package A4A.Application.MBTCP_Server_Config is

   --  Modbus TCP Server configuration

   package Server is new A4A.MBTCP_Server
     (Coils_Number           => 65536,
      Input_Bits_Number      => 65536,
      Input_Registers_Number => 65536,
      Registers_Number       => 65536);

   Config1 : aliased Server.Server_Configuration :=
     (Server_Enabled           => True,
      Debug_On                 => False,
      Retries                  => 3,
      Server_IP_Address        => To_Bounded_String (""),
      Server_TCP_Port          => 1504); --  (1)

end A4A.Application.MBTCP_Server_Config;
1 Modbus TCP Server port : 1504

7.4. User objects Definition

package A4A.User_Objects is

   --  User Objects creation

   --  Inputs

   Input_Bits : Bool_Array (0 .. 15) := (others => False);  --  (1)

   --  Outputs

   Coils      : Bool_Array (0 .. 15) := (others => False);  --  (2)

end A4A.User_Objects;
1 An array of 16 Input bits that a Modbus TCP client can read is defined.
2 As well, an array of 16 Coils can be written by the client.

7.5. User Functions

package body A4A.User_Functions is

   --  User functions

   procedure Map_Inputs is  --  (1)

      Coils := MBTCP_IOServer.Bool_Coils (Coils'First .. Coils'Last);

   end Map_Inputs;

   procedure Map_Outputs is --  (2)

        Bool_Inputs (Input_Bits'First .. Input_Bits'Last) := Input_Bits;

   end Map_Outputs;

   procedure Mirroring is  --  (3)

      Input_Bits := Coils;

   end Mirroring;

end A4A.User_Functions;

User functions are defined to :

1 get the inputs from the server,
2 set server ouputs,
3 mirror the data.

7.6. User Application

package body A4A.Application is

   procedure Cold_Start is


   end Cold_Start;

   procedure Closing is


   end Closing;

   procedure Main_Cyclic is
      My_Ident : constant String := "A4A.Application.Main_Cyclic";
--        A4A.Log.Logger.Put
--          (Who  => My_Ident,
--           What => "Yop ! ********************** "
--           & Integer'Image(Integer(MBTCP_IOScan_Inputs(0))));

      Map_Inputs;  --  (1)

      --  Mirroring Inputs in Outputs
      Mirroring;  --  (2)

      --  Playing with tasks interface
      Main_Outputs.X := Main_Inputs.A;
      Main_Outputs.Y := Main_Inputs.B;
      Main_Outputs.Z := Main_Inputs.C;

      Map_Outputs;  --  (3)


      when Error : others =>
         A4A.Log.Logger.Put (Who  => My_Ident,
                             What => Exception_Information (Error));

         Program_Fault_Flag := True;

   end Main_Cyclic;

   procedure Periodic1_Cyclic is
      My_Ident : constant String := "A4A.Application.Periodic1_Cyclic";
--        A4A.Log.Logger.Put (Who  => My_Ident,
--                            What => "Hi !");

      --  Do something useful here
      --  Could be simulate

      --  Playing with tasks interface
      Periodic1_Outputs.A := not Periodic1_Inputs.X;
      Periodic1_Outputs.B := Periodic1_Inputs.Y + 2;
      Periodic1_Outputs.C := Periodic1_Inputs.Z + 1;


      when Error : others =>
         A4A.Log.Logger.Put (Who  => My_Ident,
                             What => Exception_Information (Error));

         Program_Fault_Flag := True;

   end Periodic1_Cyclic;

   function Program_Fault return Boolean is
      return Program_Fault_Flag;
   end Program_Fault;

end A4A.Application;

The application cyclically :

1 gets the inputs from the server,
2 copies the Coils to the Input bits,
3 sets server ouputs.