# IO-Link Kommunikation per S7comm



## Hans54216 (24 Mai 2020)

Ich hab mit dem Thema schon in meinem alten Thread angefangen. Auf Wunsch von Thomas_v2.1 hier nun die Abspaltung des Themas in einen eigenen Thread.


Thema:
Mit dem Siemens S7-PCT Tool (Step7 v5 oder Step7 v10-16 (TIA)) kann die Siemens IO-Link Master Baugruppe auf einer ET200 (ProfiBus/ProfiNet) parametriert werden und auch die Daten der Angeschlossenen Sensoren ausgelesen werden. Interessant dabei ist, dass das Programmiergerät (PC) lediglich per Netzwerk mit der Steuerung (in meinem Fall eine 840d sl (NCU 730.3 PN mit einer 317F-3 PN/DP)) verbunden ist.


Es gibt bereits von einigen Herstellern Software die mit ProfiNet IO-Link Mastern kommunizieren, wobei das ProfiNet-Netzwerk dabei als "normales" TCP/IP Netz verwendet wird. Damit ist jedoch kein Zugriff auf ProfiBus Master oder auf die Siemens ET200 Master möglich.
Was jedoch immer funktioniert ist der klassische Weg über die PLC. Dies hat jedoch einen Großen Programmieraufwand + Parametrieraufwand zur Folge. Auch ist der Speicher der PLC recht begrenzt.


Ich habe mir daraufhin die Kommunikation der S7-PCT Tools angesehen und darin s7comm Pakete entdeckt. Da ich zusammen mit dem Thomas_v2.1 schon die NC Kommunikation in die LibNoDave integriert habe ist das Ziel nun auch den IO-Link Part mit aufzunehmen.




Verlauf aus dem Thema "DotNetSiemensPLCToolBoxLibrary (LibNoDave) Zugriff auf Dual-Port RAM / FB15"


DotNetSiemensPLCToolBoxLibrary (LibNoDave) Zugriff auf Dual-Port RAM / FB15




Hans54216 schrieb:


> Hab mir das PCT-Tool vom Step7 angesehen und das kann vom PC Step7 Manager unter Zuhilfenahme der Hardwarekonfig mit IO-Link Master Baugruppen auf der ET200 kommunizieren.
> Bei meinem Versuchsaufbau hab ich an der 840d sl (PLC 317) eine ET200 per ProfiBus und auf dieser nen Siemens 4-Port IO-Link Master.
> 
> 
> ...






Hans54216 schrieb:


> Ich hab noch mal nen schrieb von der Kommunikation gemacht und ein paar Screenshots der Übertragenen Werte.
> Auch hab ich die IODD (Beschreibungsdatei IO-Link) auch mit rein gepackt.
> 
> 
> ...






Thomas_v2.1 schrieb:


> Das was da im Datenteil transportiert wird, scheint zumindest sehr speziell zu sein. Vom PCT werden öfters die gleichen Anfragen geschickt, aber in der Antwort kommt immer etwas anderes zurück. Das scheint auf den ersten Blick irgendwie zustandsabhängig zu sein was dort zurückkommt, oder es kann so etwas wie ein Verzeichnis gebrowst werden. Es gibt auch nur ganz selten mal eine Längenangabe vorab, der dann genau diese Anzahl an Bytes folgen.
> 
> 
> Ich habe auch schon mal in die Spezifikation geschaut, auf den ersten Blick sehe ich da auch keine Anhaltspunkte. Mit wem spricht die PCT Software denn da, mit dem Master, oder gibt es auch Durchgriff auf die angeschlossenen Geräte?






Hans54216 schrieb:


> PCT Tool muss, zumindest über den Master, mit dem Druckschalter IFM reden, da nur er die ganzen actual Daten hat.
> 
> 
> IO-Link verfügt über Zyklische Prozessdaten zur PLC und einen Asynchronen Kommunikationskanal. Dieser Asynchrone Kommunikationskanal kann auch aus der PLC heraus genutzt werden um weitere Daten auszulesen oder z.B. Schaltpunke einzustellen.
> ...






Thomas_v2.1 schrieb:


> Ok, mit deinem letzten Logfile kommt man weiter, und es lässt sich ein Zusammenhang zur IO-Link Spezifikation herstellen.
> 
> 
> Z.B. in #257 kommt die Seriennummer zurück. In #248 scheint dieser Datensatz vorselektiert zu werden, mit der 0x15 die sich am Ende findet. 0x15 ist laut Spec der Index für Serial-Number.
> ...






Hans54216 schrieb:


> Kann mir auch keinen Reim draus machen.
> 
> 
> Hab noch mal ne Aufzeichnung gemacht. Diesmal hab ich in der Hardware Konfig den Parameter des IO-Link Masters auf "Ohne PCT" gestellt. Somit ist kein Download der Beschreibung erforderlich und andererseits kennt das PCT somit die Beschreibung nicht und zeigt nur die Empfangenen Parameter an.
> ...






Thomas_v2.1 schrieb:


> Die Angaben Index und Subindex und den Wert in der Antwort findet sich wieder. Das entspricht aber nicht dem Aufbau aus dem IO-Link PDF. Es scheint, das ist eine Mixtur aus IO-Link Standard und einem Eigengewächs drumherum.






Thomas_v2.1 schrieb:


> Auf IO-Link Ebene wird so wie ich das gesehen habe viel mit einzelnen Bits, Nibbles und Bytes und hantiert um das Ganze effektiv zu halten. Das macht vermutlich kein Sinn das alles bis auf Master-Ebene durchzureichen. In den Nutzdaten auf Netzwerkebene stehen an vielen Stellen in einem Byte nur entweder eine 1 oder eine 0. Ich habe auch schon vermutet ob dort vlt. einzelne Bits in einem ganzen Byte untergebracht werden. Da fehlt aktuell der Startpunkt an dem sich ansetzen lässt. Das einzige was man sicher finden kann sind die Indizes/Subindizes und die Daten der Antwort.






Hans54216 schrieb:


> CAP:
> Siemens spezifisch: 227
> Andere Hersteller (Master): 0xB400 (-19456)
> 
> ...


----------



## Hans54216 (24 Mai 2020)

Thomas_v2.1 schrieb:


> Soweit ich das sehe wird die CAP häufig nur zur Anfrage/Antwort-Kennung verwendet.
> Die ID der Anfangsadresse der Baugruppe finde zumindest ich so wie ich das aktuell sehe nicht wieder. Da stellt sich die Frage, was macht das PCT wenn du mehrere IO-Link Baugruppen an einer CPU hast, wie werden diese angesprochen? Da hilft nur ausprobieren, Adressen ändern, laden, abfragen und prüfen ob und wo sich etwas ändert.



Hab mal verschiedene Startadressen für den IO-Link Master vergeben.
E264 (alte Konstellation), E300, E500, E3300, E10000
Anhang anzeigen Eingangsadresstest_20200524.rar





Thomas_v2.1 schrieb:


> So wie es aussieht ist die Interpretation einiger Daten von vorigen Daten abhängig (kontextsensitiv), was es nicht leichter macht das Protokoll nachzuvollziehen.
> Auch wenn ich IO-Link selber bisher nicht verwendet habe, könnte ich es mir schon nützlich vorstellen, diverse Diagnosedaten von extern abfragen zu können ohne das SPS-Programm anfassen zu müssen. Darum ist es vielleicht sinnvoll, alles zu diesen Thema in einen separaten Thread zu verschieben, da es mit NC ja nicht mehr direkt etwas zu tun hat. Wobei das mit der S7-300 ja auch alles "Legacy" Hardware ist.



PCT-Tool ist auch in TIA und S7-1200 und S7-1500 weiter im Einsatz.

ET200 IO-Link Masterbaugruppe ist sowie so noch nicht so alt und hat mit der PLC erstmal nicht zu tun. Einzige ist, dass bei S7-1500 sicher S7commPlus zum Einsatz kommt.

Noch funktioniert aber das gute alte S7comm ja auch mit den neuen PLCs und in der NC Welt wird die 300 eh noch ne weile im Einsatz sein, da die neue Sinumerik One und TIA aktuell ne rechte Krankheit sind.


----------



## Thomas_v2.1 (24 Mai 2020)

Hans54216 schrieb:


> Hab mal verschiedene Startadressen für den IO-Link Master vergeben.
> E264 (alte Konstellation), E300, E500, E3300, E10000



Das macht überhaupt keinen Unterschied. Vielleicht läuft die Adressierung über Teilnehmernummer [3] und Steckplatz [8] wie es auch in PCT angezeigt wird.


----------



## Hans54216 (24 Mai 2020)

Thomas_v2.1 schrieb:


> Das macht überhaupt keinen Unterschied. Vielleicht läuft die Adressierung über Teilnehmernummer [3] und Steckplatz [8] wie es auch in PCT angezeigt wird.



Hab jetzt das auslesen der Parameter für die Eingangsadresse E10000 aufgezeichnet. Vorher war nur der Verbindungsaufbau + Prüfen der Port dabei.

Anhang anzeigen DP3-ST8-E10000_GetParameter.rar


----------



## Thomas_v2.1 (24 Mai 2020)

Macht so wie ich das sehe auch keinen Unterschied, oder ist nur eine einzige IO-Link Baugruppe erlaubt?
Ich habe dir mal eine Wireshark Testversion gebaut in der ein paar Feldwerte angezeigt werden.


----------



## Hans54216 (24 Mai 2020)

Mach morgen mal ne Erweiterung zu meinem Testaufbau. Zusätzliche ET200, weitere IO-Link Master und mehr Sensoren.


----------



## Hans54216 (25 Mai 2020)

Hab jetzt mal ne Vergleich mit unterschiedlichen DP Adressen und unterschiedlichem Steckplatz.

PS: Was in den vorherigen Aufnahmen DP3 war ist nun DP6.
Zusätzlich ist eine Neue ET200 mit DP3 hinzugekommen.








Anhang anzeigen Adresstest_20200525.rar


Anhang anzeigen Adresstest_20200525_2.rar


----------



## Hans54216 (25 Mai 2020)

Hab noch eine Aufzeichnung des aktuellen Aufbaus gemacht, wenn ich den kompletten ProfiBus auswähle.




Anhang anzeigen DP_Online.rar


----------



## Thomas_v2.1 (25 Mai 2020)

Die Adresse scheint also lokalisiert.
Hast du schon mal versucht ob du auch Parameter schreiben kannst?


----------



## Hans54216 (25 Mai 2020)

Thomas_v2.1 schrieb:


> Hast du schon mal versucht ob du auch Parameter schreiben kannst?


Einen einzelnen Parameter kann ich scheinbar nicht schreiben, hab ich zumindest nicht gefunden. Ich kann jedoch die Parameter verändern und das ganze dann laden.




Anhang anzeigen DP6-ST8-P1_ParameterDownload_20200525.rar


----------



## Thomas_v2.1 (25 Mai 2020)

Was es übrigens auch noch gibt, ist die Funktion READREC in S7comm. Das wird z.B. verwendet wenn du eine PN-CPU hast und dort den Zustand der Netzwerkports abfragst. Ich habe noch nicht getestet ob das generell bei allen Baugruppen möglich ist, aber da die Siemens Bausteine laut Doku den READREC intern verwenden (müsste man mal reingucken), lässt sich das vlt. selber aus ein paar READREC Aufrufen nachstellen. Dann solltest du zumindest ein paar Werte lesen können. Das ist relativ einfach und von den Parametern her auf dem Netzwerk fast identisch zum READREC Aufruf aus dem SPS-Programm heraus.


----------



## Hans54216 (25 Mai 2020)

Wie muss man das mit dem ReadRec angehen? Wie du richtig schreibst wird in der PLC der SFB52/SFB53 (RDREC/WRREC) verwendet.
Es ist jedoch auch zu beachten ob dann die Begrenzung der PLC Zutrifft. Bei manchen PLCs darf nur ein SFB (denk es war der RDREC/WRREC) aktiv sein.


----------



## Thomas_v2.1 (25 Mai 2020)

Für RDREC benötigst du die ID und die Baugruppenadresse, genau so wie beim Aufruf aus dem SPS-Programm. Ich habe ein rudimentäres Python Programm mit dem ich die Funktion ausführen kann, mit dem ich ein paar Fehlaufrufe ausgetestet habe. Wenn du Python hast dann kannst du das damit mal ausprobieren.
Wie gesagt, ich weiß nicht ob damit generell alles gelesen werden kann, oder nur die für Profinet relevanten Datensätze. Ich denke mal das wird bei IO-Link auch nur funktionieren, wenn die Daten mit einem einzigen RDREC Aufruf zurückkommen.


----------



## Hans54216 (25 Mai 2020)

Thomas_v2.1 schrieb:


> Für RDREC benötigst du die ID und die Baugruppenadresse, genau so wie beim Aufruf aus dem SPS-Programm.


Hab mir den IO-Link Baustein gerade angesehen. Ist recht unübersichtlicher AWL Coder der aus einer SCL Quelle generiert ist.



Thomas_v2.1 schrieb:


> Wie gesagt, ich weiß nicht ob damit generell alles gelesen werden kann, oder nur die für Profinet relevanten Datensätze. Ich denke mal das wird bei IO-Link auch nur funktionieren, wenn die Daten mit einem einzigen RDREC Aufruf zurückkommen.


Soweit ich das gesehen habe wird nur der RDREC/RDREC für ProfiBus (sollte auch für ProfiNet funktionieren) verwendet. Mit der Angabe des Index/Subindex wird über ein paar Schleifen die Länge berechnet und ein paar Daten im Instanz-DB rum kopiert.




Thomas_v2.1 schrieb:


> Ich habe ein rudimentäres Python Programm mit dem ich die Funktion ausführen kann, mit dem ich ein paar Fehlaufrufe ausgetestet habe. Wenn du Python hast dann kannst du das damit mal ausprobieren.


Kannst du mir das Programm zukommen lassen?


----------



## Thomas_v2.1 (25 Mai 2020)

Ich hänge die Python Testfunktion einmal an. Die Antworten werden nicht angezeigt, das müsstest du dir in Wireshark ansehen. In der s7-rdrec.py musst du die IP-Adresse, vermutlich den TSAP und dann die ID und die Diagnoseadresse anpassen. Die ID 0xf841 oder 0xf831 sollten bei einer PN-CPU auf die Diagnoseadresse der PN-Schnittstelle funktionieren.


----------



## Hans54216 (25 Mai 2020)

Bekomme schon beim Verbindungsaufbau nen Fehler.




Anhang anzeigen Verbindungsaufbau.rar



Was muss ich bei TSAP eintragen?


----------



## Thomas_v2.1 (25 Mai 2020)

