diff --git a/S7.Net/Helper/MemoryStreamExtension.cs b/S7.Net/Helper/MemoryStreamExtension.cs index dd10fbc9..b451e2d4 100644 --- a/S7.Net/Helper/MemoryStreamExtension.cs +++ b/S7.Net/Helper/MemoryStreamExtension.cs @@ -1,6 +1,10 @@ - +using System; +using System.Buffers; +using System.IO; + namespace S7.Net.Helper { +#if !NET5_0_OR_GREATER internal static class MemoryStreamExtension { /// @@ -10,9 +14,25 @@ internal static class MemoryStreamExtension /// /// /// - public static void WriteByteArray(this System.IO.MemoryStream stream, byte[] value) + public static void Write(this MemoryStream stream, byte[] value) { stream.Write(value, 0, value.Length); } + + /// + /// Helper function to write the whole content of the given byte span to a memory stream. + /// + /// + /// + public static void Write(this MemoryStream stream, ReadOnlySpan value) + { + byte[] buffer = ArrayPool.Shared.Rent(value.Length); + + value.CopyTo(buffer); + stream.Write(buffer, 0, value.Length); + + ArrayPool.Shared.Return(buffer); + } } +#endif } diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index 77159225..c0fbd78f 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -1,7 +1,6 @@ using S7.Net.Helper; using S7.Net.Protocol.S7; using S7.Net.Types; -using System; using System.Collections.Generic; using System.Linq; using DateTime = S7.Net.Types.DateTime; @@ -18,13 +17,13 @@ public partial class Plc private static void BuildHeaderPackage(System.IO.MemoryStream stream, int amount = 1) { //header size = 19 bytes - stream.WriteByteArray(new byte[] { 0x03, 0x00 }); + stream.Write(new byte[] { 0x03, 0x00 }); //complete package size - stream.WriteByteArray(Types.Int.ToByteArray((short)(19 + (12 * amount)))); - stream.WriteByteArray(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 }); + stream.Write(Int.ToByteArray((short)(19 + (12 * amount)))); + stream.Write(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 }); //data part size - stream.WriteByteArray(Types.Word.ToByteArray((ushort)(2 + (amount * 12)))); - stream.WriteByteArray(new byte[] { 0x00, 0x00, 0x04 }); + stream.Write(Word.ToByteArray((ushort)(2 + (amount * 12)))); + stream.Write(new byte[] { 0x00, 0x00, 0x04 }); //amount of requests stream.WriteByte((byte)amount); } @@ -41,7 +40,7 @@ private static void BuildHeaderPackage(System.IO.MemoryStream stream, int amount private static void BuildReadDataRequestPackage(System.IO.MemoryStream stream, DataType dataType, int db, int startByteAdr, int count = 1) { //single data req = 12 - stream.WriteByteArray(new byte[] { 0x12, 0x0a, 0x10 }); + stream.Write(new byte[] { 0x12, 0x0a, 0x10 }); switch (dataType) { case DataType.Timer: @@ -53,8 +52,8 @@ private static void BuildReadDataRequestPackage(System.IO.MemoryStream stream, D break; } - stream.WriteByteArray(Word.ToByteArray((ushort)(count))); - stream.WriteByteArray(Word.ToByteArray((ushort)(db))); + stream.Write(Word.ToByteArray((ushort)(count))); + stream.Write(Word.ToByteArray((ushort)(db))); stream.WriteByte((byte)dataType); var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 stream.WriteByte((byte)overflow); @@ -62,10 +61,10 @@ private static void BuildReadDataRequestPackage(System.IO.MemoryStream stream, D { case DataType.Timer: case DataType.Counter: - stream.WriteByteArray(Types.Word.ToByteArray((ushort)(startByteAdr))); + stream.Write(Word.ToByteArray((ushort)(startByteAdr))); break; default: - stream.WriteByteArray(Types.Word.ToByteArray((ushort)((startByteAdr) * 8))); + stream.Write(Word.ToByteArray((ushort)((startByteAdr) * 8))); break; } } diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index c4aeb90e..36fef8c6 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -95,7 +95,6 @@ private async Task SetupConnection(Stream stream, CancellationToken cancellation MaxPDUSize = s7data[18] * 256 + s7data[19]; } - /// /// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. /// If the read was not successful, check LastErrorCode or LastErrorString. @@ -110,20 +109,12 @@ private async Task SetupConnection(Stream stream, CancellationToken cancellation public async Task ReadBytesAsync(DataType dataType, int db, int startByteAdr, int count, CancellationToken cancellationToken = default) { var resultBytes = new byte[count]; - int index = 0; - while (count > 0) - { - //This works up to MaxPDUSize-1 on SNAP7. But not MaxPDUSize-0. - var maxToRead = Math.Min(count, MaxPDUSize - 18); - await ReadBytesWithSingleRequestAsync(dataType, db, startByteAdr + index, resultBytes, index, maxToRead, cancellationToken).ConfigureAwait(false); - count -= maxToRead; - index += maxToRead; - } + + await ReadBytesAsync(resultBytes, dataType, db, startByteAdr, cancellationToken).ConfigureAwait(false); + return resultBytes; } -#if NET5_0_OR_GREATER - /// /// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. /// If the read was not successful, check LastErrorCode or LastErrorString. @@ -148,8 +139,6 @@ public async Task ReadBytesAsync(Memory buffer, DataType dataType, int db, } } -#endif - /// /// Read and decode a certain number of bytes of the "VarType" provided. /// This can be used to read multiple consecutive variables of the same type (Word, DWord, Int, etc). @@ -323,7 +312,6 @@ public async Task> ReadMultipleVarsAsync(List dataItems return dataItems; } - /// /// Write a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. /// If the write was not successful, check LastErrorCode or LastErrorString. @@ -335,21 +323,11 @@ public async Task> ReadMultipleVarsAsync(List dataItems /// The token to monitor for cancellation requests. The default value is None. /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. /// A task that represents the asynchronous write operation. - public async Task WriteBytesAsync(DataType dataType, int db, int startByteAdr, byte[] value, CancellationToken cancellationToken = default) + public Task WriteBytesAsync(DataType dataType, int db, int startByteAdr, byte[] value, CancellationToken cancellationToken = default) { - int localIndex = 0; - int count = value.Length; - while (count > 0) - { - var maxToWrite = (int)Math.Min(count, MaxPDUSize - 35); - await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite, cancellationToken).ConfigureAwait(false); - count -= maxToWrite; - localIndex += maxToWrite; - } + return WriteBytesAsync(dataType, db, startByteAdr, value.AsMemory(), cancellationToken); } -#if NET5_0_OR_GREATER - /// /// Write a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. /// If the write was not successful, check LastErrorCode or LastErrorString. @@ -373,8 +351,6 @@ public async Task WriteBytesAsync(DataType dataType, int db, int startByteAdr, R } } -#endif - /// /// Write a single bit from a DB with the specified index. /// @@ -496,18 +472,6 @@ public async Task WriteClassAsync(object classValue, int db, int startByteAdr = await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes, cancellationToken).ConfigureAwait(false); } - private async Task ReadBytesWithSingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - var dataToSend = BuildReadRequestPackage(new[] { new DataItemAddress(dataType, db, startByteAdr, count) }); - - var s7data = await RequestTsduAsync(dataToSend, cancellationToken); - AssertReadResponse(s7data, count); - - Array.Copy(s7data, 18, buffer, offset, count); - } - -#if NET5_0_OR_GREATER - private async Task ReadBytesWithSingleRequestAsync(DataType dataType, int db, int startByteAdr, Memory buffer, CancellationToken cancellationToken) { var dataToSend = BuildReadRequestPackage(new[] { new DataItemAddress(dataType, db, startByteAdr, buffer.Length) }); @@ -518,8 +482,6 @@ private async Task ReadBytesWithSingleRequestAsync(DataType dataType, int db, in s7data.AsSpan(18, buffer.Length).CopyTo(buffer.Span); } -#endif - /// /// Write DataItem(s) to the PLC. Throws an exception if the response is invalid /// or when the PLC reports errors for item(s) written. @@ -538,35 +500,6 @@ public async Task WriteAsync(params DataItem[] dataItems) S7WriteMultiple.ParseResponse(response, response.Length, dataItems); } - /// - /// Writes up to 200 bytes to the PLC. You must specify the memory area type, memory are address, byte start address and bytes count. - /// - /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. - /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. - /// Start byte address. If you want to read DB1.DBW200, this is 200. - /// Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion. - /// A task that represents the asynchronous write operation. - private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count, CancellationToken cancellationToken) - { - try - { - var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, dataOffset, count); - var s7data = await RequestTsduAsync(dataToSend, cancellationToken).ConfigureAwait(false); - - ValidateResponseCode((ReadWriteErrorCode)s7data[14]); - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception exc) - { - throw new PlcException(ErrorCode.WriteData, exc); - } - } - -#if NET5_0_OR_GREATER - /// /// Writes up to 200 bytes to the PLC. You must specify the memory area type, memory are address, byte start address and bytes count. /// @@ -594,8 +527,6 @@ private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, } } -#endif - private async Task WriteBitWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue, CancellationToken cancellationToken) { try diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 0b035baa..4a3aaadb 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -39,20 +39,12 @@ public void Open() public byte[] ReadBytes(DataType dataType, int db, int startByteAdr, int count) { var result = new byte[count]; - int index = 0; - while (count > 0) - { - //This works up to MaxPDUSize-1 on SNAP7. But not MaxPDUSize-0. - var maxToRead = Math.Min(count, MaxPDUSize - 18); - ReadBytesWithSingleRequest(dataType, db, startByteAdr + index, result, index, maxToRead); - count -= maxToRead; - index += maxToRead; - } + + ReadBytes(result, dataType, db, startByteAdr); + return result; } -#if NET5_0_OR_GREATER - /// /// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. /// If the read was not successful, check LastErrorCode or LastErrorString. @@ -75,8 +67,6 @@ public void ReadBytes(Span buffer, DataType dataType, int db, int startByt } } -#endif - /// /// Read and decode a certain number of bytes of the "VarType" provided. /// This can be used to read multiple consecutive variables of the same type (Word, DWord, Int, etc). @@ -137,7 +127,6 @@ public void ReadBytes(Span buffer, DataType dataType, int db, int startByt return ReadStruct(typeof(T), db, startByteAdr) as T?; } - /// /// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC. /// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. @@ -205,22 +194,9 @@ public int ReadClass(object sourceClass, int db, int startByteAdr = 0) /// Bytes to write. If more than 200, multiple requests will be made. public void WriteBytes(DataType dataType, int db, int startByteAdr, byte[] value) { - int localIndex = 0; - int count = value.Length; - while (count > 0) - { - //TODO: Figure out how to use MaxPDUSize here - //Snap7 seems to choke on PDU sizes above 256 even if snap7 - //replies with bigger PDU size in connection setup. - var maxToWrite = Math.Min(count, MaxPDUSize - 28);//TODO tested only when the MaxPDUSize is 480 - WriteBytesWithASingleRequest(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite); - count -= maxToWrite; - localIndex += maxToWrite; - } + WriteBytes(dataType, db, startByteAdr, value.AsSpan()); } -#if NET5_0_OR_GREATER - /// /// Write a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. /// If the write was not successful, check LastErrorCode or LastErrorString. @@ -244,8 +220,6 @@ public void WriteBytes(DataType dataType, int db, int startByteAdr, ReadOnlySpan } } -#endif - /// /// Write a single bit from a DB with the specified index. /// @@ -347,43 +321,18 @@ public void WriteClass(object classValue, int db, int startByteAdr = 0) WriteClassAsync(classValue, db, startByteAdr).GetAwaiter().GetResult(); } - private void ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, byte[] buffer, int offset, int count) - { - try - { - // first create the header - int packageSize = 19 + 12; // 19 header + 12 for 1 request - var package = new System.IO.MemoryStream(packageSize); - BuildHeaderPackage(package); - // package.Add(0x02); // datenart - BuildReadDataRequestPackage(package, dataType, db, startByteAdr, count); - - var dataToSend = package.ToArray(); - var s7data = RequestTsdu(dataToSend); - AssertReadResponse(s7data, count); - - Array.Copy(s7data, 18, buffer, offset, count); - } - catch (Exception exc) - { - throw new PlcException(ErrorCode.ReadData, exc); - } - } - -#if NET5_0_OR_GREATER - private void ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, Span buffer) { try { // first create the header - int packageSize = 19 + 12; // 19 header + 12 for 1 request - var package = new System.IO.MemoryStream(packageSize); + const int packageSize = 19 + 12; // 19 header + 12 for 1 request + var dataToSend = new byte[packageSize]; + var package = new MemoryStream(dataToSend); BuildHeaderPackage(package); // package.Add(0x02); // datenart BuildReadDataRequestPackage(package, dataType, db, startByteAdr, buffer.Length); - var dataToSend = package.ToArray(); var s7data = RequestTsdu(dataToSend); AssertReadResponse(s7data, buffer.Length); @@ -395,8 +344,6 @@ private void ReadBytesWithSingleRequest(DataType dataType, int db, int startByte } } -#endif - /// /// Write DataItem(s) to the PLC. Throws an exception if the response is invalid /// or when the PLC reports errors for item(s) written. @@ -406,7 +353,6 @@ public void Write(params DataItem[] dataItems) { AssertPduSizeForWrite(dataItems); - var message = new ByteArray(); var length = S7WriteMultiple.CreateRequest(message, dataItems); var response = RequestTsdu(message.Array, 0, length); @@ -414,23 +360,6 @@ public void Write(params DataItem[] dataItems) S7WriteMultiple.ParseResponse(response, response.Length, dataItems); } - private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count) - { - try - { - var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, dataOffset, count); - var s7data = RequestTsdu(dataToSend); - - ValidateResponseCode((ReadWriteErrorCode)s7data[14]); - } - catch (Exception exc) - { - throw new PlcException(ErrorCode.WriteData, exc); - } - } - -#if NET5_0_OR_GREATER - private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, ReadOnlySpan value) { try @@ -446,107 +375,72 @@ private void WriteBytesWithASingleRequest(DataType dataType, int db, int startBy } } -#endif - - private byte[] BuildWriteBytesPackage(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count) - { - int varCount = count; - // first create the header - int packageSize = 35 + varCount; - var package = new MemoryStream(new byte[packageSize]); - - package.WriteByte(3); - package.WriteByte(0); - //complete package size - package.WriteByteArray(Int.ToByteArray((short)packageSize)); - package.WriteByteArray(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 }); - package.WriteByteArray(Word.ToByteArray((ushort)(varCount - 1))); - package.WriteByteArray(new byte[] { 0, 0x0e }); - package.WriteByteArray(Word.ToByteArray((ushort)(varCount + 4))); - package.WriteByteArray(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 }); - package.WriteByteArray(Word.ToByteArray((ushort)varCount)); - package.WriteByteArray(Word.ToByteArray((ushort)(db))); - package.WriteByte((byte)dataType); - var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 - package.WriteByte((byte)overflow); - package.WriteByteArray(Word.ToByteArray((ushort)(startByteAdr * 8))); - package.WriteByteArray(new byte[] { 0, 4 }); - package.WriteByteArray(Word.ToByteArray((ushort)(varCount * 8))); - - // now join the header and the data - package.Write(value, dataOffset, count); - - return package.ToArray(); - } - -#if NET5_0_OR_GREATER - private byte[] BuildWriteBytesPackage(DataType dataType, int db, int startByteAdr, ReadOnlySpan value) { int varCount = value.Length; // first create the header int packageSize = 35 + varCount; - var package = new MemoryStream(new byte[packageSize]); + var packageData = new byte[packageSize]; + var package = new MemoryStream(packageData); package.WriteByte(3); package.WriteByte(0); //complete package size - package.WriteByteArray(Int.ToByteArray((short)packageSize)); - package.WriteByteArray(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 }); - package.WriteByteArray(Word.ToByteArray((ushort)(varCount - 1))); - package.WriteByteArray(new byte[] { 0, 0x0e }); - package.WriteByteArray(Word.ToByteArray((ushort)(varCount + 4))); - package.WriteByteArray(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 }); - package.WriteByteArray(Word.ToByteArray((ushort)varCount)); - package.WriteByteArray(Word.ToByteArray((ushort)(db))); + package.Write(Int.ToByteArray((short)packageSize)); + // This overload doesn't allocate the byte array, it refers to assembly's static data segment + package.Write(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 }); + package.Write(Word.ToByteArray((ushort)(varCount - 1))); + package.Write(new byte[] { 0, 0x0e }); + package.Write(Word.ToByteArray((ushort)(varCount + 4))); + package.Write(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 }); + package.Write(Word.ToByteArray((ushort)varCount)); + package.Write(Word.ToByteArray((ushort)(db))); package.WriteByte((byte)dataType); var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 package.WriteByte((byte)overflow); - package.WriteByteArray(Word.ToByteArray((ushort)(startByteAdr * 8))); - package.WriteByteArray(new byte[] { 0, 4 }); - package.WriteByteArray(Word.ToByteArray((ushort)(varCount * 8))); + package.Write(Word.ToByteArray((ushort)(startByteAdr * 8))); + package.Write(new byte[] { 0, 4 }); + package.Write(Word.ToByteArray((ushort)(varCount * 8))); // now join the header and the data package.Write(value); - return package.ToArray(); + return packageData; } -#endif - private byte[] BuildWriteBitPackage(DataType dataType, int db, int startByteAdr, bool bitValue, int bitAdr) { var value = new[] { bitValue ? (byte)1 : (byte)0 }; int varCount = 1; // first create the header int packageSize = 35 + varCount; - var package = new MemoryStream(new byte[packageSize]); + var packageData = new byte[packageSize]; + var package = new MemoryStream(packageData); package.WriteByte(3); package.WriteByte(0); //complete package size - package.WriteByteArray(Int.ToByteArray((short)packageSize)); - package.WriteByteArray(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 }); - package.WriteByteArray(Word.ToByteArray((ushort)(varCount - 1))); - package.WriteByteArray(new byte[] { 0, 0x0e }); - package.WriteByteArray(Word.ToByteArray((ushort)(varCount + 4))); - package.WriteByteArray(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x01 }); //ending 0x01 is used for writing a sinlge bit - package.WriteByteArray(Word.ToByteArray((ushort)varCount)); - package.WriteByteArray(Word.ToByteArray((ushort)(db))); + package.Write(Int.ToByteArray((short)packageSize)); + package.Write(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 }); + package.Write(Word.ToByteArray((ushort)(varCount - 1))); + package.Write(new byte[] { 0, 0x0e }); + package.Write(Word.ToByteArray((ushort)(varCount + 4))); + package.Write(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x01 }); //ending 0x01 is used for writing a sinlge bit + package.Write(Word.ToByteArray((ushort)varCount)); + package.Write(Word.ToByteArray((ushort)(db))); package.WriteByte((byte)dataType); var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 package.WriteByte((byte)overflow); - package.WriteByteArray(Word.ToByteArray((ushort)(startByteAdr * 8 + bitAdr))); - package.WriteByteArray(new byte[] { 0, 0x03 }); //ending 0x03 is used for writing a sinlge bit - package.WriteByteArray(Word.ToByteArray((ushort)(varCount))); + package.Write(Word.ToByteArray((ushort)(startByteAdr * 8 + bitAdr))); + package.Write(new byte[] { 0, 0x03 }); //ending 0x03 is used for writing a sinlge bit + package.Write(Word.ToByteArray((ushort)(varCount))); // now join the header and the data - package.WriteByteArray(value); + package.Write(value); - return package.ToArray(); + return packageData; } - private void WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue) { try @@ -578,7 +472,8 @@ public void ReadMultipleVars(List dataItems) { // first create the header int packageSize = 19 + (dataItems.Count * 12); - var package = new System.IO.MemoryStream(packageSize); + var dataToSend = new byte[packageSize]; + var package = new MemoryStream(dataToSend); BuildHeaderPackage(package, dataItems.Count); // package.Add(0x02); // datenart foreach (var dataItem in dataItems) @@ -586,8 +481,7 @@ public void ReadMultipleVars(List dataItems) BuildReadDataRequestPackage(package, dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count)); } - var dataToSend = package.ToArray(); - var s7data = RequestTsdu(dataToSend); + byte[] s7data = RequestTsdu(dataToSend); ValidateResponseCode((ReadWriteErrorCode)s7data[14]); diff --git a/S7.Net/S7.Net.csproj b/S7.Net/S7.Net.csproj index fa85b08c..d41f08fe 100644 --- a/S7.Net/S7.Net.csproj +++ b/S7.Net/S7.Net.csproj @@ -26,6 +26,10 @@ NET_FULL + + + +