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