Also bei deinen Aufzeichnungen wird f402 verwendet, also 0xf4, 0x02.
Das erste Byte ist die Verbindungsressource, im zweiten Byte ist Rack/Slot eincodiert. Hier Rack 0, Slot 2. Was bei dir die Ressource 0xf4 bedeutet kann ich nicht sagen, ich kenne nur 1, 2, 3 oder für projektierte Verbindungen ab 0x10.


----------



## Hans54216 (26 Mai 2020)

Problem war scheinbar der Virenscanner.
Habs jetzt in ner VM ausgeführt und folgende Ergebnisse erhalten

#IO-Link 
#Index 227
#ID=Adresse 300
resp = s7.sendReadrec(227, 0x12C)

Antwort: Invalid address (0x05)

Anhang anzeigen rdrec_20200526.rar


----------



## Thomas_v2.1 (26 Mai 2020)

Ich kann das letzte rar nicht entpacken.

Scheint so als ließe sich der RDREC nicht für beliebige Datensätze über S7comm nutzen, oder das funktioniert nicht mit jeder CPU. Funktioniert das mit dem RDREC Aufruf aus dem SPS-Programm heraus denn so einfach? Dann frage ich mir warum Siemens da noch einen Baustein drumherumbauen musste.

Aber wenn RDREC nicht geht, dann müssen wir wohl doch noch bei der anderen Variante gucken ob wir dahinterkommen.


----------



## Hans54216 (26 Mai 2020)

Thomas_v2.1 schrieb:


> Ich kann das letzte rar nicht entpacken.



Hier als Zip.

Anhang anzeigen rdrec_20200526.zip




Thomas_v2.1 schrieb:


> Scheint so als ließe sich der RDREC nicht für beliebige Datensätze über S7comm nutzen, oder das funktioniert nicht mit jeder CPU. Funktioniert das mit dem RDREC Aufruf aus dem SPS-Programm heraus denn so einfach? Dann frage ich mir warum Siemens da noch einen Baustein drum herumbauen musste.


In der PLC funktioniert der RDREC. Wird ja wie ich gestern bereits geschrieben hab im IO-Link Baustein verwendet.
Siemens hat den Baustein drum rum, um die gelesenen Daten zusammen zu kopieren oder um z.B. eine Offset zu berechnen.



