Use socket instead of TcpClient and stream.

Async implemented with wrapper.
This commit is contained in:
Thomas Jäger
2018-05-16 11:24:21 +02:00
parent b8b890977e
commit 1b34716a30
10 changed files with 399 additions and 183 deletions

View File

@@ -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();
}
}
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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());

View File

@@ -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)

View File

@@ -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());

View File

@@ -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
View 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;
}
}
}

View File

@@ -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;