Skip to content

Commit

Permalink
Merge branch 'develop' into closeConnectionIOException
Browse files Browse the repository at this point in the history
  • Loading branch information
mycroes authored Jun 6, 2021
2 parents 53045c5 + 77b2ecf commit d65b836
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 86 deletions.
36 changes: 18 additions & 18 deletions S7.Net.UnitTest/ConnectionRequestTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,52 +9,52 @@ public class ConnectionRequestTest
[TestMethod]
public void Test_ConnectionRequest_S7_200()
{
CollectionAssert.AreEqual(MakeConnectionRequest(16, 0, 16, 0),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7200, 0, 0));
CollectionAssert.AreEqual(MakeConnectionRequest(16, 0, 16, 1),
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7200, 0, 0)));
}

[TestMethod]
public void Test_ConnectionRequest_S7_300()
{
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7300, 0, 0));
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7300, 0, 0)));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7300, 0, 1));
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7300, 0, 1)));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7300, 1, 1));
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7300, 1, 1)));
}

[TestMethod]
public void Test_ConnectionRequest_S7_400()
{
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7400, 0, 0));
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7400, 0, 0)));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7400, 0, 1));
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7400, 0, 1)));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7400, 1, 1));
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7400, 1, 1)));
}

[TestMethod]
public void Test_ConnectionRequest_S7_1200()
{
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71200, 0, 0));
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71200, 0, 0)));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71200, 0, 1));
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71200, 0, 1)));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71200, 1, 1));
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71200, 1, 1)));
}

[TestMethod]
public void Test_ConnectionRequest_S7_1500()
{
CollectionAssert.AreEqual(MakeConnectionRequest(0x10, 0x2, 3, 0),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 0, 0));
CollectionAssert.AreEqual(MakeConnectionRequest(0x10, 0x2, 3, 1),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 0, 1));
CollectionAssert.AreEqual(MakeConnectionRequest(0x10, 0x2, 3, 33),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 1, 1));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0),
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71500, 0, 0)));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1),
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71500, 0, 1)));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33),
ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71500, 1, 1)));
}

private static byte[] MakeConnectionRequest(byte sourceTsap1, byte sourceTsap2, byte destTsap1, byte destTsap2)
Expand All @@ -63,7 +63,7 @@ private static byte[] MakeConnectionRequest(byte sourceTsap1, byte sourceTsap2,
{
3, 0, 0, 22, //TPKT
17, //COTP Header Length
224, //Connect Request
224, //Connect Request
0, 0, //Destination Reference
0, 46, //Source Reference
0, //Flags
Expand Down
33 changes: 33 additions & 0 deletions S7.Net.UnitTest/S7NetTestsAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,39 @@ public async Task Test_Async_WriteLargeByteArrayWithCancellation()
// Depending on how tests run, this can also just succeed without getting cancelled at all. Do nothing in this case.
Console.WriteLine("Task was not cancelled as expected.");
}

/// <summary>
/// Write a large amount of data and test cancellation
/// </summary>
[TestMethod]
public async Task Test_Async_ParseDataIntoDataItemsAlignment()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");

var db = 2;
// First write a sensible S7 string capacity
await plc.WriteBytesAsync(DataType.DataBlock, db, 0, new byte[] {5, 0});

// Read two data items, with the first having odd number of bytes (7),
// and the second has to be aligned on a even address
var dataItems = new List<DataItem>
{
new DataItem
{
DataType = DataType.DataBlock,
DB = db,
VarType = VarType.S7String,
Count = 5
},
new DataItem
{
DataType = DataType.DataBlock,
DB = db,
VarType = VarType.Word,
}
};
await plc.ReadMultipleVarsAsync(dataItems, CancellationToken.None);
}
#endregion
}
}
5 changes: 5 additions & 0 deletions S7.Net/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public enum CpuType
/// </summary>
Logo0BA8 = 1,

/// <summary>
/// S7 200 Smart
/// </summary>
S7200Smart = 2,