```
SET   
      SAVE  
      =     L     26.1
      U     #R_TRIG_REQ_old
      NOT   
      U     #REQ
      =     #R_TRIG_REQ
      U     #REQ
      =     #R_TRIG_REQ_old
      L     #sequencecontrol
      L     0
      ==I   
      U     #R_TRIG_REQ
      SPBN  A7d0
      CLR   
      =     #DONE_VALID
      SET   
      =     #BUSY
      CLR   
      =     #ERROR
      L     DW#16#0
      T     #STATUS
      T     #SF_STATUS
      L     L#0
      T     #LENGTH
      T     #segment
      T     #address
      T     #totallength
      U     #RD_WR
      SPBN  A7d1
      L     2
      T     #typeofrequest
      L     DW#16#10022800
      T     LD    32
      L     DIW [AR2,P#22.0]
      T     LW    36
      L     DID [AR2,P#24.0]
      T     LD    38
      L     DW#16#10022800
      T     LD    42
      L     DINO
      T     LW    46
      TAR2  
      +     L#6544
      T     LD    48
      TAR2  LD    28
      UC    SFC   20
            P#L 32.0
            P#L 0.0
            P#L 42.0
      LAR2  LD    28
      L     #RetVal_BLKMOV
      SPA   A7d2
A7d1: L     1
      T     #typeofrequest
      L     0
      T     #i
A7d3: L     #i
      L     10239
      <=I   
      SPBN  A7d2
      L     #i
      ITD   
      L     L#0
      +D    
      L     L#8
      *D    
      TAR2  
      +D    
      L     B#16#0
      TAK   
      LAR1  
      TAK   
      T     DIB [AR1,P#818.0]
      L     #i
      L     1
      +I    
      T     #i
      SPA   A7d3
A7d2: L     1
      T     #sequencecontrol
      SPA   A7d5
A7d0: CLR   
A7d5: L     #sequencecontrol
      L     1
      TAK   
      ==I   
      T     LW    32
      SPB   A7d7
      SPA   A7d8
A7d7: L     B#16#1
      T     #rec.reset.ExtendedFunctionNum
      L     DW#16#FFFFFFFF
      T     #rec.reset.SequenceNo
      L     DW#16#FF
      T     #STATUS
      L     2
      T     #sequencecontrol
      SPA   A7d6
A7d8: L     2
      L     LW    32
      ==I   
      SPB   A7d9
      SPA   A7da
A7d9: SET   
      =     #WRREC_Function.REQ
      L     #ID
      T     #WRREC_Function.ID
      L     #index
      T     #WRREC_Function.INDEX
      L     6
      T     #WRREC_Function.LEN
      L     DW#16#100200F0
      T     DID [AR2,P#72.0]
      L     DINO
      T     DIW [AR2,P#76.0]
      TAR2  
      +     L#784
      T     DID [AR2,P#78.0]
      +AR2  P#56.0
      UC    SFB   53
      +AR2  P#8136.0
      U     #WRREC_Function.DONE
      =     #wrrec_if.done
      U     #WRREC_Function.BUSY
      =     #wrrec_if.busy
      U     #WRREC_Function.ERROR
      =     #wrrec_if.error
      L     #WRREC_Function.STATUS
      T     #wrrec_if.status
      U     #wrrec_if.done
      SPBN  A7db
      L     #wrrec_if.status
      T     #SF_STATUS
      L     3
      T     #sequencecontrol
      SPA   A7d6
A7db: CLR   
      U     #wrrec_if.error
      L     #rdrec_if.status
      L     DW#16#DF80B000
      =     L     26.2
      ==D   
      U     L     26.2
      SPBN  A7dd
      SET   
      =     #ERROR
      L     DW#16#30000
      T     #STATUS
      L     #rdrec_if.status
      T     #SF_STATUS
      L     99
      T     #sequencecontrol
      SPA   A7d6
A7dd: CLR   
      U     #wrrec_if.error
      SPBN  A7de
      SET   
      =     #ERROR
      L     DW#16#30000
      T     #STATUS
      L     #wrrec_if.status
      T     #SF_STATUS
      L     #sequencecontrol
      T     #errorTrigger
      L     99
      T     #sequencecontrol
      SPA   A7d6
A7de: SPA   A7d6
A7da: L     3
      L     LW    32
      ==I   
      SPB   A7df
      SPA   A7e0
A7df: CLR   
      =     #WRREC_Function.REQ
      L     #ID
      T     #WRREC_Function.ID
      L     #index
      T     #WRREC_Function.INDEX
      L     6
      T     #WRREC_Function.LEN
      L     DW#16#100200F0
      T     DID [AR2,P#72.0]
      L     DINO
      T     DIW [AR2,P#76.0]
      TAR2  
      +     L#784
      T     DID [AR2,P#78.0]
      +AR2  P#56.0
      UC    SFB   53
      +AR2  P#8136.0
      U     #WRREC_Function.DONE
      =     #wrrec_if.done
      U     #WRREC_Function.BUSY
      =     #wrrec_if.busy
      U     #WRREC_Function.ERROR
      =     #wrrec_if.error
      L     #WRREC_Function.STATUS
      T     #wrrec_if.status
      U     #wrrec_if.done
      NOT   
      SPBN  A7e1
      L     DW#16#FF
      T     #STATUS
      L     4
      T     #sequencecontrol
      SPA   A7d6
A7e1: CLR   
      U     #wrrec_if.error
      SPBN  A7e3
      SET   
      =     #ERROR
      L     #wrrec_if.status
      T     #SF_STATUS
      L     #sequencecontrol
      T     #errorTrigger
      L     99
      T     #sequencecontrol
      SPA   A7d6
A7e3: SPA   A7d6
A7e0: L     4
      L     LW    32
      ==I   
      SPB   A7e4
      SPA   A7e5
A7e4: L     #typeofrequest
      L     1
      ==I   
      SPBN  A7e6
      L     DW#16#100FF
      T     #STATUS
      L     10
      T     #sequencecontrol
      SPA   A7d6
A7e6: L     #typeofrequest
      L     2
      ==I   
      SPBN  A7e8
      L     DW#16#200FF
      T     #STATUS
      L     20
      T     #sequencecontrol
      SPA   A7d6
A7e8: SET   
      =     #ERROR
      L     DW#16#300FF
      T     #STATUS
      L     #sequencecontrol
      T     #errorTrigger
      L     99
      T     #sequencecontrol
      SPA   A7d6
A7e5: L     10
      L     LW    32
      ==I   
      SPB   A7e9
      SPA   A7ea
A7e9: SET   
      =     #RDREC_Function.REQ
      L     #ID
      T     #RDREC_Function.ID
      L     #index
      T     #RDREC_Function.INDEX
      L     240
      T     #RDREC_Function.MLEN
      L     DW#16#100200F0
      T     DID [AR2,P#46.0]
      L     DINO
      T     DIW [AR2,P#50.0]
      TAR2  
      +     L#2704
      T     DID [AR2,P#52.0]
      +AR2  P#28.0
      UC    SFB   52
      +AR2  P#8164.0
      U     #RDREC_Function.VALID
      =     #rdrec_if.valid
      U     #RDREC_Function.BUSY
      =     #rdrec_if.busy
      U     #RDREC_Function.ERROR
      =     #rdrec_if.error
      L     #RDREC_Function.STATUS
      T     #rdrec_if.status
      L     #RDREC_Function.LEN
      T     #rdrec_if.length
      U     #rdrec_if.valid
      SPBN  A7eb
      L     #rdrec_if.length
      L     6
      >I    
      L     #rec.backup.ExtendedFunctionNum
      L     B#16#2
      =     L     26.2
      ==I   
      U     L     26.2
      SPBN  A7ec
      L     DW#16#10000
      L     #rec.backup.SequenceNo
      OD    
      T     #STATUS
      L     #rdrec_if.status
      T     #SF_STATUS
      L     #rec.backup.SequenceNo
      T     #segment
      L     #rdrec_if.length
      L     6
      -I    
      T     #tmplength
      L     DW#16#100200EA
      T     LD    34
      L     DINO
      T     LW    38
      TAR2  
      +     L#2752
      T     LD    40
      L     LD    34
      T     LD     4
      L     LD    38
      T     LD     8
      L     LW    42
      T     LW    12
      L     #tmplength
      T     #ANYsource.length
      L     #address
      L     L#0
      +D    
      L     L#8
      *D    
      TAR2  
      +D    
      LAR1  
      L     DIB [AR1,P#818.0]
      TAK   
      T     LD    44
      TAK   
      L     DW#16#10020001
      T     LD    34
      L     DINO
      T     LW    38
      L     LD    44
      +     L#6544
      OD    DW#16#84000000
      T     LD    40
      L     LD    34
      T     LD    14
      L     LD    38
      T     LD    18
      L     LW    42
      T     LW    22
      L     #tmplength
      T     #ANYdestination.length
      L     LD     4
      T     LD    34
      L     LD     8
      T     LD    38
      L     LW    12
      T     LW    42
      L     LD    14
      T     LD    48
      L     LD    18
      T     LD    52
      L     LW    22
      T     LW    56
      TAR2  LD    28
      UC    SFC   20
            P#L 34.0
            P#L 0.0
            P#L 48.0
      LAR2  LD    28
      L     #RetVal_BLKMOV
      L     #RetVal_BLKMOV
      L     0
      <>I   
      SPBN  A7ed
      SET   
      =     #ERROR
      L     DW#16#110FF
      T     #STATUS
      L     #RetVal_BLKMOV
      UD    DW#16#FFFF
      T     #SF_STATUS
      L     99
      T     #sequencecontrol
      SPA   A7ee
A7ed: CLR   
A7ee: L     #tmplength
      ITD   
      L     #totallength
      +D    
      T     #totallength
      L     #tmplength
      ITD   
      L     #address
      +D    
      T     #address
      SPA   A7d6
A7ec: L     #rdrec_if.length
      L     6
      ==I   
      SPBN  A7f0
      L     DW#16#10022800
      T     LD    34
      L     DINO
      T     LW    38
      TAR2  
      +     L#6544
      T     LD    40
      L     LD    34
      T     LD     4
      L     LD    38
      T     LD     8
      L     LW    42
      T     LW    12
      L     W#16#2800
      T     #ANYsource.length
      L     DW#16#10022800
      T     LD    34
      L     DIW [AR2,P#22.0]
      T     LW    38
      L     DID [AR2,P#24.0]
      T     LD    40
      L     LD    34
      T     LD    14
      L     LD    38
      T     LD    18
      L     LW    42
      T     LW    22
      L     W#16#2800
      T     #ANYdestination.length
      L     LD     4
      T     LD    34
      L     LD     8
      T     LD    38
      L     LW    12
      T     LW    42
      L     LD    14
      T     LD    48
      L     LD    18
      T     LD    52
      L     LW    22
      T     LW    56
      TAR2  LD    28
      UC    SFC   20
            P#L 34.0
            P#L 0.0
            P#L 48.0
      LAR2  LD    28
      L     #RetVal_BLKMOV
      L     #RetVal_BLKMOV
      L     0
      <>I   
      SPBN  A7f1
      SET   
      =     #ERROR
      L     DW#16#110FF
      T     #STATUS
      L     #RetVal_BLKMOV
      UD    DW#16#FFFF
      T     #SF_STATUS
      L     99
      T     #sequencecontrol
      SPA   A7f2
A7f1: CLR   
A7f2: SET   
      =     #DONE_VALID
      CLR   
      =     #BUSY
      =     #ERROR
      L     DW#16#10000
      L     #rec.backup.SequenceNo
      OD    
      T     #STATUS
      L     #rdrec_if.status
      T     #SF_STATUS
      L     #totallength
      T     #LENGTH
      L     11
      T     #sequencecontrol
      SPA   A7d6
A7f0: SPA   A7d6
A7eb: CLR   
      U     #rdrec_if.error
      SPBN  A7f4
      CLR   
      =     #DONE_VALID
      =     #BUSY
      SET   
      =     #ERROR
      L     #rdrec_if.status
      T     #SF_STATUS
      L     #rdrec_if.length
      ITD   
      T     #LENGTH
      L     #sequencecontrol
      T     #errorTrigger
      L     99
      T     #sequencecontrol
      SPA   A7d6
A7f4: SPA   A7d6
A7ea: L     11
      L     LW    32
      ==I   
      SPB   A7f5
      SPA   A7f6
A7f5: CLR   
      =     #RDREC_Function.REQ
      L     #ID
      T     #RDREC_Function.ID
      L     #index
      T     #RDREC_Function.INDEX
      L     240
      T     #RDREC_Function.MLEN
      L     DW#16#100200F0
      T     DID [AR2,P#46.0]
      L     DINO
      T     DIW [AR2,P#50.0]
      TAR2  
      +     L#2704
      T     DID [AR2,P#52.0]
      +AR2  P#28.0
      UC    SFB   52
      +AR2  P#8164.0
      U     #RDREC_Function.VALID
      =     #rdrec_if.valid
      U     #RDREC_Function.BUSY
      =     #rdrec_if.busy
      U     #RDREC_Function.ERROR
      =     #rdrec_if.error
      L     #RDREC_Function.STATUS
      T     #rdrec_if.status
      L     #RDREC_Function.LEN
      T     #rdrec_if.length
      U     #rdrec_if.valid
      NOT   
      SPBN  A7f7
      L     98
      T     #sequencecontrol
      SPA   A7d6
A7f7: CLR   
      U     #rdrec_if.error
      SPBN  A7f9
      CLR   
      =     #DONE_VALID
      =     #BUSY
      SET   
      =     #ERROR
      L     #rdrec_if.status
      T     #SF_STATUS
      L     #rdrec_if.length
      ITD   
      T     #LENGTH
      L     #sequencecontrol
      T     #errorTrigger
      L     99
      T     #sequencecontrol
      SPA   A7d6
A7f9: SPA   A7d6
A7f6: L     20
      L     LW    32
      ==I   
      SPB   A7fa
      SPA   A7fb
A7fa: L     B#16#1
      T     #rec.restore.ExtendedFunctionNum
      L     #segment
      T     #rec.restore.SequenceNo
      L     DW#16#20000
      L     #rec.restore.SequenceNo
      OD    
      T     #STATUS
      L     #address
      L     L#0
      +D    
      L     L#8
      *D    
      TAR2  
      +D    
      LAR1  
      L     DIB [AR1,P#818.0]
      TAK   
      T     LD    48
      TAK   
      L     DW#16#10020001
      T     LD    34
      L     DINO
      T     LW    38
      L     LD    48
      +     L#6544
      OD    DW#16#84000000
      T     LD    40
      L     LD    34
      T     LD     4
      L     LD    38
      T     LD     8
      L     LW    42
      T     LW    12
      L     W#16#EA
      T     #ANYsource.length
      L     DW#16#100200EA
      T     LD    34
      L     DINO
      T     LW    38
      TAR2  
      +     L#4672
      T     LD    40
      L     LD    34
      T     LD    14
      L     LD    38
      T     LD    18
      L     LW    42
      T     LW    22
      L     W#16#EA
      T     #ANYdestination.length
      L     LD     4
      T     LD    34
      L     LD     8
      T     LD    38
      L     LW    12
      T     LW    42
      L     LD    14
      T     LD    52
      L     LD    18
      T     LD    56
      L     LW    22
      T     LW    60
      TAR2  LD    28
      UC    SFC   20
            P#L 34.0
            P#L 0.0
            P#L 52.0
      LAR2  LD    28
      L     #RetVal_BLKMOV
      L     #RetVal_BLKMOV
      L     0
      <>I   
      SPBN  A7fc
      SET   
      =     #ERROR
      L     #RetVal_BLKMOV
      UD    DW#16#FFFF
      T     #SF_STATUS
      L     99
      T     #sequencecontrol
      SPA   A7d6
A7fc: L     21
      T     #sequencecontrol
      SPA   A7d6
A7fb: L     21
      L     LW    32
      ==I   
      SPB   A7fe
      SPA   A7ff
A7fe: SET   
      =     #WRREC_Function.REQ
      L     #ID
      T     #WRREC_Function.ID
      L     #index
      T     #WRREC_Function.INDEX
      L     240
      T     #WRREC_Function.LEN
      L     DW#16#100200F0
      T     DID [AR2,P#72.0]
      L     DINO
      T     DIW [AR2,P#76.0]
      TAR2  
      +     L#4624
      T     DID [AR2,P#78.0]
      +AR2  P#56.0
      UC    SFB   53
      +AR2  P#8136.0
      U     #WRREC_Function.DONE
      =     #wrrec_if.done
      U     #WRREC_Function.BUSY
      =     #wrrec_if.busy
      U     #WRREC_Function.ERROR
      =     #wrrec_if.error
      L     #WRREC_Function.STATUS
      T     #wrrec_if.status
      U     #wrrec_if.done
      SPBN  A800
      L     #totallength
      L     L#234
      +D    
      T     #totallength
      L     #wrrec_if.status
      T     #SF_STATUS
      L     22
      T     #sequencecontrol
      SPA   A7d6
A800: CLR   
      U     #wrrec_if.error
      SPBN  A802
      SET   
      =     #ERROR
      L     #wrrec_if.status
      T     #SF_STATUS
      L     #sequencecontrol
      T     #errorTrigger
      L     99
      T     #sequencecontrol
      SPA   A7d6
A802: SPA   A7d6
A7ff: L     22
      L     LW    32
      ==I   
      SPB   A803
      SPA   A804
A803: CLR   
      =     #WRREC_Function.REQ
      L     #ID
      T     #WRREC_Function.ID
      L     #index
      T     #WRREC_Function.INDEX
      L     240
      T     #WRREC_Function.LEN
      L     DW#16#100200F0
      T     DID [AR2,P#72.0]
      L     DINO
      T     DIW [AR2,P#76.0]
      TAR2  
      +     L#4624
      T     DID [AR2,P#78.0]
      +AR2  P#56.0
      UC    SFB   53
      +AR2  P#8136.0
      U     #WRREC_Function.DONE
      =     #wrrec_if.done
      U     #WRREC_Function.BUSY
      =     #wrrec_if.busy
      U     #WRREC_Function.ERROR
      =     #wrrec_if.error
      L     #WRREC_Function.STATUS
      T     #wrrec_if.status
      U     #wrrec_if.done
      NOT   
      SPBN  A805
      L     #wrrec_if.status
      T     #SF_STATUS
      L     23
      T     #sequencecontrol
      SPA   A7d6
A805: CLR   
      U     #wrrec_if.error
      SPBN  A807
      SET   
      =     #ERROR
      L     #wrrec_if.status
      T     #SF_STATUS
      L     #sequencecontrol
      T     #errorTrigger
      L     99
      T     #sequencecontrol
      SPA   A7d6
A807: SPA   A7d6
A804: L     23
      L     LW    32
      ==I   
      SPB   A808
      SPA   A809
A808: SET   
      =     #RDREC_Function.REQ
      L     #ID
      T     #RDREC_Function.ID
      L     #index
      T     #RDREC_Function.INDEX
      L     240
      T     #RDREC_Function.MLEN
      L     DW#16#100200F0
      T     DID [AR2,P#46.0]
      L     DINO
      T     DIW [AR2,P#50.0]
      TAR2  
      +     L#2704
      T     DID [AR2,P#52.0]
      +AR2  P#28.0
      UC    SFB   52
      +AR2  P#8164.0
      U     #RDREC_Function.VALID
      =     #rdrec_if.valid
      U     #RDREC_Function.BUSY
      =     #rdrec_if.busy
      U     #RDREC_Function.ERROR
      =     #rdrec_if.error
      L     #RDREC_Function.STATUS
      T     #rdrec_if.status
      L     #RDREC_Function.LEN
      T     #rdrec_if.length
      U     #rdrec_if.valid
      L     #rec.backup.ExtendedFunctionNum
      L     B#16#2
      =     L     26.2
      ==I   
      U     L     26.2
      SPBN  A80a
      L     #rec.backup.SequenceNo
      L     DW#16#FFFFFF01
      ==D   
      SPBN  A80b
      L     #segment
      L     L#1
      +D    
      T     #segment
      L     #address
      L     L#234
      +D    
      T     #address
      L     DW#16#FFFFFF01
      T     #STATUS
      L     24
      T     #sequencecontrol
      SPA   A7d6
A80b: L     #rec.backup.SequenceNo
      L     DW#16#FFFFFF02
      ==D   
      SPBN  A80d
      L     #segment
      L     L#1
      +D    
      T     #segment
      L     #address
      L     L#234
      +D    
      T     #address
      L     DW#16#FFFFFF02
      T     #STATUS
      L     24
      T     #sequencecontrol
      SPA   A7d6
A80d: L     #rec.backup.SequenceNo
      L     DW#16#FFFFFF03
      ==D   
      SPBN  A80e
      T     #STATUS
      L     25
      T     #sequencecontrol
      SPA   A7d6
A80e: L     #rec.backup.SequenceNo
      L     DW#16#FFFFFF04
      ==D   
      SPBN  A80f
      SET   
      =     #ERROR
      T     #STATUS
      L     99
      T     #sequencecontrol
      SPA   A7d6
A80f: L     #rec.backup.SequenceNo
      L     DW#16#FFFFFF05
      ==D   
      SPBN  A810
      SET   
      =     #ERROR
      T     #STATUS
      L     99
      T     #sequencecontrol
      SPA   A7d6
A810: L     #rec.backup.SequenceNo
      L     DW#16#FFFFFF06
      ==D   
      SPBN  A811
      SET   
      =     #ERROR
      T     #STATUS
      L     99
      T     #sequencecontrol
      SPA   A7d6
A811: L     #rec.backup.SequenceNo
      L     DW#16#FFFFFF07
      ==D   
      SPBN  A812
      SET   
      =     #ERROR
      T     #STATUS
      L     99
      T     #sequencecontrol
      SPA   A7d6
A812: SPA   A7d6
A80a: CLR   
      U     #rdrec_if.error
      SPBN  A814
      SET   
      =     #ERROR
      L     #rdrec_if.status
      T     #SF_STATUS
      L     #sequencecontrol
      T     #errorTrigger
      L     99
      T     #sequencecontrol
      SPA   A7d6
A814: SPA   A7d6
A809: L     24
      L     LW    32
      ==I   
      SPB   A815
      SPA   A816
A815: CLR   
      =     #RDREC_Function.REQ
      L     #ID
      T     #RDREC_Function.ID
      L     #index
      T     #RDREC_Function.INDEX
      L     240
      T     #RDREC_Function.MLEN
      L     DW#16#100200F0
      T     DID [AR2,P#46.0]
      L     DINO
      T     DIW [AR2,P#50.0]
      TAR2  
      +     L#2704
      T     DID [AR2,P#52.0]
      +AR2  P#28.0
      UC    SFB   52
      +AR2  P#8164.0
      U     #RDREC_Function.VALID
      =     #rdrec_if.valid
      U     #RDREC_Function.BUSY
      =     #rdrec_if.busy
      U     #RDREC_Function.ERROR
      =     #rdrec_if.error
      L     #RDREC_Function.STATUS
      T     #rdrec_if.status
      L     #RDREC_Function.LEN
      T     #rdrec_if.length
      U     #rdrec_if.valid
      NOT   
      SPBN  A817
      L     20
      T     #sequencecontrol
      SPA   A7d6
A817: CLR   
      U     #rdrec_if.error
      SPBN  A819
      SET   
      =     #ERROR
      L     #rdrec_if.status
      T     #SF_STATUS
      L     #sequencecontrol
      T     #errorTrigger
      L     99
      T     #sequencecontrol
      SPA   A7d6
A819: SPA   A7d6
A816: L     25
      L     LW    32
      ==I   
      SPB   A81a
      SPA   A81b
A81a: CLR   
      =     #RDREC_Function.REQ
      L     #ID
      T     #RDREC_Function.ID
      L     #index
      T     #RDREC_Function.INDEX
      L     240
      T     #RDREC_Function.MLEN
      L     DW#16#100200F0
      T     DID [AR2,P#46.0]
      L     DINO
      T     DIW [AR2,P#50.0]
      TAR2  
      +     L#2704
      T     DID [AR2,P#52.0]
      +AR2  P#28.0
      UC    SFB   52
      +AR2  P#8164.0
      U     #RDREC_Function.VALID
      =     #rdrec_if.valid
      U     #RDREC_Function.BUSY
      =     #rdrec_if.busy
      U     #RDREC_Function.ERROR
      =     #rdrec_if.error
      L     #RDREC_Function.STATUS
      T     #rdrec_if.status
      L     #RDREC_Function.LEN
      T     #rdrec_if.length
      U     #rdrec_if.valid
      NOT   
      SPBN  A81c
      SET   
      =     #DONE_VALID
      CLR   
      =     #BUSY
      =     #ERROR
      L     #rdrec_if.status
      T     #SF_STATUS
      L     #totallength
      T     #LENGTH
      L     98
      T     #sequencecontrol
      SPA   A7d6
A81c: CLR   
      U     #rdrec_if.error
      SPBN  A81e
      SET   
      =     #ERROR
      L     #rdrec_if.status
      T     #SF_STATUS
      L     #sequencecontrol
      T     #errorTrigger
      L     99
      T     #sequencecontrol
      SPA   A7d6
A81e: SPA   A7d6
A81b: L     98
      L     LW    32
      ==I   
      SPB   A81f
      SPA   A820
A81f: CLR   
      =     #RDREC_Function.REQ
      L     DW#16#100200F0
      T     DID [AR2,P#46.0]
      L     DINO
      T     DIW [AR2,P#50.0]
      TAR2  
      +     L#2704
      T     DID [AR2,P#52.0]
      +AR2  P#28.0
      UC    SFB   52
      +AR2  P#8164.0
      CLR   
      =     #WRREC_Function.REQ
      +AR2  P#56.0
      UC    SFB   53
      +AR2  P#8136.0
      U     #REQ
      NOT   
      SPBN  A7d6
      CLR   
      =     #DONE_VALID
      L     DW#16#0
      T     #STATUS
      T     #SF_STATUS
      L     L#0
      T     #LENGTH
      L     0
      T     #sequencecontrol
      T     #typeofrequest
      SPA   A7d6
A820: L     99
      L     LW    32
      ==I   
      SPB   A822
      SPA   A823
A822: CLR   
      =     #RDREC_Function.REQ
      L     DW#16#100200F0
      T     DID [AR2,P#46.0]
      L     DINO
      T     DIW [AR2,P#50.0]
      TAR2  
      +     L#2704
      T     DID [AR2,P#52.0]
      +AR2  P#28.0
      UC    SFB   52
      +AR2  P#8164.0
      CLR   
      =     #WRREC_Function.REQ
      +AR2  P#56.0
      UC    SFB   53
      +AR2  P#8136.0
      CLR   
      =     #DONE_VALID
      =     #BUSY
      L     L#0
      T     #LENGTH
      L     0
      T     #sequencecontrol
      T     #typeofrequest
      SPA   A7d6
A823: CLR   
A7d6: CLR   
      U     L     26.1
      SAVE  
      BE
```


