# Klasse zur Kommunikation mit Siemens SPS über TCP



## pvbrowser (20 Februar 2013)

Hallo,

in pvbrowser haben wir eine eigene Klasse zur Kommunikation mit Siemens SPS über TCP.
Darin wird das alte von der S5 bekannte Fetch/Write Protokoll,
als auch das aktuelle Protokoll für Siemens SPS unterstützt.

Was beim "reverse engineering" des Protokoll noch unsicher ist,
ist der Austausch der TSAP's am Beginn der Kommunikation.

Diese Bytefolgen haben wir als funktionierend herausgefunden.
------------------------------------------------------------------------------
        | CB16         CB17         CB18
------------------------------------------------------------------------------
S7_200  |  2           'M'          'W'
------------------------------------------------------------------------------
S7_300  |  2            1            2
------------------------------------------------------------------------------
S7_400  |  2            1            3
------------------------------------------------------------------------------
S7_1200 |  2            1            0
------------------------------------------------------------------------------
LOGO    | function     rack+1       slot (LOGO 0BA7, 
        |                                 client TSAP 01.00.
        |                                 server TSAP 20.00 
        |                                 according to mhe_fr from our forum)
------------------------------------------------------------------------------

Es wurden auch schon Theorien aufgestellt:
# according to an unproofen theory siemens chooses the TSAP as follows
# connect_block[17] = 2; Function (1=PG,2=OP,3=Step7Basic)
# connect_block[18] = upper_3_bit_is_rack / lower_5_bit_is_slot
#   if(function != -1) connect_block[17] = function;
#   if(rack_slot != -1) connect_block[18] = rack_slot;

Der Quelltext unserer rlSiemensTCP Klasse kann hier eingesehen werden:
https://github.com/pvbrowser/pvb/blob/master/rllib/lib/rlsiemenstcp.h
https://github.com/pvbrowser/pvb/blob/master/rllib/lib/rlsiemenstcp.cpp

Man kann die Quellen hier herunterladen (ZIP oder git).
https://github.com/pvbrowser/pvb
Die rllib befindet sich dann im Unterordner pvb/rllib/lib

Es wäre schön, wenn jemand sachdienliche weitere Hinweise hätte.


----------



## Thomas_v2.1 (20 Februar 2013)

Die ganze TSAP Geschichte ist in der Siemens Step7 Hilfe ausführlich beschrieben, eigentlich gibt es da nichts mehr zu rätseln.

Ich weiß nicht in welchem PDF Handbuch das auch noch drin stehen könnte. Am einfachsten ist es, in Step7 eine PC-Station mit einem OPC-Server anzulegen.
Dort eine S7-Verbindung zu einer beliebigen Station anlegen. Direkt im Konfigurationsfenster der Verbindung werden die beiden Bytes des TSAP angezeigt. Man kann an Rack/Slot umstellen und sieht sofort was sich an dem TSAP ändert. Wenn man dann auf die Hilfe klickt bekommt man auch noch beschrieben was es mit der Verbindungsressource auf sich hat.


----------



## pvbrowser (21 Februar 2013)

Hallo Thomas_v2.1,

alles geht aus der Siemens Doku ja nicht hervor.
Hier fasse ich noch mal meinen Kenntnisstand zusammen und bitte euch um Kommentare.

Verbindung von PC an Siemens SPS über TCP mit ISO on TCP:
Der PC ist Client. Die SPS ist Server.

Anzahl der Verbindungen = 4   // 0-8 einstellbar (laut Beispiel in der Siemens Doku)
Wir benutzen eine Server Verbindung. Die SPS ist der Server.
Remote TSAP 10.00 (default TSAP) frei editierbar ? Der TSAP ist von der SPS aus gesehen der Remote TSAP.
Local  TSAP 10.00, 11.00, 12.00, 13.00 automatisch inkrementiert: connection0, connection1, connection2, connection3