/// <summary>
/// S7 300 cpu type
/// </summary>
Expand Down
65 changes: 46 additions & 19 deletions S7.Net/PLC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ namespace S7.Net
/// </summary>
public partial class Plc : IDisposable
{
/// <summary>
/// The default port for the S7 protocol.
/// </summary>
public const int DefaultPort = 102;

private readonly TaskQueue queue = new TaskQueue();

private const int CONNECTION_TIMED_OUT_ERROR_CODE = 10060;
Expand All @@ -36,6 +41,11 @@ public partial class Plc : IDisposable
/// </summary>
public int Port { get; }

/// <summary>
/// The TSAP addresses used during the connection request.
/// </summary>
public TsapPair TsapPair { get; set; }

/// <summary>
/// CPU type of the PLC
/// </summary>
Expand Down Expand Up @@ -108,50 +118,67 @@ public int WriteTimeout
/// </summary>
/// <param name="cpu">CpuType of the PLC (select from the enum)</param>
/// <param name="ip">Ip address of the PLC</param>
/// <param name="port">Port address of the PLC, default 102</param>
/// <param name="rack">rack of the PLC, usually it's 0, but check in the hardware configuration of Step7 or TIA portal</param>
/// <param name="slot">slot of the CPU of the PLC, usually it's 2 for S7300-S7400, 0 for S7-1200 and S7-1500.
/// If you use an external ethernet card, this must be set accordingly.</param>
public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot)
: this(cpu, ip, DefaultPort, rack, slot)
{
}

/// <summary>
/// Creates a PLC object with all the parameters needed for connections.
/// For S7-1200 and S7-1500, the default is rack = 0 and slot = 0.
/// You need slot > 0 if you are connecting to external ethernet card (CP).
/// For S7-300 and S7-400 the default is rack = 0 and slot = 2.
/// </summary>
/// <param name="cpu">CpuType of the PLC (select from the enum)</param>
/// <param name="ip">Ip address of the PLC</param>
/// <param name="port">Port number used for the connection, default 102.</param>
/// <param name="rack">rack of the PLC, usually it's 0, but check in the hardware configuration of Step7 or TIA portal</param>
/// <param name="slot">slot of the CPU of the PLC, usually it's 2 for S7300-S7400, 0 for S7-1200 and S7-1500.
/// If you use an external ethernet card, this must be set accordingly.</param>
public Plc(CpuType cpu, string ip, int port, Int16 rack, Int16 slot)
: this(ip, port, TsapPair.GetDefaultTsapPair(cpu, rack, slot))
{
if (!Enum.IsDefined(typeof(CpuType), cpu))
throw new ArgumentException($"The value of argument '{nameof(cpu)}' ({cpu}) is invalid for Enum type '{typeof(CpuType).Name}'.", nameof(cpu));

if (string.IsNullOrEmpty(ip))
throw new ArgumentException("IP address must valid.", nameof(ip));
throw new ArgumentException(
$"The value of argument '{nameof(cpu)}' ({cpu}) is invalid for Enum type '{typeof(CpuType).Name}'.",
nameof(cpu));

CPU = cpu;
IP = ip;
Port = port;
Rack = rack;
Slot = slot;
MaxPDUSize = 240;
}

/// <summary>
/// Creates a PLC object with all the parameters needed for connections.
/// For S7-1200 and S7-1500, the default is rack = 0 and slot = 0.
/// You need slot > 0 if you are connecting to external ethernet card (CP).
/// For S7-300 and S7-400 the default is rack = 0 and slot = 2.
/// </summary>
/// <param name="cpu">CpuType of the PLC (select from the enum)</param>
/// <param name="ip">Ip address of the PLC</param>
/// <param name="rack">rack of the PLC, usually it's 0, but check in the hardware configuration of Step7 or TIA portal</param>
/// <param name="slot">slot of the CPU of the PLC, usually it's 2 for S7300-S7400, 0 for S7-1200 and S7-1500.
/// If you use an external ethernet card, this must be set accordingly.</param>
public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot)
/// <param name="tsapPair">The TSAP addresses used for the connection request.</param>
public Plc(string ip, TsapPair tsapPair) : this(ip, DefaultPort, tsapPair)
{
if (!Enum.IsDefined(typeof(CpuType), cpu))
throw new ArgumentException($"The value of argument '{nameof(cpu)}' ({cpu}) is invalid for Enum type '{typeof(CpuType).Name}'.", nameof(cpu));
}