----------



## Thomas_v2.1 (26 Mai 2020)

Welcher Index/Subindex wird denn gelesen, wenn du mit RDREC aus dem SPS-Programm aufrufst? Du verwendest ja nur die CAP Kennung.


----------



## Hans54216 (26 Mai 2020)

Thomas_v2.1 schrieb:


> Welcher Index/Subindex wird denn gelesen, wenn du mit RDREC aus dem SPS-Programm aufrufst? Du verwendest ja nur die CAP Kennung.





-----------------------------------


----------



## Thomas_v2.1 (26 Mai 2020)

Aber woher weiß der nackte RDREC, dass du Index 18, Subindex 0 lesen willst?
Was stehen bei der RDREC-Antwort noch für Bytes vor dem eigentlichen String, bestehen dort Ähnlichkeiten zu denen die auf dem Netzwerk zu sehen sind?


----------



## Hans54216 (26 Mai 2020)

Thomas_v2.1 schrieb:


> Aber woher weiß der nackte RDREC, dass du Index 18, Subindex 0 lesen willst?
> Was stehen bei der RDREC-Antwort noch für Bytes vor dem eigentlichen String, bestehen dort Ähnlichkeiten zu denen die auf dem Netzwerk zu sehen sind?



RDREC scheint immer einen Header + die durch den IO-Link angewählten Nutzdaten zu lesen.

Vorgehen: IOL Baustein ausgeführt und anschließend den RDREC


----------



## Thomas_v2.1 (26 Mai 2020)

Du hast bei deiner AWL-Quelle vermutlich den IO_LINK_MASTER gepostet, von dem liefert Siemens auch die SCL-Quelle mit.

Wenn man aber in den IO_LINK_DEVICE reinschaut, dann sieht man zumindest in den STAT-Variablen in der Struct "read.header" den Aufbau des Datenteils das auch übers Netzwerk geht. Glücklicherweise mit Kommentar  Leider fehlt hier die SCL-Quelle, müsste man sich anfertigen wenn das weiterhilft.


----------



## Hans54216 (26 Mai 2020)

Thomas_v2.1 schrieb:


> Du hast bei deiner AWL-Quelle vermutlich den IO_LINK_MASTER gepostet, von dem liefert Siemens auch die SCL-Quelle mit.


IO_LINK_MASTER_4
Ist als SCL gleich viel übersichtlicher 

Sollte eigentlich IO_LINK_DEVICE sein.

rwIOL (Screenshot) entspricht IO_LINK_DEVICE


----------



## Thomas_v2.1 (26 Mai 2020)

Der Aufbau der Header ist hier näher beschrieben:

https://www.profibus.com/download/io-link-integration-for-profibus/


----------



## Hans54216 (27 Mai 2020)

Bei TIA sind die SCL Quellen dabei.

IO_LINK_DEVICE_FB_300

```
(*---------------------------------------------------------------------------------------------------------------------
SIEMENS AG
©Copyright 2015. All Rights Reserved.
-----------------------------------------------------------------------------------------------------------------------
Limitation of liability
This software was developed with largest care. However, the author can not guarantee, that the software runs under
each version of STEP7 on each PLC flawlessly.
There is no warranty for the software, to the extent permitted by applicable law. The copyright holder provides the
software "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance
of the software is with you. Should the software prove defective, you assume the cost of all necessary servicing,
repair or correction. In no event unless required by applicable law will the copyright holder be liable to you for
damages, including any general, special, incidental or consequential damages arising out of the use or inability to
use the software (including but not limited to loss of data or data being rendered inaccurate or losses sustained by
you or third parties or a failure of the software to operate with any other programs), even if such holder or other
party has been advised of the possibility of such damages.
Permission to use or copy this software for any purpose is hereby granted without fee, provided the above notices are
retained on all copies. Permission to modify the code and to distribute modified code is granted, provided the above
notices are retained, and a notice that the code was modified is included with the above copyright notice.
-----------------------------------------------------------------------------------------------------------------------
Functionality provided by this source (short description):
Function block IO_LINK_DEVICE - used to read and write of acyclic data records on devices with IO-Link.
Note:
To read data records, the IO-Link master modules needs to know which data record is required. Therefore it is necessary
to send at first a request via write data record (only header of 8 byte). As response the IO-Link master module
will send the required data record.


Status:     Released (tested)
File name:  IO_LINK_DEVICE
System :    SIMATIC S7-300/400
Version :   3.0
-----------------------------------------------------------------------------------------------------------------------
Script to be stored on level ... in project (mark appropriate levels with [x] below)
[ ] External
[ ] All levels possible
[x] Project level
[ ] Controller level
[ ] Device level
[ ] Subobject level
-----------------------------------------------------------------------------------------------------------------------
Restrictions, known limitations:
The limitation of 240 byte is based on PROFIBUS DP protocol. With PROFINET IO there wouldn't no limit but because of 
compatibility reason it wasn't changed.
  - header =   8 byte
  - data   = 232 byte
-----------------------------------------------------------------------------------------------------------------------
Requirements, prerequisites, External resources used: nothing
-----------------------------------------------------------------------------------------------------------------------
Change log table (latest change is at the bottom OF the list):
Version Date Changes applied
V2.0.0 <2013-09-30> First created with TIA Portal V12 SP1
V2.1.0 <2014-03-14> Upgrade system function 'WRREC' from V1.0 to V1.1 and released for TIA Portal V13
V3.0.0 <2015-02-28> Upgrade and released for TIA Portal V13 SP1 (without backup/restore master parameter)
V3.0.1 <2015-07-07> Change of #IOL_INDEX and #Port lower boundry to 0
---------------------------------------------------------------------------------------------------------------------*)


(*---------------------------------------------------------------------------------------------------------------------
Description of programmed IO-Link error codes
8000 = time out
8001 = wrong port
8002 = wrong index
8003 = wrong subindex
8005 = wrong length for write data record
8006 = wrong length for read data record
8052 = error during reading data record
8053 = error during writing data record
All other codes are coming from a higher level!
---------------------------------------------------------------------------------------------------------------------*)


(*---------------------------------------------------------------------------------------------------------------------
            NEW REQUEST
---------------------------------------------------------------------------------------------------------------------*)
// detection rising edge of "REQ"
#R_TRIG_REQ := #REQ = TRUE AND #R_TRIG_REQ_old = FALSE;
#R_TRIG_REQ_old := #REQ = TRUE;


// start new request if not busy
IF #R_TRIG_REQ = TRUE AND #sequencecontrol = 0 THEN
    // Freeze record ID and CAP
    #WRREC_Function.ID := #ID;
    #WRREC_Function.INDEX := #CAP;
    #RDREC_Function.ID := #ID;
    #RDREC_Function.INDEX := #CAP;
    
    // Initialize outputs
    #DONE_VALID := FALSE;
    #BUSY := TRUE;
    #ERROR := FALSE;
    #STATUS := DW#16#00000000;
    #IOL_STATUS := DW#16#00000000;
    #RD_LEN := INT#0;
    
    // Initialize data area
    #read.header."CALL"."Port" := B#16#0;
    #read.header.IOL.Index_LowByte := B#16#0;
    #read.header.IOL.Index_HighByte := B#16#0;
    #read.header.IOL.Subindex := B#16#0;
    #i := INT#0; // initialize loop counter      
    FOR #i := 0 TO 231 DO
        #write.data[#i] := B#16#0;
        #read.data[#i] := B#16#0;
    END_FOR;
    
(*---------------------------------------------------------------------------------------------------------------------
    Setup IO-LINK Call needs a specific header
---------------------------------------------------------------------------------------------------------------------*)
// setup CALL header
#write.header."CALL".Extended_Function_Num := 16#08;  // extended function number (fix coded 08h)
#write.header."CALL"."Port" := INT_TO_BYTE(#PORT);  // 1..63 = PORT number, 64..255=reserved
#write.header."CALL".FI_Index := 65098;  // IOL-Header is following (fix coded 65098)


// setup IOL header 
// CONTROL = please see below at BODY
// INDEX
#write.header.IOL.Index_LowByte := INT_TO_BYTE(#IOL_INDEX);
#tmpWORD := INT_TO_WORD(#IOL_INDEX);
#tmpWORD := SHR(IN := #tmpWORD, N := 8);
#write.header.IOL.Index_HighByte := WORD_TO_BYTE(#tmpWORD);
// SUBINDEX
#write.header.IOL.Subindex := INT_TO_BYTE(#IOL_SUBINDEX);


// BODY (data max. 232 Byte, Array 0..231 of Byte) 
IF #RD_WR = TRUE THEN
    // IO-Link write data record requested, copy data from "RECORD_IOL_DATA"
    #write.header.IOL.Control := B#16#02; // CONTROL B#16#02 = write data record
    #wrrec.length := #LEN + INT#8;  // data length header + data = IO-Link write data record
    #typeofrequest := 2; // write data record
    // copy IOL_RECORD_DATA in working area
    #i := INT#0;    // initialize loop counter 
    FOR #i := 0 TO #LEN - 1 DO
        #write.data[#i] := #RECORD_IOL_DATA[#i];
    END_FOR;
ELSE
    // IO-Link read data record requested
    #write.header.IOL.Control := 16#03;  // CONTROL 16#03 = read data record
    #wrrec.length := INT#8; // data length only header = IO-Link read data record
    #typeofrequest := 1; // read data record        
END_IF;
#sequencecontrol := 1;  // Start sequence
ELSE
    ;
END_IF;


(*---------------------------------------------------------------------------------------------------------------------
            SEQUENCE CONTROL
---------------------------------------------------------------------------------------------------------------------*)
CASE #sequencecontrol OF
    1:  // Check input parameter        
        IF #PORT < INT#0 OR #PORT > INT#63 THEN // PORT (Wrong port address = 0x8001)
            #ERROR := TRUE;
            #IOL_STATUS := DW#16#80010000;
            #sequencecontrol := 99; // aborting function with error
        ELSIF #IOL_INDEX < INT#0 OR #IOL_INDEX > INT#32767 THEN    // INDEX (Wrong index = 0x8002)
            #ERROR := TRUE;
            #IOL_STATUS := DW#16#80020000;
            #sequencecontrol := 99; // aborting function with error
        ELSIF #IOL_SUBINDEX < INT#0 OR #IOL_SUBINDEX > INT#255 THEN    // SUBINDEX (Wrong subindex = 0x8003)
            #ERROR := TRUE;
            #IOL_STATUS := DW#16#80030000;
            #sequencecontrol := 99; // aborting function with error
        ELSIF #typeofrequest = 2 AND (#LEN < 1 OR #LEN > 232) THEN  // LEN (wrong length for write data record = 0x8004)
            #ERROR := TRUE;
            #IOL_STATUS := DW#16#80050000;
            #sequencecontrol := 99; // aborting function with error
        ELSIF #typeofrequest = 1 AND (#LEN < 0 OR #LEN > 232) THEN  // LEN (wrong length for read data record = 0x8005)
            #ERROR := TRUE;
            #IOL_STATUS := DW#16#80060000;
            #sequencecontrol := 99; // aborting function with error
        ELSE    // all input parameter are right
            #sequencecontrol := 2;
        END_IF;
        
    2:  (*-------------------------------------------------------------------------------------------------------------
        WRREC = Write Data Record
        Note:
        To read data records, the IO-Link master modules needs to know which PORT is required. Therefore it is necessary
        to send at first a request via write data record (header, 8 byte). As response the IO-Link master module will 
        send the required data record.
        -------------------------------------------------------------------------------------------------------------*)
        #WRREC_Function(REQ := TRUE,
                        LEN := #wrrec.length,
                        DONE => #wrrec.done,
                        BUSY => #wrrec.busy,
                        ERROR => #wrrec.error,
                        STATUS => #wrrec.status,
                        RECORD := #write);
        // output status during write data record
        IF #wrrec.busy THEN
            #STATUS := #wrrec.status;
            #IOL_STATUS := DW#16#00020000;  // 0002 = writing in progress 
        ELSIF #wrrec.error THEN
            #tmpDWORD := #wrrec.status AND DW#16#00FFFF00;  // filter status
            IF #tmpDWORD = DW#16#0080C200 THEN    // if resource #BUSY (#STATUS=80C2), then ... 
                #sequencecontrol := 3;  // ... repeat write data record
            ELSE
                #ERROR := TRUE;
                #STATUS := #wrrec.status;   // ... otherwise output error status of "WRREC"
                #IOL_STATUS := DW#16#80530000;  // ... with note '8053' = error status of "WRREC"
                #sequencecontrol := 99; // aborting function with error
            END_IF;
        ELSIF #wrrec.done = TRUE THEN   // wait for write data record finish without error
            #TP_poll(IN := TRUE,
                     PT := T#100ms);    // poll for response
            #sequencecontrol := 4;
        ELSE
            ;
        END_IF;
        
    3:  // repeat write data record because resource was busy
        #WRREC_Function(REQ := FALSE);
        #sequencecontrol := 2;
        
    4:  // wait and poll for response = Read Data Record (RDREC)
        #WRREC_Function(REQ := FALSE);
        #TP_poll(IN := TRUE,
                 PT := T#100ms);
        
        // if poll rate executed then start read data record (on falling edge)
        #F_TRIG_poll := #TP_poll.Q = FALSE AND #F_TRIG_poll_old = TRUE;
        #F_TRIG_poll_old := #TP_poll.Q = TRUE;
        
        // RDREC = Read Data Record
        #RDREC_Function(REQ := #F_TRIG_poll,
                        MLEN := INT#232, // max. length = 232 = read all data which are available
                        VALID => #rdrec.valid,
                        BUSY => #rdrec.busy,
                        ERROR => #rdrec.error,
                        STATUS => #rdrec.status,
                        LEN => #rdrec.length,
                        RECORD := #read);
        // output status during read data record
        IF #rdrec.busy THEN
            #STATUS := #rdrec.status;
            #IOL_STATUS := DW#16#00030000; // 16#0003 = reading in progress
        ELSIF #rdrec.error = TRUE THEN
            #tmpDWORD := #rdrec.status AND DW#16#00FFFF00;  // filter status
            IF #tmpDWORD = DW#16#0080C200 THEN  // if resource busy (status=80C2), then ... 
                #sequencecontrol := 5;  // ... repeat read data record / continue polling
            ELSE
                #ERROR := TRUE;
                #STATUS := #rdrec.status;   // ... otherwise output error code of "RDREC"
                #IOL_STATUS := DW#16#80520000;  // ... with note '8052' = error status of "RDREC"
                #sequencecontrol := 99; // aborting function with error
            END_IF;
        ELSIF #rdrec.valid THEN
            IF  // check the response wether it matches the request (compare PORT, INDEX and SUBINDEX)
                (#write.header."CALL"."Port" <> #read.header."CALL"."Port") OR
                (#write.header.IOL.Index_HighByte <> #read.header.IOL.Index_HighByte) OR
                (#write.header.IOL.Index_LowByte <> #read.header.IOL.Index_LowByte) OR
                (#write.header.IOL.Subindex <> #read.header.IOL.Subindex)
            THEN    // read data record don't match the request
                #ERROR := TRUE;
                #STATUS := #rdrec.status;
                #IOL_STATUS := DW#16#70000000;  // output status '7000', no consistent data
                #sequencecontrol := 99; // aborting function with error
            ELSIF #read.header.IOL.Control = B#16#80 THEN   // check the response for IO-Link errors
                // State 0x80 = IOL_CALL_RES PDU shows IO-Link error detect
                #ERROR := TRUE;
                #STATUS := DW#16#00000000;
                #AT_IOL_STATUS[3] := #read.data[3];   // IO-Link master error code
                #AT_IOL_STATUS[2] := #read.data[2];   // IO-Link master error code
                #AT_IOL_STATUS[1] := #read.data[1];  // IO-Link device error code
                #AT_IOL_STATUS[0] := #read.data[0];  // IO-Link device additional error code
                #IOL_STATUS := #tmpDWORD;
                #sequencecontrol := 99; // aborting function with error
            ELSE    // read data are valid = output
                #DONE_VALID := TRUE;
                #BUSY := FALSE;
                #ERROR := FALSE;
                #STATUS := DW#16#00000000;
                #IOL_STATUS := DW#16#00000000;  // request finished
                
                IF #typeofrequest = 1 THEN
                    #RD_LEN := #rdrec.length - INT#8;  // device parameter cover header and data,
                    // only data will be output
                    #i := INT#0; // initialize loop counter
                    FOR #i := 0 TO #rdrec.length - INT#8 - INT#1 DO
                        #RECORD_IOL_DATA[#i] := #read.data[#i];
                    END_FOR;
                ELSE
                    ;
                END_IF;
                #sequencecontrol := 98;
            END_IF;
        ELSE
            ;
        END_IF;
        
    5:  // poll again for response because resource was busy
        #TP_poll(IN := FALSE,
                 PT := T#100ms);
        #RDREC_Function(REQ := FALSE,
                        RECORD := #read);
        #sequencecontrol := 4;  // ... repeat read data record / continue polling   
        
    98: // request done successfully
        #TP_poll(IN := FALSE,
                 PT := T#100ms);
        #RDREC_Function(REQ := FALSE,
                        RECORD := #read);
        IF #REQ = FALSE THEN
            #DONE_VALID := FALSE;
            #BUSY := FALSE;
            #ERROR := FALSE;
            #RD_LEN := INT#0;
            #STATUS := DW#16#00000000;
            #IOL_STATUS := DW#16#00010000; // ready for new request
            #sequencecontrol := 0;
            #typeofrequest := 0;
        ELSE
            ;
        END_IF;
        
    99: // Function aborted with error
        #TP_poll(IN := FALSE,
                 PT := T#100ms);
        #WRREC_Function(REQ := FALSE);
        #RDREC_Function(REQ := FALSE,
                        RECORD := #read);
        #DONE_VALID := FALSE;
        #BUSY := FALSE;
        #RD_LEN := INT#0;
        #sequencecontrol := 0;
        #typeofrequest := 0;
        
    ELSE  // no relevant case, ready for new request
        #WRREC_Function(REQ := FALSE);
        #RDREC_Function(REQ := FALSE,
                        RECORD := #read);
        #TP_poll(IN := FALSE,
                 PT := T#100ms);
        #TON_monitoring(IN := FALSE,
                        PT := T#20s);
        #sequencecontrol := 0;
        #typeofrequest := 0;
END_CASE;


(*---------------------------------------------------------------------------------------------------------------------
            MONITORING
---------------------------------------------------------------------------------------------------------------------*)
#TON_monitoring(IN := #sequencecontrol > 0 AND #sequencecontrol < 98,
                PT := T#20s);
IF #TON_monitoring.Q THEN
    #ERROR := TRUE;
    #IOL_STATUS := DW#16#80000000; // request time out       
    #sequencecontrol := 99; // aborting function with error
END_IF;
```