Das wird direkt nach dem connect() vom PC an die SPS gesendet:
connect_block[index]
0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21
-----------------------------------------------------------------------------------
3, 0, 0,16,11,E0, 0, 0, 0, 1, 0,C1, 2,'M,'W,C2, 2,'M,'W,C0, 1, 9  S7-200/hex,ascii
3, 0, 0,16,11,E0, 0, 0, 0, 1, 0,C1, 2,4D,57,C2, 2,4D,57,C0, 1, 9  S7-200/hex
3, 0, 0,16,11,E0, 0, 0, 0, 1, 0,C1, 2, 1, 0,C2, 2, 1, 2,C0, 1, 9  S7-300/hex
3, 0, 0,16,11,E0, 0, 0, 0, 1, 0,C1, 2, 1, 0,C2, 2, 1, 3,C0, 1, 9  S7-400/hex
3, 0, 0,16,11,E0, 0, 0, 0, 1, 0,C1, 2, 1, 0,C2, 2, 1, 0,C0, 1, 9  S7-1200/hex
                                      13,14       17,18 index
                                      Remote TSAP Local TSAP
Remote TSAP scheint frei wählbar.
Local TSAP wird mit Step 7 projektiert.
Rest scheint immer gleich zu sein. Oder gibt es da eine "magische" Bedeutung.

TSAP              10.00    entspricht 4096 dezimal
Bits:           0001:0000 # 0000:0000
connect_block:  CB17:CB18 # CB19:CB20
                   1:0    #   C0:1
                   4096   #  8+4:1
                  +0*2048 #   
                  +0*1024 #
                  +0*512  #
                  +0*256  #

Schlussfolgerung:
Die kompletten TSAP sollten sein (TSAPs haben 64 Bit):
Remote TSAP := CB13, CB14, CB15, CB16 ,
               wobei immer gilt: CB15=C2 hex CB16=2,
               CB13 und CB14 scheint frei wählbar, d.h. der Remote TSAP ist der SPS egal
Local TSAP  := CB17, CB18, CB19, CB20 ,
               wobei immer gilt: CB19=C0 hex CB20=1, 
               CB17 und CB18 werden mit Step 7 projektiert 

Es wundert mich allerding, wie es bei der S7-200 zu dem 'M','W' kommt.
Ist damit eine besondere "connection" gemeint und nicht connection0 ... connectionN ?

Der Start des connect_block ist der ISO Header mit:
ih = 3   version
     0   reserved
     0   length_high
     16  length_low hex , 22 length_low dezimal

Die folgenden CB4 bis CB12 kann ich nicht interpretieren:
     11,E0, 0, 0, 0, 1, 0,
Das abschliessende CB21 kann ich nicht interpretieren:
     9

Dies ist ein Request For Comment.
Wer kann den RFC kommentieren ?

Viele Grüße:
http://pvbrowser.org


----------



## Thomas_v2.1 (21 Februar 2013)

http://www.ietf.org/rfc/rfc0905.txt


----------



## pvbrowser (21 Februar 2013)

Hallo Thomas_v2.1,

danke für den Link.
Damit habe ich das mal aufgedröselt.
Kannst Du noch mal drauf sehen ?

CB00: 3, ISO_HEADER_VERSION
CB01: 0, ISO_HEADER_RESERVED
CB02: 0, ISO_HEADER_LENGHT_HIGH
CB03:16, ISO_HEADER_LENGHT_LOW = 22 Byte (hex 16)
CB04:11, Length Indicator Field 17 dec = 22 - 1 byte_length_indicator - 2 byte_ISO_Header
CB05:E0, Connection Request Code (Bits 8-5) 1110=E, Initial Credit Allocation (Bits 4-1) Class 0
CB06: 0, DESTINATION-REF-HIGH
CB07: 0, DESTINATION-REF-LOW
CB08: 0, SOURCE-REF-HIGH
CB09: 1, SOURCE-REF-LOW
CB10: 0, Class and Option
CB11:C1, Identifier Calling TSAP will follow
CB12: 2, Parameter Length
CB13: 1, Remote TSAP, frei wählbar
CB14: 0, Remote TSAP, frei wählbar
CB15:C2, Identifier Called TSAP will follow
CB16: 2, Parameter Length
CB17: 1, Local TSAP,  in Step7 projektiert = 1
CB18: 0, Local TSAP,  in Step7 projektiert = 0...connectionN
CB19:C0, Identifier Maximum TPDU size
CB20: 1, Parameter Length
CB21: 9, max 512 octets 

(Aus Beispiel für S7-1200)


----------



## Thomas_v2.1 (21 Februar 2013)