/// <summary>
/// Creates a PLC object with all the parameters needed for connections. Use this constructor
/// if you want to manually override the TSAP addresses used during the connection request.
/// </summary>
/// <param name="ip">Ip address of the PLC</param>
/// <param name="port">Port number used for the connection, default 102.</param>
/// <param name="tsapPair">The TSAP addresses used for the connection request.</param>
public Plc(string ip, int port, TsapPair tsapPair)
{
if (string.IsNullOrEmpty(ip))
throw new ArgumentException("IP address must valid.", nameof(ip));

CPU = cpu;
IP = ip;
Port = 102;
Rack = rack;
Slot = slot;
Port = port;
MaxPDUSize = 240;
TsapPair = tsapPair;
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions S7.Net/PLCHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ private void ParseDataIntoDataItems(byte[] s7data, List<DataItem> dataItems)
// next Item
offset += byteCnt;

// Fill byte in response when bytecount is odd
if (dataItem.Count % 2 != 0 && (dataItem.VarType == VarType.Byte || dataItem.VarType == VarType.Bit))
// Always align to even offset
if (offset % 2 != 0)
offset++;
}
}
Expand Down
2 changes: 1 addition & 1 deletion S7.Net/PlcAsynchronous.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private async Task EstablishConnection(Stream stream, CancellationToken cancella

private async Task RequestConnection(Stream stream, CancellationToken cancellationToken)
{
var requestData = ConnectionRequest.GetCOTPConnectionRequest(CPU, Rack, Slot);
var requestData = ConnectionRequest.GetCOTPConnectionRequest(TsapPair);
var response = await NoLockRequestTpduAsync(stream, requestData, cancellationToken).ConfigureAwait(false);

if (response.PDUType != COTP.PduType.ConnectionConfirmed)
Expand Down
51 changes: 5 additions & 46 deletions S7.Net/Protocol/ConnectionRequest.cs
Original file line number Diff line number Diff line change
@@ -1,68 +1,27 @@
using System;

namespace S7.Net.Protocol
namespace S7.Net.Protocol
{
internal static class ConnectionRequest
{
public static byte[] GetCOTPConnectionRequest(CpuType cpu, Int16 rack, Int16 slot)
public static byte[] GetCOTPConnectionRequest(TsapPair tsapPair)
{
byte[] bSend1 = {
3, 0, 0, 22, //TPKT
17, //COTP Header Length
224, //Connect Request
224, //Connect Request
0, 0, //Destination Reference
0, 46, //Source Reference
0, //Flags
193, //Parameter Code (src-tasp)
2, //Parameter Length
1, 0, //Source TASP
tsapPair.Local.FirstByte, tsapPair.Local.SecondByte, //Source TASP
194, //Parameter Code (dst-tasp)
2, //Parameter Length
3, 0, //Destination TASP
tsapPair.Remote.FirstByte, tsapPair.Remote.SecondByte, //Destination TASP
192, //Parameter Code (tpdu-size)
1, //Parameter Length
10 //TPDU Size (2^10 = 1024)
};

switch (cpu)
{
case CpuType.S7200:
//S7200: Chr(193) & Chr(2) & Chr(16) & Chr(0) 'Eigener Tsap
bSend1[13] = 0x10;
bSend1[14] = 0x00;
//S7200: Chr(194) & Chr(2) & Chr(16) & Chr(0) 'Fremder Tsap
bSend1[17] = 0x10;
bSend1[18] = 0x00;
break;
case CpuType.Logo0BA8:
// These values are taken from NodeS7, it's not verified if these are
// exact requirements to connect to the Logo0BA8.
bSend1[13] = 0x01;
bSend1[14] = 0x00;
bSend1[17] = 0x01;
bSend1[18] = 0x02;
break;
case CpuType.S71200:
case CpuType.S7300:
case CpuType.S7400:
//S7300: Chr(193) & Chr(2) & Chr(1) & Chr(0) 'Eigener Tsap
bSend1[13] = 0x01;
bSend1[14] = 0x00;
//S7300: Chr(194) & Chr(2) & Chr(3) & Chr(2) 'Fremder Tsap
bSend1[17] = 0x03;
bSend1[18] = (byte) ((rack << 5) | (int) slot);
break;
case CpuType.S71500:
// Eigener Tsap
bSend1[13] = 0x10;
bSend1[14] = 0x02;
// Fredmer Tsap
bSend1[17] = 0x03;
bSend1[18] = (byte) ((rack << 5) | (int) slot);
break;
default:
throw new Exception("Wrong CPU Type Secified");
}
return bSend1;
}
}
Expand Down
31 changes: 31 additions & 0 deletions S7.Net/Protocol/Tsap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace S7.Net.Protocol
{
/// <summary>
/// Provides a representation of the Transport Service Access Point, or TSAP in short. TSAP's are used
/// to specify a client and server address. For most PLC types a default TSAP is available that allows
/// connection from any IP and can be calculated using the rack and slot numbers.
/// </summary>
public struct Tsap
{
/// <summary>
/// First byte of the TSAP.
/// </summary>
public byte FirstByte { get; set; }

/// <summary>
/// Second byte of the TSAP.
/// </summary>
public byte SecondByte { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="Tsap" /> class using the specified values.
/// </summary>
/// <param name="firstByte">The first byte of the TSAP.</param>
/// <param name="secondByte">The second byte of the TSAP.</param>
public Tsap(byte firstByte, byte secondByte)
{
FirstByte = firstByte;
SecondByte = secondByte;
}
}
}
Loading

0 comments on commit d65b836

Please sign in to comment.