IO_LINK_DEVICE_FB_1200 / IO_LINK_DEVICE_FB_1500

```
(*---------------------------------------------------------------------------------------------------------------------
SIEMENS AG
©Copyright 2015. All Rights Reserved.
-----------------------------------------------------------------------------------------------------------------------
Limitation of liability
This software was developed with largest care. However, the author can not guarantee, that the software runs under
each version of STEP7 on each PLC flawlessly.
There is no warranty for the software, to the extent permitted by applicable law. The copyright holder provides the
software "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance
of the software is with you. Should the software prove defective, you assume the cost of all necessary servicing,
repair or correction. In no event unless required by applicable law will the copyright holder be liable to you for
damages, including any general, special, incidental or consequential damages arising out of the use or inability to
use the software (including but not limited to loss of data or data being rendered inaccurate or losses sustained by
you or third parties or a failure of the software to operate with any other programs), even if such holder or other
party has been advised of the possibility of such damages.
Permission to use or copy this software for any purpose is hereby granted without fee, provided the above notices are
retained on all copies. Permission to modify the code and to distribute modified code is granted, provided the above
notices are retained, and a notice that the code was modified is included with the above copyright notice.
-----------------------------------------------------------------------------------------------------------------------
Functionality provided by this source (short description):
Function block IO_LINK_DEVICE - used to read and write of acyclic data records on devices with IO-Link.
Note:
To read data records, the IO-Link master modules needs to know which data record is required. Therefore it is necessary
to send at first a request via write data record (only header of 8 byte). As response the IO-Link master module
will send the required data record.


Status:     Released (tested)
File name:  IO_LINK_DEVICE
System :    SIMATIC S7-1200/1500
Version :   3.0
-----------------------------------------------------------------------------------------------------------------------
Script to be stored on level ... in project (mark appropriate levels with [x] below)
[ ] External
[ ] All levels possible
[x] Project level
[ ] Controller level
[ ] Device level
[ ] Subobject level
-----------------------------------------------------------------------------------------------------------------------
Restrictions, known limitations:
The limitation of 240 byte is based on PROFIBUS DP protocol. With PROFINET IO there wouldn't no limit but because of 
compatibility reason it wasn't changed.
  - header =   8 byte
  - data   = 232 byte
-----------------------------------------------------------------------------------------------------------------------
Requirements, prerequisites, External resources used: nothing
-----------------------------------------------------------------------------------------------------------------------
Change log table (latest change is at the bottom OF the list):
Version Date Changes applied
V2.0.0 <2013-09-30> First created with TIA Portal V12 SP1
V2.1.0 <2014-03-14> Upgrade system function 'WRREC' from V1.0 to V1.1 and released for TIA Portal V13
V3.0.0 <2015-02-28> Upgrade and released for TIA Portal V13 SP1 (without backup/restore master parameter)
V3.0.2 <2015-07-07> Change of #IOL_INDEX and #Port lower boundry to 0
---------------------------------------------------------------------------------------------------------------------*)


(*---------------------------------------------------------------------------------------------------------------------
Description of programmed IO-Link error codes
8000 = time out
8001 = wrong port
8002 = wrong index
8003 = wrong subindex
8005 = wrong length for write data record
8006 = wrong length for read data record
8052 = error during reading data record
8053 = error during writing data record
All other codes are coming from a higher level!
---------------------------------------------------------------------------------------------------------------------*)


(*---------------------------------------------------------------------------------------------------------------------
            NEW REQUEST
---------------------------------------------------------------------------------------------------------------------*)
// detection rising edge of "REQ"
#R_TRIG_REQ := #REQ = TRUE AND #R_TRIG_REQ_old = FALSE;
#R_TRIG_REQ_old := #REQ = TRUE;


// start new request if not busy
IF #R_TRIG_REQ = TRUE AND #sequencecontrol = 0 THEN
    // Freeze record ID and CAP
    #WRREC_Function.ID := #ID;
    #WRREC_Function.INDEX := #CAP;
    #RDREC_Function.ID := #ID;
    #RDREC_Function.INDEX := #CAP;
    
    // Initialize outputs
    #DONE_VALID := FALSE;
    #BUSY := TRUE;
    #ERROR := FALSE;
    #STATUS := DW#16#00000000;
    #IOL_STATUS := DW#16#00000000;
    #RD_LEN := INT#0;
    
    // Initialize data area
    #read.header."CALL"."Port" := B#16#0;
    #read.header.IOL.Index_LowByte := B#16#0;
    #read.header.IOL.Index_HighByte := B#16#0;
    #read.header.IOL.Subindex := B#16#0;
    #i := INT#0; // initialize loop counter      
    FOR #i := 0 TO 231 DO
        #write.data[#i] := B#16#0;
        #read.data[#i] := B#16#0;
    END_FOR;
    
(*---------------------------------------------------------------------------------------------------------------------
    Setup IO-LINK Call needs a specific header
---------------------------------------------------------------------------------------------------------------------*)
// setup CALL header
#write.header."CALL".Extended_Function_Num := 16#08;  // extended function number (fix coded 08h)
#write.header."CALL"."Port" := INT_TO_BYTE(#PORT);  // 1..63 = PORT number, 64..255=reserved
#write.header."CALL".FI_Index := 65098;  // IOL-Header is following (fix coded 65098)


// setup IOL header 
// CONTROL = please see below at BODY
// INDEX
#write.header.IOL.Index_LowByte := INT_TO_BYTE(#IOL_INDEX);
#tmpWORD := INT_TO_WORD(#IOL_INDEX);
#tmpWORD := SHR(IN := #tmpWORD, N := 8);
#write.header.IOL.Index_HighByte := WORD_TO_BYTE(#tmpWORD);
// SUBINDEX
#write.header.IOL.Subindex := INT_TO_BYTE(#IOL_SUBINDEX);


// BODY (data max. 232 Byte, Array 0..231 of Byte) 
IF #RD_WR = TRUE THEN
    // IO-Link write data record requested, copy data from "RECORD_IOL_DATA"
    #write.header.IOL.Control := B#16#02; // CONTROL B#16#02 = write data record
    #wrrec.length := INT_TO_UINT(#LEN) + UINT#8;  // data length header + data = IO-Link write data record
    #typeofrequest := 2; // write data record
    // copy IOL_RECORD_DATA in working area
    #i := INT#0;    // initialize loop counter 
    FOR #i := 0 TO #LEN - 1 DO
        #write.data[#i] := #RECORD_IOL_DATA[#i];
    END_FOR;
ELSE
    // IO-Link read data record requested
    #write.header.IOL.Control := 16#03;  // CONTROL 16#03 = read data record
    #wrrec.length := UINT#8; // data length only header = IO-Link read data record
    #typeofrequest := 1; // read data record        
END_IF;
#sequencecontrol := 1;  // Start sequence
ELSE
    ;
END_IF;


(*---------------------------------------------------------------------------------------------------------------------
            SEQUENCE CONTROL
---------------------------------------------------------------------------------------------------------------------*)
CASE #sequencecontrol OF
    1:  // Check input parameter        
        IF #PORT < INT#0 OR #PORT > INT#63 THEN // PORT (Wrong port address = 0x8001)
            #ERROR := TRUE;
            #IOL_STATUS := DW#16#80010000;
            #sequencecontrol := 99; // aborting function with error
        ELSIF #IOL_INDEX < INT#0 OR #IOL_INDEX > INT#32767 THEN    // INDEX (Wrong index = 0x8002)
            #ERROR := TRUE;
            #IOL_STATUS := DW#16#80020000;
            #sequencecontrol := 99; // aborting function with error
        ELSIF #IOL_SUBINDEX < INT#0 OR #IOL_SUBINDEX > INT#255 THEN    // SUBINDEX (Wrong subindex = 0x8003)
            #ERROR := TRUE;
            #IOL_STATUS := DW#16#80030000;
            #sequencecontrol := 99; // aborting function with error
        ELSIF #typeofrequest = 2 AND (#LEN < 1 OR #LEN > 232) THEN  // LEN (wrong length for write data record = 0x8004)
            #ERROR := TRUE;
            #IOL_STATUS := DW#16#80050000;
            #sequencecontrol := 99; // aborting function with error
        ELSIF #typeofrequest = 1 AND (#LEN < 0 OR #LEN > 232) THEN  // LEN (wrong length for read data record = 0x8005)
            #ERROR := TRUE;
            #IOL_STATUS := DW#16#80060000;
            #sequencecontrol := 99; // aborting function with error
        ELSE    // all input parameter are right
            #sequencecontrol := 2;
        END_IF;
        
    2:  (*-------------------------------------------------------------------------------------------------------------
        WRREC = Write Data Record
        Note:
        To read data records, the IO-Link master modules needs to know which PORT is required. Therefore it is necessary
        to send at first a request via write data record (header, 8 byte). As response the IO-Link master module will 
        send the required data record.
        -------------------------------------------------------------------------------------------------------------*)
        #WRREC_Function(REQ := TRUE,
                        LEN := #wrrec.length,
                        DONE => #wrrec.done,
                        BUSY => #wrrec.busy,
                        ERROR => #wrrec.error,
                        STATUS => #wrrec.status,
                        RECORD := #write);
        // output status during write data record
        IF #wrrec.busy THEN
            #STATUS := #wrrec.status;
            #IOL_STATUS := DW#16#00020000;  // 0002 = writing in progress 
        ELSIF #wrrec.error THEN
            #tmpDWORD := #wrrec.status AND DW#16#00FFFF00;  // filter status
            IF #tmpDWORD = DW#16#0080C200 THEN    // if resource #BUSY (#STATUS=80C2), then ... 
                #sequencecontrol := 3;  // ... repeat write data record
            ELSE
                #ERROR := TRUE;
                #STATUS := #wrrec.status;   // ... otherwise output error status of "WRREC"
                #IOL_STATUS := DW#16#80530000;  // ... with note '8053' = error status of "WRREC"
                #sequencecontrol := 99; // aborting function with error
            END_IF;
        ELSIF #wrrec.done = TRUE THEN   // wait for write data record finish without error
            #TP_poll(IN := TRUE,
                     PT := T#100ms);    // poll for response
            #sequencecontrol := 4;
        ELSE
            ;
        END_IF;
        
    3:  // repeat write data record because resource was busy
        #WRREC_Function(REQ := FALSE);
        #sequencecontrol := 2;
        
    4:  // wait and poll for response = Read Data Record (RDREC)
        #WRREC_Function(REQ := FALSE);
        #TP_poll(IN := TRUE,
                 PT := T#100ms);
        
        // if poll rate executed then start read data record (on falling edge)
        #F_TRIG_poll := #TP_poll.Q = FALSE AND #F_TRIG_poll_old = TRUE;
        #F_TRIG_poll_old := #TP_poll.Q = TRUE;
        
        // RDREC = Read Data Record
        #RDREC_Function(REQ := #F_TRIG_poll,
                        MLEN := UINT#0, // length = 0 = read all data which are available
                        VALID => #rdrec.valid,
                        BUSY => #rdrec.busy,
                        ERROR => #rdrec.error,
                        STATUS => #rdrec.status,
                        LEN => #rdrec.length,
                        RECORD := #read);
        // output status during read data record
        IF #rdrec.busy THEN
            #STATUS := #rdrec.status;
            #IOL_STATUS := DW#16#00030000; // 16#0003 = reading in progress
        ELSIF #rdrec.error = TRUE THEN
            #tmpDWORD := #rdrec.status AND DW#16#00FFFF00;  // filter status
            IF #tmpDWORD = DW#16#0080C200 THEN  // if resource busy (status=80C2), then ... 
                #sequencecontrol := 5;  // ... repeat read data record / continue polling
            ELSE
                #ERROR := TRUE;
                #STATUS := #rdrec.status;   // ... otherwise output error code of "RDREC"
                #IOL_STATUS := DW#16#80520000;  // ... with note '8052' = error status of "RDREC"
                #sequencecontrol := 99; // aborting function with error
            END_IF;
        ELSIF #rdrec.valid THEN
            IF  // check the response wether it matches the request (compare PORT, INDEX and SUBINDEX)
                (#write.header."CALL"."Port" <> #read.header."CALL"."Port") OR
                (#write.header.IOL.Index_HighByte <> #read.header.IOL.Index_HighByte) OR
                (#write.header.IOL.Index_LowByte <> #read.header.IOL.Index_LowByte) OR
                (#write.header.IOL.Subindex <> #read.header.IOL.Subindex)
            THEN    // read data record don't match the request
                #ERROR := TRUE;
                #STATUS := #rdrec.status;
                #IOL_STATUS := DW#16#70000000;  // output status '7000', no consistent data
                #sequencecontrol := 99; // aborting function with error
            ELSIF #read.header.IOL.Control = B#16#80 THEN   // check the response for IO-Link errors
                // State 0x80 = IOL_CALL_RES PDU shows IO-Link error detect
                #ERROR := TRUE;
                #STATUS := DW#16#00000000;
                #IOL_STATUS.%B3 := #read.data[0];   // IO-Link master error code
                #IOL_STATUS.%B2 := #read.data[1];   // IO-Link master error code
                #IOL_STATUS.%B1 := #read.data[2];  // IO-Link device error code
                #IOL_STATUS.%B0 := #read.data[3];  // IO-Link device additional error code
                
                #sequencecontrol := 99; // aborting function with error
            ELSE    // read data are valid = output
                #DONE_VALID := TRUE;
                #BUSY := FALSE;
                #ERROR := FALSE;
                #STATUS := DW#16#00000000;
                #IOL_STATUS := DW#16#00000000;  // request finished
                
                IF #typeofrequest = 1 THEN
                    #RD_LEN := UINT_TO_INT(#rdrec.length - UINT#8);  // device parameter cover header and data,
                    // only data will be output
                    #i := INT#0; // initialize loop counter
                    FOR #i := 0 TO UINT_TO_INT(#rdrec.length - UINT#8 - UINT#1) DO
                        #RECORD_IOL_DATA[#i] := #read.data[#i];
                    END_FOR;
                ELSE
                    ;
                END_IF;
                #sequencecontrol := 98;
            END_IF;
        ELSE
            ;
        END_IF;
        
    5:  // poll again for response because resource was busy
        #TP_poll(IN := FALSE,
                 PT := T#100ms);
        #RDREC_Function(REQ := FALSE,
                        RECORD := #read);
        #sequencecontrol := 4;  // ... repeat read data record / continue polling   
        
    98: // request done successfully
        #TP_poll(IN := FALSE,
                 PT := T#100ms);
        #RDREC_Function(REQ := FALSE,
                        RECORD := #read);
        IF #REQ = FALSE THEN
            #DONE_VALID := FALSE;
            #BUSY := FALSE;
            #ERROR := FALSE;
            #RD_LEN := INT#0;
            #STATUS := DW#16#00000000;
            #IOL_STATUS := DW#16#00010000; // ready for new request
            #sequencecontrol := 0;
            #typeofrequest := 0;
        ELSE
            ;
        END_IF;
        
    99: // Function aborted with error
        #TP_poll(IN := FALSE,
                 PT := T#100ms);
        #WRREC_Function(REQ := FALSE);
        #RDREC_Function(REQ := FALSE,
                        RECORD := #read);
        #DONE_VALID := FALSE;
        #BUSY := FALSE;
        #RD_LEN := INT#0;
        #sequencecontrol := 0;
        #typeofrequest := 0;
        
    ELSE  // no relevant case, ready for new request
        #WRREC_Function(REQ := FALSE);
        #RDREC_Function(REQ := FALSE,
                        RECORD := #read);
        #TP_poll(IN := FALSE,
                 PT := T#100ms);
        #TON_monitoring(IN := FALSE,
                        PT := T#20s);
        #sequencecontrol := 0;
        #typeofrequest := 0;
END_CASE;


(*---------------------------------------------------------------------------------------------------------------------
            MONITORING
---------------------------------------------------------------------------------------------------------------------*)
#TON_monitoring(IN := #sequencecontrol > 0 AND #sequencecontrol < 98,
                PT := T#20s);
IF #TON_monitoring.Q THEN
    #ERROR := TRUE;
    #IOL_STATUS := DW#16#80000000; // request time out       
    #sequencecontrol := 99; // aborting function with error
END_IF;
```