Für einen Client mag das ausreichend sein.
Aber ich würde fast dazu tendieren eine Klassenhierarchie aufzubauen, also TPKT -> TPDU -> S7comm.
Eine TPDU hat nämlich die Möglichkeit zu fragmentieren. Das kommt z.B. bei einer WinAC vor. Dort ist dann das Bit für "Last Data Unit" nicht gesetzt, und es ist überhaupt kein Dateninhalt vorhanden. Zottel hat das in libnodave als "funny 7 byte packet" bezeichnet.
Eigentlich sind diese Pakete auch völlig sinnlos, aber es sind trotzdem gültige Telegramme. Diese Ausnahme muss man einprogrammieren um auch mit einer WinAC kommunizieren zu können, zumindest ist es mir nur bei dieser aufgefallen.


----------



## Thomas_v2.1 (21 Februar 2013)

Zum Remote TSAP sagt Siemens:


			
				Simatic Net schrieb:
			
		

> Remote TSAP (Remote Transport Service Access Point, entfernter Dienstzugangspunkt)
> Die Darstellung ist die gleiche wie beim Local TSAP, allerdings hat das zweite Bytes eine
> andere Bedeutung:
> ● erstes Byte: enthält eine Gerätekennung, erlaubte Werte sind 02 oder 03:
> ...


----------



## pvbrowser (21 Februar 2013)

Noch mal Danke für den Kommentar und
im Besonderen für den Ausnahmefall.

In unserer Klasse gibt es ein private: write_iso() und read_iso(), da ist die Hierarchie also schon drin.
Bei dem "connect_block" direkt nach dem Aufbau der TCP Verbindung ist das meiner Meinung nach ok so wie es ist.
Im Kommentar steht ja auch drin, dass der ISO_HEADER vorne weg kommt.

Die Erkenntnisse habe ich jetzt in den Doxygen Kommentar an Kopf unseres rlsienenstcp.h header reingeschrieben.

Wundern tut mich allerdings noch, dass wir bei der S7-200 immer mit 'M' 'W' als TSAPs gearbeitet haben.

Hier der Kommentar:
<pre>
class for communication with Siemens PLC's via TCP

(1) There is the old Fetch/Write protocol from the old S5 PLC (fetch_write=1).

(2) And there is the current Siemens PLC protocol introduced with the S7 series of PLC (fetch_write=0).

According to
http://www.ietf.org/rfc/rfc0905.txt
the client will send a connection request to the PLC after it has establisched a TCP connection().

Here is a example connect_block for a Siemens S7 PLC (CBxx in hex):
CB00= 3, ISO_HEADER_VERSION
CB01= 0, ISO_HEADER_RESERVED
CB02= 0, ISO_HEADER_LENGHT_HIGH
CB03=16, ISO_HEADER_LENGHT_LOW = 22 Byte (hex 16)
CB04=11, Length Indicator Field = 17 dec = 22 byte_total_length - 1 byte_length_indicator - 4 byte_ISO_HEADER
CB05=E0, Connection Request Code (Bits 8-5) 1110=E, Initial Credit Allocation (Bits 4-1) Class 0
CB06= 0, DESTINATION-REF-HIGH
CB07= 0, DESTINATION-REF-LOW
CB08= 0, SOURCE-REF-HIGH
CB09= 1, SOURCE-REF-LOW
CB10= 0, Class and Option
CB11=C1, Identifier: Calling TSAP will follow
CB12= 2, Parameter Length, 2 byte will follow
CB13= 1, Remote TSAP, free to choose on client side
CB14= 0, Remote TSAP, free to choose on client side
CB15=C2, Identifier: Called TSAP will follow
CB16= 2, Parameter Length, 2 byte will follow
CB17= 1, Local TSAP,  set within Step7 = 1
CB18= 0, Local TSAP,  set within Step7 = 0...connectionN
CB19=C0, Identifier: Maximum TPDU size will follow
CB20= 1, Parameter Length, 1 byte will follow 
CB21= 9, max 512 octets 

According to the Remote TSAP Siemens makes the following statement:
######################################################################################
Remote TSAP (Remote Transport Service Access Point, entfernter Dienstzugangspunkt)
The representation is the same as with the Local TSAP, 
but the second byte has another meaning:
- first Byte: contains a device id (allowed 02 or 03)
02 OS (Operating Station Bedienen und Beobachten)
03 other
suggested: 02
- second Byte: contains the adressing within the SIMATIC S7-CPU,
divided in:
Bit 7 ... 5 Rack (Subsystem) of the S7-CPU
Bit 4 ... 0 Slot of the S7-CPU
Hint: It is suggested to choose the same settings for Byte 1 in Remote and Local TSAP
######################################################################################

