diff --git a/S7.Net.UnitTest/ConnectionRequestTest.cs b/S7.Net.UnitTest/ConnectionRequestTest.cs
index 440a962..ec17125 100644
--- a/S7.Net.UnitTest/ConnectionRequestTest.cs
+++ b/S7.Net.UnitTest/ConnectionRequestTest.cs
@@ -9,52 +9,52 @@ namespace S7.Net.UnitTest
[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)
@@ -63,7 +63,7 @@ namespace S7.Net.UnitTest
{
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
diff --git a/S7.Net/Enums.cs b/S7.Net/Enums.cs
index 436d94b..fc412d3 100644
--- a/S7.Net/Enums.cs
+++ b/S7.Net/Enums.cs
@@ -15,6 +15,11 @@
///
Logo0BA8 = 1,
+ ///
+ /// S7 200 Smart
+ ///
+ S7200Smart = 2,
+
///
/// S7 300 cpu type
///
diff --git a/S7.Net/PLC.cs b/S7.Net/PLC.cs
index 6180ad5..5ec5618 100644
--- a/S7.Net/PLC.cs
+++ b/S7.Net/PLC.cs
@@ -15,6 +15,11 @@ namespace S7.Net
///
public partial class Plc : IDisposable
{
+ ///
+ /// The default port for the S7 protocol.
+ ///
+ public const int DefaultPort = 102;
+
private readonly TaskQueue queue = new TaskQueue();
private const int CONNECTION_TIMED_OUT_ERROR_CODE = 10060;
@@ -36,6 +41,11 @@ namespace S7.Net
///
public int Port { get; }
+ ///
+ /// The TSAP addresses used during the connection request.
+ ///
+ public TsapPair TsapPair { get; set; }
+
///
/// CPU type of the PLC
///
@@ -108,25 +118,14 @@ namespace S7.Net
///
/// CpuType of the PLC (select from the enum)
/// Ip address of the PLC
- /// Port address of the PLC, default 102
/// rack of the PLC, usually it's 0, but check in the hardware configuration of Step7 or TIA portal
/// 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.
- public Plc(CpuType cpu, string ip, int port, Int16 rack, Int16 slot)
+ public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot)
+ : this(cpu, ip, DefaultPort, 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));
-
- CPU = cpu;
- IP = ip;
- Port = port;
- Rack = rack;
- Slot = slot;
- MaxPDUSize = 240;
}
+
///
/// 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.
@@ -135,23 +134,51 @@ namespace S7.Net
///
/// CpuType of the PLC (select from the enum)
/// Ip address of the PLC
+ /// Port number used for the connection, default 102.
/// rack of the PLC, usually it's 0, but check in the hardware configuration of Step7 or TIA portal
/// 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.
- public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot)
+ 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));
+ throw new ArgumentException(
+ $"The value of argument '{nameof(cpu)}' ({cpu}) is invalid for Enum type '{typeof(CpuType).Name}'.",
+ nameof(cpu));
+ CPU = cpu;
+ Rack = rack;
+ Slot = slot;
+ }
+
+ ///
+ /// 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.
+ ///
+ /// Ip address of the PLC
+ /// The TSAP addresses used for the connection request.
+ public Plc(string ip, TsapPair tsapPair) : this(ip, DefaultPort, tsapPair)
+ {
+ }
+
+ ///
+ /// 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.
+ ///
+ /// Ip address of the PLC
+ /// Port number used for the connection, default 102.
+ /// The TSAP addresses used for the connection request.
+ 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;
}
///
diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs
index c7a5868..030cc3d 100644
--- a/S7.Net/PlcAsynchronous.cs
+++ b/S7.Net/PlcAsynchronous.cs
@@ -60,7 +60,7 @@ namespace S7.Net
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)
diff --git a/S7.Net/Protocol/ConnectionRequest.cs b/S7.Net/Protocol/ConnectionRequest.cs
index 64e2b5d..9dbd396 100644
--- a/S7.Net/Protocol/ConnectionRequest.cs
+++ b/S7.Net/Protocol/ConnectionRequest.cs
@@ -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;
}
}
diff --git a/S7.Net/Protocol/Tsap.cs b/S7.Net/Protocol/Tsap.cs
new file mode 100644
index 0000000..dc9d46c
--- /dev/null
+++ b/S7.Net/Protocol/Tsap.cs
@@ -0,0 +1,31 @@
+namespace S7.Net.Protocol
+{
+ ///
+ /// 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.
+ ///
+ public struct Tsap
+ {
+ ///
+ /// First byte of the TSAP.
+ ///
+ public byte FirstByte { get; set; }
+
+ ///
+ /// Second byte of the TSAP.
+ ///
+ public byte SecondByte { get; set; }
+
+ ///
+ /// Initializes a new instance of the class using the specified values.
+ ///
+ /// The first byte of the TSAP.
+ /// The second byte of the TSAP.
+ public Tsap(byte firstByte, byte secondByte)
+ {
+ FirstByte = firstByte;
+ SecondByte = secondByte;
+ }
+ }
+}
\ No newline at end of file
diff --git a/S7.Net/Protocol/TsapPair.cs b/S7.Net/Protocol/TsapPair.cs
new file mode 100644
index 0000000..e54fc32
--- /dev/null
+++ b/S7.Net/Protocol/TsapPair.cs
@@ -0,0 +1,96 @@
+using System;
+
+namespace S7.Net.Protocol
+{
+ ///
+ /// Implements a pair of TSAP addresses used to connect to a PLC.
+ ///
+ public class TsapPair
+ {
+ ///
+ /// The local .
+ ///
+ public Tsap Local { get; set; }
+
+ ///
+ /// The remote
+ ///
+ public Tsap Remote { get; set; }
+
+ ///
+ /// Initializes a new instance of the class using the specified local and
+ /// remote TSAP.
+ ///
+ /// The local TSAP.
+ /// The remote TSAP.
+ public TsapPair(Tsap local, Tsap remote)
+ {
+ Local = local;
+ Remote = remote;
+ }
+
+ ///
+ /// Builds a that can be used to connect to a PLC using the default connection
+ /// addresses.
+ ///
+ ///
+ /// The remote TSAP is constructed using new Tsap(0x03, (byte) ((rack << 5) | slot)).
+ ///
+ /// The CPU type of the PLC.
+ /// The rack of the PLC's network card.
+ /// The slot of the PLC's network card.
+ /// A TSAP pair that matches the given parameters.
+ /// The is invalid.
+ ///
+ /// -or-
+ ///
+ /// The parameter is less than 0.
+ ///
+ /// -or-
+ ///
+ /// The parameter is greater than 15.
+ ///
+ /// -or-
+ ///
+ /// The parameter is less than 0.
+ ///
+ /// -or-
+ ///
+ /// The parameter is greater than 15.
+ public static TsapPair GetDefaultTsapPair(CpuType cpuType, int rack, int slot)
+ {
+ if (rack < 0) throw InvalidRackOrSlot(rack, nameof(rack), "minimum", 0);
+ if (rack > 0x0F) throw InvalidRackOrSlot(rack, nameof(rack), "maximum", 0x0F);
+
+ if (slot < 0) throw InvalidRackOrSlot(slot, nameof(slot), "minimum", 0);
+ if (slot > 0x0F) throw InvalidRackOrSlot(slot, nameof(slot), "maximum", 0x0F);
+
+ switch (cpuType)
+ {
+ case CpuType.S7200:
+ return new TsapPair(new Tsap(0x10, 0x00), new Tsap(0x10, 0x01));
+ case CpuType.Logo0BA8:
+ // The actual values are probably on a per-project basis
+ return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x01, 0x02));
+ case CpuType.S7200Smart:
+ case CpuType.S71200:
+ case CpuType.S71500:
+ case CpuType.S7300:
+ case CpuType.S7400:
+ // Testing with S7 1500 shows only the remote TSAP needs to match. This might differ for other
+ // PLC types.
+ return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x03, (byte) ((rack << 5) | slot)));
+ default:
+ throw new ArgumentOutOfRangeException(nameof(cpuType), "Invalid CPU Type specified");
+ }
+ }
+
+ private static ArgumentOutOfRangeException InvalidRackOrSlot(int value, string name, string extrema,
+ int extremaValue)
+ {
+ return new ArgumentOutOfRangeException(name,
+ $"Invalid {name} value specified (decimal: {value}, hexadecimal: {value:X}), {extrema} value " +
+ $"is {extremaValue} (decimal) or {extremaValue:X} (hexadecimal).");
+ }
+ }
+}
\ No newline at end of file