----------



## klaly (27 Mai 2020)

Hallo liebe SPS Freunde, 
was ihr da macht siehr recht interessant aus. 
Ich (Klaus Loy, Yasakawa, ehemals VIPA) lese hier nur sehr selten mit. 
Ein Kollege hat mich auf dieses IO-Link Thema aufmerksam gemacht. 
Mich würde die Tool Kommuikation PCT-Tool zu IO-Link Modul auch interessieren. 
Bin aber grad S7Comm mässig etwas aus der Übung. 

>Verbindungsaufbau geschieht zu TSAP 0xf402, ist das in irgendeiner Weise auf die IO-Link Baugruppe zurückzuführen?

Ich kenne dieses TSAP API Byte, in folger Form: 
00:        verboten, bzw unüblich
01:        PG
02:        OP
03:        Other, sonstige
04...0F:  verboten, bzw unüblich
10...DF:  projektierte S7 Verbindungen
E0:         offene Communikation TCON
FA:         TCI Routing
FD:         S7-Basis Kommunikation, Teleservice
FE:         KBus Kommunikation, CPU-->CP, SDB-Übertragung

F4:         sehe ich auch zum erstenmal, Vermutlich eine spezielle Kennung für das PCT-Tool
              Da gibt es dann noch das Stichwort "Datensatz routing" evtl. ist genau diese API KEnnung. 

Ich möchte Euerer Diskussion hier weiter folgen. 

Hier ein paar Fragen: 
Diese PCT-Tool Verbindung geht ja sicherlich zu einer CPU und dann gehen die Datensätze von dort weiter über 
Profibus oder Profinet zum entsprechenden "Zielgerät" hab ich das richtig verstanden ? 

Wenn ihr z.B. mit diesem netten Python Code Daten hin und her transferiert und da nicht "jeder" Datensatz funktioniert, 
dann könnte man das ja am Projebus oder Profinet mit sniffern, welche Fehlermeldung da kommt, 
oder ob der entsprechende Datensatz da überhaupt raus geht. 

Mehr fällt mir grad nicht zu dem Thema ein, ich werd es aber im Auge behalten. 

mfG. K.Loy


----------



## Thomas_v2.1 (27 Mai 2020)

Das was Siemens da in S7comm eingepackt hat, entspricht so wie ich das bisher gesehen habe dem von mir in #27 verlinkten Standard für Profibus / Profinet zu IO-Link Gateways. Das sind 6 oder 8 Bytes die Siemens noch davor gestellt hat. Ich habe gestern auch ein paar Handbücher zu IO-Link Gateways in Augenschein genommen in denen diese Protokolldetails auch erwähnt werden.

Ein Routing TSAP sieht auf jeden Fall anders aus, der ist länger als 2 Bytes. Er verwendet ja hier auch eine NC-Steuerung, da scheint mir einiges etwas anders zu sein. Da müsste mal jemand mit einer "normalen" CPU und dem PCT eine Aufzeichnung erstellen.


----------



## klaly (27 Mai 2020)

Hallo Thomas, 

ich meinte keinen Routing TSAP im Sinne von PG --> CPU1 --> Net2 --> CPU2,  da ist der SAP richtig lang.
Sondern ich meinte  "Datensatz Routing", ich hab grad mal hierzu gegoogelt, ... 
*
Welcher Unterschied besteht zwischen "normalem Routing" und Datensatz-Routing?*
Beitrags-ID:  7000978  https://support.industry.siemens.com/cs/document/7000978

In diesem Beitrag findet sich auch folgender Hinweis:
Wenn Sie Daten über über Netzwerkgrenzen hinweg übertragen, verwenden Sie die Funktion "S7-Routing". 
Hierbei können Sie Informationen von einem Sender über verschiedene Netzwerke hinweg zu einem Empfänger verschicken. 
Weitere Informationen zur Funktion "S7-Routing" finden Sie unter der Beitrags-ID 584459. 


Was einen Test mit S7-PCT und Siemens CPUs angeht, könnte ich schon was probieren aber ich hab halt nur eine altes S7-PCT V3.2 von ca. 2013 zur Hand. 
Ich weiß nicht ob das aktuell genug ist. 

mfG. Klaus Loy


----------



## Hans54216 (27 Mai 2020)

klaly schrieb:


> Was einen Test mit S7-PCT und Siemens CPUs angeht, könnte ich schon was probieren aber ich hab halt nur eine altes S7-PCT V3.2 von ca. 2013 zur Hand.



Die aktuelle Version kann man bei Siemens runter laden.

https://support.industry.siemens.co...ct-v3-5-sp1-für-io-link-master?dti=0&lc=de-WW


----------



## klaly (28 Mai 2020)

Danke für die Info, 
ich hab den Download gestern auch gefunden als ich nach einer aktuelleren Version gesucht hatte. 
Nun hab ich V3.5 SP1 installiert. Mal schaun was man damit tun kann. 
Und was man von der Kommunikation zwischen Tool und CPU sieht. 

mfG. K.Loy


----------



## klaly (28 Mai 2020)

Ich hab hier mal ein wenig mit dem S7-PCT V3.5 SP1 rum gemacht. Meine Konfig ist in den Screenshots zu sehen. 
Leider bekomme ich eine Online Verbindung nur dann hin, wenn der PC/PG direkt mit dem Profinet de PN-Devices verbunden ist. 
Dann erfolgt aber eine "Profinet" UDP-Kommunkation mit PN-Datensätzen. 
Eine S7-Verbindung durch die CPU hindurch konnte ich nicht zustande bringen. Ich habs auch mit zwei getrennten Netzen versucht. 
Aber S7-PCT wollte IMMER nur durekt mit dem PN-Device kommunzieren. 
Keine Ahnung, nicht mal wenn Routing einstelle geht es.



Ich werde später noch Tests mit TIA machen. 

mfG. K.Loy


----------



## Hans54216 (28 Mai 2020)

Kannst du mal mit ner ET200 ProfiBus testen?

Und einen Wireshark der Kommunikation mit der ET200 ProfiNet? PN-Device Kommunikation ist ja auch interessant (PC->ET200->IOL-Master)


----------



## klaly (28 Mai 2020)

Hallo Hans54216, 
ich denke du hast mich gemeint. Leider habe ich keine Profibus Anschaltung für ET200SP, daher kann ich das nicht testen. 
Die Kommunikation S7-PCT --> IOL-Master kann ich gerne aufzeichnen. 
Nur ist die Frage, welche Online "Anfragen" soll ich da machen, ich kenn mich zu wenig mit der S7-PCT DSoftware aus. 
Ich würde einfach mal auf den IL-Master anklicken und dann Online schalten. Hierzu würde ich dann die Wireshark Aufzeichnung und mein Step7 Projekt posten. 
Wäre das so gewünscht ? 

Wenn ich noch eine ander "Ansicht" mit schneiden soll, sag mir was und wie. 

mfG. K.Loy


----------



## klaly (28 Mai 2020)

Online Schalten gemacht, sie Screenshot, Wireshark. Kommunikation über Ethernet direkt zu CPU P1, PN-Device hängt an P2, 
so dass die PN_IO Telegramme nicht im wireshark enthalten sind.



Anhang anzeigen Wireshark_S7-PCT_Slot1_Online.zip
Anhang anzeigen Wireshark_S7-PCT_Slot1_Online.zip



mfG. K.Loy


----------



## Thomas_v2.1 (28 Mai 2020)

klaly schrieb:


> Online Schalten gemacht, sie Screenshot, Wireshark. Kommunikation über Ethernet direkt zu CPU P1, PN-Device hängt an P2,
> so dass die PN_IO Telegramme nicht im wireshark enthalten sind.



Und über S7comm / TCP Port 102 geht gar nichts?
So wie ich das sehe sind in den PN-Telegrammen nur die Standard I&M Abfragen enthalten und nichts IOL spezifisches.

In dem Dokument zur IO-Link Integration in Profibus/Profinet ist bei Aufbau eines IOL-Calls nur der DPV1-Header für Profibus aufgelistet, in einer Anmerkung steht dass das auch PN-IO sein kann mit einem entsprechenden PN-IO Header mit API/Slot/Subslot. Alles danach (Call-Header, IOL-Header, Body) findet sich auch beim Transport über S7comm wieder, wenn auch mit abweichenden Längen. Und der DPV1-Header fehlt komplett, oder sieht zumindest komplett anders aus.


----------



## Draco Malfoy (29 Mai 2020)

Hans54216 schrieb:


