This is my modified Open-code which supports negotiated PDU size. The original code hardcodes an effective 222-byte PDU size (240-18). S7-400 supports 480-18 and I believe S7-1500 supports 960-18.
This code requests highest possible PDU size and PLC will respond with its highest supported size which is saved to the PDUSize property.
I am using this code successfully with 300 and 400 PLCs and I've briefly tested with 1500 but I don't remember if I got a 480 or 960 PDU.
PS. Sorry for posting code here instead of contributing :)
public ErrorCode Open()
{
byte[] bReceive = new byte[256];
if (OnlyConnectIfRespondsToPing && !Ping())
{
LastErrorCode = ErrorCode.IPAddressNotAvailable;
LastErrorString = string.Format("Destination IP-Address '{0}' is not available!", IP);
return LastErrorCode;
}
try {
// open the channel
if (_mSocket != null)
_mSocket.Dispose();
_mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_mSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, ReceiveTimeout);
_mSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, SendTimeout);
IPEndPoint server = new IPEndPoint(IPAddress.Parse(IP), Port);
IAsyncResult result = _mSocket.BeginConnect(server, null, null);
result.AsyncWaitHandle.WaitOne(1000, true);
if (!_mSocket.Connected)
throw new TimeoutException();
}
catch (TimeoutException ex)
{
LastErrorCode = ErrorCode.ConnectionTimeout;
LastErrorString = ex.Message;
return ErrorCode.ConnectionError;
}
catch (Exception ex)
{
LastErrorCode = ErrorCode.ConnectionError;
LastErrorString = ex.Message;
return ErrorCode.ConnectionError;
}
try
{
byte[] bSend1 = { 3, 0, 0, 22, 17, 224, 0, 0, 0, 46, 0, 193, 2, 1, 0, 194, 2, 3, 0, 192, 1, 9 };
// 0 = version number (for S7 communication always 0x03)
// 1 = reserved (always 0x00)
// 2-3 = size of ISO-TCP message (including this header) in byte
// 4 = length (in byte) of TPDU header (without this byte and possible user data)
// 5 = CR code (1110) & credit (always 0000)
// 6-7 = destination reference
// 8-9 = source reference
// 10 = class option (always class 0)
// 11 = 0xC1 (code: calling TSAP-ID)
// 12 = 0x02 (number of bytes following)
// 13 = 0x02 (unknown function; part of TSAP-ID)
// 14 = TSAP-ID (rack & slot)
// 15 = 0xC2 (code: called TSAP-ID)
// 16 = 0x02 (number of bytes following)
// 17 = 0x02 (unknown function; part of TSAP-ID)
// 18 = TSAP-ID (rack & slot)
// 19 = 0xC0 (code: TPDU size)
// 20 = 0x01 (number of bytes following)
// 21 = TPDU size (as exponent to base of 2)
switch (CPU) {
case CpuType.S7200:
//S7200: Chr(193) & Chr(2) & Chr(16) & Chr(0) 'Eigener Tsap
bSend1[11] = 193;
bSend1[12] = 2;
bSend1[13] = 16;
bSend1[14] = 0;
//S7200: Chr(194) & Chr(2) & Chr(16) & Chr(0) 'Fremder Tsap
bSend1[15] = 194;
bSend1[16] = 2;
bSend1[17] = 16;
bSend1[18] = 0;
break;
case CpuType.S7300:
//S7300: Chr(193) & Chr(2) & Chr(1) & Chr(0) 'Eigener Tsap
bSend1[11] = 193;
bSend1[12] = 2;
bSend1[13] = 1;
bSend1[14] = 0;
//S7300: Chr(194) & Chr(2) & Chr(3) & Chr(2) 'Fremder Tsap
bSend1[15] = 194;
bSend1[16] = 2;
bSend1[17] = 3;
bSend1[18] = (byte)(Rack * 2 * 16 + Slot);
break;
case CpuType.S7400:
//S7400: Chr(193) & Chr(2) & Chr(1) & Chr(0) 'Eigener Tsap
bSend1[11] = 193;
bSend1[12] = 2;
bSend1[13] = 1;
bSend1[14] = 0;
//S7400: Chr(194) & Chr(2) & Chr(3) & Chr(3) 'Fremder Tsap
bSend1[15] = 194;
bSend1[16] = 2;
bSend1[17] = 3;
bSend1[18] = (byte)(Rack * 2 * 16 + Slot);
break;
//case CpuType.S71200:
case CpuType.S71500:
// Eigener Tsap
bSend1[11] = 193;
bSend1[12] = 2;
bSend1[13] = 0x10;
bSend1[14] = 0x2;
// Fremder Tsap
bSend1[15] = 194;
bSend1[16] = 2;
bSend1[17] = 0x3;
bSend1[18] = 0x0;
break;
default:
return ErrorCode.WrongCPU_Type;
}
//Console.WriteLine("> " + string.Join(" ", bSend1.Select(b => b.ToString("x2"))));
_mSocket.Send(bSend1, 22, SocketFlags.None);
int receiveCount = _mSocket.Receive(bReceive, 22, SocketFlags.None);
/*
0-3 ISO-TCP header
4 length (in byte) of TPDU header (without this byte and possible user data)
5 CC code (1101) & credit (always 0000)
6-7 destination reference
8-9 source reference
10 class option (always class 0)
11 0xC0 (code: TPDU size)
12 0x01 (length of bytes following)
13 TPDU size (as exponent to base of 2)
14 0xC1 (code: calling TSAP-ID)
15 0x02 (length of id)
16 0x02 (unknown function; part of TSAP-ID)
17 TSAP-ID (rack & slot)
18 0xC2 (code: called TSAP-ID)
19 0x02 (length of id)
20 0x02 (unknown function; part of TSAP-ID)
21 TSAP-ID (rack & slot)
*
* More about data structurs can be found on http://www.bj-ig.de/147.html
*/
//Console.WriteLine("< " + string.Join(" ", bReceive.Take(receiveCount).Select(b => b.ToString("x2"))));
//Console.WriteLine("PDU: " + bReceive[13] + " (" + Math.Pow(2, bReceive[13]) + ")");
if (receiveCount != 22)
{
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
}
byte[] bsend2 = { 3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 255, 255, 0, 8, 0, 0, 240, 0, 0, 3, 0, 3, 0x3, 0xC0 };
//Console.WriteLine("> " + string.Join(" ", bsend2.Select(b => b.ToString("x2"))));
_mSocket.Send(bsend2, 25, SocketFlags.None);
receiveCount = _mSocket.Receive(bReceive, 27, SocketFlags.None);
//Console.WriteLine("< " + string.Join(" ", bReceive.Take(receiveCount).Select(b => b.ToString("x2"))));
if (receiveCount == 27)
{
PDUSize = IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(bReceive, 25));
}
else
{
//Console.WriteLine("< " + string.Join(" ", bReceive.Select(b => b.ToString("x2"))));
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
}
}
catch
{
LastErrorCode = ErrorCode.ConnectionError;
LastErrorString = string.Format("Couldn't establish the connection to {0}!", IP);
return ErrorCode.ConnectionError;
}
return ErrorCode.NoError;
}