From 9c8b453326a14fe61e7f8bc34183d09db81f13f4 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Wed, 19 Jul 2023 23:18:26 +0200 Subject: [PATCH 01/44] refactor: Extract WriteTpktHeader --- S7.Net/PLCHelpers.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index c0fbd78..6e6f306 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -9,6 +9,12 @@ namespace S7.Net { public partial class Plc { + private static void WriteTpktHeader(System.IO.MemoryStream stream, int length) + { + stream.Write(new byte[] { 0x03, 0x00 }); + stream.Write(Int.ToByteArray((short)length)); + } + /// /// Creates the header to read bytes from the PLC /// @@ -16,10 +22,9 @@ namespace S7.Net /// private static void BuildHeaderPackage(System.IO.MemoryStream stream, int amount = 1) { - //header size = 19 bytes - stream.Write(new byte[] { 0x03, 0x00 }); - //complete package size - stream.Write(Int.ToByteArray((short)(19 + (12 * amount)))); + // Header size 19, 12 bytes per item + WriteTpktHeader(stream, 19 + (12 * amount)); + stream.Write(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 }); //data part size stream.Write(Word.ToByteArray((ushort)(2 + (amount * 12)))); From 42194aa7882883cbd1ea0e96b72c1e4a96baef23 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Wed, 19 Jul 2023 23:19:32 +0200 Subject: [PATCH 02/44] refactor: Extract WriteDataHeader --- S7.Net/PLCHelpers.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index 6e6f306..97623cc 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -15,6 +15,11 @@ namespace S7.Net stream.Write(Int.ToByteArray((short)length)); } + private static void WriteDataHeader(System.IO.MemoryStream stream) + { + stream.Write(new byte[] { 0x02, 0xf0, 0x80 }); + } + /// /// Creates the header to read bytes from the PLC /// @@ -24,8 +29,8 @@ namespace S7.Net { // Header size 19, 12 bytes per item WriteTpktHeader(stream, 19 + (12 * amount)); - - stream.Write(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 }); + WriteDateHeader(stream); + stream.Write(new byte[] { 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 }); //data part size stream.Write(Word.ToByteArray((ushort)(2 + (amount * 12)))); stream.Write(new byte[] { 0x00, 0x00, 0x04 }); From ebf3da6280f1f60a1990bbc534b8bd945798f028 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Wed, 19 Jul 2023 23:29:11 +0200 Subject: [PATCH 03/44] refactor: Extract WriteS7Header --- S7.Net/PLCHelpers.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index 97623cc..ce5f6d2 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -20,6 +20,16 @@ namespace S7.Net stream.Write(new byte[] { 0x02, 0xf0, 0x80 }); } + private static void WriteS7Header(System.IO.MemoryStream stream, byte messageType, int parameterLength, int dataLength) + { + stream.Write(0x32); // S7 protocol ID + stream.Write(messageType); // Message type + stream.Write(new byte[] { 0x00, 0x00 }); // Reserved + stream.Write(new byte[] { 0x00, 0x00 }); // PDU ref + stream.Write(Word.ToByteArray((ushort) parameterLength)); + stream.Write(Word.ToByteArray((ushort) dataLength)); + } + /// /// Creates the header to read bytes from the PLC /// @@ -30,10 +40,9 @@ namespace S7.Net // Header size 19, 12 bytes per item WriteTpktHeader(stream, 19 + (12 * amount)); WriteDateHeader(stream); - stream.Write(new byte[] { 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 }); - //data part size - stream.Write(Word.ToByteArray((ushort)(2 + (amount * 12)))); - stream.Write(new byte[] { 0x00, 0x00, 0x04 }); + WriteS7Header(stream, 0x01, 2 + amount * 12, 0); + // Function code: read request + stream.Write(0x04); //amount of requests stream.WriteByte((byte)amount); } From 296ead69c7899770e16bb2024576a40444988615 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Wed, 19 Jul 2023 23:30:02 +0200 Subject: [PATCH 04/44] refactor: Use Word.ToByteArray in WriteTpktHeader --- S7.Net/PLCHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index ce5f6d2..6b8e0ee 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -12,7 +12,7 @@ namespace S7.Net private static void WriteTpktHeader(System.IO.MemoryStream stream, int length) { stream.Write(new byte[] { 0x03, 0x00 }); - stream.Write(Int.ToByteArray((short)length)); + stream.Write(Word.ToByteArray((ushort) length)); } private static void WriteDataHeader(System.IO.MemoryStream stream) From 8becc562a8b17d8acc703efecf09bd27d0ce65c1 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Wed, 19 Jul 2023 23:32:00 +0200 Subject: [PATCH 05/44] refactor: Cleanup inline math in BuildHeaderPackage - Remove unnecessary parentheses - Use constant value first in multiplication --- S7.Net/PLCHelpers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index 6b8e0ee..15c25d9 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -38,9 +38,9 @@ namespace S7.Net private static void BuildHeaderPackage(System.IO.MemoryStream stream, int amount = 1) { // Header size 19, 12 bytes per item - WriteTpktHeader(stream, 19 + (12 * amount)); + WriteTpktHeader(stream, 19 + 12 * amount); WriteDateHeader(stream); - WriteS7Header(stream, 0x01, 2 + amount * 12, 0); + WriteS7Header(stream, 0x01, 2 + 12 * amount, 0); // Function code: read request stream.Write(0x04); //amount of requests From cf94f8ad112737f8218055aec5bc6d0c6d16fc6c Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Thu, 20 Jul 2023 21:25:24 +0200 Subject: [PATCH 06/44] fix(PLCHelpers): Fix errors from refactors --- S7.Net/PLCHelpers.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index 15c25d9..1f73503 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -22,8 +22,8 @@ namespace S7.Net private static void WriteS7Header(System.IO.MemoryStream stream, byte messageType, int parameterLength, int dataLength) { - stream.Write(0x32); // S7 protocol ID - stream.Write(messageType); // Message type + stream.WriteByte(0x32); // S7 protocol ID + stream.WriteByte(messageType); // Message type stream.Write(new byte[] { 0x00, 0x00 }); // Reserved stream.Write(new byte[] { 0x00, 0x00 }); // PDU ref stream.Write(Word.ToByteArray((ushort) parameterLength)); @@ -39,10 +39,10 @@ namespace S7.Net { // Header size 19, 12 bytes per item WriteTpktHeader(stream, 19 + 12 * amount); - WriteDateHeader(stream); + WriteDataHeader(stream); WriteS7Header(stream, 0x01, 2 + 12 * amount, 0); // Function code: read request - stream.Write(0x04); + stream.WriteByte(0x04); //amount of requests stream.WriteByte((byte)amount); } From 38b26e0ce1e1cfebb73970fae3081593ec757ee1 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Thu, 20 Jul 2023 21:39:58 +0200 Subject: [PATCH 07/44] fix: Update test project target frameworks Ensures tests are actually run on GitHub --- S7.Net.UnitTest/S7.Net.UnitTest.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S7.Net.UnitTest/S7.Net.UnitTest.csproj b/S7.Net.UnitTest/S7.Net.UnitTest.csproj index eb63e48..2f93284 100644 --- a/S7.Net.UnitTest/S7.Net.UnitTest.csproj +++ b/S7.Net.UnitTest/S7.Net.UnitTest.csproj @@ -1,7 +1,7 @@  - net452;netcoreapp3.1;net5.0 + net462;net6.0 true Properties\S7.Net.snk From 7d212134e3b422173fa0d861ce76d5ae38b4148b Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 21 Jul 2023 21:23:00 +0200 Subject: [PATCH 08/44] refactor: Rename BuildHeaderPackage to WriteReadHeader --- S7.Net/PLCHelpers.cs | 4 ++-- S7.Net/PlcSynchronous.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index 1f73503..110c71b 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -35,7 +35,7 @@ namespace S7.Net /// /// /// - private static void BuildHeaderPackage(System.IO.MemoryStream stream, int amount = 1) + private static void WriteReadHeader(System.IO.MemoryStream stream, int amount = 1) { // Header size 19, 12 bytes per item WriteTpktHeader(stream, 19 + 12 * amount); @@ -271,7 +271,7 @@ namespace S7.Net int packageSize = 19 + (dataItems.Count * 12); var package = new System.IO.MemoryStream(packageSize); - BuildHeaderPackage(package, dataItems.Count); + WriteReadHeader(package, dataItems.Count); foreach (var dataItem in dataItems) { diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 4a3aaad..7915e58 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -329,7 +329,7 @@ namespace S7.Net const int packageSize = 19 + 12; // 19 header + 12 for 1 request var dataToSend = new byte[packageSize]; var package = new MemoryStream(dataToSend); - BuildHeaderPackage(package); + WriteReadHeader(package); // package.Add(0x02); // datenart BuildReadDataRequestPackage(package, dataType, db, startByteAdr, buffer.Length); @@ -474,7 +474,7 @@ namespace S7.Net int packageSize = 19 + (dataItems.Count * 12); var dataToSend = new byte[packageSize]; var package = new MemoryStream(dataToSend); - BuildHeaderPackage(package, dataItems.Count); + WriteReadHeader(package, dataItems.Count); // package.Add(0x02); // datenart foreach (var dataItem in dataItems) { From 1f26833244c30c30298007bbe1a0da9a9576eaad Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 21 Jul 2023 21:26:13 +0200 Subject: [PATCH 09/44] fix: Add missing xmldoc nodes in PLCHelpers --- S7.Net/PLCHelpers.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index 110c71b..d3c66d8 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -31,10 +31,10 @@ namespace S7.Net } /// - /// Creates the header to read bytes from the PLC + /// Creates the header to read bytes from the PLC. /// - /// - /// + /// The to write to. + /// The amount of items to read. private static void WriteReadHeader(System.IO.MemoryStream stream, int amount = 1) { // Header size 19, 12 bytes per item @@ -51,6 +51,7 @@ namespace S7.Net /// Create the bytes-package to request data from the PLC. You have to specify the memory type (dataType), /// the address of the memory, the address of the byte and the bytes count. /// + /// The to write to. /// MemoryType (DB, Timer, Counter, etc.) /// Address of the memory to be read /// Start address of the byte From 18c3883dc0e37b7a1d2ddcd5ee505fde7d4f5587 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 21 Jul 2023 22:27:30 +0200 Subject: [PATCH 10/44] feat: Add WriteUserDataHeader --- S7.Net/PLCHelpers.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index d3c66d8..fc24a3a 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -47,6 +47,15 @@ namespace S7.Net stream.WriteByte((byte)amount); } + private static void WriteUserDataHeader(System.IO.MemoryStream stream, int parameterLength, int dataLength) + { + const byte s7MessageTypeUserData = 0x07; + + WriteTpktHeader(stream, 17 + parameterLength + dataLength); + WriteDataHeader(stream); + WriteS7Header(stream, s7MessageTypeUserData, parameterLength, dataLength); + } + /// /// Create the bytes-package to request data from the PLC. You have to specify the memory type (dataType), /// the address of the memory, the address of the byte and the bytes count. From 1fc6899905a85f9412b9f1921b18ae8410bc4a8a Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 21 Jul 2023 22:28:22 +0200 Subject: [PATCH 11/44] feat: Add WriteSzlReadRequest --- S7.Net/PLCHelpers.cs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index fc24a3a..bda6092 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -56,6 +56,45 @@ namespace S7.Net WriteS7Header(stream, s7MessageTypeUserData, parameterLength, dataLength); } + private static void WriteSzlReadRequest(System.IO.MemoryStream stream, ushort szlId, ushort szlIndex) + { + WriteUserDataHeader(stream, 8, 8); + + // Parameter + const byte szlMethodRequest = 0x11; + const byte szlTypeRequest = 0b100; + const byte szlFunctionGroupCpuFunctions = 0b100; + const byte subFunctionReadSzl = 0x01; + + // Parameter head + stream.Write(new byte[] { 0x00, 0x01, 0x12 }); + // Parameter length + stream.WriteByte(0x04); + // Method + stream.WriteByte(szlMethodRequest); + // Type / function group + stream.WriteByte(szlTypeRequest << 4 | szlFunctionGroupCpuFunctions); + // Subfunction + stream.WriteByte(subFunctionReadSzl); + // Sequence number + stream.WriteByte(0); + + // Data + const byte success = 0xff; + const byte transportSizeOctetString = 0x09; + + // Return code + stream.WriteByte(success); + // Transport size + stream.WriteByte(transportSizeOctetString); + // Length + stream.Write(Word.ToByteArray(4)); + // SZL-ID + stream.Write(Word.ToByteArray(szlId)); + // SZL-Index + stream.Write(Word.ToByteArray(szlIndex)); + } + /// /// Create the bytes-package to request data from the PLC. You have to specify the memory type (dataType), /// the address of the memory, the address of the byte and the bytes count. From 0d9ccea11b7d78e5f10b0cf4f6af3aa13a157982 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Sat, 22 Jul 2023 22:53:45 +0200 Subject: [PATCH 12/44] feat: Add Plc.ReadStatusAsync --- S7.Net/PLCHelpers.cs | 10 ++++++++++ S7.Net/PlcAsynchronous.cs | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index bda6092..89156cc 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -329,5 +329,15 @@ namespace S7.Net return package.ToArray(); } + + private static byte[] BuildSzlReadRequestPackage(ushort szlId, ushort szlIndex) + { + var stream = new System.IO.MemoryStream(); + + WriteSzlReadRequest(stream, szlId, szlIndex); + stream.SetLength(stream.Position); + + return stream.ToArray(); + } } } diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 36fef8c..b29e738 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -312,6 +312,17 @@ namespace S7.Net return dataItems; } + /// + /// Read the current status from the PLC. A value of 0x08 indicates the PLC is in run status, regardless of the PLC type. + /// + /// A task that represents the asynchronous operation, with it's result set to the current PLC status on completion. + public async Task ReadStatusAsync(CancellationToken cancellationToken) { + var dataToSend = BuildSzlReadRequestPackage(0x0424, 0); + var s7data = await RequestTsduAsync(dataToSend, cancellationToken); + + return (byte) (s7data[94] & 0x0f); + } + /// /// 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. From 3185d1fccfffab010990d57f3a3f0c0fa14a297a Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Sun, 23 Jul 2023 22:48:52 +0200 Subject: [PATCH 13/44] fix: Revert Ubuntu target back to 20.04 for snap7 ppa availability --- .github/workflows/dotnet.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 16e7a33..15b16f7 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -71,14 +71,14 @@ jobs: artifacts: ${{ github.workspace }}/artifacts strategy: matrix: - os: [windows-latest, ubuntu-22.04, macos-latest] + os: [windows-latest, ubuntu-20.04, macos-latest] test-framework: [net6.0, net7.0] include: - - os: ubuntu-22.04 + - os: ubuntu-20.04 test-framework: net6.0 installSnap7: true dotnet-sdk: '6.x' - - os: ubuntu-22.04 + - os: ubuntu-20.04 test-framework: net7.0 installSnap7: true dotnet-sdk: '7.x' From 7558b9a691aff643d6f68532457da9ab30709f8b Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Sun, 23 Jul 2023 22:53:11 +0200 Subject: [PATCH 14/44] fix: Retarget test project to net462, net6.0 and net7.0 These are the frameworks currently used in the GitHub workflow, when missing the tests aren't executed and the job will succeed nonetheless. --- S7.Net.UnitTest/S7.Net.UnitTest.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S7.Net.UnitTest/S7.Net.UnitTest.csproj b/S7.Net.UnitTest/S7.Net.UnitTest.csproj index eb63e48..3a67af5 100644 --- a/S7.Net.UnitTest/S7.Net.UnitTest.csproj +++ b/S7.Net.UnitTest/S7.Net.UnitTest.csproj @@ -1,7 +1,7 @@  - net452;netcoreapp3.1;net5.0 + net462;net6.0;net7.0 true Properties\S7.Net.snk From 53f651a482ce80fa84af3c692ec5d80d6162658e Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Sun, 23 Jul 2023 22:58:37 +0200 Subject: [PATCH 15/44] fix: Constrain dotnet restore to matrix runtime --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 15b16f7..64d1ba5 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -131,7 +131,7 @@ jobs: ${{ runner.os }}-${{ matrix.test-framework }}-nuget - name: Restore - run: dotnet restore S7.Net.UnitTest + run: dotnet restore --runtime ${{ matrix.test-framework }} S7.Net.UnitTest - name: Test run: dotnet test --no-restore --nologo --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }} From 18402604d109b82a78ba1bcf9742b39944b24f2e Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Sun, 23 Jul 2023 23:04:03 +0200 Subject: [PATCH 16/44] feat: Add net462, net6.0 and net7.0 targeting to S7NetPlus This should be the actual baseline, which is also what the test project targets now. --- S7.Net/S7.Net.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/S7.Net/S7.Net.csproj b/S7.Net/S7.Net.csproj index d41f08f..bfe62f9 100644 --- a/S7.Net/S7.Net.csproj +++ b/S7.Net/S7.Net.csproj @@ -1,7 +1,7 @@  - net452;netstandard2.0;netstandard1.3;net5.0 + net452;net462;netstandard2.0;netstandard1.3;net5.0;net6.0;net7.0 true Properties\S7.Net.snk S7.Net.UnitTest @@ -23,11 +23,11 @@ true - + NET_FULL - + From 12ea40276980bf02f586bd30bbf20662de197ea6 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Sun, 23 Jul 2023 23:10:21 +0200 Subject: [PATCH 17/44] fix: Remove separate restore step --- .github/workflows/dotnet.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 64d1ba5..d974cfc 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -129,12 +129,9 @@ jobs: key: ${{ runner.os }}-${{ matrix.test-framework }}-nuget-${{ hashFiles('**/packages.lock.json') }} restore-keys: | ${{ runner.os }}-${{ matrix.test-framework }}-nuget - - - name: Restore - run: dotnet restore --runtime ${{ matrix.test-framework }} S7.Net.UnitTest - name: Test - run: dotnet test --no-restore --nologo --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }} + run: dotnet test --nologo --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }} deploy: # Publish only when creating a GitHub Release From aa151451849ea8abe45edc75b5348510e8df2516 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Sun, 23 Jul 2023 23:15:30 +0200 Subject: [PATCH 18/44] fix: Install dotnet 7.x always --- .github/workflows/dotnet.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index d974cfc..433f5d2 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -119,7 +119,9 @@ jobs: - name: Setup Dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: ${{ matrix.dotnet-sdk }} + dotnet-version: | + ${{ matrix.dotnet-sdk }} + 7.x - name: Nuget Cache uses: actions/cache@v2 From 8da292ad2f7157fde4d35e7f55882c2216273298 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Mon, 24 Jul 2023 21:32:49 +0200 Subject: [PATCH 19/44] ci: Run tests against all target frameworks on all OS-es --- .github/workflows/dotnet.yml | 39 ++++++------------------------------ 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 433f5d2..72150f7 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -72,47 +72,20 @@ jobs: strategy: matrix: os: [windows-latest, ubuntu-20.04, macos-latest] - test-framework: [net6.0, net7.0] - include: - - os: ubuntu-20.04 - test-framework: net6.0 - installSnap7: true - dotnet-sdk: '6.x' - - os: ubuntu-20.04 - test-framework: net7.0 - installSnap7: true - dotnet-sdk: '7.x' - - os: macos-latest - test-framework: net6.0 - installSnap7: true - dotnet-sdk: '6.x' - - os: macos-latest - test-framework: net7.0 - installSnap7: true - dotnet-sdk: '7.x' - - os: windows-latest - test-framework: net6.0 - dotnet-sdk: '6.x' - - os: windows-latest - test-framework: net7.0 - dotnet-sdk: '7.x' - - os: windows-latest - test-framework: net462 - dotnet-sdk: '7.x' fail-fast: false steps: - uses: actions/checkout@v2 - name: Install Snap7 Linux - if: ${{ matrix.installSnap7 && matrix.os == 'ubuntu-20.04' }} + if: ${{ matrix.os == 'ubuntu-20.04' }} run: | sudo add-apt-repository ppa:gijzelaar/snap7 sudo apt-get update sudo apt-get install libsnap7-1 libsnap7-dev - name: Install Snap7 MacOs - if: ${{ matrix.installSnap7 && matrix.os == 'macos-latest' }} + if: ${{ matrix.os == 'macos-latest' }} run: | brew install snap7 @@ -120,7 +93,7 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: | - ${{ matrix.dotnet-sdk }} + 6.x 7.x - name: Nuget Cache @@ -128,12 +101,12 @@ jobs: with: path: ~/.nuget/packages # Look to see if there is a cache hit for the corresponding requirements file - key: ${{ runner.os }}-${{ matrix.test-framework }}-nuget-${{ hashFiles('**/packages.lock.json') }} + key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} restore-keys: | - ${{ runner.os }}-${{ matrix.test-framework }}-nuget + ${{ runner.os }}-nuget - name: Test - run: dotnet test --nologo --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }} + run: dotnet test --nologo --verbosity normal --logger GitHubActions deploy: # Publish only when creating a GitHub Release From 534d9fd69dedb6db1a61f0ce13b94ab8e7f98f26 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Mon, 24 Jul 2023 21:41:32 +0200 Subject: [PATCH 20/44] fix: Remove leftover test-framework in runner name --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 72150f7..bc7b024 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -64,7 +64,7 @@ jobs: ${{ env.NuGetDirectory }}/*.snupkg run_test: - name: test-${{ matrix.os }}-${{ matrix.test-framework }} + name: test-${{ matrix.os }} runs-on: ${{ matrix.os }} env: configuration: Release From 4e4071f07f7aaf5890597f22a9e1a53248fe086e Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Mon, 24 Jul 2023 21:53:37 +0200 Subject: [PATCH 21/44] test: Only target net462 on Windows --- S7.Net.UnitTest/S7.Net.UnitTest.csproj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/S7.Net.UnitTest/S7.Net.UnitTest.csproj b/S7.Net.UnitTest/S7.Net.UnitTest.csproj index 3a67af5..f6b43ec 100644 --- a/S7.Net.UnitTest/S7.Net.UnitTest.csproj +++ b/S7.Net.UnitTest/S7.Net.UnitTest.csproj @@ -1,8 +1,13 @@  - + + net6.0;net7.0 + + net462;net6.0;net7.0 + + true Properties\S7.Net.snk false From 8126018afdae67d590e80c431dfd7b7a17386cd5 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Tue, 25 Jul 2023 23:17:25 +0200 Subject: [PATCH 22/44] test: Fix target framework*s* specification --- S7.Net.UnitTest/S7.Net.UnitTest.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S7.Net.UnitTest/S7.Net.UnitTest.csproj b/S7.Net.UnitTest/S7.Net.UnitTest.csproj index f6b43ec..2b29986 100644 --- a/S7.Net.UnitTest/S7.Net.UnitTest.csproj +++ b/S7.Net.UnitTest/S7.Net.UnitTest.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0 + net6.0;net7.0 net462;net6.0;net7.0 From 12e180ea2db8f60367b75b4eaf5aeaf277e2fef9 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Wed, 26 Jul 2023 23:14:38 +0200 Subject: [PATCH 23/44] build: Don't warn on missing xmldoc While definitely desirable, at least temporarily disabled in order to find other warnings. --- S7.Net/S7.Net.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/S7.Net/S7.Net.csproj b/S7.Net/S7.Net.csproj index bfe62f9..8faec79 100644 --- a/S7.Net/S7.Net.csproj +++ b/S7.Net/S7.Net.csproj @@ -21,6 +21,7 @@ true snupkg true + CS1591 From 8ad25033d5aa7145275491f14f10070cdfcc802c Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Wed, 26 Jul 2023 23:38:32 +0200 Subject: [PATCH 24/44] chore: Fix xmldoc warnings --- S7.Net/COTP.cs | 2 ++ S7.Net/PLCHelpers.cs | 5 +++-- S7.Net/PlcAsynchronous.cs | 2 +- S7.Net/PlcSynchronous.cs | 1 - S7.Net/StreamExtensions.cs | 1 + S7.Net/TPKT.cs | 1 + S7.Net/Types/Class.cs | 8 +++++++- S7.Net/Types/DateTime.cs | 2 +- 8 files changed, 16 insertions(+), 6 deletions(-) diff --git a/S7.Net/COTP.cs b/S7.Net/COTP.cs index 3e5abbb..4f8b1cd 100644 --- a/S7.Net/COTP.cs +++ b/S7.Net/COTP.cs @@ -55,6 +55,7 @@ namespace S7.Net /// See: https://tools.ietf.org/html/rfc905 /// /// The socket to read from + /// A cancellation token that can be used to cancel the asynchronous operation. /// COTP DPDU instance public static async Task ReadAsync(Stream stream, CancellationToken cancellationToken) { @@ -89,6 +90,7 @@ namespace S7.Net /// See: https://tools.ietf.org/html/rfc905 /// /// The stream to read from + /// A cancellation token that can be used to cancel the asynchronous operation. /// Data in TSDU public static async Task ReadAsync(Stream stream, CancellationToken cancellationToken) { diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index c0fbd78..ef26e87 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -12,8 +12,8 @@ namespace S7.Net /// /// Creates the header to read bytes from the PLC /// - /// - /// + /// The stream to write to. + /// The number of items to read. private static void BuildHeaderPackage(System.IO.MemoryStream stream, int amount = 1) { //header size = 19 bytes @@ -32,6 +32,7 @@ namespace S7.Net /// Create the bytes-package to request data from the PLC. You have to specify the memory type (dataType), /// the address of the memory, the address of the byte and the bytes count. /// + /// The stream to write the read data request to. /// MemoryType (DB, Timer, Counter, etc.) /// Address of the memory to be read /// Start address of the byte diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 36fef8c..77cd0c8 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -428,7 +428,6 @@ namespace S7.Net /// /// Writes a single variable from the PLC, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. - /// If the write was not successful, check or . /// /// Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. /// Value to be written to the PLC @@ -507,6 +506,7 @@ namespace S7.Net /// 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 cancellation token that can be used to cancel the asynchronous operation. /// A task that represents the asynchronous write operation. private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, ReadOnlyMemory value, CancellationToken cancellationToken) { diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 4a3aaad..afd122a 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -289,7 +289,6 @@ namespace S7.Net /// /// Writes a single variable from the PLC, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. - /// If the write was not successful, check or . /// /// Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. /// Value to be written to the PLC diff --git a/S7.Net/StreamExtensions.cs b/S7.Net/StreamExtensions.cs index 749b915..504e4dc 100644 --- a/S7.Net/StreamExtensions.cs +++ b/S7.Net/StreamExtensions.cs @@ -39,6 +39,7 @@ namespace S7.Net /// the buffer to read into /// the offset in the buffer to read into /// the amount of bytes to read into the buffer + /// A cancellation token that can be used to cancel the asynchronous operation. /// returns the amount of read bytes public static async Task ReadExactAsync(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellationToken) { diff --git a/S7.Net/TPKT.cs b/S7.Net/TPKT.cs index a311dce..f24b4c0 100644 --- a/S7.Net/TPKT.cs +++ b/S7.Net/TPKT.cs @@ -29,6 +29,7 @@ namespace S7.Net /// Reads a TPKT from the socket Async /// /// The stream to read from + /// A cancellation token that can be used to cancel the asynchronous operation. /// Task TPKT Instace public static async Task ReadAsync(Stream stream, CancellationToken cancellationToken) { diff --git a/S7.Net/Types/Class.cs b/S7.Net/Types/Class.cs index 819b626..fa5eb06 100644 --- a/S7.Net/Types/Class.cs +++ b/S7.Net/Types/Class.cs @@ -76,6 +76,8 @@ namespace S7.Net.Types /// Gets the size of the class in bytes. /// /// An instance of the class + /// The offset of the current field. + /// if this property belongs to a class being serialized as member of the class requested for serialization; otherwise, . /// the number of bytes public static double GetClassSize(object instance, double numBytes = 0.0, bool isInnerProperty = false) { @@ -213,6 +215,8 @@ namespace S7.Net.Types /// /// The object to fill in the given array of bytes /// The array of bytes + /// The offset for the current field. + /// if this class is the type of a member of the class to be serialized; otherwise, . public static double FromBytes(object sourceClass, byte[] bytes, double numBytes = 0, bool isInnerClass = false) { if (bytes == null) @@ -320,7 +324,9 @@ namespace S7.Net.Types /// /// Creates a byte array depending on the struct type. /// - /// The struct object + /// The struct object. + /// The target byte array. + /// The offset for the current field. /// A byte array or null if fails. public static double ToBytes(object sourceClass, byte[] bytes, double numBytes = 0.0) { diff --git a/S7.Net/Types/DateTime.cs b/S7.Net/Types/DateTime.cs index 9cafa67..a685a21 100644 --- a/S7.Net/Types/DateTime.cs +++ b/S7.Net/Types/DateTime.cs @@ -141,7 +141,7 @@ namespace S7.Net.Types /// Converts an array of values to a byte array. /// /// The DateTime values to convert. - /// A byte array containing the S7 date time representations of . + /// A byte array containing the S7 date time representations of . /// Thrown when any value of /// is before /// or after . From 3d0dd693ba666f0a9520a4df485be505917b2d8b Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Wed, 26 Jul 2023 23:47:32 +0200 Subject: [PATCH 25/44] fix: Fix nullability warnings in Class.ToBytes --- S7.Net/Types/Class.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/S7.Net/Types/Class.cs b/S7.Net/Types/Class.cs index fa5eb06..0b2b34b 100644 --- a/S7.Net/Types/Class.cs +++ b/S7.Net/Types/Class.cs @@ -333,19 +333,21 @@ namespace S7.Net.Types var properties = GetAccessableProperties(sourceClass.GetType()); foreach (var property in properties) { + var value = property.GetValue(sourceClass, null) ?? + throw new ArgumentException($"Property {property.Name} on sourceClass can't be null.", nameof(sourceClass)); + if (property.PropertyType.IsArray) { - Array array = (Array)property.GetValue(sourceClass, null); + Array array = (Array) value; IncrementToEven(ref numBytes); - Type elementType = property.PropertyType.GetElementType(); for (int i = 0; i < array.Length && numBytes < bytes.Length; i++) { - numBytes = SetBytesFromProperty(array.GetValue(i), property, bytes, numBytes); + numBytes = SetBytesFromProperty(array.GetValue(i)!, property, bytes, numBytes); } } else { - numBytes = SetBytesFromProperty(property.GetValue(sourceClass, null), property, bytes, numBytes); + numBytes = SetBytesFromProperty(value, property, bytes, numBytes); } } return numBytes; From c3f86c32a2a2ac1d55583e42de7975149354403c Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Wed, 26 Jul 2023 23:52:28 +0200 Subject: [PATCH 26/44] fix: Fix nullability warnings in Class.FromBytes --- S7.Net/Types/Class.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/S7.Net/Types/Class.cs b/S7.Net/Types/Class.cs index 0b2b34b..c333d83 100644 --- a/S7.Net/Types/Class.cs +++ b/S7.Net/Types/Class.cs @@ -227,9 +227,11 @@ namespace S7.Net.Types { if (property.PropertyType.IsArray) { - Array array = (Array)property.GetValue(sourceClass, null); + Array array = (Array?) property.GetValue(sourceClass, null) ?? + throw new ArgumentException($"Property {property.Name} on sourceClass must be an array instance.", nameof(sourceClass)); + IncrementToEven(ref numBytes); - Type elementType = property.PropertyType.GetElementType(); + Type elementType = property.PropertyType.GetElementType()!; for (int i = 0; i < array.Length && numBytes < bytes.Length; i++) { array.SetValue( From 0bb7c5351a2384cda33a023d93213fef6aa56347 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Wed, 26 Jul 2023 23:59:21 +0200 Subject: [PATCH 27/44] ci: Update actions to Node 16 compatible versions --- .github/workflows/dotnet.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index bc7b024..8116e8b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -75,7 +75,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Snap7 Linux if: ${{ matrix.os == 'ubuntu-20.04' }} @@ -90,14 +90,14 @@ jobs: brew install snap7 - name: Setup Dotnet - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: | 6.x 7.x - name: Nuget Cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.nuget/packages # Look to see if there is a cache hit for the corresponding requirements file From 4aca9e4e53457fbaf5ac357d524f62bf4d3bda1c Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Thu, 27 Jul 2023 00:11:05 +0200 Subject: [PATCH 28/44] fix: Fix remaining nullability warnings in Class --- S7.Net/Types/Class.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/S7.Net/Types/Class.cs b/S7.Net/Types/Class.cs index c333d83..be84c2b 100644 --- a/S7.Net/Types/Class.cs +++ b/S7.Net/Types/Class.cs @@ -64,7 +64,8 @@ namespace S7.Net.Types numBytes += attribute.ReservedLengthInBytes; break; default: - var propertyClass = Activator.CreateInstance(type); + var propertyClass = Activator.CreateInstance(type) ?? + throw new ArgumentException($"Failed to create instance of type {type}.", nameof(type)); numBytes = GetClassSize(propertyClass, numBytes, true); break; } @@ -86,8 +87,10 @@ namespace S7.Net.Types { if (property.PropertyType.IsArray) { - Type elementType = property.PropertyType.GetElementType(); - Array array = (Array)property.GetValue(instance, null); + Type elementType = property.PropertyType.GetElementType()!; + Array array = (Array?) property.GetValue(instance, null) ?? + throw new ArgumentException($"Property {property.Name} on {instance} must have a non-null value to get it's size.", nameof(instance)); + if (array.Length <= 0) { throw new Exception("Cannot determine size of class, because an array is defined which has no fixed size greater than zero."); @@ -201,7 +204,9 @@ namespace S7.Net.Types numBytes += sData.Length; break; default: - var propClass = Activator.CreateInstance(propertyType); + var propClass = Activator.CreateInstance(propertyType) ?? + throw new ArgumentException($"Failed to create instance of type {propertyType}.", nameof(propertyType)); + numBytes = FromBytes(propClass, bytes, numBytes); value = propClass; break; From 71f7f8b400075d0783e37b00a6e936d5499528e1 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Thu, 27 Jul 2023 00:16:40 +0200 Subject: [PATCH 29/44] fix: Fix nullability warning in String.ToByteArray --- S7.Net/Types/String.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/S7.Net/Types/String.cs b/S7.Net/Types/String.cs index 3917635..b0ccc19 100644 --- a/S7.Net/Types/String.cs +++ b/S7.Net/Types/String.cs @@ -12,13 +12,15 @@ /// The amount of bytes reserved for the in the PLC. public static byte[] ToByteArray(string value, int reservedLength) { - var length = value?.Length; - if (length > reservedLength) length = reservedLength; var bytes = new byte[reservedLength]; + if (value == null) return bytes; - if (length == null || length == 0) return bytes; + var length = value.Length; + if (length == 0) return bytes; - System.Text.Encoding.ASCII.GetBytes(value, 0, length.Value, bytes, 0); + if (length > reservedLength) length = reservedLength; + + System.Text.Encoding.ASCII.GetBytes(value, 0, length, bytes, 0); return bytes; } From b27e1c90831d29d98e9365c64c3a9d5177d6ba28 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 28 Jul 2023 23:50:53 +0200 Subject: [PATCH 30/44] build: Set LangVersion to latest --- S7.Net/S7.Net.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S7.Net/S7.Net.csproj b/S7.Net/S7.Net.csproj index 8faec79..eeed409 100644 --- a/S7.Net/S7.Net.csproj +++ b/S7.Net/S7.Net.csproj @@ -15,7 +15,7 @@ git PLC Siemens Communication S7 Derek Heiser 2015 - 8.0 + latest Enable portable true From b61ac3291368b4f8db43a5573ee05e60f52c7c25 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 28 Jul 2023 23:52:57 +0200 Subject: [PATCH 31/44] fix: Permit nulls in string ToByteArray conversions --- S7.Net/Types/S7String.cs | 2 +- S7.Net/Types/S7WString.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/S7.Net/Types/S7String.cs b/S7.Net/Types/S7String.cs index 46c4808..290aa53 100644 --- a/S7.Net/Types/S7String.cs +++ b/S7.Net/Types/S7String.cs @@ -58,7 +58,7 @@ namespace S7.Net.Types /// The string to convert to byte array. /// The length (in characters) allocated in PLC for the string. /// A containing the string header and string value with a maximum length of + 2. - public static byte[] ToByteArray(string value, int reservedLength) + public static byte[] ToByteArray(string? value, int reservedLength) { if (value is null) { diff --git a/S7.Net/Types/S7WString.cs b/S7.Net/Types/S7WString.cs index 8d8aabf..001c7ef 100644 --- a/S7.Net/Types/S7WString.cs +++ b/S7.Net/Types/S7WString.cs @@ -48,7 +48,7 @@ namespace S7.Net.Types /// The string to convert to byte array. /// The length (in characters) allocated in PLC for the string. /// A containing the string header and string value with a maximum length of + 4. - public static byte[] ToByteArray(string value, int reservedLength) + public static byte[] ToByteArray(string? value, int reservedLength) { if (value is null) { From c5023c10e412903781fbb6d3aed16a85b3a26dde Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 28 Jul 2023 23:54:15 +0200 Subject: [PATCH 32/44] style: Cleanup line endings in S7String --- S7.Net/Types/S7String.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/S7.Net/Types/S7String.cs b/S7.Net/Types/S7String.cs index 290aa53..d45c534 100644 --- a/S7.Net/Types/S7String.cs +++ b/S7.Net/Types/S7String.cs @@ -8,17 +8,17 @@ namespace S7.Net.Types /// An S7 String has a preceeding 2 byte header containing its capacity and length /// public static class S7String - { - private static Encoding stringEncoding = Encoding.ASCII; - + { + private static Encoding stringEncoding = Encoding.ASCII; + /// /// The Encoding used when serializing and deserializing S7String (Encoding.ASCII by default) /// - /// StringEncoding must not be null - public static Encoding StringEncoding - { - get => stringEncoding; - set => stringEncoding = value ?? throw new ArgumentNullException(nameof(StringEncoding)); + /// StringEncoding must not be null + public static Encoding StringEncoding + { + get => stringEncoding; + set => stringEncoding = value ?? throw new ArgumentNullException(nameof(StringEncoding)); } /// From 6e103cea63588102a5a7cfdfde732ab4bcbd3aa0 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 28 Jul 2023 23:55:12 +0200 Subject: [PATCH 33/44] fix: Fix warnings in Struct --- S7.Net/Types/Struct.cs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/S7.Net/Types/Struct.cs b/S7.Net/Types/Struct.cs index 1e95508..6f29447 100644 --- a/S7.Net/Types/Struct.cs +++ b/S7.Net/Types/Struct.cs @@ -98,8 +98,8 @@ namespace S7.Net.Types int bytePos = 0; int bitPos = 0; double numBytes = 0.0; - object structValue = Activator.CreateInstance(structType); - + object structValue = Activator.CreateInstance(structType) ?? + throw new ArgumentException($"Failed to create an instance of the type {structType}.", nameof(structType)); var infos = structValue.GetType() #if NETSTANDARD1_3 @@ -254,6 +254,14 @@ namespace S7.Net.Types foreach (var info in infos) { + static TValue GetValueOrThrow(FieldInfo fi, object structValue) where TValue : struct + { + var value = fi.GetValue(structValue) as TValue? ?? + throw new ArgumentException($"Failed to convert value of field {fi.Name} of {structValue} to type {typeof(TValue)}"); + + return value; + } + bytes2 = null; switch (info.FieldType.Name) { @@ -261,7 +269,7 @@ namespace S7.Net.Types // get the value bytePos = (int)Math.Floor(numBytes); bitPos = (int)((numBytes - (double)bytePos) / 0.125); - if ((bool)info.GetValue(structValue)) + if (GetValueOrThrow(info, structValue)) bytes[bytePos] |= (byte)Math.Pow(2, bitPos); // is true else bytes[bytePos] &= (byte)(~(byte)Math.Pow(2, bitPos)); // is false @@ -270,26 +278,26 @@ namespace S7.Net.Types case "Byte": numBytes = (int)Math.Ceiling(numBytes); bytePos = (int)numBytes; - bytes[bytePos] = (byte)info.GetValue(structValue); + bytes[bytePos] = GetValueOrThrow(info, structValue); numBytes++; break; case "Int16": - bytes2 = Int.ToByteArray((Int16)info.GetValue(structValue)); + bytes2 = Int.ToByteArray(GetValueOrThrow(info, structValue)); break; case "UInt16": - bytes2 = Word.ToByteArray((UInt16)info.GetValue(structValue)); + bytes2 = Word.ToByteArray(GetValueOrThrow(info, structValue)); break; case "Int32": - bytes2 = DInt.ToByteArray((Int32)info.GetValue(structValue)); + bytes2 = DInt.ToByteArray(GetValueOrThrow(info, structValue)); break; case "UInt32": - bytes2 = DWord.ToByteArray((UInt32)info.GetValue(structValue)); + bytes2 = DWord.ToByteArray(GetValueOrThrow(info, structValue)); break; case "Single": - bytes2 = Real.ToByteArray((float)info.GetValue(structValue)); + bytes2 = Real.ToByteArray(GetValueOrThrow(info, structValue)); break; case "Double": - bytes2 = LReal.ToByteArray((double)info.GetValue(structValue)); + bytes2 = LReal.ToByteArray(GetValueOrThrow(info, structValue)); break; case "String": S7StringAttribute? attribute = info.GetCustomAttributes().SingleOrDefault(); @@ -298,8 +306,8 @@ namespace S7.Net.Types bytes2 = attribute.Type switch { - S7StringType.S7String => S7String.ToByteArray((string)info.GetValue(structValue), attribute.ReservedLength), - S7StringType.S7WString => S7WString.ToByteArray((string)info.GetValue(structValue), attribute.ReservedLength), + S7StringType.S7String => S7String.ToByteArray((string?)info.GetValue(structValue), attribute.ReservedLength), + S7StringType.S7WString => S7WString.ToByteArray((string?)info.GetValue(structValue), attribute.ReservedLength), _ => throw new ArgumentException("Please use a valid string type for the S7StringAttribute") }; break; From e26860b0c02df7c995e317261db4d85685617223 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 28 Jul 2023 23:57:55 +0200 Subject: [PATCH 34/44] build: Extend NoWarn - Amend existing NoWarn if set - Ignore out of support target framework warning --- S7.Net/S7.Net.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S7.Net/S7.Net.csproj b/S7.Net/S7.Net.csproj index eeed409..a5643f5 100644 --- a/S7.Net/S7.Net.csproj +++ b/S7.Net/S7.Net.csproj @@ -21,7 +21,7 @@ true snupkg true - CS1591 + $(NoWarn);CS1591;NETSDK1138 From 714ac62ab1f92b9a200c1c0a3c30eff98239830f Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Mon, 31 Jul 2023 22:57:03 +0200 Subject: [PATCH 35/44] test: Add CommunicationSequence --- S7.Net.UnitTest/Framework/IsExternalInit.cs | 7 ++ .../Infrastructure/CommunicationSequence.cs | 82 +++++++++++++++++++ .../Infrastructure/RequestResponsePair.cs | 3 + S7.Net.UnitTest/Infrastructure/Responder.cs | 80 ++++++++++++++++++ S7.Net.UnitTest/S7.Net.UnitTest.csproj | 1 + 5 files changed, 173 insertions(+) create mode 100644 S7.Net.UnitTest/Framework/IsExternalInit.cs create mode 100644 S7.Net.UnitTest/Infrastructure/CommunicationSequence.cs create mode 100644 S7.Net.UnitTest/Infrastructure/RequestResponsePair.cs create mode 100644 S7.Net.UnitTest/Infrastructure/Responder.cs diff --git a/S7.Net.UnitTest/Framework/IsExternalInit.cs b/S7.Net.UnitTest/Framework/IsExternalInit.cs new file mode 100644 index 0000000..f70856c --- /dev/null +++ b/S7.Net.UnitTest/Framework/IsExternalInit.cs @@ -0,0 +1,7 @@ +using System.ComponentModel; + +namespace System.Runtime.CompilerServices +{ + [EditorBrowsable(EditorBrowsableState.Never)] + internal record IsExternalInit; +} \ No newline at end of file diff --git a/S7.Net.UnitTest/Infrastructure/CommunicationSequence.cs b/S7.Net.UnitTest/Infrastructure/CommunicationSequence.cs new file mode 100644 index 0000000..429b346 --- /dev/null +++ b/S7.Net.UnitTest/Infrastructure/CommunicationSequence.cs @@ -0,0 +1,82 @@ +using System; +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace S7.Net.UnitTest; + +internal class CommunicationSequence : IEnumerable +{ + private readonly List _requestResponsePairs = new List(); + + public CommunicationSequence() + { + + } + + public void Add(string requestPattern, string responsePattern) + { + _requestResponsePairs.Add(new RequestResponsePair(requestPattern, responsePattern)); + } + + public IEnumerator GetEnumerator() + { + return _requestResponsePairs.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public Task Serve(out int port) + { + var socket = CreateBoundListenSocket(out port); + socket.Listen(0); + + async Task Impl() + { + await Task.Yield(); + var socketIn = socket.Accept(); + + var buffer = ArrayPool.Shared.Rent(1024); + try + { + foreach (var pair in _requestResponsePairs) + { + var bytesReceived = socketIn.Receive(buffer, SocketFlags.None); + + var received = buffer.Take(bytesReceived).ToArray(); + Console.WriteLine($"=> {BitConverter.ToString(received)}"); + + var response = Responder.Respond(pair, received); + + Console.WriteLine($"<= {BitConverter.ToString(response)}"); + socketIn.Send(response); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + socketIn.Close(); + } + + return Impl(); + } + + private static Socket CreateBoundListenSocket(out int port) + { + var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + var endpoint = new IPEndPoint(IPAddress.Loopback, 0); + + socket.Bind(endpoint); + + var localEndpoint = (IPEndPoint)socket.LocalEndPoint!; + port = localEndpoint.Port; + + return socket; + } +} diff --git a/S7.Net.UnitTest/Infrastructure/RequestResponsePair.cs b/S7.Net.UnitTest/Infrastructure/RequestResponsePair.cs new file mode 100644 index 0000000..390ee62 --- /dev/null +++ b/S7.Net.UnitTest/Infrastructure/RequestResponsePair.cs @@ -0,0 +1,3 @@ +namespace S7.Net.UnitTest; + +internal record RequestResponsePair(string RequestPattern, string ResponsePattern); diff --git a/S7.Net.UnitTest/Infrastructure/Responder.cs b/S7.Net.UnitTest/Infrastructure/Responder.cs new file mode 100644 index 0000000..0fa15ea --- /dev/null +++ b/S7.Net.UnitTest/Infrastructure/Responder.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +namespace S7.Net.UnitTest; + +internal static class Responder +{ + private const string Comment = "//"; + private static char[] Space = " ".ToCharArray(); + + public static byte[] Respond(RequestResponsePair pair, byte[] request) + { + var offset = 0; + var matches = new Dictionary(); + var res = new List(); + using var requestReader = new StringReader(pair.RequestPattern); + + string line; + while ((line = requestReader.ReadLine()) != null) + { + var tokens = line.Split(Space, StringSplitOptions.RemoveEmptyEntries); + foreach (var token in tokens) + { + if (token.StartsWith(Comment)) break; + + if (offset >= request.Length) + { + throw new Exception("Request pattern has more data than request."); + } + + var received = request[offset]; + + if (token.Length == 2 && byte.TryParse(token, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var value)) + { + // Number, exact match + if (value != received) + { + throw new Exception($"Incorrect data at offset {offset}. Expected {value:X2}, received {received:X2}."); + } + } + else + { + matches[token] = received; + } + + offset++; + } + } + + if (offset != request.Length) throw new Exception("Request contained more data than request pattern."); + + using var responseReader = new StringReader(pair.ResponsePattern); + while ((line = responseReader.ReadLine()) != null) + { + var tokens = line.Split(Space, StringSplitOptions.RemoveEmptyEntries); + foreach (var token in tokens) + { + if (token.StartsWith(Comment)) break; + + if (token.Length == 2 && byte.TryParse(token, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var value)) + { + res.Add(value); + } + else + { + if (!matches.TryGetValue(token, out var match)) + { + throw new Exception($"Unmatched token '{token}' in response."); + } + + res.Add(match); + } + } + } + + return res.ToArray(); + } +} \ No newline at end of file diff --git a/S7.Net.UnitTest/S7.Net.UnitTest.csproj b/S7.Net.UnitTest/S7.Net.UnitTest.csproj index 2b29986..a22bf0d 100644 --- a/S7.Net.UnitTest/S7.Net.UnitTest.csproj +++ b/S7.Net.UnitTest/S7.Net.UnitTest.csproj @@ -8,6 +8,7 @@ + latest true Properties\S7.Net.snk false From 8b8ad134648c3644d2e498b687765333f9776f81 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Mon, 31 Jul 2023 23:58:15 +0200 Subject: [PATCH 36/44] test: Add ConnectionOpen communication test --- .../CommunicationTests/ConnectionOpen.cs | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 S7.Net.UnitTest/CommunicationTests/ConnectionOpen.cs diff --git a/S7.Net.UnitTest/CommunicationTests/ConnectionOpen.cs b/S7.Net.UnitTest/CommunicationTests/ConnectionOpen.cs new file mode 100644 index 0000000..d19f7c3 --- /dev/null +++ b/S7.Net.UnitTest/CommunicationTests/ConnectionOpen.cs @@ -0,0 +1,127 @@ +using System.Net; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using S7.Net.Protocol; + +namespace S7.Net.UnitTest.CommunicationTests; + +[TestClass] +public class ConnectionOpen +{ + [TestMethod] + public async Task Does_Not_Throw() + { + var cs = new CommunicationSequence { + { + """ + // TPKT + 03 // Version + 00 // Reserved + 00 16 // Length + + // CR + 11 // Number of bytes following + E0 // CR / Credit + 00 00 // Destination reference, unused + __ __ // Source reference, unused + 00 // Class / Option + + // Source TSAP + C1 // Parameter code + 02 // Parameter length + TSAP_SRC_CHAN // Channel + TSAP_SRC_POS // Position + + // Destination TSAP + C2 // Parameter code + 02 // Parameter length + TSAP_DEST_CHAN // Channel + TSAP_DEST_POS // Position + + // PDU Size parameter + C0 // Parameter code + 01 // Parameter length + 0A // 1024 byte PDU (2 ^ 10) + """, + """ + // TPKT + 03 // Version + 00 // Reserved + 00 0B // Length + + // CC + 06 // Length + D0 // CC / Credit + 00 00 // Destination reference + 00 00 // Source reference + 00 // Class / Option + """ + }, + { + """ + // TPKT + 03 // Version + 00 // Reserved + 00 19 // Length + + // Data header + 02 // Length + F0 // Data identifier + 80 // PDU number and end of transmission + + // S7 header + 32 // Protocol ID + 01 // Message type job request + 00 00 // Reserved + PDU1 PDU2 // PDU reference + 00 08 // Parameter length (Communication Setup) + 00 00 // Data length + + // Communication Setup + F0 // Function code + 00 // Reserved + 00 03 // Max AMQ caller + 00 03 // Max AMQ callee + 03 C0 // PDU size (960) + """, + """ + // TPKT + 03 // Version + 00 // Reserved + 00 1B // Length + + // Data header + 02 // Length + F0 // Data identifier + 80 // PDU number and end of transmission + + // S7 header + 32 // Protocol ID + 03 // Message type ack data + 00 00 // Reserved + PDU1 PDU2 // PDU reference + 00 08 // Parameter length (Communication Setup) + 00 00 // Data length + 00 // Error class + 00 // Error code + + // Communication Setup + F0 // Function code + 00 // Reserved + 00 03 // Max AMQ caller + 00 03 // Max AMQ callee + 03 C0 // PDU size (960) + """ + } + }; + + async Task Client(int port) + { + var conn = new Plc(IPAddress.Loopback.ToString(), port, new TsapPair(new Tsap(1, 2), new Tsap(3, 4))); + await conn.OpenAsync(); + conn.Close(); + } + + await Task.WhenAll(cs.Serve(out var port), Client(port)); + } +} \ No newline at end of file From 54dadec75a35c1939ec54f36a1398ec3bd4fe9bd Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Tue, 1 Aug 2023 22:50:03 +0200 Subject: [PATCH 37/44] test: Extract connection open templates --- .../CommunicationTests/ConnectionOpen.cs | 103 +---------------- .../ConnectionOpenTemplates.cs | 107 ++++++++++++++++++ .../Infrastructure/CommunicationSequence.cs | 4 +- 3 files changed, 111 insertions(+), 103 deletions(-) create mode 100644 S7.Net.UnitTest/CommunicationTests/ConnectionOpenTemplates.cs diff --git a/S7.Net.UnitTest/CommunicationTests/ConnectionOpen.cs b/S7.Net.UnitTest/CommunicationTests/ConnectionOpen.cs index d19f7c3..15568d5 100644 --- a/S7.Net.UnitTest/CommunicationTests/ConnectionOpen.cs +++ b/S7.Net.UnitTest/CommunicationTests/ConnectionOpen.cs @@ -12,107 +12,8 @@ public class ConnectionOpen public async Task Does_Not_Throw() { var cs = new CommunicationSequence { - { - """ - // TPKT - 03 // Version - 00 // Reserved - 00 16 // Length - - // CR - 11 // Number of bytes following - E0 // CR / Credit - 00 00 // Destination reference, unused - __ __ // Source reference, unused - 00 // Class / Option - - // Source TSAP - C1 // Parameter code - 02 // Parameter length - TSAP_SRC_CHAN // Channel - TSAP_SRC_POS // Position - - // Destination TSAP - C2 // Parameter code - 02 // Parameter length - TSAP_DEST_CHAN // Channel - TSAP_DEST_POS // Position - - // PDU Size parameter - C0 // Parameter code - 01 // Parameter length - 0A // 1024 byte PDU (2 ^ 10) - """, - """ - // TPKT - 03 // Version - 00 // Reserved - 00 0B // Length - - // CC - 06 // Length - D0 // CC / Credit - 00 00 // Destination reference - 00 00 // Source reference - 00 // Class / Option - """ - }, - { - """ - // TPKT - 03 // Version - 00 // Reserved - 00 19 // Length - - // Data header - 02 // Length - F0 // Data identifier - 80 // PDU number and end of transmission - - // S7 header - 32 // Protocol ID - 01 // Message type job request - 00 00 // Reserved - PDU1 PDU2 // PDU reference - 00 08 // Parameter length (Communication Setup) - 00 00 // Data length - - // Communication Setup - F0 // Function code - 00 // Reserved - 00 03 // Max AMQ caller - 00 03 // Max AMQ callee - 03 C0 // PDU size (960) - """, - """ - // TPKT - 03 // Version - 00 // Reserved - 00 1B // Length - - // Data header - 02 // Length - F0 // Data identifier - 80 // PDU number and end of transmission - - // S7 header - 32 // Protocol ID - 03 // Message type ack data - 00 00 // Reserved - PDU1 PDU2 // PDU reference - 00 08 // Parameter length (Communication Setup) - 00 00 // Data length - 00 // Error class - 00 // Error code - - // Communication Setup - F0 // Function code - 00 // Reserved - 00 03 // Max AMQ caller - 00 03 // Max AMQ callee - 03 C0 // PDU size (960) - """ - } + ConnectionOpenTemplates.ConnectionRequestConfirm, + ConnectionOpenTemplates.CommunicationSetup }; async Task Client(int port) diff --git a/S7.Net.UnitTest/CommunicationTests/ConnectionOpenTemplates.cs b/S7.Net.UnitTest/CommunicationTests/ConnectionOpenTemplates.cs new file mode 100644 index 0000000..cb9cc0a --- /dev/null +++ b/S7.Net.UnitTest/CommunicationTests/ConnectionOpenTemplates.cs @@ -0,0 +1,107 @@ +namespace S7.Net.UnitTest.CommunicationTests; + +internal static class ConnectionOpenTemplates +{ + public static RequestResponsePair ConnectionRequestConfirm { get; } = new RequestResponsePair( + """ + // TPKT + 03 // Version + 00 // Reserved + 00 16 // Length + + // CR + 11 // Number of bytes following + E0 // CR / Credit + 00 00 // Destination reference, unused + __ __ // Source reference, unused + 00 // Class / Option + + // Source TSAP + C1 // Parameter code + 02 // Parameter length + TSAP_SRC_CHAN // Channel + TSAP_SRC_POS // Position + + // Destination TSAP + C2 // Parameter code + 02 // Parameter length + TSAP_DEST_CHAN // Channel + TSAP_DEST_POS // Position + + // PDU Size parameter + C0 // Parameter code + 01 // Parameter length + 0A // 1024 byte PDU (2 ^ 10) + """, + """ + // TPKT + 03 // Version + 00 // Reserved + 00 0B // Length + + // CC + 06 // Length + D0 // CC / Credit + 00 00 // Destination reference + 00 00 // Source reference + 00 // Class / Option + """ + ); + + public static RequestResponsePair CommunicationSetup { get; } = new RequestResponsePair( + """ + // TPKT + 03 // Version + 00 // Reserved + 00 19 // Length + + // Data header + 02 // Length + F0 // Data identifier + 80 // PDU number and end of transmission + + // S7 header + 32 // Protocol ID + 01 // Message type job request + 00 00 // Reserved + PDU1 PDU2 // PDU reference + 00 08 // Parameter length (Communication Setup) + 00 00 // Data length + + // Communication Setup + F0 // Function code + 00 // Reserved + 00 03 // Max AMQ caller + 00 03 // Max AMQ callee + 03 C0 // PDU size (960) + """, + """ + // TPKT + 03 // Version + 00 // Reserved + 00 1B // Length + + // Data header + 02 // Length + F0 // Data identifier + 80 // PDU number and end of transmission + + // S7 header + 32 // Protocol ID + 03 // Message type ack data + 00 00 // Reserved + PDU1 PDU2 // PDU reference + 00 08 // Parameter length (Communication Setup) + 00 00 // Data length + 00 // Error class + 00 // Error code + + // Communication Setup + F0 // Function code + 00 // Reserved + 00 03 // Max AMQ caller + 00 03 // Max AMQ callee + 03 C0 // PDU size (960) + """ + ); +} \ No newline at end of file diff --git a/S7.Net.UnitTest/Infrastructure/CommunicationSequence.cs b/S7.Net.UnitTest/Infrastructure/CommunicationSequence.cs index 429b346..c3e4488 100644 --- a/S7.Net.UnitTest/Infrastructure/CommunicationSequence.cs +++ b/S7.Net.UnitTest/Infrastructure/CommunicationSequence.cs @@ -13,9 +13,9 @@ internal class CommunicationSequence : IEnumerable { private readonly List _requestResponsePairs = new List(); - public CommunicationSequence() + public void Add(RequestResponsePair requestResponsePair) { - + _requestResponsePairs.Add(requestResponsePair); } public void Add(string requestPattern, string responsePattern) From 9b1faa01231fa9fa0020ccb42847068051b444f0 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Tue, 1 Aug 2023 22:50:50 +0200 Subject: [PATCH 38/44] test: Add test for reading PLC status --- .../CommunicationTests/ReadPlcStatus.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 S7.Net.UnitTest/CommunicationTests/ReadPlcStatus.cs diff --git a/S7.Net.UnitTest/CommunicationTests/ReadPlcStatus.cs b/S7.Net.UnitTest/CommunicationTests/ReadPlcStatus.cs new file mode 100644 index 0000000..13cc733 --- /dev/null +++ b/S7.Net.UnitTest/CommunicationTests/ReadPlcStatus.cs @@ -0,0 +1,57 @@ +using System.Net; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using S7.Net.Protocol; + +namespace S7.Net.UnitTest.CommunicationTests; + +[TestClass] +public class ReadPlcStatus +{ + [TestMethod] + public async Task Read_Status_Run() + { + var cs = new CommunicationSequence { + ConnectionOpenTemplates.ConnectionRequestConfirm, + ConnectionOpenTemplates.CommunicationSetup, + { + """ + // TPKT + 03 00 00 21 + + // COTP + 02 f0 80 + + // S7 SZL read + 32 07 00 00 PDU1 PDU2 00 08 00 08 00 01 12 04 11 44 + 01 00 ff 09 00 04 04 24 00 00 + """, + """ + // TPKT + 03 00 00 3d + + // COTP + 02 f0 80 + + // S7 SZL response + 32 07 00 00 PDU1 PDU2 00 0c 00 20 00 01 12 08 12 84 + 01 02 00 00 00 00 ff 09 00 1c 04 24 00 00 00 14 + 00 01 51 44 ff 08 00 00 00 00 00 00 00 00 14 08 + 20 12 05 28 34 94 + """ + } + }; + + async Task Client(int port) + { + var conn = new Plc(IPAddress.Loopback.ToString(), port, new TsapPair(new Tsap(1, 2), new Tsap(3, 4))); + await conn.OpenAsync(); + var status = await conn.ReadStatusAsync(); + + Assert.AreEqual(0x08, status); + conn.Close(); + } + + await Task.WhenAll(cs.Serve(out var port), Client(port)); + } +} \ No newline at end of file From 97e27ccc2bc815686dabc6ab323984913cd726a4 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Tue, 1 Aug 2023 22:51:47 +0200 Subject: [PATCH 39/44] chore(ReadStatusAsync): Make cancellationToken optional --- S7.Net/PlcAsynchronous.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index c280b0b..726e6cb 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -316,7 +316,7 @@ namespace S7.Net /// Read the current status from the PLC. A value of 0x08 indicates the PLC is in run status, regardless of the PLC type. /// /// A task that represents the asynchronous operation, with it's result set to the current PLC status on completion. - public async Task ReadStatusAsync(CancellationToken cancellationToken) { + public async Task ReadStatusAsync(CancellationToken cancellationToken = default) { var dataToSend = BuildSzlReadRequestPackage(0x0424, 0); var s7data = await RequestTsduAsync(dataToSend, cancellationToken); From e5823f280604ef11ed66911a43d30f45c2d3e315 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Tue, 1 Aug 2023 22:52:10 +0200 Subject: [PATCH 40/44] doc(ReadStatusAsync): Add missing cancellationToken documentation --- S7.Net/PlcAsynchronous.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 726e6cb..526dd0a 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -315,6 +315,8 @@ namespace S7.Net /// /// Read the current status from the PLC. A value of 0x08 indicates the PLC is in run status, regardless of the PLC type. /// + /// 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 operation, with it's result set to the current PLC status on completion. public async Task ReadStatusAsync(CancellationToken cancellationToken = default) { var dataToSend = BuildSzlReadRequestPackage(0x0424, 0); From c3934c3493278c5fecca79c5692b7f7ff2512133 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Tue, 1 Aug 2023 22:52:44 +0200 Subject: [PATCH 41/44] fix(ReadStatusAsync): Fix index of status in response message --- S7.Net/PlcAsynchronous.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 526dd0a..d7f0db0 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -322,7 +322,7 @@ namespace S7.Net var dataToSend = BuildSzlReadRequestPackage(0x0424, 0); var s7data = await RequestTsduAsync(dataToSend, cancellationToken); - return (byte) (s7data[94] & 0x0f); + return (byte) (s7data[37] & 0x0f); } /// From 970e9d43950f7ed7504b9ff69d2e76fb5c06c927 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Tue, 1 Aug 2023 22:55:19 +0200 Subject: [PATCH 42/44] feat: Add sync version of ReadStatus --- S7.Net/PlcSynchronous.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index c30e0cf..1b3af97 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -492,6 +492,18 @@ namespace S7.Net } } + /// + /// Read the current status from the PLC. A value of 0x08 indicates the PLC is in run status, regardless of the PLC type. + /// + /// The current PLC status. + public byte ReadStatus() + { + var dataToSend = BuildSzlReadRequestPackage(0x0424, 0); + var s7data = RequestTsdu(dataToSend); + + return (byte) (s7data[37] & 0x0f); + } + private byte[] RequestTsdu(byte[] requestData) => RequestTsdu(requestData, 0, requestData.Length); private byte[] RequestTsdu(byte[] requestData, int offset, int length) From addf6068bbf1d521ca690792b93b835c4d47e38e Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Tue, 1 Aug 2023 22:56:08 +0200 Subject: [PATCH 43/44] style(ReadStatusAsync): Move opening brace to new line --- S7.Net/PlcAsynchronous.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index d7f0db0..eb49e5d 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -318,7 +318,8 @@ namespace S7.Net /// 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 operation, with it's result set to the current PLC status on completion. - public async Task ReadStatusAsync(CancellationToken cancellationToken = default) { + public async Task ReadStatusAsync(CancellationToken cancellationToken = default) + { var dataToSend = BuildSzlReadRequestPackage(0x0424, 0); var s7data = await RequestTsduAsync(dataToSend, cancellationToken); From 0797c5858fed786c4714f88fd5bfc6f81163c253 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Thu, 3 Aug 2023 21:45:46 +0200 Subject: [PATCH 44/44] chore: Update GitHubActionsTestLogger --- S7.Net.UnitTest/S7.Net.UnitTest.csproj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/S7.Net.UnitTest/S7.Net.UnitTest.csproj b/S7.Net.UnitTest/S7.Net.UnitTest.csproj index a22bf0d..3b43284 100644 --- a/S7.Net.UnitTest/S7.Net.UnitTest.csproj +++ b/S7.Net.UnitTest/S7.Net.UnitTest.csproj @@ -4,7 +4,7 @@ net6.0;net7.0 - net462;net6.0;net7.0 + net6.0;net7.0;net462 @@ -17,7 +17,10 @@ - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all +