mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-17 14:28:25 +08:00
Cleanup OpenAsync function.
- Separate into Connect and EstablishConnection step. - Remove redundant null checks for returned data. - Only assing PLC stream object once we fully established a connection, and Close otherwise. - Replace sync implementation with Async call.
This commit is contained in:
@@ -11,6 +11,11 @@ namespace S7.Net
|
||||
/// </summary>
|
||||
internal class COTP
|
||||
{
|
||||
public enum PduType : byte
|
||||
{
|
||||
Data = 0xf0,
|
||||
ConnectionConfirmed = 0xd0
|
||||
}
|
||||
/// <summary>
|
||||
/// Describes a COTP TPDU (Transport protocol data unit)
|
||||
/// </summary>
|
||||
@@ -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;
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace S7.Net
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect();
|
||||
ConnectAsync().Wait();
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -24,26 +24,50 @@ namespace S7.Net
|
||||
/// <returns>A task that represents the asynchronous open operation.</returns>
|
||||
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<NetworkStream> 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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,34 +17,9 @@ namespace S7.Net
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
|
||||
|
||||
@@ -10,15 +10,15 @@ namespace S7.Net.Types
|
||||
/// <summary>
|
||||
/// Converts a S7 Int (2 bytes) to short (Int16)
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user