A4A : Infrastructure Messagerie Hilscher cifX – Layer 3 – Indications

Bonjour,

Pour que l’application puisse recevoir les indications il est nécessaire de s’enregistrer auprès de la pile de protocole afin qu’elle sache que l’application va les gérer d’une part et d’autre part lui indiquer la queue de destination.

On trouve dans le document netX Dual Port Memory Interface les requêtes RCX_REGISTER_APP_REQ et RCX_UNREGISTER_APP_REQ.

En remplissant les requêtes RCX_REGISTER_APP_REQ et RCX_UNREGISTER_APP_REQ, on indique la référence de la queue de réception des indications dans les champs Src et Src_Id.

Pour chacune de ces requêtes un bloc fonction est disponible qui ne possède pas de queue de réception propre mais reçoit la référence de la queue à utiliser à l’initialisation.

Après s’être enregistré auprès de la pile de protocole, il faut bien évidemment gérer les indications et y répondre.

On va donc avoir un bloc fonction pour chaque indication qui fournit à l’application le moyen de savoir qu’une indication a été reçue, une méthode pour récupérer les données de l’indication, et une méthode qui permet de déclencher la réponse.

Comme toutes les indications parviennent à la même queue de réception il est nécessaire de prévoir un mécanisme de distribution des paquets vers les blocs fonction gérant les indications, ces blocs ne possédant pas de queue de réception propre.

Pour gérer ce mécanisme de distribution on a recours à un bloc fonction qui dispose de sa propre tâche pour :

  • gérer sa machine d’état,
  • s’enregistrer auprès de la pile de protocole en fournissant sa propre queue de réception en paramètre,
  • lorsqu’une indication est reçue, la distribuer au bloc en charge de sa gestion si celui-ci est disponible pour le faire,
  • se dés-enregistrer auprès de la pile de protocole lorsque l’application se termine.

On initialise ce bloc en lui fournissant d’une part une référence du canal de communication et d’autre part les références de chaque bloc fonction gérant une indication.

Le code source pour la gestion des requêtes RCX_REGISTER_APP_REQ et RCX_UNREGISTER_APP_REQ se trouve dans le dossier « A4A/hilscherx/src/rcx_public ». Je ne pense pas qu’il soit nécessaire d’en donner davantage d’explications car cela ressemble fort à ce qui a été présenté précédemment.

Voici ci-dessous la spécification du bloc fonction gérant les indications pour PROFIBUS DP V1. Il est notablement incomplet car il ne gère que l’indication de connexion fermée pour la classe 2, ce qui est suffisant pour mon exemple.

Au cas où vous souhaiteriez un bloc fonction plus complet, n’hésitez pas à vous manifester.
Surveillez les logs, une indication non gérée devrait être signalée.

On pourrait tout à fait instancier plusieurs fois cet objet comme par exemple pour gérer ce type de communication sur les deux canaux d’une carte Hilscher cifX 50-2DP.

On peut également décliner ce concept pour tous les bus de terrain supportés par ces cartes. Cela sera fait en fonction des demandes exprimées, ou contributions… 😉

Ça se passe dans le dossier « A4A/hilscherx/src/profibus_dpm ».

-----------------------------------------------------------------------
--                       Ada for Automation                          --
--                                                                   --
--              Copyright (C) 2012-2014, Stephane LOS                --
--                                                                   --
-- This library is free software; you can redistribute it and/or     --
-- modify it under the terms of the GNU General Public               --
-- License as published by the Free Software Foundation; either      --
-- version 2 of the License, 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    --
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU --
-- General Public License for more details.                          --
--                                                                   --
-- You should have received a copy of the GNU General Public         --
-- License along with this library; if not, write to the             --
-- Free Software Foundation, Inc., 59 Temple Place - Suite 330,      --
-- Boston, MA 02111-1307, USA.                                       --
--                                                                   --
-- As a special exception, if other files instantiate generics from  --
-- this unit, or you link this unit with other files to produce an   --
-- executable, this  unit  does not  by itself cause  the resulting  --
-- executable to be covered by the GNU General Public License. This  --
-- exception does not however invalidate any other reasons why the   --
-- executable file  might be covered by the  GNU Public License.     --
-----------------------------------------------------------------------

-- <summary>
-- This package is part of the binding to Hilscher cifX Device Driver,
-- a library allowing access to the cifX boards with numerous protocols.
-- </summary>
-- <description>
-- It provides :
-- - PROFIBUS DP Master data types,
-- - cifX C functions binding.
-- </description>
-- <group>Protocols</group>
-- <c_version>3.0.1</c_version>


with A4A.Log;

