CoE / SoE / AoE

Mit Hilfe der EtherCAT Simulation ist es möglich, die Protokolle CoE (Can over EtherCAT), SoE (Sercos over EtherCAT) und AoE (ADS over EtherCAT) weiter zu routen (z.B. an die SPS). Es ist somit möglich, aus der SPS heraus über diese Protokolle Daten zu lesen und zu schreiben.

Routen von CoE und SoE an eine PLC

Folgende zwei Schritte sind erforderlich:

1. Die NetId und der Port an die die Daten weitergeleitet werden sollen, müssen dem EtherCAT-Simulation Device bekannt gemacht werden. Dies geschieht über ein ADS-Write an die NetID des Simulation Devices, PortNr.: 0xFFFF, IndexGroup: 0xFF01, IndexOffset:1

Beispiel: Anmelden des Mailbox-Empfängers

//AMSNetID of Simulation Device
netIdSimu := '5.35.2.224.2.1';

//AMSNetID of PLC Project on Sim. Dev.
arrNetId[0]:= 5;        
arrNetId[1]:= 35;
arrNetId[2]:= 2;
arrNetId[3]:= 224;
arrNetId[4]:= 1; //system ID 1.1
arrNetId[5]:= 1;
arrNetId[6]:= 16#53; //Port of PLC runtime (1)
arrNetId[7]:= 16#03; //Port of PLC runtime (2) (Port 851 = 16#353)