> Hier als Zip.
> 
> 
> ```
> ...



Es ist doch hoffentlich nicht dein Ernst. Es gibt doch eine offizielle Quelle für diesen Baustein.


----------



## Draco Malfoy (29 Mai 2020)

Thomas_v2.1 schrieb:


> Du hast bei deiner AWL-Quelle vermutlich den IO_LINK_MASTER gepostet, von dem liefert Siemens auch die SCL-Quelle mit.
> 
> Wenn man aber in den IO_LINK_DEVICE reinschaut, dann sieht man zumindest in den STAT-Variablen in der Struct "read.header" den Aufbau des Datenteils das auch übers Netzwerk geht. Glücklicherweise mit Kommentar  Leider fehlt hier die SCL-Quelle, müsste man sich anfertigen wenn das weiterhilft.



Doch, die gibt es. Muß man halt woanders schauen, nicht im Beitrag zu dem Baustein. Aber die ist veröffentlicht. Von der Firma Siemens, liebe Moderation, nicht von slowakischen Hackern.


----------



## Faceman (29 Mai 2020)

Draco Malfoy schrieb:


> Doch, die gibt es. Muß man halt woanders schauen, nicht im Beitrag zu dem Baustein. Aber die ist veröffentlicht. Von der Firma Siemens, liebe Moderation, nicht von slowakischen Hackern.



Plonk......


----------



## Draco Malfoy (29 Mai 2020)

> Wenn man aber in den IO_LINK_DEVICE reinschaut, dann sieht man zumindest  in den STAT-Variablen in der Struct "read.header" den Aufbau des  Datenteils das auch übers Netzwerk geht. Glücklicherweise mit Kommentar :smile: Leider fehlt hier die SCL-Quelle, müsste man sich anfertigen wenn das weiterhilft.



Ich habe diesbezüglich schon mehrfach gefragt, auch eigens einen Thread dafür aufgemacht, woher die Datenstruktur kommt, die wir dort in dem Header sehen. Eine sinnstiftende & vernunftbehaftete Antwort habe ich bis heute nicht erhalten.



Faceman schrieb:


> Plonk......


Es müsste eigentlich mal Sanktionen geben für sinnfreie Antworten, die nichts zu dem Thema beitragen.


----------



## Thomas_v2.1 (29 Mai 2020)

Draco Malfoy schrieb:


> Ich habe diesbezüglich schon mehrfach gefragt, auch eigens einen Thread dafür aufgemacht, woher die Datenstruktur kommt, die wir dort in dem Header sehen. Eine sinnstiftende & vernunftbehaftete Antwort habe ich bis heute nicht erhalten.



Die Datenstruktur ist in dem Dokument zur IO-Link Integration in Profibus / Profinet die auch in deinem Thread verlinkt wurde dokumentiert. Habe ich hier in #27 nochmal verlinkt. Da ist einmal der Aufbau der Struktur beschrieben, und auch wie die Parameter eines Programm-FBs auf die Strukturen abgebildet werden.


----------



## Hans54216 (29 Mai 2020)

Draco Malfoy schrieb:


> Es ist doch hoffentlich nicht dein Ernst. Es gibt doch eine offizielle Quelle für diesen Baustein.



Wenn du den Verlauf mal lesen würdest und hier nicht so einen ... schreibst, würdest du sehen, dass ich im Beitrag #28 bereits die Offizielle Quelle gepostet habe.

IO-Link Kommunikation per S7comm


----------



## Thomas_v2.1 (29 Mai 2020)

Anfrage / Antwort zu dem Lesen von Index/Subindex sieht demnach so aus:



Zur Anfrage gibt es noch eine Antwort der SPS (evtl. zur Bestätigung der Anfrage), und vor den eigentlichen Daten gibt es noch eine Anfrage (evtl. um mitzuteilen wie groß der Buffer für die Antwort ist). Diese haben aber so wie es aussieht nichts mehr mit dem IOL Call zu tun.
Wenn in CAP = 255 steht dann gibt es auch noch eine Besonderheit dass damit so wie ich es verstehe I&M Datensätze gelesen werden, und manche Telegramme passen überhaupt nicht ins Schema. Manchmal steht bei Länge eine 8 wenn aber überhaupt nichts mehr folgt. Und was genau die ersten 2+2+2 Bytes bedeuten ist mir auch nicht ganz klar.
Ich weiß nicht wie man dahinter kommen kann. Bei dem was das PCT macht passiert zu viel gleichzeitig. Man müsste einzelne Funktionen auslösen die nur eine Anfrage/Antwort haben, und dich gleich mehrere 100 davon.


----------



## Hans54216 (29 Mai 2020)

Hab gerade ne Aufzeichnung der ProfiNet Kommunikation zwischen PLC und ET200 gemacht.

PLC PN -> Beckhoff IPC ProfiNet Port -> ET200





Anhang anzeigen PN_20200529.rar


PS: S7-PCT funktioniert bei mir mit ProfiNet ebenfalls nicht über die PLC. Muss mich mal direkt ans ProfiNet stecken und testen.


----------



## Thomas_v2.1 (29 Mai 2020)

Ok, in den PN Nutzdaten steckt also das gleiche wie bei S7comm im IOL-Payload.
Der Ablauf ist auch identisch, also
1. WriteRequest mit dem anzufordernden Index/Subindex
2. WriteResponse
3. ReadRequest mit Angabe der Record-Länge
4. ReadResponse mit den angefragten Daten

Nur bei dem Transport über S7comm haben WriteResponse und ReadResponse die gleichen Daten, bei WriteResponse mit einer Längenangabe von 8 obwohl keine Daten mehr folgen. Eigentlich gehört hier ein Fehlercode rein.

Aber wenn du sagst, PCT macht das auch bei Profinet nicht über die CPU, dann wäre deine Variante ja exklusiv nur für Profibus verwendbar, ob da der Aufwand lohnt da Profibus ja doch nicht ganz aktuell ist.

Aber man könnte ja einfach mal probieren, ob es über S7comm reicht nur diese 2 Telegramme zu schicken, ohne die ganzen Abfragen vorher von denen wir nicht wissen was die auf sich haben.


----------



## Hans54216 (29 Mai 2020)

Thomas_v2.1 schrieb:


> Ok, in den PN Nutzdaten steckt also das gleiche wie bei S7comm im IOL-Payload.
> Der Ablauf ist auch identisch, also
> 1. WriteRequest mit dem anzufordernden Index/Subindex
> 2. WriteResponse
> ...


Kannst du/wir versuchen das nachzubauen? Im 1. Schritt z.B. mit deinem Python Script?



Thomas_v2.1 schrieb:


> Aber wenn du sagst, PCT macht das auch bei Profinet nicht über die CPU, dann wäre deine Variante ja exklusiv nur für Profibus verwendbar, ob da der Aufwand lohnt da Profibus ja doch nicht ganz aktuell ist.


Profibus ist zwar alt, dafür aber für alle Steuerungen verfügbar. Was Siemens schon an dem ProfiNet rumgedoktert hat spricht auch nicht gerade für eine durchdachte Umsetzung, wobei in Zukunft alles in Richtung Netzwerk gehen wird.
Ob das PCT nicht über die PLC kommunizieren kann oder die Programmieren halt gleich den einfacheren Weg über TCP/IP gegangen sind ist damit ja noch nicht bewiesen.



Thomas_v2.1 schrieb:


> Aber man könnte ja einfach mal probieren, ob es über S7comm reicht nur diese 2 Telegramme zu schicken, ohne die ganzen Abfragen vorher von denen wir nicht wissen was die auf sich haben.


Das hört sich doch gut an.


----------



## Thomas_v2.1 (29 Mai 2020)

Ich habe die meiner Meinung nach dafür notwendigen Funktionen einmal nachgebildet. Kannst ja mal probieren ob dafür alle notwendig sind, oder ob z.B. nur die beiden sendIolReadStep1() und sendIolReadStep2() ausreichend sind, alles andere dann mit # auskommentieren. Die Antworten werden nicht ausgewertet sondern nur gedumpt, am Besten auch mal mit Wireshark mitloggen.

Wenn es funktioniert könnte man ja mal ein paar fehlerhafte Telegramme senden und prüfen wie dazu die Antworten aussehen. Da gibt es auch eine feste Struktur mit entsprechenden Fehlercodes.


----------



## Hans54216 (30 Mai 2020)

Bis auf den Verbindungsaufbau sind leider alle Anfragen mit Fehler Quittiert worden.
Hab nur die IP angepasst.

Anhang anzeigen py-iol_20200529.zip


----------



## Thomas_v2.1 (30 Mai 2020)

Du musst vermutlich auch wieder den TSAP anpassen 0xf4, 0x02 bei dir bisher.


----------



## Hans54216 (30 Mai 2020)

was ist mit der DP Adresse?


----------



## Hans54216 (30 Mai 2020)

Thomas_v2.1 schrieb:


> Du musst vermutlich auch wieder den TSAP anpassen 0xf4, 0x02 bei dir bisher.


Mit der Änderung bekomme ich eine Antwort.

Anhang anzeigen py-iol_20200530.zip


----------



## Hans54216 (30 Mai 2020)

Bezüglich der DP Adresse hab ich gerade nen Schrieb gemacht und folgendes gefunden.
Für mich sieht es so aus, dass zweischen  "sendIolInit(self)" und "sendIolReadStep1" noch die DP Adresse zu übertragen ist.




Anhang anzeigen DP3-DP6_20200530.zip


----------



## Thomas_v2.1 (30 Mai 2020)

Ja, das scheint die Adresse zu sein. Leider sind dort noch etliche weitere unbekannte Felder dabei, und die ersten 6 Bytes unterscheiden sich auch nicht einmal von den ganzen anderen Paketen, bis auf dass hier wo sonst der Slot eingetragen wird, hier eine 0 steht. Also wenn Slot==0 dann ist das komplett anders aufgebaut.


----------



## Thomas_v2.1 (30 Mai 2020)

Vermutlich ist das eine "Context ASE" aus der Profibus Spezifikation. Leider ist diese nicht frei verfügbar, und die besteht aus zudem mehreren Teildokumenten mit teilweise jeweils über 1000 Seiten. Das ist sehr zeitraubend sich da durchzuwühlen.


----------



## Hans54216 (30 Mai 2020)

1. Siehst du einen Weg wie man das mit ProfiNet testen kann?

2. Eine andere Möglichkeit der Verbindung für den RDREC?

3. Die PN Übertragung die ich aufgezeichnet habe hatte ja die PNIO-CM IOWrite und IORead Request. Kann man die Einfach so schicken oder ist da auch ein Verbindungsaufbau erforderlich.


----------



## Hans54216 (30 Mai 2020)

Thomas_v2.1 schrieb:


> Vermutlich ist das eine "Context ASE" aus der Profibus Spezifikation. Leider ist diese nicht frei verfügbar, und die besteht aus zudem mehreren Teildokumenten mit teilweise jeweils über 1000 Seiten. Das ist sehr zeitraubend sich da durchzuwühlen.



Wenn man die unbekannten Parameter einfach mal so übernimmt und ich mach ein paar Tests? Eventuell muss man gar nicht verstehen was die genau bedeuten.

Wenn sich herausstellt, dass das ganze für einen typischen Aufbau mit ProfiBus und ProfiNet funktioniert, kann man das ganze ja mit Vorbehalt einsetzten.


----------



## Thomas_v2.1 (31 Mai 2020)

Mit Profinet würde ich persönlich selber niemals anfangen so etwas selber zu entwickeln. Das ist sehr komplex gestaltet und umständlich dokumentiert. Und die Protokolldokumentationen musst du wie bei Profibus kaufen, am besten gleich noch Profibus dazu, weil das teilweise aufeinander aufbaut. Gelegentlich findest du die Entwürfe (Draft) der IEC Normen im Netz, da weiß man aber nie ob das immer noch so ist. Ich habe für Wireshark mich mal um einen Bug im Profinet-Teil gekümmert, alleine das Finden der Stellen in den Normen ist schon sehr aufwändig.

Du kannst natürlich auch ohne zu wissen wie das funktioniert das eine Telegramm mit einbauen und dort an der einen Position die Profibusadresse einsetzen. Dann wird es bei deinen aktuellen Gegebenheiten funktionieren, aber ob es in einer anderen Konstellation funktioniert ist fraglich.

Unter https://www.felser.ch gibt es ein paar Drafts der Normen zum Download, du brauchst mindestens die IEC 61158 Part 5 und vermutlich auch Part 6. So ungefähr ist die Variante mit Slot = 0 auch in dem IO-Link Integration Dokument erwähnt, zumindest liefert das ein paar Stichworte mit denen man in die anderen Dokumente gehen kann. Kannst dir das ja mal ansehen, dann weißt du so ungefähr was dich bei Profinet erwarten würde.


----------



## Thomas_v2.1 (31 Mai 2020)

Noch ein interessantes Ergebnis:

Ich habe hier eine IM151-8 PN/DP CPU komplett ohne Baugruppen.

Wenn ich mich zu dieser mit TSAP 0x01, 0x02 verbinde und dann diese IOL-Telegramme schicke, bekomme ich im Parameterteil schon den Fehlercode 0x8104 zurück (This service is not implemented on the module or a frame error was reported). D.h. der angesprochene Programmteil weiß mit diesen Telegrammen nichts anzufangen.

Wenn ich mich zu dieser mit TSAP 0xf4, 0x02 verbinde und dann diese IOL-Telegramme schicke, bekomme ich im Parameterteil keinen Fehlercode mehr, sondern im Datenteil ein paar Daten, mit vermutlich einem Fehlercode auf unterlagerter Ebene (beispielsweise weil die DP Adresse 3 die ich ansprechen will nicht vorhanden ist).

Ich vermute darum einmal, dass ich mit dem TSAP 0xf4 als Verbindungsressource eine bestimmte Funktion ansprechen kann. Vielleicht ist es das von klaly verlinkte "Datensatz Routing" was z.B. auch von Simatic PDM verwendet wird (habe ich leider nicht zum Testen).


----------



## Hans54216 (1 Juni 2020)

Ich hab den Teil mit der DP-Adresse mal 1 zu 1 nachgebaut und es scheint zu funktionieren.

Zum ProfiNet Teil:
Ich hab ja die PLC->ET200 ProfiNet Kommunikation aufgezeichnet und für den PLC IOL Leseauftrag vier Pakete detektiert (2x Request und 2x Response). Erste einen schreib und anschließend den Leseauftrag.
Ich möchte jetzt nicht tausende von Seiten lesen um das komplette PN zu verstehen. Mir reichen ja zunächst diese beiden Telegramme. Hast du noch einen Tipp auf was zu achten ist? Sonst bau ich die halt mal 1 zu 1 nach.


----------



## Thomas_v2.1 (1 Juni 2020)

Ich habe mit Profinet selber noch nicht solche Versuche angestellt. Aber die Funktionen die du dort aufgezeichnet hast scheinen ohne vorherigen Aufbau einer Beziehung möglich zu sein, also probieren.

Bezüglich des Datensatz-Routings scheint der Teil in der SPS sehr empfindlich auf fehlerhafte Telegramme zu reagieren. Ich habe gestern versucht ein paar fehlerhafte Antworten zu generieren in dem ich nur einzelne Byte-Werte anpasse. Ich hatte fast jedes Mal den Christbaum an der SPS am leuchten (alle LED blinken), und SPS nicht mehr erreichbar. Zurücksetzen war nur über Spannung aus-ein möglich.


----------



## Hans54216 (1 Juni 2020)

Thomas_v2.1 schrieb:


> Ich habe mit Profinet selber noch nicht solche Versuche angestellt. Aber die Funktionen die du dort aufgezeichnet hast scheinen ohne vorherigen Aufbau einer Beziehung möglich zu sein, also probieren.


Hab nun 1 zu 1 das Telegramm nachgebaut. Hat jedoch nicht zum Erfolg geführt.
Aufzeichnung + Python Dummy
Anhang anzeigen py-iol-pn.zip

Anhang anzeigen pn_20200601.zip


Aufzeichnung S7-PCT PN Kommunikation
Anhang anzeigen S7-PCT-172.30.1.81-ST1-P4_20200601.zip





Thomas_v2.1 schrieb:


> Bezüglich des Datensatz-Routings scheint der Teil in der SPS sehr empfindlich auf fehlerhafte Telegramme zu reagieren. Ich habe gestern versucht ein paar fehlerhafte Antworten zu generieren in dem ich nur einzelne Byte-Werte anpasse. Ich hatte fast jedes Mal den Christbaum an der SPS am leuchten (alle LED blinken), und SPS nicht mehr erreichbar. Zurücksetzen war nur über Spannung aus-ein möglich.


Hab mit deinem Python Script + Virenscanner die SPS so abgeschossen, dass nur ein Urlöschen geholfen hat.


----------



## Thomas_v2.1 (1 Juni 2020)

Hans54216 schrieb:


> Hab nun 1 zu 1 das Telegramm nachgebaut. Hat jedoch nicht zum Erfolg geführt.
> Aufzeichnung + Python Dummy


Ich vermute der ConnectRequest sowie das Auswerten der ConnectResponse gehören auch noch dazu.
Und am Ende deines Datenaustausches noch der ReleaseRequest um das auch sauber wieder abzubauen.


----------



## olliew (2 Juni 2020)

Thomas_v2.1 schrieb:


> Ich vermute der ConnectRequest sowie das Auswerten der ConnectResponse gehören auch noch dazu.
> Und am Ende deines Datenaustausches noch der ReleaseRequest um das auch sauber wieder abzubauen.



Ein PROFINET write request geht nicht ohne Verbindung, also Ja, ConnectRequest und ReleaseRequest werden benötigt.


----------



## Thomas_v2.1 (2 Juni 2020)

olliew schrieb:


> Ein PROFINET write request geht nicht ohne Verbindung, also Ja, ConnectRequest und ReleaseRequest werden benötigt.



Ist das eigentlich so vorgesehen, dass sich jeder über diesen Kanal mit einem Device verbinden kann und beliebige Daten auslesen und evtl. auch schreiben kann? Für einen PG zur Projektierung, Inbetriebnahme und Diagnose ist klar, dass es notwendig ist. Aber es scheint ja auch relativ einfach zu sein, damit zyklisch "azyklische" Daten auszulesen. Die Frage ist nur, wie viele Ressourcen stellt das Device dafür zur Verfügung.


----------



## olliew (2 Juni 2020)

Ja, das ist vorgesehen.  Das Device 'limitiert' jedoch die Anzahl gleichzeitige Verbindungen.  Aus dem Grund ist es auch Gut eine Verbindung sauber zu beenden 

Im übrigen sind diese Art "Verbindungen ohne IO" optional (in GSDML nachsehen: DeviceAccessSupported), sind aber durchaus praktisch für Diagnose usw..


----------



## klaly (3 Juni 2020)

Ich wollte grad mal mit TIA einen IO-Link Aufbau machen. 
Da ich nicht wusste wie aus TIA V15 heraus das S7-PCT gestartet werden kann hab ich ein wenig gegoogelt und hab bei Siemens einen interessanten Beitrag gefunden (englisch)
*How do you parameterize IO-Link master and devices across gateways with the S7 Port Configuration Tool (S7-PCT)?*
Beitrags ID: 87611392
Abbbildung 9, 10, 11
Mit S7 300/400 CPUs ist scheinbar der Zugriff mit S7-PCT auf PN-Devices nur direkt möglich, Außnahme IM_CPUs. 
Leider hab ich keine solche alte S7-classik IM-CPU. Ich hab nur eine CPU1510SP-1 PN, da werd ich mal das IO-Link Modul dran hängen und dann mit S7-PCT online gehen. 
Mal schaun ob ich da "indirekt" dran komme, evtl. über S7-Routing. So dass S7-PCT die S7-Kommunikation nehmen muss. 

mfG. Klaus Loy


----------



## klaly (3 Juni 2020)

jetzt hab ich mich mal mit S7_PCT über die CPU1510SP-1 PN CPU an das IO-Link Modul dran gehängt, da läuft dann halt dummerweise die S7-1500 er Kommunikation. Dann hab ich versucht, vor die 1500er eine 300er mit CP343-1EX30, zwecks Routing dran zu hängen. Dann mit S7_PCT über Routing auf das IO-Link Modul durch verbunden, läuft wieder die 1500er Kommunikation mit langen Routing SAPs (Aufzeichnung liegt vor und könnte ich hoch laden). 
OK, d.h. S7-PCT kann prinzipiell über Routing sich verbinden. Alao hab ich statt der 1500er wieder eine 300er mit ET200SP-PN-Device, mit IO-Link projektiert. 
Alles mit TIA, dann wollte ich S7_PCT über Routing an den IO-Link welcher über PN an der 300er hängt dran gehen. Device Tool starten frägt er mich nach dem zugangsweg, ist aber nur Direkt über PN, ohne Routing auswählbar. D.h. S7_PCT lässt sich nicht dazu bringen, mit einem IO-Link Master über "klassische" S7 Kommunikation zu verbinden. Es geht nur direkz über Profinet, oder nur mit einer ET200SP 1500er oder vieleicht mit einer ET200SP_300er CPU falls es sowas gibt.

mfG. Klaus Loy


----------



## Thomas_v2.1 (3 Juni 2020)

Was möchtest du denn noch herausfinden?
Wie die IOL-Master Abfragen über Profinet-IO ablaufen ist geklärt, lässt sich relativ einfach nachbilden.

Das Datensatz-Routing von Ethernet auf Profibus ist so wie es aussieht nur auf Profibus und nicht auf Profinet möglich. Hier ist zwar klar wie die Nutzdaten für IO-Link verpackt werden, aber nicht wie Siemens das Datensatz-Routing realisiert hat. Das Datensatz-Routing wird sonst so wie es aussieht nur noch von Simatic PDM verwendet, und das scheint mir nicht sonderlich verbreitet zu sein. Wenn ich das verfügbar hätte, dann wäre das mein Tool der Wahl um weitere Nachforschungen anzustellen. Ich habe das nur vor längerer Zeit mal verwendet um ein paar Sitrans über HART zu parametrieren. Muss ich mal sehen ob ich das noch auf einer VM verfügbar habe.


----------



## klaly (4 Juni 2020)

Ok, für mich war es interessant, aber wenn es für PN halt nicht geht dann ist es halt so. 
Ich denk aber dass die CPUs das sehr wohl könnten, aber es im Tool halt nicht realisiert ist. 
Es ist auch eigenartig, dass das IO-Link "Zeug" bei TIA so lose angekoppelt ist, mit einem externen Tool. 
Ich dachte immer bei TIA wäre das alles voll integriert. 

Für mich kann diese Diskussion hier enden. 

mfG. Klaus Loy


----------



## Thomas_v2.1 (4 Juni 2020)

Zeig doch mal die Aufzeichnung bei der 1500er. Bei den 300/400er CPUs gab es ja keine Modelle mit zwei Ethernet-Ports, vielleicht hat man darum das Datensatz-Routing ignoriert.


----------



## klaly (4 Juni 2020)

Aufbau war wie folgt: 315PN, mit CP343 wegen Routing, CPU 1510SP-1PN, mit nur einem Ethernet Port. 

S7_PCT über Ethernet --> CP343 --> 315PN über PN-Port --> 1510SP 

Hier die zugehörige Aufzeichnung: 
	

		
			
		

		
	

Anhang anzeigen S7_PCD_online_an_CPU1510SP-1.zip


mfG. Klaus Loy


----------



## Thomas_v2.1 (4 Juni 2020)

Das nutzt aber keinen Routing-TSAP, sondern den normalen TSAP für die S7comm-plus Kommunikation.
Bei S7comm-plus ist ja alles "objektifiziert", so auch die Abfrage der Diagnosedaten.

Es wird erst eine Variable geschrieben, mit dem angefragten IO-Link Datensatz. Aufbau ist soweit identisch mit dem auch über S7comm bekannten Aufbau.
Dann erfolgt eine Bestätigung ohne Daten.
Dann erfolgt eine Abfrage, und in der Antwort sind dann die Daten enthalten.


----------



## klaly (4 Juni 2020)

Sorry Thomas, 
da hab ich dir die falsche Aufzeichnung geschickt, es ging definitiv über Routing, ich hatte die Netze physikalisch getrennt. 
Aufzeichnung hab icj leider nicht mehr, könnte ich aber bei Interesse nochmal machen. 

mfG Klaus Loy


----------



## Thomas_v2.1 (4 Juni 2020)

klaly schrieb:


> da hab ich dir die falsche Aufzeichnung geschickt, es ging definitiv über Routing, ich hatte die Netze physikalisch getrennt.
> Aufzeichnung hab icj leider nicht mehr, könnte ich aber bei Interesse nochmal machen.


Wie ist die Aufzeichnung denn entstanden? Mit PCT per Ethernet auf einer 1500er CPU und dann über Profibus oder Profinet weiter?


----------



## klaly (8 Juni 2020)

Guten Morgen, 
Routing weg war wie folgt: 
S7_PCT --> S7-300 Routing Knoten --> 1500 PN Port (S7-comm) --> PN-Device (UDP) 

Aufzeichnung lief am PC, auf dem S7_PCT lief. 
Einen ET200SP DP-Slave habe ich leider nicht. 

DEn weg über den Routing Knoten habe ich gewählt um PCT zur "S7-comm" zu zwingen, ansonsten wäre es direkt über UDP an das PN-Device gegangen.

mfG. Klaus Loy


----------



## Hans54216 (8 Juni 2020)

Kannst du davon nochmal ne Aufzeichnung machen?


----------



## klaly (8 Juni 2020)

werde ich tun.


----------



## klaly (8 Juni 2020)

Aufzeichnung wurde nochmal gemacht. 
Erster Vorgang: S7_PCT Online über Routing, siehe Bild, dann Laden der Konfiguration aus IO-Master in "PG", Wireshark: S7_PCT_Laden_in_PG.pcapng
Zweiter Vorgang: S7_PCT Online über Routing, Laden von "PG" in IO-Master, Wireshark: S7_PCT_Laden_in_Baugruppe.pcapng


Hier ein ZipFile mit TIA-Projekt und den beiden Wireshark Aufzeichnungen.
Anhang anzeigen CPU_1510SP_over_Routing.zip


mfG. Klaus Loy


----------



## Thomas_v2.1 (8 Juni 2020)

Die S7-315 leitet also auch S7comm-plus per Routing weiter, interessant.
Vermutlich interessiert die SPS sich überhaupt nicht um die Nutzdaten, sondern leitet wenn der TSAP dem Format nach stimmt, die gesamten COTP Nutzdaten an das angegebene Ziel weiter. Da ist eine zweite Netzwerkschnittstelle ja auch bezüglich Netztrennung etwas anders zu bewerten, wenn sich da jeder so einfach durchrouten kann, und sich das auch nicht deaktivieren lässt.


----------



## klaly (9 Juni 2020)

Ja, das Routing kann "alles" weiter was in S7-SAPs verpackt werden kann. 
Da sind dann halt die "langen SAPs" im CR Telegramm enthalten. 
Auch Projektierung für Siemens Touch Panels können so geroutet werden. 

mfG. Klaus Loy


----------



## Thomas_v2.1 (11 Juni 2020)

Ich habe mit der Simatic PDM Demo mal versucht weiter herauszufinden, was die Parameter beim Datensatz-Routing bedeuten. Leider lassen sich in PDM keine Kommunikationsdetails manuell einstellen, anhand denen sich ein Zusammenhang mit den Daten auf dem Netzwerk herstellen lässt. Bis auf die schon bekannte Position der Profibus-Slave Adresse. Die restlichen Bytes beim Verbindungsaufbau sind auch identisch zu denen von Hans54216 aufgezeichneten Daten.

Mangels entsprechender Hardware kann ich auch nur den Verbindungsaufbau beobachten. PDM ist zudem nur mit sehr speziellen Baugruppen aus dem Katalog kompatibel. Wobei sich die I&M Datensätze vermutlich von jeder Baugruppe auslesen lassen sollten, aber soweit komme ich mangels Hardware auch nicht.

Wenn ich eine 300er CPU mit separatem Profibus-CP einsetze, dann erfolgt der Verbindungsaufbau über den TSAP 0xf409 wenn sich der Profibus-CP auf Slot 9 befindet.


----------



## Hans54216 (11 Juni 2020)

Thomas_v2.1 schrieb:


> Wenn ich eine 300er CPU mit separatem Profibus-CP einsetze, dann erfolgt der Verbindungsaufbau über den TSAP 0xf409 wenn sich der Profibus-CP auf Slot 9 befindet.



Das spricht dafür, dass sich meine 840d sl (317er PLC) nicht anders verhält wie deine 300er. TSAP  0xf4, 0x02 -> Slot 2


----------



## Thomas_v2.1 (11 Juni 2020)

Hab nun doch noch etwas herausfinden können.
Vor der Profibus-Adresse der Baugruppe steht die Subnetz-ID des Profibus-Netzes, übertragen so wie es aussieht 4 Bytes je Teil. Erst kommen die 4 Bytes der Subnetz-ID für das Projekt, dann 4 Bytes mit der Subnetz-ID des Subnetzes. Dann folgt die Profibus-Adresse (1 Byte).


----------



## Thomas_v2.1 (12 Juni 2020)

Die ganzen Daten scheinen ähnlich wie ein Routing TSAP aufgebaut zu sein:
https://www.tanindustrie.de/de/Help/ConfigClient/tsap_routing.htm

Die 06 01 02 finden sich dort auch wieder. Allerdings finden sich keine weiteren Informationen darüber was diese Werte zu bedeuten haben.


----------