When you use our rlSiemensTCP class you can do it as follows:

unsigned char cb[22];
rlSiemensTCP *plc = new rlSiemensTCP(adr);
plc->getDefaultConnectBlock(cb);
cb[13] = 1; // set 1 Byte of Remote TSAP
cb[17] = 1; // set Local TSAP of the PLC to the
cb[18] = 0; // configuration done within Step 7 
plc->setConnectBlock(cb);

Now you can read/write the PLC.

The following matrix shows some combinations:
--------------------------------------
        | CB17    CB18    CB13   CB14
--------------------------------------
S7_200  | 'M'     'W'      'M'    'W'
--------------------------------------
S7_300  |  1       2
--------------------------------------
S7_400  |  1       3
--------------------------------------
S7_1200 |  1       0
--------------------------------------

A TSAP within Step 7 of 10.00 results in: cb[17] = 1; cb[18] = 0; 
A TSAP within Step 7 of 10.01 results in: cb[17] = 1; cb[18] = 1; 
A TSAP within Step 7 of 10.02 results in: cb[17] = 1; cb[18] = 2; 
A TSAP within Step 7 of 10.03 results in: cb[17] = 1; cb[18] = 3; 

# according to an unproofen theory siemens chooses the TSAP as follows
# connect_block[17] = 2; Function (1=PG,2=OP,3=Step7Basic)
# connect_block[18] = upper_3_bit_is_rack / lower_5_bit_is_slot
Thus the above would be:
A TSAP within Step 7 of 10.00 results in: cb[17] = PG; cb[18] = 0; // rack=0 slot=0 
A TSAP within Step 7 of 10.01 results in: cb[17] = PG; cb[18] = 1; // rack=0 slot=1
A TSAP within Step 7 of 10.02 results in: cb[17] = PG; cb[18] = 2; // rack=0 slot=2
A TSAP within Step 7 of 10.03 results in: cb[17] = PG; cb[18] = 3; // rack=0 slot=3

You may use rlSiemensTCP with the individual plc_type for conveniance.
But you can set the whole connect_block and use ANY_SIEMENS_COMPATIBLE_PLC.

Please use Wireshark or tcpdump if the settings of the above matrix do not work for you.
Send us your results.

PS: Still wondering about 'M' 'W' on S7-200
</pre>


----------



## pvbrowser (22 Februar 2013)

Womit ich immer noch Probleme habe, ist zu verstehen, nach welchen Konventionen die TSAPs vergeben werden.
Das wird ja nicht vom RFC vorgegeben, sondern ist Sache von Siemens.

So ein TSAP kann ja prinzipiell 2-8 Byte lang sein.
Früher hat Siemens auch lange TSAPs verwendet und darin darstellbare ASCII Zeichen als Konvention gehabt Wobei Calling und Called TSAP gleich sein sollten.
Die heutige S7 Kommunikation verwendet nur noch 2 Byte lange TSAP und binäre Inhalte.

Wenn man keine Verbindung explizit projektiert hat, geht bei der S7-200 TSAP = "MW" sowohl für Calling als auch Called TSAP (darstellbare Ascii Zeichen).

Die projektierten Verbindungen werden in Step 7 ja mit TSAP=xx.yy dargestellt Z.B. 10.00.
Bei der Zuordnung dieser Notation zur Kodierung als Byte bin ich noch etwas unsicher.

Es gibt ja folgende Zuordnung:
Function (1=PG/PC,2=OP,3=Step7Basic)
upper_3_bit_is_rack / lower_5_bit_is_slot

Wäre diese Beipiele dann richtig ?

TSAP=10.00 -> bytes: 0x01,0x00  PG/PC connection_0
 TSAP=10.01 -> bytes: 0x01,0x01  PG/PC connection_1
TSAP=10.01 -> bytes: 0x01,0x02  PG/PC connection_2
TSAP=10.01 -> bytes: 0x01,0x03   PG/PC connection_3

TSAP=20.00 -> bytes: 0x01,0x00  OP connection_0

Wo würde sich das Rack jetzt wiederfinde ?
In der 2-ten Ziffer der ersten Zahl (xx.yy) ?
Oder würde das mit in yy reincodiert ?


----------