// sent registration to Simulation driver
fbAdsWrite(WRITE:= FALSE);
fbAdsWrite(NETID:= netIdSimu, PORT:= 16#FFFF, IDXGRP:= 16#FF01, IDXOFFS:= 1, SRCADDR:= ADR(arrNetId), LEN:= 8, WRITE:= TRUE);
2. Abfangen und beantworten der ADS Indications. Dabei ist in der Indexgroup der Mailboxdienst hinterlegt, die PortID identifiziert den sendenden oder empfangenden Teilnehmer und anhand von Index und Sub-Index werden die Daten angegeben, auf die zugegriffen werden soll.

Beispiel: Abfangen und beantworten von Indications:

//capture all write requests
fbAdsWriteInd();
IF fbAdsWriteInd.VALID = TRUE THEN //true if a write command has been sent
    IF fbAdsWriteInd.IDXGRP = 16#8000F302 THEN //F302 = CoE request, F420 = SoE request
        //fill in your custom CoE Object Dictionary
        // example reaction
        IF fbAdsWriteInd.IDXOFFS = 16#80000000 THEN (*error*)
            fbAdsWriteRes(
                          NETID:= fbAdsWriteInd.NETID,
                          PORT:= fbAdsWriteInd.PORT,
                          INVOKEID:= fbAdsWriteInd.INVOKEID,
                          RESULT:=  0,
                          RESPOND:= TRUE);
            fbAdsWriteRes(RESPOND:= FALSE);
        ELSE
            fbAdsWriteRes(
                          NETID:= fbAdsWriteInd.NETID,
                          PORT:= fbAdsWriteInd.PORT,
                          INVOKEID:= fbAdsWriteInd.INVOKEID,
                          RESULT:= 0,
                          RESPOND:= TRUE);
            fbAdsWriteRes(RESPOND:= FALSE);
        END_IF
    END_IF
    fbAdsWriteInd(CLEAR:= TRUE);
    fbAdsWriteInd(CLEAR:= FALSE);
END_IF

//capture all read requests
fbAdsReadInd();
IF fbAdsReadInd.VALID = TRUE THEN
    IF fbAdsReadInd.IDXGRP = 16#8000F302 THEN (*CoE*)
        // fill in your CoE Object Dictionary
        // example reaction
        IF fbAdsReadInd.IDXOFFS = 16#80000000 THEN (*error*)
            fbAdsReadRes(
                          NETID:= fbAdsReadInd.NETID,
                          PORT:= fbAdsReadInd.PORT,
                          INVOKEID:= fbAdsReadInd.INVOKEID,
                          LEN:= 0,
                          RESULT:= 16#34567890,
                          RESPOND:= TRUE);
        ELSE
            IF fbAdsReadInd.IDXOFFS = 16#60000000 THEN (*short response*)
                fbAdsReadRes(
                          NETID:= fbAdsReadInd.NETID,
                          PORT:= fbAdsReadInd.PORT,
                          INVOKEID:= fbAdsReadInd.INVOKEID,
                          DATAADDR:= ADR(arrNetId),
                          LEN:= 2,
                          RESULT:= 0,
                          RESPOND:=  TRUE);
            ELSE
                fbAdsReadRes(
                          NETID:= fbAdsReadInd.NETID,
                          PORT:= fbAdsReadInd.PORT,
                          INVOKEID:= fbAdsReadInd.INVOKEID,
                          DATAADDR:= ADR(arrNetId),
                          LEN:= 6,
                          RESULT:= 0,
                          RESPOND:= TRUE);
            END_IF
        END_IF
        fbAdsReadRes(RESPOND:= FALSE);
    END_IF
    fbAdsReadInd(CLEAR:= TRUE);
    fbAdsReadInd(CLEAR:= FALSE);
END_IF

Wie im Beispiel zu sehen ist, erfolgt eine Prüfung, ob die Indication gültig ist und wenn ja, ob diese vom Simulation-Device gesendet wurde. Ist beides der Fall, handelt es sich um eine weitergeleitete Mailbox-Nachricht auf die entsprechend reagiert werden muss. Die IndexGroup zeigt das Mailboxprotokoll an (16#8000F302 für CoE und 16#8000F420 für SoE).

Das oben beschriebene Sample kann hier heruntergeladen werden: Download

Versenden von CoE Emergency Fehlermeldungen

Das Versenden von CoE Emergency Fehlermeldungen erfolgt analog wie oben gezeigt per ADS Write an die NetId des EtherCAT-Simulation Devices, PortNr.: Portnummer der Klemme, die die Emergency versenden soll, IndexGroup: 0x00FF41, IndexOffset:0.

Versenden von SoE Notifications

Das Versenden von SoE Notifications Fehlermeldungen erfolgt analog wie oben gezeigt per ADS Write an die NetId des EtherCAT-Simulation Devices, PortNr.: Portnummer der Klemme, die die Notification versenden soll, IndexGroup: 0x00FF42, IndexOffset: ergibt sich anhand der Typdefinition TETHERCAT_SOE_ADS_IOFFS:

typedef struct TETHERCAT_SOE_ADS_IOFFS
{
       USHORT IDN;
       union
       {
              struct
              {
                     BYTE          DataState     : 1;
                     BYTE          Name          : 1;
                     BYTE          Attribute     : 1;
                     BYTE          Unit          : 1;
                     BYTE          Min           : 1;
                     BYTE          Max           : 1;
                     BYTE          Value         : 1;
                     BYTE          Default       : 1;
              };
              BYTE   Elements;
       };
       BYTE          DriveNo       : 3;
       BYTE          Reserved2     : 4;
       BYTE          Command       : 1;
} ETHERCAT_SOE_ADS_IOFFS

Beispiel: 0x00401234 -> Für die SoE IDN 1234 wird hier das Value Flag adressiert.

Routen von AoE an eine PLC

Die Anmeldung des Mailbox-Empfängers erfolgt identisch wie unter CoE und SoE beschrieben. Das Handling von AoE in der PLC erfolgt ebenfalls in ähnlicher Struktur, ist jedoch aufgrund des Funktionsumfangs von AoE mit mehreren Abfragen verbunden. Ein Beispiel ist im Folgenden aufgeführt und kann hier heruntergeladen werden: Download

fbAdsReadWriteInd();
IF fbAdsReadWriteInd.VALID = TRUE THEN
    MEMCPY(ADR(arrTmp), fbAdsReadWriteInd.DATAADDR, fbAdsReadWriteInd.WRTLENGTH);
    //copy of data in temp variable
    //evaluate destination of AeE command (e.g. terminal)-first 6 byte in arrTmp
    FOR Idx := 0 TO 5 DO
        arrDest[Idx] := arrTmp[Idx];
    END_FOR         
    IF smyDestId = F_CreateAmsNetId(arrDest) THEN
        //evaluate port (different physical ports or different services of terminal may be possible)
        IF fbAdsReadWriteInd.PORT = 16#1000 THEN //Io-Link Port 1
            // evaluate command type - byte 16 : write = 3, read = 2
            IF arrTmp[16] = 3 THEN // write command 
                //evaluate index group and index offset as shown in CoE and SoE
                //index group: evaluate service  (e.g. object register of Io-Link Terminal)
                IF fbAdsReadWriteInd.IDXGRP = 16#8000F302 THEN 
                    IF fbAdsReadWriteInd.IDXOFFS = 16#80000000 THEN //adressing of service
                        MEMCPY(ADR(arrTmp), fbAdsReadWriteInd.DATAADDR + 8, 8);
                        //change destination net ID and source net ID                    
                        MEMCPY(ADR(arrTmp) + 8, fbAdsReadWriteInd.DATAADDR, 8);
                        arrTmp[18].0:= 1; (*set Respond Bit*)
                        arrTmp[20]:= 4; (*Length*)
                        arrTmp[21]:= 0;
                        arrTmp[22]:= 0;
                        arrTmp[23]:= 0;
                        arrTmp[32]:= 0;  (*Response Code*)
                        arrTmp[33]:= 0;
                        arrTmp[34]:= 0;
                        arrTmp[35]:= 0;
                        fbAdsReadWriteRes(
                                          NETID:= fbAdsReadWriteInd.NETID,
                                          PORT:= fbAdsReadWriteInd.PORT,
                                          INVOKEID:= fbAdsReadWriteInd.INVOKEID,
                                          DATAADDR:= ADR(arrTmp),
                                          LEN:= 36, RESPOND:= TRUE);
                    END_IF
                END_IF
            ELSIF arrTmp[16] = 2 THEN //read command 
                IF fbAdsReadWriteInd.IDXGRP = 16#8000F302 THEN 
                    IF fbAdsReadWriteInd.IDXOFFS = 16#80000000 THEN //adressing of service
                        MEMCPY(ADR(arrTmp), fbAdsReadWriteInd.DATAADDR + 8, 8);
                        //change destination net ID and source net ID                    
                        MEMCPY(ADR(arrTmp) + 8, fbAdsReadWriteInd.DATAADDR, 8);
                        arrTmp[18].0:= 1; (* set Respond Bit*)
                        arrTmp[20]:= 14; (*Len*)
                        arrTmp[21]:= 0;
                        arrTmp[22]:= 0;
                        arrTmp[23]:= 0;
                        arrTmp[32]:= 0; (*Response Code*)
                        arrTmp[33]:= 0;
                        arrTmp[34]:= 0;
                        arrTmp[35]:= 0;
                        arrTmp[36]:= 6; (*Len*)
                        arrTmp[37]:= 0;
                        arrTmp[38]:= 0;
                        arrTmp[39]:= 0;
                        arrTmp[40]:= arrNetId[0]; (*Daten*)
                        arrTmp[41]:= arrNetId[1];
                        arrTmp[42]:= arrNetId[2];
                        arrTmp[43]:= arrNetId[3];
                        arrTmp[44]:= arrNetId[4];
                        arrTmp[45]:= arrNetId[5];
                        fbAdsReadWriteRes(
                                          NETID:= fbAdsReadWriteInd.NETID,
                                          PORT:= fbAdsReadWriteInd.PORT,
                                          INVOKEID:= fbAdsReadWriteInd.INVOKEID,
                                          DATAADDR:= ADR(arrTmp),
                                          LEN:= 46, RESPOND:= TRUE);
                    END_IF;    
                END_IF;        
            END_IF                
        END_IF
    END_IF; 
    fbAdsReadWriteRes(
                     NETID:= fbAdsReadWriteInd.NETID,
                     PORT:= fbAdsReadWriteInd.PORT,
                     INVOKEID:= fbAdsReadWriteInd.INVOKEID,
                     DATAADDR:= ADR(arrTmp),
                     LEN:= fbAdsReadWriteInd.WRTLENGTH,
                     RESPOND:= TRUE);
    fbAdsReadWriteRes(RESPOND:= FALSE);
    fbAdsReadWriteInd(CLEAR:= TRUE);
    fbAdsReadWriteInd(CLEAR:= FALSE);
END_IF