mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-17 14:28:25 +08:00
@@ -10,51 +10,51 @@ namespace S7.Net.UnitTest
|
||||
public void Test_ConnectionRequest_S7_200()
|
||||
{
|
||||
CollectionAssert.AreEqual(MakeConnectionRequest(16, 0, 16, 0),
|
||||
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7200, 0, 0));
|
||||
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
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
/// </summary>
|
||||
Logo0BA8 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// S7 200 Smart
|
||||
/// </summary>
|
||||
S7200Smart = 2,
|
||||
|
||||
/// <summary>
|
||||
/// S7 300 cpu type
|
||||
/// </summary>
|
||||
|
||||
@@ -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;
|
||||
@@ -36,6 +41,11 @@ namespace S7.Net
|
||||
/// </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>
|
||||
@@ -108,25 +118,14 @@ namespace S7.Net
|
||||
/// </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, 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;
|
||||
}
|
||||
|
||||
/// <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.
|
||||
@@ -135,23 +134,51 @@ namespace S7.Net
|
||||
/// </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, 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;
|
||||
}
|
||||
|
||||
/// <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="ip">Ip address of the PLC</param>
|
||||
/// <param name="tsapPair">The TSAP addresses used for the connection request.</param>
|
||||
public Plc(string ip, TsapPair tsapPair) : this(ip, DefaultPort, tsapPair)
|
||||
{
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
31
S7.Net/Protocol/Tsap.cs
Normal file
31
S7.Net/Protocol/Tsap.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
96
S7.Net/Protocol/TsapPair.cs
Normal file
96
S7.Net/Protocol/TsapPair.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
|
||||
namespace S7.Net.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a pair of TSAP addresses used to connect to a PLC.
|
||||
/// </summary>
|
||||
public class TsapPair
|
||||
{
|
||||
/// <summary>
|
||||
/// The local <see cref="Tsap" />.
|
||||
/// </summary>
|
||||
public Tsap Local { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The remote <see cref="Tsap" />
|
||||
/// </summary>
|
||||
public Tsap Remote { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TsapPair" /> class using the specified local and
|
||||
/// remote TSAP.
|
||||
/// </summary>
|
||||
/// <param name="local">The local TSAP.</param>
|
||||
/// <param name="remote">The remote TSAP.</param>
|
||||
public TsapPair(Tsap local, Tsap remote)
|
||||
{
|
||||
Local = local;
|
||||
Remote = remote;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a <see cref="TsapPair" /> that can be used to connect to a PLC using the default connection
|
||||
/// addresses.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The remote TSAP is constructed using <code>new Tsap(0x03, (byte) ((rack << 5) | slot))</code>.
|
||||
/// </remarks>
|
||||
/// <param name="cpuType">The CPU type of the PLC.</param>
|
||||
/// <param name="rack">The rack of the PLC's network card.</param>
|
||||
/// <param name="slot">The slot of the PLC's network card.</param>
|
||||
/// <returns>A TSAP pair that matches the given parameters.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">The <paramref name="cpuType"/> is invalid.
|
||||
///
|
||||
/// -or-
|
||||
///
|
||||
/// The <paramref name="rack"/> parameter is less than 0.
|
||||
///
|
||||
/// -or-
|
||||
///
|
||||
/// The <paramref name="rack"/> parameter is greater than 15.
|
||||
///
|
||||
/// -or-
|
||||
///
|
||||
/// The <paramref name="slot"/> parameter is less than 0.
|
||||
///
|
||||
/// -or-
|
||||
///
|
||||
/// The <paramref name="slot"/> parameter is greater than 15.</exception>
|
||||
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, 0x00));
|
||||
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).");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user