From 2a451bc049c3ff212ea9153a235e41d70c668ee7 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Mon, 17 Aug 2020 20:06:42 +0200 Subject: [PATCH 01/13] TSDU: Use Array.Copy for Read functions. --- S7.Net/COTP.cs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/S7.Net/COTP.cs b/S7.Net/COTP.cs index 8f60423..c1eb32e 100644 --- a/S7.Net/COTP.cs +++ b/S7.Net/COTP.cs @@ -106,19 +106,17 @@ namespace S7.Net var segment = TPDU.Read(stream); var buffer = new byte[segment.Data.Length]; - var output = new MemoryStream(buffer); - output.Write(segment.Data, 0, segment.Data.Length); + Array.Copy(segment.Data, buffer, segment.Data.Length); while (!segment.LastDataUnit) { segment = TPDU.Read(stream); + var previousLength = buffer.Length; Array.Resize(ref buffer, buffer.Length + segment.Data.Length); - var lastPosition = output.Position; - output = new MemoryStream(buffer); - output.Write(segment.Data, (int) lastPosition, segment.Data.Length); + Array.Copy(segment.Data, 0, buffer, previousLength, segment.Data.Length); } - return buffer.Take((int)output.Position).ToArray(); + return buffer; } /// @@ -132,18 +130,17 @@ namespace S7.Net var segment = await TPDU.ReadAsync(stream); var buffer = new byte[segment.Data.Length]; - var output = new MemoryStream(buffer); - output.Write(segment.Data, 0, segment.Data.Length); + Array.Copy(segment.Data, buffer, segment.Data.Length); while (!segment.LastDataUnit) { segment = await TPDU.ReadAsync(stream); + var previousLength = buffer.Length; Array.Resize(ref buffer, buffer.Length + segment.Data.Length); - var lastPosition = output.Position; - output = new MemoryStream(buffer); - output.Write(segment.Data, (int) lastPosition, segment.Data.Length); + Array.Copy(segment.Data, 0, buffer, previousLength, segment.Data.Length); } - return buffer.Take((int)output.Position).ToArray(); + + return buffer; } } } From fbd8a13c6ce87cfc0550335af1d833ba5d0be530 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Mon, 17 Aug 2020 20:37:07 +0200 Subject: [PATCH 02/13] ReadBytesAsync: Replace list with a plain byte array. --- S7.Net/PlcAsynchronous.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 601a3a4..2e9ecd8 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -67,20 +67,20 @@ namespace S7.Net /// Returns the bytes in an array public async Task ReadBytesAsync(DataType dataType, int db, int startByteAdr, int count) { - List resultBytes = new List(); - int index = startByteAdr; + 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 = (int)Math.Min(count, MaxPDUSize - 18); - byte[] bytes = await ReadBytesWithSingleRequestAsync(dataType, db, index, maxToRead); + byte[] bytes = await ReadBytesWithSingleRequestAsync(dataType, db, index + startByteAdr, maxToRead); if (bytes == null) - return resultBytes.ToArray(); - resultBytes.AddRange(bytes); + return resultBytes; + Array.Copy(bytes, 0, resultBytes, index, maxToRead); count -= maxToRead; index += maxToRead; } - return resultBytes.ToArray(); + return resultBytes; } /// From 4ef037881c76e7a3e125804fa7c2e095183572e7 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Mon, 17 Aug 2020 20:57:33 +0200 Subject: [PATCH 03/13] Simplify WriteBytes functiosn by merging common BuildPackage code. Both Synchronous and Asynchronous need to build the same binary data package to write a bytes array. Move that package building out into a common function. Also use IEnumerable to pass in data instead of converting it to array and back multiple times. Not that happy with the whole ByteArray class, we could probably just use a MemoryStream instead. --- S7.Net/PlcAsynchronous.cs | 35 +++------------------ S7.Net/PlcSynchronous.cs | 64 +++++++++++++++++++++------------------ S7.Net/Types/ByteArray.cs | 5 +++ 3 files changed, 45 insertions(+), 59 deletions(-) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 2e9ecd8..a153853 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -268,7 +268,7 @@ namespace S7.Net //Snap7 seems to choke on PDU sizes above 256 even if snap7 //replies with bigger PDU size in connection setup. var maxToWrite = (int)Math.Min(count, 200); - await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite).ToArray()); + await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite), maxToWrite); count -= maxToWrite; localIndex += maxToWrite; } @@ -435,40 +435,15 @@ namespace S7.Net /// 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) + private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, IEnumerable value, int count) { - var stream = GetStreamIfAvailable(); - - byte[] bReceive = new byte[513]; - int varCount = 0; try { - varCount = value.Length; - // first create the header - int packageSize = 35 + value.Length; - ByteArray package = new ByteArray(packageSize); + var stream = GetStreamIfAvailable(); + var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, count); - 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); - - await stream.WriteAsync(package.Array, 0, package.Array.Length); + await stream.WriteAsync(dataToSend, 0, dataToSend.Length); var s7data = await COTP.TSDU.ReadAsync(stream); if (s7data == null || s7data[14] != 0xff) diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 74b02bc..30c4cdc 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -232,7 +232,7 @@ namespace S7.Net //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.Skip(localIndex).Take(maxToWrite).ToArray()); + WriteBytesWithASingleRequest(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite), maxToWrite); count -= maxToWrite; localIndex += maxToWrite; } @@ -387,38 +387,14 @@ namespace S7.Net S7WriteMultiple.ParseResponse(response, response.Length, dataItems); } - private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value) + private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, IEnumerable value, int count) { - var stream = GetStreamIfAvailable(); - int varCount = 0; try { - varCount = value.Length; - // first create the header - int packageSize = 35 + value.Length; - ByteArray package = new ByteArray(packageSize); + var stream = GetStreamIfAvailable(); + var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, count); - package.Add(new byte[] { 3, 0 }); - //complete package size - package.Add(Int.ToByteArray((short)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); - - stream.Write(package.Array, 0, package.Array.Length); + stream.Write(dataToSend, 0, dataToSend.Length); var s7data = COTP.TSDU.Read(stream); if (s7data == null || s7data[14] != 0xff) @@ -432,6 +408,36 @@ namespace S7.Net } } + private byte[] BuildWriteBytesPackage(DataType dataType, int db, int startByteAdr, IEnumerable value, int count) + { + int varCount = count; + // first create the header + int packageSize = 35 + count; + ByteArray package = new ByteArray(packageSize); + + package.Add(new byte[] { 3, 0 }); + //complete package size + package.Add(Int.ToByteArray((short)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); + + return package.Array; + } + private void WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue) { var stream = GetStreamIfAvailable(); diff --git a/S7.Net/Types/ByteArray.cs b/S7.Net/Types/ByteArray.cs index 322cee8..e80e4b5 100644 --- a/S7.Net/Types/ByteArray.cs +++ b/S7.Net/Types/ByteArray.cs @@ -50,6 +50,11 @@ namespace S7.Net.Types list.AddRange(items); } + public void Add(IEnumerable items) + { + list.AddRange(items); + } + public void Add(ByteArray byteArray) { list.AddRange(byteArray.Array); From 2f07d430625f4f03fa4c1821dc7bbbfd0a4bfc71 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Mon, 17 Aug 2020 21:06:45 +0200 Subject: [PATCH 04/13] Change BuildWriteBytesPackage to use MemoryStream. --- S7.Net/Helper/MemoryStreamExtension.cs | 18 +++++++++++ S7.Net/PlcAsynchronous.cs | 6 ++-- S7.Net/PlcSynchronous.cs | 45 ++++++++++++++------------ 3 files changed, 45 insertions(+), 24 deletions(-) create mode 100644 S7.Net/Helper/MemoryStreamExtension.cs diff --git a/S7.Net/Helper/MemoryStreamExtension.cs b/S7.Net/Helper/MemoryStreamExtension.cs new file mode 100644 index 0000000..dd10fbc --- /dev/null +++ b/S7.Net/Helper/MemoryStreamExtension.cs @@ -0,0 +1,18 @@ + +namespace S7.Net.Helper +{ + internal static class MemoryStreamExtension + { + /// + /// Helper function to write to whole content of the given byte array to a memory stream. + /// + /// Writes all bytes in value from 0 to value.Length to the memory stream. + /// + /// + /// + public static void WriteByteArray(this System.IO.MemoryStream stream, byte[] value) + { + stream.Write(value, 0, value.Length); + } + } +} diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index a153853..4074ba9 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -268,7 +268,7 @@ namespace S7.Net //Snap7 seems to choke on PDU sizes above 256 even if snap7 //replies with bigger PDU size in connection setup. var maxToWrite = (int)Math.Min(count, 200); - await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite), maxToWrite); + await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite); count -= maxToWrite; localIndex += maxToWrite; } @@ -435,13 +435,13 @@ namespace S7.Net /// 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, IEnumerable value, int count) + private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count) { try { var stream = GetStreamIfAvailable(); - var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, count); + var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, dataOffset, count); await stream.WriteAsync(dataToSend, 0, dataToSend.Length); diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 30c4cdc..5106fe9 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -1,9 +1,11 @@ using S7.Net.Types; using System; +using System.IO; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using S7.Net.Protocol; +using S7.Net.Helper; //Implement synchronous methods here namespace S7.Net @@ -232,7 +234,7 @@ namespace S7.Net //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.Skip(localIndex).Take(maxToWrite), maxToWrite); + WriteBytesWithASingleRequest(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite); count -= maxToWrite; localIndex += maxToWrite; } @@ -387,12 +389,12 @@ namespace S7.Net S7WriteMultiple.ParseResponse(response, response.Length, dataItems); } - private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, IEnumerable value, int count) + private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count) { try { var stream = GetStreamIfAvailable(); - var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, count); + var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, dataOffset, count); stream.Write(dataToSend, 0, dataToSend.Length); @@ -408,34 +410,35 @@ namespace S7.Net } } - private byte[] BuildWriteBytesPackage(DataType dataType, int db, int startByteAdr, IEnumerable value, int count) + 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 + count; - ByteArray package = new ByteArray(packageSize); + var package = new MemoryStream(new byte[packageSize]); - package.Add(new byte[] { 3, 0 }); + package.WriteByte(3); + package.WriteByte(0); //complete package size - package.Add(Int.ToByteArray((short)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); + 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.Add((byte)overflow); - package.Add(Word.ToByteArray((ushort)(startByteAdr * 8))); - package.Add(new byte[] { 0, 4 }); - package.Add(Word.ToByteArray((ushort)(varCount * 8))); + 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.Add(value); + package.Write(value, dataOffset, count); - return package.Array; + return package.ToArray(); } private void WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue) From bb0b57c57482d6ae5d2c95f0bad2a6b217f2fe95 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Mon, 17 Aug 2020 21:29:44 +0200 Subject: [PATCH 05/13] Add method BuildWriteBitPackage Merges data creation between sync and async for writing bit values. --- S7.Net/PlcAsynchronous.cs | 32 +++---------------- S7.Net/PlcSynchronous.cs | 67 +++++++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 56 deletions(-) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 4074ba9..61727ae 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -461,41 +461,17 @@ namespace S7.Net { var stream = GetStreamIfAvailable(); - byte[] bReceive = new byte[513]; - 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 Types.ByteArray(packageSize); + var dataToSend = BuildWriteBitPackage(dataType, db, startByteAdr, bitValue, bitAdr); - 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); - - await stream.WriteAsync(package.Array, 0, package.Array.Length); + await stream.WriteAsync(dataToSend, 0, dataToSend.Length); var s7data = await COTP.TSDU.ReadAsync(stream); if (s7data == null || s7data[14] != 0xff) + { throw new PlcException(ErrorCode.WrongNumberReceivedBytes); + } } catch (Exception exc) { diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 5106fe9..367c572 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -414,7 +414,7 @@ namespace S7.Net { int varCount = count; // first create the header - int packageSize = 35 + count; + int packageSize = 35 + varCount; var package = new MemoryStream(new byte[packageSize]); package.WriteByte(3); @@ -441,43 +441,54 @@ namespace S7.Net return package.ToArray(); } + private byte[] BuildWriteBitPackage(DataType dataType, int db, int startByteAdr, bool bitValue, int bitAdr) + { + var stream = GetStreamIfAvailable(); + 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]); + + 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.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))); + + // now join the header and the data + package.WriteByteArray(value); + + return package.ToArray(); + } + + private void WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue) { var stream = GetStreamIfAvailable(); - 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); + var dataToSend = BuildWriteBitPackage(dataType, db, startByteAdr, bitValue, bitAdr); - 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); - - stream.Write(package.Array, 0, package.Array.Length); + stream.Write(dataToSend, 0, dataToSend.Length); var s7data = COTP.TSDU.Read(stream); if (s7data == null || s7data[14] != 0xff) + { throw new PlcException(ErrorCode.WrongNumberReceivedBytes); + } } catch (Exception exc) { From 688d4e2a28fcfd26355c951987472c01024f1ecd Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Mon, 17 Aug 2020 21:46:20 +0200 Subject: [PATCH 06/13] Change implementation header package creation for reading bytes. Use MemoryStream as well. --- S7.Net/PLCHelpers.cs | 43 +++++++++++++++++---------------------- S7.Net/PlcAsynchronous.cs | 20 ++++++++++-------- S7.Net/PlcSynchronous.cs | 18 ++++++++-------- 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index 2286264..9325f8a 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -1,4 +1,5 @@ -using S7.Net.Types; +using S7.Net.Helper; +using S7.Net.Types; using System; using System.Collections.Generic; using System.Linq; @@ -13,21 +14,18 @@ namespace S7.Net /// /// /// - private ByteArray ReadHeaderPackage(int amount = 1) + private void BuildHeaderPackage(System.IO.MemoryStream stream, int amount = 1) { //header size = 19 bytes - var package = new Types.ByteArray(19); - package.Add(new byte[] { 0x03, 0x00 }); + stream.WriteByteArray(new byte[] { 0x03, 0x00 }); //complete package size - package.Add(Types.Int.ToByteArray((short)(19 + (12 * amount)))); - package.Add(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 }); + stream.WriteByteArray(Types.Int.ToByteArray((short)(19 + (12 * amount)))); + stream.WriteByteArray(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 }); //data part size - package.Add(Types.Word.ToByteArray((ushort)(2 + (amount * 12)))); - package.Add(new byte[] { 0x00, 0x00, 0x04 }); + stream.WriteByteArray(Types.Word.ToByteArray((ushort)(2 + (amount * 12)))); + stream.WriteByteArray(new byte[] { 0x00, 0x00, 0x04 }); //amount of requests - package.Add((byte)amount); - - return package; + stream.WriteByte((byte)amount); } /// @@ -39,39 +37,36 @@ namespace S7.Net /// Start address of the byte /// Number of bytes to be read /// - private ByteArray CreateReadDataRequestPackage(DataType dataType, int db, int startByteAdr, int count = 1) + private void BuildReadDataRequestPackage(System.IO.MemoryStream stream, DataType dataType, int db, int startByteAdr, int count = 1) { //single data req = 12 - var package = new Types.ByteArray(12); - package.Add(new byte[] { 0x12, 0x0a, 0x10 }); + stream.WriteByteArray(new byte[] { 0x12, 0x0a, 0x10 }); switch (dataType) { case DataType.Timer: case DataType.Counter: - package.Add((byte)dataType); + stream.WriteByte((byte)dataType); break; default: - package.Add(0x02); + stream.WriteByte(0x02); break; } - package.Add(Word.ToByteArray((ushort)(count))); - package.Add(Word.ToByteArray((ushort)(db))); - package.Add((byte)dataType); + stream.WriteByteArray(Word.ToByteArray((ushort)(count))); + stream.WriteByteArray(Word.ToByteArray((ushort)(db))); + stream.WriteByte((byte)dataType); var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 - package.Add((byte)overflow); + stream.WriteByte((byte)overflow); switch (dataType) { case DataType.Timer: case DataType.Counter: - package.Add(Types.Word.ToByteArray((ushort)(startByteAdr))); + stream.WriteByteArray(Types.Word.ToByteArray((ushort)(startByteAdr))); break; default: - package.Add(Types.Word.ToByteArray((ushort)((startByteAdr) * 8))); + stream.WriteByteArray(Types.Word.ToByteArray((ushort)((startByteAdr) * 8))); break; } - - return package; } /// diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 61727ae..35265c8 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -222,15 +222,16 @@ namespace S7.Net { // first create the header int packageSize = 19 + (dataItems.Count * 12); - ByteArray package = new ByteArray(packageSize); - package.Add(ReadHeaderPackage(dataItems.Count)); + var package = new System.IO.MemoryStream(packageSize); + BuildHeaderPackage(package, dataItems.Count); // package.Add(0x02); // datenart foreach (var dataItem in dataItems) { - package.Add(CreateReadDataRequestPackage(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count))); + BuildReadDataRequestPackage(package, dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count)); } - await stream.WriteAsync(package.Array, 0, package.Array.Length); + var dataToSend = package.ToArray(); + await stream.WriteAsync(dataToSend, 0, dataToSend.Length); var s7data = await COTP.TSDU.ReadAsync(stream); //TODO use Async if (s7data == null || s7data[14] != 0xff) @@ -390,13 +391,14 @@ namespace S7.Net byte[] bytes = new byte[count]; // first create the header - int packageSize = 31; - ByteArray package = new ByteArray(packageSize); - package.Add(ReadHeaderPackage()); + int packageSize = 31; + var package = new System.IO.MemoryStream(packageSize); + BuildHeaderPackage(package); // package.Add(0x02); // datenart - package.Add(CreateReadDataRequestPackage(dataType, db, startByteAdr, count)); + BuildReadDataRequestPackage(package, dataType, db, startByteAdr, count); - await stream.WriteAsync(package.Array, 0, package.Array.Length); + var dataToSend = package.ToArray(); + await stream.WriteAsync(dataToSend, 0, dataToSend.Length); var s7data = await COTP.TSDU.ReadAsync(stream); AssertReadResponse(s7data, count); diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 367c572..f73bd6f 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -349,12 +349,13 @@ namespace S7.Net { // first create the header int packageSize = 31; - ByteArray package = new ByteArray(packageSize); - package.Add(ReadHeaderPackage()); + var package = new System.IO.MemoryStream(packageSize); + BuildHeaderPackage(package); // package.Add(0x02); // datenart - package.Add(CreateReadDataRequestPackage(dataType, db, startByteAdr, count)); + BuildReadDataRequestPackage(package, dataType, db, startByteAdr, count); - stream.Write(package.Array, 0, package.Array.Length); + var dataToSend = package.ToArray(); + stream.Write(dataToSend, 0, dataToSend.Length); var s7data = COTP.TSDU.Read(stream); AssertReadResponse(s7data, count); @@ -516,15 +517,16 @@ namespace S7.Net { // first create the header int packageSize = 19 + (dataItems.Count * 12); - ByteArray package = new ByteArray(packageSize); - package.Add(ReadHeaderPackage(dataItems.Count)); + var package = new System.IO.MemoryStream(packageSize); + BuildHeaderPackage(package, dataItems.Count); // package.Add(0x02); // datenart foreach (var dataItem in dataItems) { - package.Add(CreateReadDataRequestPackage(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count))); + BuildReadDataRequestPackage(package, dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count)); } - stream.Write(package.Array, 0, package.Array.Length); + var dataToSend = package.ToArray(); + stream.Write(dataToSend, 0, dataToSend.Length); var s7data = COTP.TSDU.Read(stream); //TODO use Async if (s7data == null || s7data[14] != 0xff) From 783c456dc922b9d4ff800d0eebea77177075b7fc Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Mon, 17 Aug 2020 22:09:17 +0200 Subject: [PATCH 07/13] Copy ReadBytes data in one go. Instead of using a loop, use Array.Copy to --- S7.Net/PlcAsynchronous.cs | 7 ++----- S7.Net/PlcSynchronous.cs | 6 ++---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 35265c8..334cbbe 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -388,8 +388,6 @@ namespace S7.Net { var stream = GetStreamIfAvailable(); - byte[] bytes = new byte[count]; - // first create the header int packageSize = 31; var package = new System.IO.MemoryStream(packageSize); @@ -403,9 +401,8 @@ namespace S7.Net var s7data = await COTP.TSDU.ReadAsync(stream); AssertReadResponse(s7data, count); - for (int cnt = 0; cnt < count; cnt++) - bytes[cnt] = s7data[cnt + 18]; - + var bytes = new byte[count]; + Array.Copy(s7data, 18, bytes, 0, count); return bytes; } diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index f73bd6f..34fa143 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -344,7 +344,6 @@ namespace S7.Net private byte[] ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, int count) { var stream = GetStreamIfAvailable(); - byte[] bytes = new byte[count]; try { // first create the header @@ -360,9 +359,8 @@ namespace S7.Net var s7data = COTP.TSDU.Read(stream); AssertReadResponse(s7data, count); - for (int cnt = 0; cnt < count; cnt++) - bytes[cnt] = s7data[cnt + 18]; - + var bytes = new byte[count]; + Array.Copy(s7data, 18, bytes, 0, count); return bytes; } catch (Exception exc) From 2bb7ac7d2ac45ed557c5b2d2901cd9e201c3093e Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Mon, 17 Aug 2020 22:30:37 +0200 Subject: [PATCH 08/13] Write large byte array in S7NetTests to check for MaxPDU problems on Snap7. For me the tests work fine even when adjust the "chunk size" in WriteBytesAsync. So Snap7 seems to be fine, at least the current version. This should probably be tested with some live PLC's as well. --- S7.Net.UnitTest/S7NetTestsAsync.cs | 20 ++++++++++++++++++++ S7.Net.UnitTest/S7NetTestsSync.cs | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/S7.Net.UnitTest/S7NetTestsAsync.cs b/S7.Net.UnitTest/S7NetTestsAsync.cs index 38f12af..e03a490 100644 --- a/S7.Net.UnitTest/S7NetTestsAsync.cs +++ b/S7.Net.UnitTest/S7NetTestsAsync.cs @@ -128,6 +128,26 @@ namespace S7.Net.UnitTest Assert.AreEqual(val3, result3); } + /// + /// Write/Read a large amount of data to test PDU max + /// + [TestMethod] + public async Task Test_Async_WriteLargeByteArray() + { + Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor."); + + var randomEngine = new Random(); + var data = new byte[8192]; + var db = 2; + randomEngine.NextBytes(data); + + await plc.WriteBytesAsync(DataType.DataBlock, db, 0, data); + + var readData = await plc.ReadBytesAsync(DataType.DataBlock, db, 0, data.Length); + + CollectionAssert.AreEqual(data, readData); + } + /// /// Read/Write a class that has the same properties of a DB with the same field in the same order /// diff --git a/S7.Net.UnitTest/S7NetTestsSync.cs b/S7.Net.UnitTest/S7NetTestsSync.cs index 3b7589b..e2e563e 100644 --- a/S7.Net.UnitTest/S7NetTestsSync.cs +++ b/S7.Net.UnitTest/S7NetTestsSync.cs @@ -743,6 +743,26 @@ namespace S7.Net.UnitTest Assert.AreEqual(tc.ShortVariable04.ShortVarialbe00, tc2.ShortVariable04.ShortVarialbe00); } + /// + /// Write/Read a large amount of data to test PDU max + /// + [TestMethod] + public void T33_WriteLargeByteArray() + { + Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor."); + + var randomEngine = new Random(); + var data = new byte[8192]; + var db = 2; + randomEngine.NextBytes(data); + + plc.WriteBytes(DataType.DataBlock, db, 0, data); + + var readData = plc.ReadBytes(DataType.DataBlock, db, 0, data.Length); + + CollectionAssert.AreEqual(data, readData); + } + [TestMethod, ExpectedException(typeof(PlcException))] public void T18_ReadStructThrowsIfPlcIsNotConnected() { From a047c5bba4c2b561fdf9839ce893fcfbce95c62e Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Tue, 18 Aug 2020 19:51:55 +0200 Subject: [PATCH 09/13] Fix chunk size for WriteBytesAsync. The previous problem of not being able to use a larger chunk size than 200 was that the complete header building code for the async implementation was incorrect. Specifically, it wrote the package size only as a byte instead of a int16, thus restricting the package size to something < 256. With the sharing of the Header building code in the previous commits, this problem was resolved by accident, and thus the chunk size can be increased to the maximum value allowed by the PDUSize. --- S7.Net/PlcAsynchronous.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 334cbbe..26cf05c 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -265,10 +265,7 @@ namespace S7.Net 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 = (int)Math.Min(count, 200); + var maxToWrite = (int)Math.Min(count, MaxPDUSize - 35); await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite); count -= maxToWrite; localIndex += maxToWrite; From ba3dd084cbe7dc661b652fe987734d5fbc8713a2 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Tue, 18 Aug 2020 20:20:51 +0200 Subject: [PATCH 10/13] TSDU: Return data early without copying when there is only 1 segment. --- S7.Net/COTP.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/S7.Net/COTP.cs b/S7.Net/COTP.cs index c1eb32e..79d60fb 100644 --- a/S7.Net/COTP.cs +++ b/S7.Net/COTP.cs @@ -105,6 +105,12 @@ namespace S7.Net { var segment = TPDU.Read(stream); + if (segment.LastDataUnit) + { + return segment.Data; + } + + // More segments are expected, prepare a buffer to store all data var buffer = new byte[segment.Data.Length]; Array.Copy(segment.Data, buffer, segment.Data.Length); @@ -129,6 +135,12 @@ namespace S7.Net { var segment = await TPDU.ReadAsync(stream); + if (segment.LastDataUnit) + { + return segment.Data; + } + + // More segments are expected, prepare a buffer to store all data var buffer = new byte[segment.Data.Length]; Array.Copy(segment.Data, buffer, segment.Data.Length); From d530f1e4226faa22330c8ff34cec51c5c14e038b Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Tue, 18 Aug 2020 20:38:11 +0200 Subject: [PATCH 11/13] COTP TPDU: change byte array copy. This removes the binary reader, and fixes too things: 1. Properly set the data length (previous implementation requested too much, but that did not matter with BinaryReader) 2. Start reading Data after HeaderLength+1 offset, not always at 3. --- S7.Net/COTP.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/S7.Net/COTP.cs b/S7.Net/COTP.cs index 79d60fb..c4af73a 100644 --- a/S7.Net/COTP.cs +++ b/S7.Net/COTP.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Threading.Tasks; -using System.Linq; namespace S7.Net { @@ -27,17 +26,17 @@ namespace S7.Net { TPkt = tPKT; - var br = new BinaryReader(new MemoryStream(tPKT.Data)); - HeaderLength = br.ReadByte(); + HeaderLength = tPKT.Data[0]; // Header length excluding this length byte if (HeaderLength >= 2) { - PDUType = br.ReadByte(); + PDUType = tPKT.Data[1]; if (PDUType == 0xf0) //DT Data { - var flags = br.ReadByte(); + var flags = tPKT.Data[2]; TPDUNumber = flags & 0x7F; LastDataUnit = (flags & 0x80) > 0; - Data = br.ReadBytes(tPKT.Length - HeaderLength - 4); //4 = TPKT Size + Data = new byte[tPKT.Data.Length - HeaderLength - 1]; // substract header length byte + header length. + Array.Copy(tPKT.Data, HeaderLength + 1, Data, 0, Data.Length); return; } //TODO: Handle other PDUTypes From 592d21c3aaee5bf29965a617d2f602ff3b47e8e8 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Fri, 21 Aug 2020 21:27:56 +0200 Subject: [PATCH 12/13] Add some response length checks in connection Open() I don't know what the correct expected connection response size is, so I just added checks for the minimal index access by the current code. This change will just change NullReferenceExceptions into WrongNumberOfBytesException when the PLC response with not enough data for a connection attempt. --- S7.Net/PlcAsynchronous.cs | 5 +++++ S7.Net/PlcSynchronous.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 26cf05c..868ddb1 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -39,11 +39,16 @@ namespace S7.Net var s7data = await COTP.TSDU.ReadAsync(stream); if (s7data == null) throw new WrongNumberOfBytesException("No data received in response to Communication Setup"); + if (s7data.Length < 2) + throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup"); //Check for S7 Ack Data if (s7data[1] != 0x03) throw new InvalidDataException("Error reading Communication Setup response", s7data, 1, 0x03); + if (s7data.Length < 20) + throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup"); + MaxPDUSize = (short)(s7data[18] * 256 + s7data[19]); } diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 34fa143..7cb37f4 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -34,11 +34,16 @@ namespace S7.Net var s7data = COTP.TSDU.Read(stream); if (s7data == null) throw new WrongNumberOfBytesException("No data received in response to Communication Setup"); + if (s7data.Length < 2) + throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup"); //Check for S7 Ack Data if (s7data[1] != 0x03) throw new InvalidDataException("Error reading Communication Setup response", s7data, 1, 0x03); + if (s7data.Length < 20) + throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup"); + MaxPDUSize = (short)(s7data[18] * 256 + s7data[19]); } catch (Exception exc) From 09851ec30b12dfdfc04b827582ff4158333b0849 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Fri, 21 Aug 2020 22:38:12 +0200 Subject: [PATCH 13/13] Optimize ReadBytes by copying less data around. --- S7.Net/PlcAsynchronous.cs | 13 ++++--------- S7.Net/PlcSynchronous.cs | 21 ++++++++------------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 868ddb1..6a63ddd 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -77,11 +77,8 @@ namespace S7.Net while (count > 0) { //This works up to MaxPDUSize-1 on SNAP7. But not MaxPDUSize-0. - var maxToRead = (int)Math.Min(count, MaxPDUSize - 18); - byte[] bytes = await ReadBytesWithSingleRequestAsync(dataType, db, index + startByteAdr, maxToRead); - if (bytes == null) - return resultBytes; - Array.Copy(bytes, 0, resultBytes, index, maxToRead); + var maxToRead = Math.Min(count, MaxPDUSize - 18); + await ReadBytesWithSingleRequestAsync(dataType, db, startByteAdr + index, resultBytes, index, maxToRead); count -= maxToRead; index += maxToRead; } @@ -386,7 +383,7 @@ namespace S7.Net await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes); } - private async Task ReadBytesWithSingleRequestAsync(DataType dataType, int db, int startByteAdr, int count) + private async Task ReadBytesWithSingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] buffer, int offset, int count) { var stream = GetStreamIfAvailable(); @@ -403,9 +400,7 @@ namespace S7.Net var s7data = await COTP.TSDU.ReadAsync(stream); AssertReadResponse(s7data, count); - var bytes = new byte[count]; - Array.Copy(s7data, 18, bytes, 0, count); - return bytes; + Array.Copy(s7data, 18, buffer, offset, count); } /// diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 7cb37f4..9ae2cc0 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -87,20 +87,17 @@ namespace S7.Net /// Returns the bytes in an array public byte[] ReadBytes(DataType dataType, int db, int startByteAdr, int count) { - List resultBytes = new List(); - int index = startByteAdr; + 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 = (int)Math.Min(count, MaxPDUSize - 18); - byte[] bytes = ReadBytesWithSingleRequest(dataType, db, index, maxToRead); - if (bytes == null) - return resultBytes.ToArray(); - resultBytes.AddRange(bytes); + var maxToRead = Math.Min(count, MaxPDUSize - 18); + ReadBytesWithSingleRequest(dataType, db, startByteAdr + index, result, index, maxToRead); count -= maxToRead; index += maxToRead; } - return resultBytes.ToArray(); + return result; } /// @@ -346,13 +343,13 @@ namespace S7.Net WriteClassAsync(classValue, db, startByteAdr).GetAwaiter().GetResult(); } - private byte[] ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, int count) + private void ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, byte[] buffer, int offset, int count) { var stream = GetStreamIfAvailable(); try { // first create the header - int packageSize = 31; + int packageSize = 19 + 12; // 19 header + 12 for 1 request var package = new System.IO.MemoryStream(packageSize); BuildHeaderPackage(package); // package.Add(0x02); // datenart @@ -364,9 +361,7 @@ namespace S7.Net var s7data = COTP.TSDU.Read(stream); AssertReadResponse(s7data, count); - var bytes = new byte[count]; - Array.Copy(s7data, 18, bytes, 0, count); - return bytes; + Array.Copy(s7data, 18, buffer, offset, count); } catch (Exception exc) {