mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-17 22:38:27 +08:00
Use socket instead of TcpClient and stream.
Async implemented with wrapper.
This commit is contained in:
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using S7.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
@@ -13,18 +14,22 @@ namespace S7.Net.UnitTest
|
||||
[TestClass]
|
||||
public class ProtocolUnitTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void TPKT_Read()
|
||||
{
|
||||
var m = new MemoryStream(StringToByteArray("0300002902f0803203000000010002001400000401ff0400807710000100000103000000033f8ccccd"));
|
||||
var t = TPKT.Read(m);
|
||||
Assert.AreEqual(0x03, t.Version);
|
||||
Assert.AreEqual(0x29, t.Length);
|
||||
m.Position = 0;
|
||||
t = TPKT.ReadAsync(m).Result;
|
||||
Assert.AreEqual(0x03, t.Version);
|
||||
Assert.AreEqual(0x29, t.Length);
|
||||
}
|
||||
/*
|
||||
* Not sure how to implment these tests cleanly now. Probably need to spin up a TcpServer.
|
||||
[TestMethod]
|
||||
public void TPKT_Read()
|
||||
{
|
||||
Socket s = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
s.Send(StringToByteArray("0300002902f0803203000000010002001400000401ff0400807710000100000103000000033f8ccccd"));
|
||||
//Socket m = new MemoryStream();
|
||||
var t = TPKT.Read(s);
|
||||
Assert.AreEqual(0x03, t.Version);
|
||||
Assert.AreEqual(0x29, t.Length);
|
||||
//m.Position = 0;
|
||||
//t = TPKT.ReadAsync(m).Result;
|
||||
//Assert.AreEqual(0x03, t.Version);
|
||||
//Assert.AreEqual(0x29, t.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TPKTInvalidException))]
|
||||
@@ -53,7 +58,7 @@ namespace S7.Net.UnitTest
|
||||
t = COTP.TSDU.ReadAsync(m).Result;
|
||||
Assert.IsTrue(expected.SequenceEqual(t));
|
||||
}
|
||||
|
||||
*/
|
||||
private static byte[] StringToByteArray(string hex)
|
||||
{
|
||||
return Enumerable.Range(0, hex.Length)
|
||||
@@ -61,5 +66,7 @@ namespace S7.Net.UnitTest
|
||||
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -162,13 +162,15 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestClass tc = new TestClass();
|
||||
tc.BitVariable00 = true;
|
||||
tc.BitVariable10 = true;
|
||||
tc.DIntVariable = -100000;
|
||||
tc.IntVariable = -15000;
|
||||
tc.RealVariable = -154.789;
|
||||
tc.DWordVariable = 850;
|
||||
TestClass tc = new TestClass
|
||||
{
|
||||
BitVariable00 = true,
|
||||
BitVariable10 = true,
|
||||
DIntVariable = -100000,
|
||||
IntVariable = -15000,
|
||||
RealVariable = -154.789,
|
||||
DWordVariable = 850
|
||||
};
|
||||
plc.WriteClass(tc, DB2);
|
||||
TestClass tc2 = new TestClass();
|
||||
// Values that are read from a class are stored inside the class itself, that is passed by reference
|
||||
@@ -189,13 +191,15 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestStruct tc = new TestStruct();
|
||||
tc.BitVariable00 = true;
|
||||
tc.BitVariable10 = true;
|
||||
tc.DIntVariable = -100000;
|
||||
tc.IntVariable = -15000;
|
||||
tc.RealVariable = -154.789;
|
||||
tc.DWordVariable = 850;
|
||||
TestStruct tc = new TestStruct
|
||||
{
|
||||
BitVariable00 = true,
|
||||
BitVariable10 = true,
|
||||
DIntVariable = -100000,
|
||||
IntVariable = -15000,
|
||||
RealVariable = -154.789,
|
||||
DWordVariable = 850
|
||||
};
|
||||
plc.WriteStruct(tc, DB2);
|
||||
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
|
||||
TestStruct tc2 = (TestStruct)plc.ReadStruct(typeof(TestStruct), DB2);
|
||||
@@ -215,31 +219,33 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestLongStruct tc = new TestLongStruct();
|
||||
tc.IntVariable0 = 0;
|
||||
tc.IntVariable1 = 1;
|
||||
tc.IntVariable10 = 10;
|
||||
tc.IntVariable11 = 11;
|
||||
tc.IntVariable20 = 20;
|
||||
tc.IntVariable21 = 21;
|
||||
tc.IntVariable30 = 30;
|
||||
tc.IntVariable31 = 31;
|
||||
tc.IntVariable40 = 40;
|
||||
tc.IntVariable41 = 41;
|
||||
tc.IntVariable50 = 50;
|
||||
tc.IntVariable51 = 51;
|
||||
tc.IntVariable60 = 60;
|
||||
tc.IntVariable61 = 61;
|
||||
tc.IntVariable70 = 70;
|
||||
tc.IntVariable71 = 71;
|
||||
tc.IntVariable80 = 80;
|
||||
tc.IntVariable81 = 81;
|
||||
tc.IntVariable90 = 90;
|
||||
tc.IntVariable91 = 91;
|
||||
tc.IntVariable100 = 100;
|
||||
tc.IntVariable101 = 101;
|
||||
tc.IntVariable110 = 200;
|
||||
tc.IntVariable111 = 201;
|
||||
TestLongStruct tc = new TestLongStruct
|
||||
{
|
||||
IntVariable0 = 0,
|
||||
IntVariable1 = 1,
|
||||
IntVariable10 = 10,
|
||||
IntVariable11 = 11,
|
||||
IntVariable20 = 20,
|
||||
IntVariable21 = 21,
|
||||
IntVariable30 = 30,
|
||||
IntVariable31 = 31,
|
||||
IntVariable40 = 40,
|
||||
IntVariable41 = 41,
|
||||
IntVariable50 = 50,
|
||||
IntVariable51 = 51,
|
||||
IntVariable60 = 60,
|
||||
IntVariable61 = 61,
|
||||
IntVariable70 = 70,
|
||||
IntVariable71 = 71,
|
||||
IntVariable80 = 80,
|
||||
IntVariable81 = 81,
|
||||
IntVariable90 = 90,
|
||||
IntVariable91 = 91,
|
||||
IntVariable100 = 100,
|
||||
IntVariable101 = 101,
|
||||
IntVariable110 = 200,
|
||||
IntVariable111 = 201
|
||||
};
|
||||
plc.WriteStruct(tc, DB2);
|
||||
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
|
||||
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
|
||||
@@ -279,31 +285,33 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestLongClass tc = new TestLongClass();
|
||||
tc.IntVariable0 = 0;
|
||||
tc.IntVariable1 = 1;
|
||||
tc.IntVariable10 = 10;
|
||||
tc.IntVariable11 = 11;
|
||||
tc.IntVariable20 = 20;
|
||||
tc.IntVariable21 = 21;
|
||||
tc.IntVariable30 = 30;
|
||||
tc.IntVariable31 = 31;
|
||||
tc.IntVariable40 = 40;
|
||||
tc.IntVariable41 = 41;
|
||||
tc.IntVariable50 = 50;
|
||||
tc.IntVariable51 = 51;
|
||||
tc.IntVariable60 = 60;
|
||||
tc.IntVariable61 = 61;
|
||||
tc.IntVariable70 = 70;
|
||||
tc.IntVariable71 = 71;
|
||||
tc.IntVariable80 = 80;
|
||||
tc.IntVariable81 = 81;
|
||||
tc.IntVariable90 = 90;
|
||||
tc.IntVariable91 = 91;
|
||||
tc.IntVariable100 = 100;
|
||||
tc.IntVariable101 = 101;
|
||||
tc.IntVariable110 = 200;
|
||||
tc.IntVariable111 = 201;
|
||||
TestLongClass tc = new TestLongClass
|
||||
{
|
||||
IntVariable0 = 0,
|
||||
IntVariable1 = 1,
|
||||
IntVariable10 = 10,
|
||||
IntVariable11 = 11,
|
||||
IntVariable20 = 20,
|
||||
IntVariable21 = 21,
|
||||
IntVariable30 = 30,
|
||||
IntVariable31 = 31,
|
||||
IntVariable40 = 40,
|
||||
IntVariable41 = 41,
|
||||
IntVariable50 = 50,
|
||||
IntVariable51 = 51,
|
||||
IntVariable60 = 60,
|
||||
IntVariable61 = 61,
|
||||
IntVariable70 = 70,
|
||||
IntVariable71 = 71,
|
||||
IntVariable80 = 80,
|
||||
IntVariable81 = 81,
|
||||
IntVariable90 = 90,
|
||||
IntVariable91 = 91,
|
||||
IntVariable100 = 100,
|
||||
IntVariable101 = 101,
|
||||
IntVariable110 = 200,
|
||||
IntVariable111 = 201
|
||||
};
|
||||
plc.WriteClass(tc, DB2);
|
||||
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
|
||||
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
|
||||
@@ -477,13 +485,15 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestClassWithPrivateSetters tc = new TestClassWithPrivateSetters();
|
||||
tc.BitVariable00 = true;
|
||||
tc.BitVariable10 = true;
|
||||
tc.DIntVariable = -100000;
|
||||
tc.IntVariable = -15000;
|
||||
tc.RealVariable = -154.789;
|
||||
tc.DWordVariable = 850;
|
||||
TestClassWithPrivateSetters tc = new TestClassWithPrivateSetters
|
||||
{
|
||||
BitVariable00 = true,
|
||||
BitVariable10 = true,
|
||||
DIntVariable = -100000,
|
||||
IntVariable = -15000,
|
||||
RealVariable = -154.789,
|
||||
DWordVariable = 850
|
||||
};
|
||||
|
||||
plc.WriteClass(tc, DB2);
|
||||
|
||||
@@ -525,13 +535,15 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestClass tc = new TestClass();
|
||||
tc.BitVariable00 = true;
|
||||
tc.BitVariable10 = true;
|
||||
tc.DIntVariable = -100000;
|
||||
tc.IntVariable = -15000;
|
||||
tc.RealVariable = -154.789;
|
||||
tc.DWordVariable = 850;
|
||||
TestClass tc = new TestClass
|
||||
{
|
||||
BitVariable00 = true,
|
||||
BitVariable10 = true,
|
||||
DIntVariable = -100000,
|
||||
IntVariable = -15000,
|
||||
RealVariable = -154.789,
|
||||
DWordVariable = 850
|
||||
};
|
||||
|
||||
plc.WriteClass(tc, DB2);
|
||||
|
||||
@@ -566,13 +578,15 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestClass tc = new TestClass();
|
||||
tc.BitVariable00 = true;
|
||||
tc.BitVariable10 = true;
|
||||
tc.DIntVariable = -100000;
|
||||
tc.IntVariable = -15000;
|
||||
tc.RealVariable = -154.789;
|
||||
tc.DWordVariable = 850;
|
||||
TestClass tc = new TestClass
|
||||
{
|
||||
BitVariable00 = true,
|
||||
BitVariable10 = true,
|
||||
DIntVariable = -100000,
|
||||
IntVariable = -15000,
|
||||
RealVariable = -154.789,
|
||||
DWordVariable = 850
|
||||
};
|
||||
|
||||
plc.WriteClass(tc, DB2);
|
||||
|
||||
@@ -619,13 +633,15 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestStruct ts = new TestStruct();
|
||||
ts.BitVariable00 = true;
|
||||
ts.BitVariable10 = true;
|
||||
ts.DIntVariable = -100000;
|
||||
ts.IntVariable = -15000;
|
||||
ts.RealVariable = -154.789;
|
||||
ts.DWordVariable = 850;
|
||||
TestStruct ts = new TestStruct
|
||||
{
|
||||
BitVariable00 = true,
|
||||
BitVariable10 = true,
|
||||
DIntVariable = -100000,
|
||||
IntVariable = -15000,
|
||||
RealVariable = -154.789,
|
||||
DWordVariable = 850
|
||||
};
|
||||
|
||||
plc.WriteStruct(ts, DB2);
|
||||
|
||||
@@ -662,13 +678,15 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestClass tc = new TestClass();
|
||||
tc.BitVariable00 = true;
|
||||
tc.BitVariable10 = true;
|
||||
tc.DIntVariable = -100000;
|
||||
tc.IntVariable = -15000;
|
||||
tc.RealVariable = -154.789;
|
||||
tc.DWordVariable = 850;
|
||||
TestClass tc = new TestClass
|
||||
{
|
||||
BitVariable00 = true,
|
||||
BitVariable10 = true,
|
||||
DIntVariable = -100000,
|
||||
IntVariable = -15000,
|
||||
RealVariable = -154.789,
|
||||
DWordVariable = 850
|
||||
};
|
||||
plc.WriteClass(tc, DB2);
|
||||
|
||||
int expectedReadBytes = Types.Class.GetClassSize(tc);
|
||||
@@ -685,8 +703,10 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestClassWithArrays tc = new TestClassWithArrays();
|
||||
tc.Bool = true;
|
||||
TestClassWithArrays tc = new TestClassWithArrays
|
||||
{
|
||||
Bool = true
|
||||
};
|
||||
tc.BoolValues[1] = true;
|
||||
tc.Int = int.MinValue;
|
||||
tc.Ints[0] = int.MinValue;
|
||||
@@ -730,9 +750,11 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
|
||||
|
||||
TestClassWithCustomType tc = new TestClassWithCustomType();
|
||||
tc.Int = int.MinValue;
|
||||
tc.CustomType = new CustomType();
|
||||
TestClassWithCustomType tc = new TestClassWithCustomType
|
||||
{
|
||||
Int = int.MinValue,
|
||||
CustomType = new CustomType()
|
||||
};
|
||||
tc.CustomType.Bools[1] = true;
|
||||
tc.CustomTypes[0] = new CustomType();
|
||||
tc.CustomTypes[1] = new CustomType();
|
||||
|
||||
@@ -47,11 +47,11 @@ namespace S7.Net
|
||||
/// Reads COTP TPDU (Transport protocol data unit) from the network stream
|
||||
/// See: https://tools.ietf.org/html/rfc905
|
||||
/// </summary>
|
||||
/// <param name="stream">The socket to read from</param>
|
||||
/// <param name="socket">The socket to read from</param>
|
||||
/// <returns>COTP DPDU instance</returns>
|
||||
public static TPDU Read(Stream stream)
|
||||
public static TPDU Read(Socket socket)
|
||||
{
|
||||
var tpkt = TPKT.Read(stream);
|
||||
var tpkt = TPKT.Read(socket);
|
||||
if (tpkt.Length > 0) return new TPDU(tpkt);
|
||||
return null;
|
||||
}
|
||||
@@ -60,11 +60,11 @@ namespace S7.Net
|
||||
/// Reads COTP TPDU (Transport protocol data unit) from the network stream
|
||||
/// See: https://tools.ietf.org/html/rfc905
|
||||
/// </summary>
|
||||
/// <param name="stream">The socket to read from</param>
|
||||
/// <param name="socket">The socket to read from</param>
|
||||
/// <returns>COTP DPDU instance</returns>
|
||||
public static async Task<TPDU> ReadAsync(Stream stream)
|
||||
public static async Task<TPDU> ReadAsync(Socket socket)
|
||||
{
|
||||
var tpkt = await TPKT.ReadAsync(stream);
|
||||
var tpkt = await TPKT.ReadAsync(socket);
|
||||
if (tpkt.Length > 0) return new TPDU(tpkt);
|
||||
return null;
|
||||
}
|
||||
@@ -91,11 +91,11 @@ namespace S7.Net
|
||||
/// Reads the full COTP TSDU (Transport service data unit)
|
||||
/// See: https://tools.ietf.org/html/rfc905
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="Socket">The stream to read from</param>
|
||||
/// <returns>Data in TSDU</returns>
|
||||
public static byte[] Read(Stream stream)
|
||||
public static byte[] Read(Socket Socket)
|
||||
{
|
||||
var segment = TPDU.Read(stream);
|
||||
var segment = TPDU.Read(Socket);
|
||||
if (segment == null) return null;
|
||||
|
||||
var output = new MemoryStream(segment.Data.Length);
|
||||
@@ -103,7 +103,7 @@ namespace S7.Net
|
||||
|
||||
while (!segment.LastDataUnit)
|
||||
{
|
||||
segment = TPDU.Read(stream);
|
||||
segment = TPDU.Read(Socket);
|
||||
output.Write(segment.Data, (int)output.Position, segment.Data.Length);
|
||||
}
|
||||
return output.GetBuffer().Take((int)output.Position).ToArray();
|
||||
@@ -113,11 +113,11 @@ namespace S7.Net
|
||||
/// Reads the full COTP TSDU (Transport service data unit)
|
||||
/// See: https://tools.ietf.org/html/rfc905
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="socket">The stream to read from</param>
|
||||
/// <returns>Data in TSDU</returns>
|
||||
public static async Task<byte[]> ReadAsync(Stream stream)
|
||||
public static async Task<byte[]> ReadAsync(Socket socket)
|
||||
{
|
||||
var segment = await TPDU.ReadAsync(stream);
|
||||
var segment = await TPDU.ReadAsync(socket);
|
||||
if (segment == null) return null;
|
||||
|
||||
var output = new MemoryStream(segment.Data.Length);
|
||||
@@ -125,7 +125,7 @@ namespace S7.Net
|
||||
|
||||
while (!segment.LastDataUnit)
|
||||
{
|
||||
segment = await TPDU.ReadAsync(stream);
|
||||
segment = await TPDU.ReadAsync(socket);
|
||||
output.Write(segment.Data, (int)output.Position, segment.Data.Length);
|
||||
}
|
||||
return output.GetBuffer().Take((int)output.Position).ToArray();
|
||||
|
||||
@@ -17,9 +17,10 @@ namespace S7.Net
|
||||
{
|
||||
private const int CONNECTION_TIMED_OUT_ERROR_CODE = 10060;
|
||||
|
||||
//TCP connection to device
|
||||
private TcpClient tcpClient;
|
||||
private NetworkStream stream;
|
||||
/// <summary>
|
||||
/// TCP Connection to device
|
||||
/// </summary>
|
||||
private Socket socket;
|
||||
|
||||
/// <summary>
|
||||
/// IP address of the PLC
|
||||
@@ -69,11 +70,11 @@ namespace S7.Net
|
||||
{
|
||||
try
|
||||
{
|
||||
if (tcpClient == null)
|
||||
if (socket == null)
|
||||
return false;
|
||||
|
||||
//TODO: Actually check communication by sending an empty TPDU
|
||||
return tcpClient.Connected;
|
||||
return socket.Connected;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
@@ -119,9 +120,9 @@ namespace S7.Net
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
if (tcpClient != null)
|
||||
if (socket != null)
|
||||
{
|
||||
if (tcpClient.Connected) tcpClient.Close();
|
||||
if (socket.Connected) socket.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using S7.Net.Types;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
@@ -23,16 +24,16 @@ namespace S7.Net
|
||||
{
|
||||
await ConnectAsync();
|
||||
|
||||
await stream.WriteAsync(GetCOPTConnectionRequest(CPU), 0, 22);
|
||||
var response = await COTP.TPDU.ReadAsync(stream);
|
||||
await socket.SendAsync(GetCOPTConnectionRequest(CPU), 0, 22, SocketFlags.None);
|
||||
var response = await COTP.TPDU.ReadAsync(socket);
|
||||
if (response.PDUType != 0xd0) //Connect Confirm
|
||||
{
|
||||
throw new WrongNumberOfBytesException("Waiting for COTP connect confirm");
|
||||
}
|
||||
|
||||
await stream.WriteAsync(GetS7ConnectionSetup(), 0, 25);
|
||||
await socket.SendAsync(GetS7ConnectionSetup(), 0, 25, SocketFlags.None);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream);
|
||||
var s7data = await COTP.TSDU.ReadAsync(socket);
|
||||
if (s7data == null || s7data[1] != 0x03) //Check for S7 Ack Data
|
||||
{
|
||||
throw new WrongNumberOfBytesException("Waiting for S7 connection setup");
|
||||
@@ -42,9 +43,9 @@ namespace S7.Net
|
||||
|
||||
private async Task ConnectAsync()
|
||||
{
|
||||
tcpClient = new TcpClient();
|
||||
await tcpClient.ConnectAsync(IP, 102);
|
||||
stream = tcpClient.GetStream();
|
||||
IPEndPoint server = new IPEndPoint(IPAddress.Parse(IP), 102);
|
||||
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await socket.ConnectAsync(server.Address, 102);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -225,9 +226,9 @@ namespace S7.Net
|
||||
package.Add(CreateReadDataRequestPackage(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count)));
|
||||
}
|
||||
|
||||
await stream.WriteAsync(package.array, 0, package.array.Length);
|
||||
await socket.SendAsync(package.array, 0, package.array.Length, SocketFlags.None);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream); //TODO use Async
|
||||
var s7data = await COTP.TSDU.ReadAsync(socket); //TODO use Async
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
|
||||
@@ -415,9 +416,9 @@ namespace S7.Net
|
||||
// package.Add(0x02); // datenart
|
||||
package.Add(CreateReadDataRequestPackage(dataType, db, startByteAdr, count));
|
||||
|
||||
await stream.WriteAsync(package.array, 0, package.array.Length);
|
||||
await socket.SendAsync(package.array, 0, package.array.Length, SocketFlags.None);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream);
|
||||
var s7data = await COTP.TSDU.ReadAsync(socket);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
|
||||
@@ -467,9 +468,9 @@ namespace S7.Net
|
||||
// now join the header and the data
|
||||
package.Add(value);
|
||||
|
||||
await stream.WriteAsync(package.array, 0, package.array.Length);
|
||||
await socket.SendAsync(package.array, 0, package.array.Length, SocketFlags.None);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream);
|
||||
var s7data = await COTP.TSDU.ReadAsync(socket);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
{
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
@@ -517,9 +518,9 @@ namespace S7.Net
|
||||
// now join the header and the data
|
||||
package.Add(value);
|
||||
|
||||
await stream.WriteAsync(package.array, 0, package.array.Length);
|
||||
await socket.SendAsync(package.array, 0, package.array.Length, SocketFlags.None);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream);
|
||||
var s7data = await COTP.TSDU.ReadAsync(socket);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
|
||||
@@ -316,6 +316,11 @@ namespace S7.Net
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get S7 Package for value
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public byte[] GetPackage(object value)
|
||||
{
|
||||
switch (value.GetType().Name)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using S7.Net.Types;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
@@ -25,16 +26,16 @@ namespace S7.Net
|
||||
}
|
||||
try
|
||||
{
|
||||
stream.Write(GetCOPTConnectionRequest(CPU), 0, 22);
|
||||
var response = COTP.TPDU.Read(stream);
|
||||
socket.Send(GetCOPTConnectionRequest(CPU), 0, 22, SocketFlags.None);
|
||||
var response = COTP.TPDU.Read(socket);
|
||||
if (response.PDUType != 0xd0) //Connect Confirm
|
||||
{
|
||||
throw new WrongNumberOfBytesException("Waiting for COTP connect confirm");
|
||||
}
|
||||
|
||||
stream.Write(GetS7ConnectionSetup(), 0, 25);
|
||||
socket.Send(GetS7ConnectionSetup(), 0, 25, SocketFlags.None);
|
||||
|
||||
var s7data = COTP.TSDU.Read(stream);
|
||||
var s7data = COTP.TSDU.Read(socket);
|
||||
if (s7data == null || s7data[1] != 0x03) //Check for S7 Ack Data
|
||||
{
|
||||
throw new WrongNumberOfBytesException("Waiting for S7 connection setup");
|
||||
@@ -54,9 +55,10 @@ namespace S7.Net
|
||||
{
|
||||
try
|
||||
{
|
||||
tcpClient = new TcpClient();
|
||||
tcpClient.Connect(IP, 102);
|
||||
stream = tcpClient.GetStream();
|
||||
IPEndPoint server = new IPEndPoint(IPAddress.Parse(IP), 102);
|
||||
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
socket.Connect(server);
|
||||
return ErrorCode.NoError;
|
||||
}
|
||||
catch (SocketException sex)
|
||||
{
|
||||
@@ -267,12 +269,13 @@ namespace S7.Net
|
||||
public ErrorCode WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, bool value)
|
||||
{
|
||||
if (bitAdr < 0 || bitAdr > 7)
|
||||
throw new InvalidAddressException(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr));
|
||||
throw new Exception(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr));
|
||||
|
||||
ErrorCode lastError = WriteBitWithASingleRequest(dataType, db, startByteAdr, bitAdr, value);
|
||||
if (lastError != ErrorCode.NoError)
|
||||
{
|
||||
return lastError; }
|
||||
return lastError;
|
||||
}
|
||||
|
||||
return ErrorCode.NoError;
|
||||
}
|
||||
@@ -348,7 +351,9 @@ namespace S7.Net
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode WriteStruct(object structValue, int db, int startByteAdr = 0)
|
||||
{
|
||||
return WriteStructAsync(structValue, db, startByteAdr).Result;
|
||||
var bytes = Struct.ToBytes(structValue).ToList();
|
||||
var errCode = WriteBytes(DataType.DataBlock, db, startByteAdr, bytes.ToArray());
|
||||
return errCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -360,7 +365,9 @@ namespace S7.Net
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode WriteClass(object classValue, int db, int startByteAdr = 0)
|
||||
{
|
||||
return WriteClassAsync(classValue, db, startByteAdr).Result;
|
||||
var bytes = Class.ToBytes(classValue).ToList();
|
||||
var errCode = WriteBytes(DataType.DataBlock, db, startByteAdr, bytes.ToArray());
|
||||
return errCode;
|
||||
}
|
||||
|
||||
private byte[] ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, int count)
|
||||
@@ -374,9 +381,9 @@ namespace S7.Net
|
||||
// package.Add(0x02); // datenart
|
||||
package.Add(CreateReadDataRequestPackage(dataType, db, startByteAdr, count));
|
||||
|
||||
stream.Write(package.array, 0, package.array.Length);
|
||||
socket.Send(package.array, 0, package.array.Length, SocketFlags.None);
|
||||
|
||||
var s7data = COTP.TSDU.Read(stream);
|
||||
var s7data = COTP.TSDU.Read(socket);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
|
||||
@@ -401,12 +408,96 @@ namespace S7.Net
|
||||
|
||||
private ErrorCode WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value)
|
||||
{
|
||||
return WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr, value).Result;
|
||||
int varCount = 0;
|
||||
try
|
||||
{
|
||||
varCount = value.Length;
|
||||
// first create the header
|
||||
int packageSize = 35 + value.Length;
|
||||
ByteArray package = new ByteArray(packageSize);
|
||||
|
||||
package.Add(new byte[] { 3, 0, 0 });
|
||||
package.Add((byte)packageSize);
|
||||
package.Add(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
|
||||
package.Add(Word.ToByteArray((ushort)(varCount - 1)));
|
||||
package.Add(new byte[] { 0, 0x0e });
|
||||
package.Add(Word.ToByteArray((ushort)(varCount + 4)));
|
||||
package.Add(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 });
|
||||
package.Add(Word.ToByteArray((ushort)varCount));
|
||||
package.Add(Word.ToByteArray((ushort)(db)));
|
||||
package.Add((byte)dataType);
|
||||
var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
|
||||
package.Add((byte)overflow);
|
||||
package.Add(Word.ToByteArray((ushort)(startByteAdr * 8)));
|
||||
package.Add(new byte[] { 0, 4 });
|
||||
package.Add(Word.ToByteArray((ushort)(varCount * 8)));
|
||||
|
||||
// now join the header and the data
|
||||
package.Add(value);
|
||||
|
||||
socket.Send(package.array, package.array.Length, SocketFlags.None);
|
||||
|
||||
var s7data = COTP.TSDU.Read(socket);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
{
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
}
|
||||
|
||||
return ErrorCode.NoError;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.WriteData;
|
||||
LastErrorString = exc.Message;
|
||||
return LastErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
private ErrorCode WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue)
|
||||
{
|
||||
return WriteBitWithASingleRequestAsync(dataType, db, startByteAdr, bitAdr, bitValue).Result;
|
||||
int varCount = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var value = new[] { bitValue ? (byte)1 : (byte)0 };
|
||||
varCount = value.Length;
|
||||
// first create the header
|
||||
int packageSize = 35 + value.Length;
|
||||
ByteArray package = new ByteArray(packageSize);
|
||||
|
||||
package.Add(new byte[] { 3, 0, 0 });
|
||||
package.Add((byte)packageSize);
|
||||
package.Add(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
|
||||
package.Add(Word.ToByteArray((ushort)(varCount - 1)));
|
||||
package.Add(new byte[] { 0, 0x0e });
|
||||
package.Add(Word.ToByteArray((ushort)(varCount + 4)));
|
||||
package.Add(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x01 }); //ending 0x01 is used for writing a sinlge bit
|
||||
package.Add(Word.ToByteArray((ushort)varCount));
|
||||
package.Add(Word.ToByteArray((ushort)(db)));
|
||||
package.Add((byte)dataType);
|
||||
int overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
|
||||
package.Add((byte)overflow);
|
||||
package.Add(Word.ToByteArray((ushort)(startByteAdr * 8 + bitAdr)));
|
||||
package.Add(new byte[] { 0, 0x03 }); //ending 0x03 is used for writing a sinlge bit
|
||||
package.Add(Word.ToByteArray((ushort)(varCount)));
|
||||
|
||||
// now join the header and the data
|
||||
package.Add(value);
|
||||
|
||||
socket.Send(package.array, package.array.Length, SocketFlags.None);
|
||||
|
||||
var s7data = COTP.TSDU.Read(socket);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
|
||||
return ErrorCode.NoError;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.WriteData;
|
||||
LastErrorString = exc.Message;
|
||||
return LastErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -417,7 +508,6 @@ namespace S7.Net
|
||||
/// DataItems must not be more than 20 (protocol restriction) and bytes must not be more than 200 + 22 of header (protocol restriction).
|
||||
/// </summary>
|
||||
/// <param name="dataItems">List of dataitems that contains the list of variables that must be read. Maximum 20 dataitems are accepted.</param>
|
||||
[Obsolete("Use ReadMultipleVarsAsync. Note: different function signature")]
|
||||
public void ReadMultipleVars(List<DataItem> dataItems)
|
||||
{
|
||||
int cntBytes = dataItems.Sum(dataItem => VarTypeToByteLength(dataItem.VarType, dataItem.Count));
|
||||
@@ -442,9 +532,9 @@ namespace S7.Net
|
||||
package.Add(CreateReadDataRequestPackage(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count)));
|
||||
}
|
||||
|
||||
stream.Write(package.array, 0, package.array.Length);
|
||||
socket.Send(package.array, 0, package.array.Length, SocketFlags.None);
|
||||
|
||||
var s7data = COTP.TSDU.Read(stream); //TODO use Async
|
||||
var s7data = COTP.TSDU.Read(socket);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
|
||||
@@ -79,12 +79,13 @@
|
||||
<Compile Include="Conversion.cs" />
|
||||
<Compile Include="COTP.cs" />
|
||||
<Compile Include="Enums.cs" />
|
||||
<Compile Include="Plc.cs" />
|
||||
<Compile Include="PlcAsynchronous.cs" />
|
||||
<Compile Include="PLC.cs" />
|
||||
<Compile Include="PLCAsynchronous.cs" />
|
||||
<Compile Include="PLCExceptions.cs" />
|
||||
<Compile Include="PlcHelpers.cs" />
|
||||
<Compile Include="PlcSynchronous.cs" />
|
||||
<Compile Include="PLCHelpers.cs" />
|
||||
<Compile Include="PLCSynchronous.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SocketExtension.cs" />
|
||||
<Compile Include="TPKT.cs" />
|
||||
<Compile Include="Types\Bit.cs" />
|
||||
<Compile Include="Types\Boolean.cs" />
|
||||
|
||||
89
S7.Net/SocketExtension.cs
Normal file
89
S7.Net/SocketExtension.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace S7.Net
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Extensions to socket for using awaitable socket operations
|
||||
/// </summary>
|
||||
public static class SocketExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// https://blogs.msdn.microsoft.com/pfxteam/2011/12/15/awaiting-socket-operations/
|
||||
/// </summary>
|
||||
/// <param name="socket"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="socketFlags"></param>
|
||||
/// <returns></returns>
|
||||
public static Task<int> ReceiveAsync(
|
||||
this Socket socket, byte[] buffer, int offset, int size,
|
||||
SocketFlags socketFlags)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>(socket);
|
||||
socket.BeginReceive(buffer, offset, size, socketFlags, iar =>
|
||||
{
|
||||
var t = (TaskCompletionSource<int>)iar.AsyncState;
|
||||
var s = (Socket)t.Task.AsyncState;
|
||||
try { t.TrySetResult(s.EndReceive(iar)); }
|
||||
catch (Exception exc) { t.TrySetException(exc); }
|
||||
}, tcs);
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://blogs.msdn.microsoft.com/pfxteam/2011/12/15/awaiting-socket-operations/
|
||||
/// </summary>
|
||||
/// <param name="socket"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="socketFlags"></param>
|
||||
/// <returns></returns>
|
||||
public static Task<int> SendAsync(
|
||||
this Socket socket, byte[] buffer, int offset, int size,
|
||||
SocketFlags socketFlags)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>(socket);
|
||||
socket.BeginSend(buffer, offset, size, socketFlags, iar =>
|
||||
{
|
||||
var t = (TaskCompletionSource<int>)iar.AsyncState;
|
||||
var s = (Socket)t.Task.AsyncState;
|
||||
try { t.TrySetResult(s.EndReceive(iar)); }
|
||||
catch (Exception exc) { t.TrySetException(exc); }
|
||||
}, tcs);
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://blogs.msdn.microsoft.com/pfxteam/2011/12/15/awaiting-socket-operations/
|
||||
/// </summary>
|
||||
/// <param name="socket"></param>
|
||||
/// <param name="addresses"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public static Task<int> ConnectAsync(this Socket socket, System.Net.IPAddress addresses, int port)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>(socket);
|
||||
socket.BeginConnect(addresses, port, iar =>
|
||||
{
|
||||
var t = (TaskCompletionSource<int>)iar.AsyncState;
|
||||
var s = (Socket)t.Task.AsyncState;
|
||||
try { t.TrySetResult(s.EndReceive(iar)); }
|
||||
catch (Exception exc) { t.TrySetException(exc); }
|
||||
}, tcs);
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,12 @@ namespace S7.Net
|
||||
/// <summary>
|
||||
/// Reads a TPKT from the socket
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="socket">The stream to read from</param>
|
||||
/// <returns>TPKT Instance</returns>
|
||||
public static TPKT Read(Stream stream)
|
||||
public static TPKT Read(Socket socket)
|
||||
{
|
||||
var buf = new byte[4];
|
||||
int len = stream.Read(buf, 0, 4);
|
||||
int len = socket.Receive(buf, 0, 4, SocketFlags.None);
|
||||
if (len < 4) throw new TPKTInvalidException("TPKT is incomplete / invalid");
|
||||
var pkt = new TPKT
|
||||
{
|
||||
@@ -35,7 +35,7 @@ namespace S7.Net
|
||||
if (pkt.Length > 0)
|
||||
{
|
||||
pkt.Data = new byte[pkt.Length - 4];
|
||||
len = stream.Read(pkt.Data, 0, pkt.Length - 4);
|
||||
len = socket.Receive(pkt.Data, 0, pkt.Length - 4, SocketFlags.None);
|
||||
if (len < pkt.Length - 4)
|
||||
throw new TPKTInvalidException("TPKT is incomplete / invalid");
|
||||
}
|
||||
@@ -45,12 +45,12 @@ namespace S7.Net
|
||||
/// <summary>
|
||||
/// Reads a TPKT from the socket Async
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="socket">The stream to read from</param>
|
||||
/// <returns>Task TPKT Instace</returns>
|
||||
public static async Task<TPKT> ReadAsync(Stream stream)
|
||||
public static async Task<TPKT> ReadAsync(Socket socket)
|
||||
{
|
||||
var buf = new byte[4];
|
||||
int len = await stream.ReadAsync(buf, 0, 4);
|
||||
int len = await socket.ReceiveAsync(buf, 0, 4, SocketFlags.None);
|
||||
if (len < 4) throw new TPKTInvalidException("TPKT is incomplete / invalid");
|
||||
var pkt = new TPKT
|
||||
{
|
||||
@@ -61,7 +61,7 @@ namespace S7.Net
|
||||
if (pkt.Length > 0)
|
||||
{
|
||||
pkt.Data = new byte[pkt.Length - 4];
|
||||
len = await stream.ReadAsync(pkt.Data, 0, pkt.Length - 4);
|
||||
len = await socket.ReceiveAsync(pkt.Data, 0, pkt.Length - 4, SocketFlags.None);
|
||||
if (len < pkt.Length - 4) throw new TPKTInvalidException("TPKT is incomplete / invalid");
|
||||
}
|
||||
return pkt;
|
||||
|
||||
Reference in New Issue
Block a user