with A4A.Protocols.HilscherX.Channel_Messaging;
use A4A.Protocols.HilscherX;

with A4A.Protocols.HilscherX.rcX_Public.rcX_Register_App.FB;
with A4A.Protocols.HilscherX.rcX_Public.rcX_Unregister_App.FB;
use A4A.Protocols.HilscherX.rcX_Public;

with A4A.Protocols.HilscherX.Profibus_DPM.DPV1C2.Closed.FB;
use A4A.Protocols.HilscherX.Profibus_DPM;

package A4A.Protocols.HilscherX.Profibus_DPM.DPV1 is

   type Instance is tagged limited private;
   type Instance_Access is access all Instance;

   procedure Initialise
     (Self           : in out Instance;
      Channel_Access : in Channel_Messaging.Instance_Access;
      DPV1C2_Closed_Access : in DPV1C2.Closed.FB.Instance_Access);
   
   procedure Quit
     (Self : in out Instance);

   function Is_Ready
     (Self : in Instance) return Boolean;
   
   function Is_Faulty
     (Self : in Instance) return Boolean;
   
   function Is_Terminated
     (Self : in Instance) return Boolean;
   
private

   Package_Ident : String := "A4A.Protocols.HilscherX.Profibus_DPM.DPV1";
   
   task type DPV1_Task_Type
     (Self : access Instance);
   
   type Block_Status is
     (X00,
      -- Initial
     
      X01,
      -- Initialized
     
      X02,
      -- Registered / Ready
     
      X03,
      -- Unregistering
     
      X04,
      -- Unregistered
     
      X05,
      -- Faulty
     
      X06
      -- Terminated
     );

   type Instance is tagged limited
      record
         
         Quit_Flag : Boolean := False;
         Init_Flag : Boolean := False;
         
         My_Channel   : Channel_Messaging.Instance_Access;
         
         Status       : Block_Status := X00;
         
         Register_App_FB : rcX_Register_App.FB.Instance;

         Do_Register_App    : Boolean := False;
         Register_App_Done  : Boolean := False;
         Register_App_Error : Boolean := False;
         
         Unregister_App_FB : rcX_Unregister_App.FB.Instance;

         Do_Unregister_App    : Boolean := False;
         Unregister_App_Done  : Boolean := False;
         Unregister_App_Error : Boolean := False;
         
         DPV1C2_Closed_FB : DPV1C2.Closed.FB.Instance_Access;
         
         Received_Packet : cifX_Packet_Access;
         
         Packet_Got   : Boolean := False;
         
         My_Receive_Queue : aliased
           Channel_Messaging.Queue_Package_Multiple.Message_Queue_Type;
         
         Error_Flag : Boolean;
         Last_Error : DWord;

         DPV1_Task    : DPV1_Task_Type (Instance'Access);
         
      end record;
   
end A4A.Protocols.HilscherX.Profibus_DPM.DPV1;

Le corps est présenté ci-dessous :

-----------------------------------------------------------------------
--                       Ada for Automation                          --
--                                                                   --
--              Copyright (C) 2012-2014, Stephane LOS                --
--                                                                   --
-- This library is free software; you can redistribute it and/or     --
-- modify it under the terms of the GNU General Public               --
-- License as published by the Free Software Foundation; either      --
-- version 2 of the License, 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    --
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU --
-- General Public License for more details.                          --
--                                                                   --
-- You should have received a copy of the GNU General Public         --
-- License along with this library; if not, write to the             --
-- Free Software Foundation, Inc., 59 Temple Place - Suite 330,      --
-- Boston, MA 02111-1307, USA.                                       --
--                                                                   --
-- As a special exception, if other files instantiate generics from  --
-- this unit, or you link this unit with other files to produce an   --
-- executable, this  unit  does not  by itself cause  the resulting  --
-- executable to be covered by the GNU General Public License. This  --
-- exception does not however invalidate any other reasons why the   --
-- executable file  might be covered by the  GNU Public License.     --
-----------------------------------------------------------------------

-- <summary>
-- This package is part of the binding to Hilscher cifX Device Driver,
-- a library allowing access to the cifX boards with numerous protocols.
-- </summary>
-- <description>
-- It provides :
-- - PROFIBUS DP Master data types,
-- - cifX C functions binding.
-- </description>
-- <group>Protocols</group>
-- <c_version>3.0.1</c_version>


package body A4A.Protocols.HilscherX.Profibus_DPM.DPV1 is

   procedure Initialise
     (Self           : in out Instance;
      Channel_Access :Channel_Messaging.Instance_Access;
      DPV1C2_Closed_Access : in DPV1C2.Closed.FB.Instance_Access) is
   begin

      Self.My_Channel := Channel_Access;
      Self.DPV1C2_Closed_FB := DPV1C2_Closed_Access;

      Self.Register_App_FB.Initialise
        (Channel_Access => Channel_Access,
         Receive_Queue  => Self.My_Receive_Queue'Unrestricted_Access);

      Self.Unregister_App_FB.Initialise
        (Channel_Access => Channel_Access,
         Receive_Queue  => Self.My_Receive_Queue'Unrestricted_Access);

      Self.DPV1C2_Closed_FB.Initialise
        (Channel_Access => Channel_Access);

      Self.Status := X00;
      Self.Error_Flag := False;

      Self.Init_Flag := True;

   end Initialise;

   procedure Quit
     (Self : in out Instance) is
   begin
      Self.Quit_Flag := True;
   end Quit;

   function Is_Ready
     (Self : in Instance) return Boolean is
   begin

      return Self.Status = X02;

   end Is_Ready;

   function Is_Faulty
     (Self : in Instance) return Boolean is
   begin

      return Self.Status = X05;

   end Is_Faulty;

   function Is_Terminated
     (Self : in Instance) return Boolean is
   begin

      return Self.Status = X06;

   end Is_Terminated;

   task body DPV1_Task_Type is

      My_Ident : String := Package_Ident & ".DPV1_Task";

      Error_String : String (1.. 20);
      Cmd_String   : String (1.. 20);

   begin

      loop

         case Self.Status is

         when X00 =>

            if Self.Init_Flag then

               A4A.Log.Logger.Put
                 (Who  => My_Ident,
                  What => "Initialised !");

               Self.Status  := X01;

            end if;

         when X01 =>

            if Self.Register_App_Done then

               if Self.Register_App_Error then

                  DWord_Text_IO.Put
                    (To   => Error_String,
                     Item => Self.Register_App_FB.Get_Last_Error,
                     Base => 16);

                  A4A.Log.Logger.Put
                    (Who  => My_Ident,
                     What => "Got an error : " & Error_String);

                  A4A.Log.Logger.Put
                    (Who  => My_Ident,
                     What => "App Not Registered !");

                  Self.Status  := X05;

               else

                  A4A.Log.Logger.Put
                    (Who  => My_Ident,
                     What => "App Registered !");

                  Self.Status  := X02;

               end if;

            end if;

         when X02 =>

            if Self.Quit_Flag then

               Self.Status  := X03;

            end if;

         when X03 =>

            if Self.Unregister_App_Done then

               if Self.Unregister_App_Error then

                  DWord_Text_IO.Put
                    (To   => Error_String,
                     Item => Self.Unregister_App_FB.Get_Last_Error,
                     Base => 16);

                  A4A.Log.Logger.Put
                    (Who  => My_Ident,
                     What => "Got an error : " & Error_String);

                  A4A.Log.Logger.Put
                    (Who  => My_Ident,
                     What => "App Not Unregistered !");

                  Self.Status  := X05;

               else

                  A4A.Log.Logger.Put
                    (Who  => My_Ident,
                     What => "App Unregistered !");

                  Self.Status  := X04;

               end if;

            end if;

         when X04 =>

            null;

         when X05 =>

            null;

         when X06 =>

            null;

         end case;

         -- Register the Application
         Self.Do_Register_App := (Self.Status = X01);

         Self.Register_App_FB.Cyclic
           (Do_Command => Self.Do_Register_App,
            Done       => Self.Register_App_Done,
            Error      => Self.Register_App_Error);

         -- Unregister the Application
         Self.Do_Unregister_App := (Self.Status = X03);

         Self.Unregister_App_FB.Cyclic
           (Do_Command => Self.Do_Unregister_App,
            Done       => Self.Unregister_App_Done,
            Error      => Self.Unregister_App_Error);

         if not Self.Packet_Got then
            Self.My_Receive_Queue.Get
              (Item   => Self.Received_Packet,
               Result => Self.Packet_Got);
         end if;

         if Self.Packet_Got then

            DWord_Text_IO.Put
              (To   => Cmd_String,
               Item => Self.Received_Packet.Header.Cmd,
               Base => 16);

            A4A.Log.Logger.Put
              (Who  => My_Ident,
               What => "Got a message : " & Cmd_String);

            case Self.Received_Packet.Header.Cmd is

               when rcX_Register_App.RCX_REGISTER_APP_CNF =>

                  Self.Register_App_FB.Handle_Answer (Self.Received_Packet);
                  Self.Packet_Got := False;

               when rcX_Unregister_App.RCX_UNREGISTER_APP_CNF =>

                  Self.Unregister_App_FB.Handle_Answer (Self.Received_Packet);
                  Self.Packet_Got := False;

               when DPV1C2.Closed.PROFIBUS_FSPMM2_CMD_CLOSED_IND =>

                  if Self.DPV1C2_Closed_FB.Is_Ready then
                     Self.DPV1C2_Closed_FB.Handle_Indication
                       (Self.Received_Packet);
                     Self.Packet_Got := False;
                  end if;

               when others =>

                  A4A.Log.Logger.Put
                    (Who  => My_Ident,
                     What => "Received unmanaged message !!!");
                     Self.Packet_Got := False;

            end case;

         end if;

         exit when
           ((Self.Status = X00) or (Self.Status = X04) or (Self.Status = X05))
           and Self.Quit_Flag;

      end loop;

      A4A.Log.Logger.Put
        (Who  => My_Ident,
         What => "Terminated !");

      Self.Status  := X06;

   end DPV1_Task_Type;

end A4A.Protocols.HilscherX.Profibus_DPM.DPV1;

La spécification du bloc fonction gérant l’indication connexion fermée :

-----------------------------------------------------------------------
--                       Ada for Automation                          --
--                                                                   --
--              Copyright (C) 2012-2014, Stephane LOS                --
--                                                                   --
-- This library is free software; you can redistribute it and/or     --
-- modify it under the terms of the GNU General Public               --
-- License as published by the Free Software Foundation; either      --
-- version 2 of the License, 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    --
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU --
-- General Public License for more details.                          --
--                                                                   --
-- You should have received a copy of the GNU General Public         --
-- License along with this library; if not, write to the             --
-- Free Software Foundation, Inc., 59 Temple Place - Suite 330,      --
-- Boston, MA 02111-1307, USA.                                       --
--                                                                   --
-- As a special exception, if other files instantiate generics from  --
-- this unit, or you link this unit with other files to produce an   --
-- executable, this  unit  does not  by itself cause  the resulting  --
-- executable to be covered by the GNU General Public License. This  --
-- exception does not however invalidate any other reasons why the   --
-- executable file  might be covered by the  GNU Public License.     --
-----------------------------------------------------------------------

-- <summary>
-- This package is part of the binding to Hilscher cifX Device Driver,
-- a library allowing access to the cifX boards with numerous protocols.
-- </summary>
-- <description>
-- It provides :
-- - PROFIBUS DP V1 Master Class 2 data types,
-- </description>
-- <group>Protocols</group>
-- <c_version>3.0.1</c_version>


with A4A.Log;

with A4A.Protocols.HilscherX.Channel_Messaging;
use A4A.Protocols.HilscherX;

package A4A.Protocols.HilscherX.Profibus_DPM.DPV1C2.Closed.FB is

   type Instance is tagged limited private;
   type Instance_Access is access all Instance;

   procedure Initialise
     (Function_Block : in out Instance;
      Channel_Access : in Channel_Messaging.Instance_Access);

   procedure Cyclic
     (Function_Block : in out Instance;
      Got_Indication : out Boolean);
   
   procedure Handle_Indication
     (Function_Block : in out Instance;
      Indication     : in cifX_Packet_Access);
   
   procedure Answer
     (Function_Block : in out Instance);
   
   function Is_Ready
     (Function_Block : in Instance) return Boolean;
   
   function Get_Data
     (Function_Block : in Instance)
      return PROFIBUS_FSPMM2_CLOSED_IND_T;
   
private

   My_Ident : String :=
     "A4A.Protocols.HilscherX.Profibus_DPM.DPV1C2.Closed.FB";
   
   type Block_Status is
     (X00,
      -- Initial
     
      X01,
      -- Indication Got
     
      X02
      -- Send packet
     );

   type Instance is tagged limited
      record
         
         My_Channel   : Channel_Messaging.Instance_Access;
         
         Status       : Block_Status := X00;
         
         Indication   : cifX_Packet_Access;
         Response     : cifX_Packet_Access;

         Closed_Ind   : PROFIBUS_FSPMM2_PACKET_CLOSED_IND_T_Access;
         Closed_Res   : PROFIBUS_FSPMM2_PACKET_CLOSED_RES_T_Access;
         
         Closed_Ind_Data : PROFIBUS_FSPMM2_CLOSED_IND_T;
         
         Indication_Got : Boolean := False;
         Answer_Got     : Boolean := False;
         Packet_Sent    : Boolean := False;
         
      end record;
   
end A4A.Protocols.HilscherX.Profibus_DPM.DPV1C2.Closed.FB;

Et son corps :

-----------------------------------------------------------------------
--                       Ada for Automation                          --
--                                                                   --
--              Copyright (C) 2012-2014, Stephane LOS                --
--                                                                   --
-- This library is free software; you can redistribute it and/or     --
-- modify it under the terms of the GNU General Public               --
-- License as published by the Free Software Foundation; either      --
-- version 2 of the License, 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    --
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU --
-- General Public License for more details.                          --
--                                                                   --
-- You should have received a copy of the GNU General Public         --
-- License along with this library; if not, write to the             --
-- Free Software Foundation, Inc., 59 Temple Place - Suite 330,      --
-- Boston, MA 02111-1307, USA.                                       --
--                                                                   --
-- As a special exception, if other files instantiate generics from  --
-- this unit, or you link this unit with other files to produce an   --
-- executable, this  unit  does not  by itself cause  the resulting  --
-- executable to be covered by the GNU General Public License. This  --
-- exception does not however invalidate any other reasons why the   --
-- executable file  might be covered by the  GNU Public License.     --
-----------------------------------------------------------------------

-- <summary>
-- This package is part of the binding to Hilscher cifX Device Driver,
-- a library allowing access to the cifX boards with numerous protocols.
-- </summary>
-- <description>
-- It provides :
-- - PROFIBUS DP V1 Master Class 2 data types,
-- </description>
-- <group>Protocols</group>
-- <c_version>3.0.1</c_version>


package body A4A.Protocols.HilscherX.Profibus_DPM.DPV1C2.Closed.FB is

   procedure Initialise
     (Function_Block : in out Instance;
      Channel_Access :Channel_Messaging.Instance_Access) is
   begin

      Function_Block.My_Channel := Channel_Access;

      Function_Block.Status := X00;

   end Initialise;

   procedure Cyclic
     (Function_Block : in out Instance;
      Got_Indication : out Boolean) is
   begin

      case Function_Block.Status is

         when X00 =>

            if Function_Block.Indication_Got then

               Function_Block.Status := X01;
               Function_Block.Indication_Got := False;

            end if;

         when X01 =>

            if Function_Block.Answer_Got then

               A4A.Log.Logger.Put
                 (Who  => My_Ident & ".Cyclic",
                  What => "Answer Got");

               Function_Block.Status := X02;
               Function_Block.Answer_Got := False;

            end if;

         when X02 =>

            Function_Block.My_Channel.Send
              (Item   => Function_Block.Response,
               Result => Function_Block.Packet_Sent);

            if Function_Block.Packet_Sent then

               A4A.Log.Logger.Put
                 (Who  => My_Ident & ".Cyclic",
                  What => "Response Sent");

               Function_Block.Status := X00;

            end if;

      end case;

      Got_Indication := Function_Block.Status = X01;

   end Cyclic;

   procedure Handle_Indication
     (Function_Block : in out Instance;
      Indication     : in cifX_Packet_Access) is
   begin

      Function_Block.Indication := Indication;
      Function_Block.Closed_Ind :=
        From_cifX_Packet (Function_Block.Indication);

      Function_Block.Closed_Ind_Data := Function_Block.Closed_Ind.Data;

      -- We use the same packet for the response
      -- So we don't need to return it to the pool nor get a new one

      Function_Block.Response   := Indication;
      Function_Block.Closed_Res :=
        From_cifX_Packet (Function_Block.Response);

      -- We change only relevant fields
      Function_Block.Closed_Res.Head.Dest := 16#20#;
      Function_Block.Closed_Res.Head.Cmd  := PROFIBUS_FSPMM2_CMD_CLOSED_RES;

      -- The response is ready to be sent back

      A4A.Log.Logger.Put
        (Who  => My_Ident & ".Handle_Indication",
         What => "Indication received");

      Function_Block.Indication_Got := True;

   end Handle_Indication;

   procedure Answer
     (Function_Block : in out Instance) is
   begin

      Function_Block.Answer_Got := True;

   end Answer;

   function Is_Ready
     (Function_Block : in Instance) return Boolean is
   begin

      return Function_Block.Status = X00;

   end Is_Ready;

   function Get_Data
     (Function_Block : in Instance)
      return PROFIBUS_FSPMM2_CLOSED_IND_T is
   begin

      return Function_Block.Closed_Ind_Data;

   end Get_Data;

end A4A.Protocols.HilscherX.Profibus_DPM.DPV1C2.Closed.FB;

On notera que l’on se sert ici du même paquet reçu pour la réponse en changeant juste les champs comme nécessaire.

Cordialement,
Stéphane