diff --git a/S7.Net/COTP.cs b/S7.Net/COTP.cs
index 2451893..d681026 100644
--- a/S7.Net/COTP.cs
+++ b/S7.Net/COTP.cs
@@ -11,6 +11,11 @@ namespace S7.Net
///
internal class COTP
{
+ public enum PduType : byte
+ {
+ Data = 0xf0,
+ ConnectionConfirmed = 0xd0
+ }
///
/// Describes a COTP TPDU (Transport protocol data unit)
///
@@ -18,7 +23,7 @@ namespace S7.Net
{
public TPKT TPkt { get; }
public byte HeaderLength;
- public byte PDUType;
+ public PduType PDUType;
public int TPDUNumber;
public byte[] Data;
public bool LastDataUnit;
@@ -30,8 +35,8 @@ namespace S7.Net
HeaderLength = tPKT.Data[0]; // Header length excluding this length byte
if (HeaderLength >= 2)
{
- PDUType = tPKT.Data[1];
- if (PDUType == 0xf0) //DT Data
+ PDUType = (PduType)tPKT.Data[1];
+ if (PDUType == PduType.Data) //DT Data
{
var flags = tPKT.Data[2];
TPDUNumber = flags & 0x7F;
diff --git a/S7.Net/PLC.cs b/S7.Net/PLC.cs
index e4a7f7f..4feb76f 100644
--- a/S7.Net/PLC.cs
+++ b/S7.Net/PLC.cs
@@ -85,7 +85,7 @@ namespace S7.Net
{
try
{
- Connect();
+ ConnectAsync().Wait();
return true;
}
catch
diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs
index 1de0ac8..f934160 100644
--- a/S7.Net/PlcAsynchronous.cs
+++ b/S7.Net/PlcAsynchronous.cs
@@ -24,26 +24,50 @@ namespace S7.Net
/// A task that represents the asynchronous open operation.
public async Task OpenAsync(CancellationToken cancellationToken = default)
{
- await ConnectAsync();
- cancellationToken.ThrowIfCancellationRequested();
- var stream = GetStreamIfAvailable();
-
- await stream.WriteAsync(ConnectionRequest.GetCOTPConnectionRequest(CPU, Rack, Slot), 0, 22);
- var response = await COTP.TPDU.ReadAsync(stream, cancellationToken);
- if (response == null)
+ var stream = await ConnectAsync();
+ try
{
- throw new Exception("Error reading Connection Confirm. Malformed TPDU packet");
+ cancellationToken.ThrowIfCancellationRequested();
+ await EstablishConnection(stream, cancellationToken);
+ _stream = stream;
}
- if (response.PDUType != 0xd0) //Connect Confirm
+ catch(Exception)
{
- throw new InvalidDataException("Error reading Connection Confirm", response.TPkt.Data, 1, 0x0d);
+ stream.Close();
}
+ }
- await stream.WriteAsync(GetS7ConnectionSetup(), 0, 25);
+ private async Task ConnectAsync()
+ {
+ tcpClient = new TcpClient();
+ ConfigureConnection();
+ await tcpClient.ConnectAsync(IP, Port);
+ return tcpClient.GetStream();
+ }
+
+ private async Task EstablishConnection(NetworkStream stream, CancellationToken cancellationToken)
+ {
+ await RequestConnection(stream, cancellationToken);
+ await SetupConnection(stream, cancellationToken);
+ }
+
+ private async Task RequestConnection(NetworkStream stream, CancellationToken cancellationToken)
+ {
+ var requestData = ConnectionRequest.GetCOTPConnectionRequest(CPU, Rack, Slot);
+ await stream.WriteAsync(requestData, 0, requestData.Length);
+ var response = await COTP.TPDU.ReadAsync(stream, cancellationToken);
+ if (response.PDUType != COTP.PduType.ConnectionConfirmed)
+ {
+ throw new InvalidDataException("Connection request was denied", response.TPkt.Data, 1, 0x0d);
+ }
+ }
+
+ private async Task SetupConnection(NetworkStream stream, CancellationToken cancellationToken)
+ {
+ var setupData = GetS7ConnectionSetup();
+ await stream.WriteAsync(setupData, 0, setupData.Length);
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
- if (s7data == null)
- throw new WrongNumberOfBytesException("No data received in response to Communication Setup");
if (s7data.Length < 2)
throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup");
@@ -54,15 +78,12 @@ namespace S7.Net
if (s7data.Length < 20)
throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup");
- MaxPDUSize = (short)(s7data[18] * 256 + s7data[19]);
- }
-
- private async Task ConnectAsync()
- {
- tcpClient = new TcpClient();
- ConfigureConnection();
- await tcpClient.ConnectAsync(IP, Port);
- _stream = tcpClient.GetStream();
+ // TODO: check if this should not rather be UInt16.
+ MaxPDUSize = Types.Int.FromByteArray(s7data, 18);
+ if (MaxPDUSize <= 0 )
+ {
+ throw new PlcException(ErrorCode.ConnectionError, "Communication Setup resulted in non-positive PDU size.");
+ }
}
diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs
index 8cf5f89..a243571 100644
--- a/S7.Net/PlcSynchronous.cs
+++ b/S7.Net/PlcSynchronous.cs
@@ -17,34 +17,9 @@ namespace S7.Net
///
public void Open()
{
- Connect();
-
try
{
- var stream = GetStreamIfAvailable();
- stream.Write(ConnectionRequest.GetCOTPConnectionRequest(CPU, Rack, Slot), 0, 22);
- var response = COTP.TPDU.Read(stream);
- if (response.PDUType != 0xd0) //Connect Confirm
- {
- throw new InvalidDataException("Error reading Connection Confirm", response.TPkt.Data, 1, 0x0d);
- }
-
- stream.Write(GetS7ConnectionSetup(), 0, 25);
-
- var s7data = COTP.TSDU.Read(stream);
- if (s7data == null)
- throw new WrongNumberOfBytesException("No data received in response to Communication Setup");
- if (s7data.Length < 2)
- throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup");
-
- //Check for S7 Ack Data
- if (s7data[1] != 0x03)
- throw new InvalidDataException("Error reading Communication Setup response", s7data, 1, 0x03);
-
- if (s7data.Length < 20)
- throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup");
-
- MaxPDUSize = (short)(s7data[18] * 256 + s7data[19]);
+ OpenAsync().Wait();
}
catch (Exception exc)
{
@@ -53,28 +28,6 @@ namespace S7.Net
}
}
- private void Connect()
- {
- try
- {
- tcpClient = new TcpClient();
- ConfigureConnection();
- tcpClient.Connect(IP, Port);
- _stream = tcpClient.GetStream();
- }
- catch (SocketException sex)
- {
- // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
- throw new PlcException(
- sex.SocketErrorCode == SocketError.TimedOut
- ? ErrorCode.IPAddressNotAvailable
- : ErrorCode.ConnectionError, sex);
- }
- catch (Exception ex)
- {
- throw new PlcException(ErrorCode.ConnectionError, ex);
- }
- }
///
/// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
diff --git a/S7.Net/Types/Int.cs b/S7.Net/Types/Int.cs
index 5489691..d16d0fd 100644
--- a/S7.Net/Types/Int.cs
+++ b/S7.Net/Types/Int.cs
@@ -10,15 +10,15 @@ namespace S7.Net.Types
///
/// Converts a S7 Int (2 bytes) to short (Int16)
///
- public static short FromByteArray(byte[] bytes)
+ public static short FromByteArray(byte[] bytes, int offset = 0)
{
- if (bytes.Length != 2)
+ if (bytes.Length < offset + 2)
{
- throw new ArgumentException("Wrong number of bytes. Bytes array must contain 2 bytes.");
+ throw new ArgumentException($"Wrong number of bytes. Bytes array must contain at least {offset+2} bytes.");
}
// bytes[0] -> HighByte
// bytes[1] -> LowByte
- return (short)((int)(bytes[1]) | ((int)(bytes[0]) << 8));
+ return (short)(bytes[offset + 1] | (bytes[offset + 0] << 8));
}