From 81208c0f03f7fa1d7b648cc854acb461a0311550 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Sun, 13 Sep 2020 10:02:16 +0200 Subject: [PATCH 01/13] Add github actions for multi-platform unit tests. --- .github/workflows/deploy.yml | 43 +++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 47 ++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..ae11e7a --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,43 @@ +name: Deploy + +on: + release: + types: release + +jobs: + deploy: + name: Deploy + needs: build_test + runs-on: windows-latest + if: ${{ false }} # not active right now + + steps: + - uses: actions/checkout@v2 + + - name: Fetch all history for all tags and branches + run: git fetch --prune --unshallow + + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0.9.2 + with: + versionSpec: '5.2.x' + + - name: Use GitVersion + id: gitversion # step id used as reference for output values + uses: gittools/actions/gitversion/execute@v0.9.2 + + - name: Setup Dotnet + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '3.1.x' # SDK Version to use; x will use the latest version of the 3.1 channel + + - name: Pack + if: ${{ matrix.pack }} + run: dotnet pack --configuration "${{ env.configuration }}" /p:Version="${{ steps.gitversion.outputs.nuGetVersion }}" -o "${{ env.artifacts }}" + + - name: Upload Artifacts + if: ${{ matrix.pack }} + uses: actions/upload-artifact@v2 + with: + name: NugetPackage + path: ${{ env.artifacts }}/** diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..ad8397a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,47 @@ +name: Test + +on: + [pull_request, push] + +jobs: + + build_test: + name: test-${{ matrix.os }}-${{ matrix.test-framework }} + runs-on: ${{ matrix.os }} + env: + configuration: Release + artifacts: ${{ github.workspace }}/artifacts + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + test-framework: [netcoreapp3.1] + include: + - os: ubuntu-latest + installSnap7: true + - os: macos-latest + installSnap7: true + - os: windows-latest + test-framework: net452 + + steps: + - uses: actions/checkout@v2 + + - name: Install Snap7 Linux + if: ${{ matrix.installSnap7 && matrix.os == 'ubuntu-latest' }} + 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' }} + run: | + brew install snap7 + + - name: Setup Dotnet + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '3.1.x' # SDK Version to use; x will use the latest version of the 3.1 channel + + - name: Test + run: dotnet test --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }} \ No newline at end of file From ce97fcf335fc4528a77852becdc1f300fabab0ec Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Sun, 13 Sep 2020 10:42:19 +0200 Subject: [PATCH 02/13] Separate restore and test steps to better see execution times. Restore could be cached, but it looks quite complicated to pull off. --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ad8397a..e94e324 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,5 +43,8 @@ jobs: with: dotnet-version: '3.1.x' # SDK Version to use; x will use the latest version of the 3.1 channel + - name: Restore + run: dotnet restore S7.Net.UnitTest + - name: Test - run: dotnet test --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }} \ No newline at end of file + run: dotnet test --no-restore --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }} \ No newline at end of file From fb44b56c164ac56b6e4f7063e65ef0a10ed730fd Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Sun, 13 Sep 2020 10:54:16 +0200 Subject: [PATCH 03/13] Skip welcome message --- .github/workflows/deploy.yml | 4 +++- .github/workflows/test.yml | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ae11e7a..1b6ea18 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,6 +9,8 @@ jobs: name: Deploy needs: build_test runs-on: windows-latest + env: + DOTNET_NOLOGO : 1 if: ${{ false }} # not active right now steps: @@ -33,7 +35,7 @@ jobs: - name: Pack if: ${{ matrix.pack }} - run: dotnet pack --configuration "${{ env.configuration }}" /p:Version="${{ steps.gitversion.outputs.nuGetVersion }}" -o "${{ env.artifacts }}" + run: dotnet pack --nologo --configuration "${{ env.configuration }}" /p:Version="${{ steps.gitversion.outputs.nuGetVersion }}" -o "${{ env.artifacts }}" - name: Upload Artifacts if: ${{ matrix.pack }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e94e324..226aa6f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,6 +11,7 @@ jobs: env: configuration: Release artifacts: ${{ github.workspace }}/artifacts + DOTNET_NOLOGO : 1 strategy: matrix: os: [windows-latest, ubuntu-latest, macos-latest] @@ -47,4 +48,4 @@ jobs: run: dotnet restore S7.Net.UnitTest - name: Test - run: dotnet test --no-restore --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }} \ No newline at end of file + run: dotnet test --no-restore --nologo --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }} \ No newline at end of file From ae70f31af25b540c56a77993d9e6ee900710c5d0 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Sat, 17 Oct 2020 11:47:16 +0200 Subject: [PATCH 04/13] Remove deploy workflow. --- .github/workflows/deploy.yml | 45 ------------------------------------ 1 file changed, 45 deletions(-) delete mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 1b6ea18..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Deploy - -on: - release: - types: release - -jobs: - deploy: - name: Deploy - needs: build_test - runs-on: windows-latest - env: - DOTNET_NOLOGO : 1 - if: ${{ false }} # not active right now - - steps: - - uses: actions/checkout@v2 - - - name: Fetch all history for all tags and branches - run: git fetch --prune --unshallow - - - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.9.2 - with: - versionSpec: '5.2.x' - - - name: Use GitVersion - id: gitversion # step id used as reference for output values - uses: gittools/actions/gitversion/execute@v0.9.2 - - - name: Setup Dotnet - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '3.1.x' # SDK Version to use; x will use the latest version of the 3.1 channel - - - name: Pack - if: ${{ matrix.pack }} - run: dotnet pack --nologo --configuration "${{ env.configuration }}" /p:Version="${{ steps.gitversion.outputs.nuGetVersion }}" -o "${{ env.artifacts }}" - - - name: Upload Artifacts - if: ${{ matrix.pack }} - uses: actions/upload-artifact@v2 - with: - name: NugetPackage - path: ${{ env.artifacts }}/** From 616dc1094c355725440a687fbba4d435a603bd6c Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 4 Jun 2021 00:09:45 +0200 Subject: [PATCH 05/13] Refactor TSAP related code --- S7.Net.UnitTest/ConnectionRequestTest.cs | 8 +-- S7.Net/Protocol/ConnectionRequest.cs | 83 ++++++++++++++---------- S7.Net/Protocol/Tsap.cs | 31 +++++++++ S7.Net/Protocol/TsapPair.cs | 30 +++++++++ 4 files changed, 113 insertions(+), 39 deletions(-) create mode 100644 S7.Net/Protocol/Tsap.cs create mode 100644 S7.Net/Protocol/TsapPair.cs diff --git a/S7.Net.UnitTest/ConnectionRequestTest.cs b/S7.Net.UnitTest/ConnectionRequestTest.cs index 440a962..f12fa16 100644 --- a/S7.Net.UnitTest/ConnectionRequestTest.cs +++ b/S7.Net.UnitTest/ConnectionRequestTest.cs @@ -49,11 +49,11 @@ namespace S7.Net.UnitTest [TestMethod] public void Test_ConnectionRequest_S7_1500() { - CollectionAssert.AreEqual(MakeConnectionRequest(0x10, 0x2, 3, 0), + CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0), ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 0, 0)); - CollectionAssert.AreEqual(MakeConnectionRequest(0x10, 0x2, 3, 1), + CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1), ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 0, 1)); - CollectionAssert.AreEqual(MakeConnectionRequest(0x10, 0x2, 3, 33), + CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33), ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 1, 1)); } @@ -63,7 +63,7 @@ namespace S7.Net.UnitTest { 3, 0, 0, 22, //TPKT 17, //COTP Header Length - 224, //Connect Request + 224, //Connect Request 0, 0, //Destination Reference 0, 46, //Source Reference 0, //Flags diff --git a/S7.Net/Protocol/ConnectionRequest.cs b/S7.Net/Protocol/ConnectionRequest.cs index 64e2b5d..83f568f 100644 --- a/S7.Net/Protocol/ConnectionRequest.cs +++ b/S7.Net/Protocol/ConnectionRequest.cs @@ -6,64 +6,77 @@ namespace S7.Net.Protocol { public static byte[] GetCOTPConnectionRequest(CpuType cpu, Int16 rack, Int16 slot) { + var tsapPair = GetDefaultTsapPair(cpu, rack, slot); + byte[] bSend1 = { 3, 0, 0, 22, //TPKT 17, //COTP Header Length - 224, //Connect Request + 224, //Connect Request 0, 0, //Destination Reference 0, 46, //Source Reference 0, //Flags 193, //Parameter Code (src-tasp) 2, //Parameter Length - 1, 0, //Source TASP + tsapPair.Local.FirstByte, tsapPair.Local.SecondByte, //Source TASP 194, //Parameter Code (dst-tasp) 2, //Parameter Length - 3, 0, //Destination TASP + tsapPair.Remote.FirstByte, tsapPair.Remote.SecondByte, //Destination TASP 192, //Parameter Code (tpdu-size) 1, //Parameter Length 10 //TPDU Size (2^10 = 1024) }; - switch (cpu) + return bSend1; + } + + /// + /// Builds a that can be used to connect to a PLC using the default connection + /// addresses. + /// + /// + /// The remote TSAP is constructed using new Tsap(0x03, (byte) ((rack << 5) | slot)). + /// + /// The CPU type of the PLC. + /// The rack of the PLC's network card. + /// The slot of the PLC's network card. + /// A TSAP pair that matches the given parameters. + /// The is invalid. + /// + /// -or- + /// + /// The parameter is greater than 15. + /// + /// -or- + /// + /// The parameter is greater than 15. + public static TsapPair GetDefaultTsapPair(CpuType cpuType, int rack, int slot) + { + if (rack > 0x0F) throw InvalidRackOrSlot(rack, nameof(rack)); + if (slot > 0x0F) throw InvalidRackOrSlot(slot, nameof(slot)); + + switch (cpuType) { case CpuType.S7200: - //S7200: Chr(193) & Chr(2) & Chr(16) & Chr(0) 'Eigener Tsap - bSend1[13] = 0x10; - bSend1[14] = 0x00; - //S7200: Chr(194) & Chr(2) & Chr(16) & Chr(0) 'Fremder Tsap - bSend1[17] = 0x10; - bSend1[18] = 0x00; - break; + return new TsapPair(new Tsap(0x10, 0x00), new Tsap(0x10, 0x00)); case CpuType.Logo0BA8: - // These values are taken from NodeS7, it's not verified if these are - // exact requirements to connect to the Logo0BA8. - bSend1[13] = 0x01; - bSend1[14] = 0x00; - bSend1[17] = 0x01; - bSend1[18] = 0x02; - break; + // The actual values are probably on a per-project basis + return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x01, 0x02)); case CpuType.S71200: + case CpuType.S71500: case CpuType.S7300: case CpuType.S7400: - //S7300: Chr(193) & Chr(2) & Chr(1) & Chr(0) 'Eigener Tsap - bSend1[13] = 0x01; - bSend1[14] = 0x00; - //S7300: Chr(194) & Chr(2) & Chr(3) & Chr(2) 'Fremder Tsap - bSend1[17] = 0x03; - bSend1[18] = (byte) ((rack << 5) | (int) slot); - break; - case CpuType.S71500: - // Eigener Tsap - bSend1[13] = 0x10; - bSend1[14] = 0x02; - // Fredmer Tsap - bSend1[17] = 0x03; - bSend1[18] = (byte) ((rack << 5) | (int) slot); - break; + // Testing with S7 1500 shows only the remote TSAP needs to match. This might differ for other + // PLC types. + return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x03, (byte) ((rack << 5) | slot))); default: - throw new Exception("Wrong CPU Type Secified"); + throw new ArgumentOutOfRangeException(nameof(cpuType), "Invalid CPU Type specified"); } - return bSend1; + } + + private static ArgumentOutOfRangeException InvalidRackOrSlot(int value, string name) + { + return new ArgumentOutOfRangeException(name, + $"Invalid {name} value specified (decimal: {value}, hexadecimal: {value:X}), maximum value is 15 (decimal) or 0x0F (hexadecimal)."); } } } diff --git a/S7.Net/Protocol/Tsap.cs b/S7.Net/Protocol/Tsap.cs new file mode 100644 index 0000000..dc9d46c --- /dev/null +++ b/S7.Net/Protocol/Tsap.cs @@ -0,0 +1,31 @@ +namespace S7.Net.Protocol +{ + /// + /// Provides a representation of the Transport Service Access Point, or TSAP in short. TSAP's are used + /// to specify a client and server address. For most PLC types a default TSAP is available that allows + /// connection from any IP and can be calculated using the rack and slot numbers. + /// + public struct Tsap + { + /// + /// First byte of the TSAP. + /// + public byte FirstByte { get; set; } + + /// + /// Second byte of the TSAP. + /// + public byte SecondByte { get; set; } + + /// + /// Initializes a new instance of the class using the specified values. + /// + /// The first byte of the TSAP. + /// The second byte of the TSAP. + public Tsap(byte firstByte, byte secondByte) + { + FirstByte = firstByte; + SecondByte = secondByte; + } + } +} \ No newline at end of file diff --git a/S7.Net/Protocol/TsapPair.cs b/S7.Net/Protocol/TsapPair.cs new file mode 100644 index 0000000..0869b23 --- /dev/null +++ b/S7.Net/Protocol/TsapPair.cs @@ -0,0 +1,30 @@ +namespace S7.Net.Protocol +{ + /// + /// Implements a pair of TSAP addresses used to connect to a PLC. + /// + public class TsapPair + { + /// + /// The local . + /// + public Tsap Local { get; set; } + + /// + /// The remote + /// + public Tsap Remote { get; set; } + + /// + /// Initializes a new instance of the class using the specified local and + /// remote TSAP. + /// + /// The local TSAP. + /// The remote TSAP. + public TsapPair(Tsap local, Tsap remote) + { + Local = local; + Remote = remote; + } + } +} \ No newline at end of file From fcd61c1236c41041e5ce5d883dc375b8ad660f28 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 4 Jun 2021 21:46:42 +0200 Subject: [PATCH 06/13] Add CpuType.S7200Smart --- S7.Net/Enums.cs | 5 +++++ S7.Net/Protocol/ConnectionRequest.cs | 1 + 2 files changed, 6 insertions(+) diff --git a/S7.Net/Enums.cs b/S7.Net/Enums.cs index 436d94b..fc412d3 100644 --- a/S7.Net/Enums.cs +++ b/S7.Net/Enums.cs @@ -15,6 +15,11 @@ /// Logo0BA8 = 1, + /// + /// S7 200 Smart + /// + S7200Smart = 2, + /// /// S7 300 cpu type /// diff --git a/S7.Net/Protocol/ConnectionRequest.cs b/S7.Net/Protocol/ConnectionRequest.cs index 83f568f..9c6c5bb 100644 --- a/S7.Net/Protocol/ConnectionRequest.cs +++ b/S7.Net/Protocol/ConnectionRequest.cs @@ -61,6 +61,7 @@ namespace S7.Net.Protocol case CpuType.Logo0BA8: // The actual values are probably on a per-project basis return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x01, 0x02)); + case CpuType.S7200Smart: case CpuType.S71200: case CpuType.S71500: case CpuType.S7300: From 6465e3c8c7ae7200ad9954d1594915540a28f0e6 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 4 Jun 2021 21:58:15 +0200 Subject: [PATCH 07/13] Move GetDefaultTsapPair to TsapPair class --- S7.Net/Protocol/ConnectionRequest.cs | 53 +--------------------- S7.Net/Protocol/TsapPair.cs | 68 +++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 53 deletions(-) diff --git a/S7.Net/Protocol/ConnectionRequest.cs b/S7.Net/Protocol/ConnectionRequest.cs index 9c6c5bb..19b441c 100644 --- a/S7.Net/Protocol/ConnectionRequest.cs +++ b/S7.Net/Protocol/ConnectionRequest.cs @@ -6,7 +6,7 @@ namespace S7.Net.Protocol { public static byte[] GetCOTPConnectionRequest(CpuType cpu, Int16 rack, Int16 slot) { - var tsapPair = GetDefaultTsapPair(cpu, rack, slot); + var tsapPair = TsapPair.GetDefaultTsapPair(cpu, rack, slot); byte[] bSend1 = { 3, 0, 0, 22, //TPKT @@ -28,56 +28,5 @@ namespace S7.Net.Protocol return bSend1; } - - /// - /// Builds a that can be used to connect to a PLC using the default connection - /// addresses. - /// - /// - /// The remote TSAP is constructed using new Tsap(0x03, (byte) ((rack << 5) | slot)). - /// - /// The CPU type of the PLC. - /// The rack of the PLC's network card. - /// The slot of the PLC's network card. - /// A TSAP pair that matches the given parameters. - /// The is invalid. - /// - /// -or- - /// - /// The parameter is greater than 15. - /// - /// -or- - /// - /// The parameter is greater than 15. - public static TsapPair GetDefaultTsapPair(CpuType cpuType, int rack, int slot) - { - if (rack > 0x0F) throw InvalidRackOrSlot(rack, nameof(rack)); - if (slot > 0x0F) throw InvalidRackOrSlot(slot, nameof(slot)); - - switch (cpuType) - { - case CpuType.S7200: - return new TsapPair(new Tsap(0x10, 0x00), new Tsap(0x10, 0x00)); - case CpuType.Logo0BA8: - // The actual values are probably on a per-project basis - return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x01, 0x02)); - case CpuType.S7200Smart: - case CpuType.S71200: - case CpuType.S71500: - case CpuType.S7300: - case CpuType.S7400: - // Testing with S7 1500 shows only the remote TSAP needs to match. This might differ for other - // PLC types. - return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x03, (byte) ((rack << 5) | slot))); - default: - throw new ArgumentOutOfRangeException(nameof(cpuType), "Invalid CPU Type specified"); - } - } - - private static ArgumentOutOfRangeException InvalidRackOrSlot(int value, string name) - { - return new ArgumentOutOfRangeException(name, - $"Invalid {name} value specified (decimal: {value}, hexadecimal: {value:X}), maximum value is 15 (decimal) or 0x0F (hexadecimal)."); - } } } diff --git a/S7.Net/Protocol/TsapPair.cs b/S7.Net/Protocol/TsapPair.cs index 0869b23..4a329d3 100644 --- a/S7.Net/Protocol/TsapPair.cs +++ b/S7.Net/Protocol/TsapPair.cs @@ -1,4 +1,6 @@ -namespace S7.Net.Protocol +using System; + +namespace S7.Net.Protocol { /// /// Implements a pair of TSAP addresses used to connect to a PLC. @@ -26,5 +28,69 @@ Local = local; Remote = remote; } + + /// + /// Builds a that can be used to connect to a PLC using the default connection + /// addresses. + /// + /// + /// The remote TSAP is constructed using new Tsap(0x03, (byte) ((rack << 5) | slot)). + /// + /// The CPU type of the PLC. + /// The rack of the PLC's network card. + /// The slot of the PLC's network card. + /// A TSAP pair that matches the given parameters. + /// The is invalid. + /// + /// -or- + /// + /// The parameter is less than 0. + /// + /// -or- + /// + /// The parameter is greater than 15. + /// + /// -or- + /// + /// The parameter is less than 0. + /// + /// -or- + /// + /// The parameter is greater than 15. + public static TsapPair GetDefaultTsapPair(CpuType cpuType, int rack, int slot) + { + if (rack < 0) throw InvalidRackOrSlot(rack, nameof(rack), "minimum", 0); + if (rack > 0x0F) throw InvalidRackOrSlot(rack, nameof(rack), "maximum", 0x0F); + + if (slot < 0) throw InvalidRackOrSlot(slot, nameof(slot), "minimum", 0); + if (slot > 0x0F) throw InvalidRackOrSlot(slot, nameof(slot), "maximum", 0x0F); + + switch (cpuType) + { + case CpuType.S7200: + return new TsapPair(new Tsap(0x10, 0x00), new Tsap(0x10, 0x00)); + case CpuType.Logo0BA8: + // The actual values are probably on a per-project basis + return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x01, 0x02)); + case CpuType.S7200Smart: + case CpuType.S71200: + case CpuType.S71500: + case CpuType.S7300: + case CpuType.S7400: + // Testing with S7 1500 shows only the remote TSAP needs to match. This might differ for other + // PLC types. + return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x03, (byte) ((rack << 5) | slot))); + default: + throw new ArgumentOutOfRangeException(nameof(cpuType), "Invalid CPU Type specified"); + } + } + + private static ArgumentOutOfRangeException InvalidRackOrSlot(int value, string name, string extrema, + int extremaValue) + { + return new ArgumentOutOfRangeException(name, + $"Invalid {name} value specified (decimal: {value}, hexadecimal: {value:X}), {extrema} value " + + $"is {extremaValue} (decimal) or {extremaValue:X} (hexadecimal)."); + } } } \ No newline at end of file From 2fae2c01d5d60624020559fe16709e7be24c3edc Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Fri, 4 Jun 2021 22:25:05 +0200 Subject: [PATCH 08/13] Add TsapPair support to PLC Add support for custom addressing by supplying a TsapPair to the PLC. CPU, Rack and Slot properties are still present to preserve backwards compatibility and to support alternate functionality based on the PLC type. --- S7.Net.UnitTest/ConnectionRequestTest.cs | 26 ++++----- S7.Net/PLC.cs | 67 +++++++++++++++++------- S7.Net/PlcAsynchronous.cs | 2 +- S7.Net/Protocol/ConnectionRequest.cs | 8 +-- 4 files changed, 63 insertions(+), 40 deletions(-) diff --git a/S7.Net.UnitTest/ConnectionRequestTest.cs b/S7.Net.UnitTest/ConnectionRequestTest.cs index f12fa16..acd2ee0 100644 --- a/S7.Net.UnitTest/ConnectionRequestTest.cs +++ b/S7.Net.UnitTest/ConnectionRequestTest.cs @@ -10,51 +10,51 @@ namespace S7.Net.UnitTest public void Test_ConnectionRequest_S7_200() { CollectionAssert.AreEqual(MakeConnectionRequest(16, 0, 16, 0), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7200, 0, 0)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7200, 0, 0))); } [TestMethod] public void Test_ConnectionRequest_S7_300() { CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7300, 0, 0)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7300, 0, 0))); CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7300, 0, 1)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7300, 0, 1))); CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7300, 1, 1)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7300, 1, 1))); } [TestMethod] public void Test_ConnectionRequest_S7_400() { CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7400, 0, 0)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7400, 0, 0))); CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7400, 0, 1)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7400, 0, 1))); CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7400, 1, 1)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S7400, 1, 1))); } [TestMethod] public void Test_ConnectionRequest_S7_1200() { CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71200, 0, 0)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71200, 0, 0))); CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71200, 0, 1)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71200, 0, 1))); CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71200, 1, 1)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71200, 1, 1))); } [TestMethod] public void Test_ConnectionRequest_S7_1500() { CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 0, 0)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71500, 0, 0))); CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 0, 1)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71500, 0, 1))); CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33), - ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 1, 1)); + ConnectionRequest.GetCOTPConnectionRequest(TsapPair.GetDefaultTsapPair(CpuType.S71500, 1, 1))); } private static byte[] MakeConnectionRequest(byte sourceTsap1, byte sourceTsap2, byte destTsap1, byte destTsap2) diff --git a/S7.Net/PLC.cs b/S7.Net/PLC.cs index 6180ad5..5ec5618 100644 --- a/S7.Net/PLC.cs +++ b/S7.Net/PLC.cs @@ -15,6 +15,11 @@ namespace S7.Net /// public partial class Plc : IDisposable { + /// + /// The default port for the S7 protocol. + /// + public const int DefaultPort = 102; + private readonly TaskQueue queue = new TaskQueue(); private const int CONNECTION_TIMED_OUT_ERROR_CODE = 10060; @@ -36,6 +41,11 @@ namespace S7.Net /// public int Port { get; } + /// + /// The TSAP addresses used during the connection request. + /// + public TsapPair TsapPair { get; set; } + /// /// CPU type of the PLC /// @@ -108,25 +118,14 @@ namespace S7.Net /// /// CpuType of the PLC (select from the enum) /// Ip address of the PLC - /// Port address of the PLC, default 102 /// rack of the PLC, usually it's 0, but check in the hardware configuration of Step7 or TIA portal /// slot of the CPU of the PLC, usually it's 2 for S7300-S7400, 0 for S7-1200 and S7-1500. /// If you use an external ethernet card, this must be set accordingly. - public Plc(CpuType cpu, string ip, int port, Int16 rack, Int16 slot) + public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot) + : this(cpu, ip, DefaultPort, rack, slot) { - if (!Enum.IsDefined(typeof(CpuType), cpu)) - throw new ArgumentException($"The value of argument '{nameof(cpu)}' ({cpu}) is invalid for Enum type '{typeof(CpuType).Name}'.", nameof(cpu)); - - if (string.IsNullOrEmpty(ip)) - throw new ArgumentException("IP address must valid.", nameof(ip)); - - CPU = cpu; - IP = ip; - Port = port; - Rack = rack; - Slot = slot; - MaxPDUSize = 240; } + /// /// Creates a PLC object with all the parameters needed for connections. /// For S7-1200 and S7-1500, the default is rack = 0 and slot = 0. @@ -135,23 +134,51 @@ namespace S7.Net /// /// CpuType of the PLC (select from the enum) /// Ip address of the PLC + /// Port number used for the connection, default 102. /// rack of the PLC, usually it's 0, but check in the hardware configuration of Step7 or TIA portal /// slot of the CPU of the PLC, usually it's 2 for S7300-S7400, 0 for S7-1200 and S7-1500. /// If you use an external ethernet card, this must be set accordingly. - public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot) + public Plc(CpuType cpu, string ip, int port, Int16 rack, Int16 slot) + : this(ip, port, TsapPair.GetDefaultTsapPair(cpu, rack, slot)) { if (!Enum.IsDefined(typeof(CpuType), cpu)) - throw new ArgumentException($"The value of argument '{nameof(cpu)}' ({cpu}) is invalid for Enum type '{typeof(CpuType).Name}'.", nameof(cpu)); + throw new ArgumentException( + $"The value of argument '{nameof(cpu)}' ({cpu}) is invalid for Enum type '{typeof(CpuType).Name}'.", + nameof(cpu)); + CPU = cpu; + Rack = rack; + Slot = slot; + } + + /// + /// Creates a PLC object with all the parameters needed for connections. + /// For S7-1200 and S7-1500, the default is rack = 0 and slot = 0. + /// You need slot > 0 if you are connecting to external ethernet card (CP). + /// For S7-300 and S7-400 the default is rack = 0 and slot = 2. + /// + /// Ip address of the PLC + /// The TSAP addresses used for the connection request. + public Plc(string ip, TsapPair tsapPair) : this(ip, DefaultPort, tsapPair) + { + } + + /// + /// Creates a PLC object with all the parameters needed for connections. Use this constructor + /// if you want to manually override the TSAP addresses used during the connection request. + /// + /// Ip address of the PLC + /// Port number used for the connection, default 102. + /// The TSAP addresses used for the connection request. + public Plc(string ip, int port, TsapPair tsapPair) + { if (string.IsNullOrEmpty(ip)) throw new ArgumentException("IP address must valid.", nameof(ip)); - CPU = cpu; IP = ip; - Port = 102; - Rack = rack; - Slot = slot; + Port = port; MaxPDUSize = 240; + TsapPair = tsapPair; } /// diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index c7a5868..030cc3d 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -60,7 +60,7 @@ namespace S7.Net private async Task RequestConnection(Stream stream, CancellationToken cancellationToken) { - var requestData = ConnectionRequest.GetCOTPConnectionRequest(CPU, Rack, Slot); + var requestData = ConnectionRequest.GetCOTPConnectionRequest(TsapPair); var response = await NoLockRequestTpduAsync(stream, requestData, cancellationToken).ConfigureAwait(false); if (response.PDUType != COTP.PduType.ConnectionConfirmed) diff --git a/S7.Net/Protocol/ConnectionRequest.cs b/S7.Net/Protocol/ConnectionRequest.cs index 19b441c..9dbd396 100644 --- a/S7.Net/Protocol/ConnectionRequest.cs +++ b/S7.Net/Protocol/ConnectionRequest.cs @@ -1,13 +1,9 @@ -using System; - -namespace S7.Net.Protocol +namespace S7.Net.Protocol { internal static class ConnectionRequest { - public static byte[] GetCOTPConnectionRequest(CpuType cpu, Int16 rack, Int16 slot) + public static byte[] GetCOTPConnectionRequest(TsapPair tsapPair) { - var tsapPair = TsapPair.GetDefaultTsapPair(cpu, rack, slot); - byte[] bSend1 = { 3, 0, 0, 22, //TPKT 17, //COTP Header Length From a23408d67ef968b54dacd637fc64611d284f2864 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Sat, 5 Jun 2021 17:58:05 +0200 Subject: [PATCH 09/13] Add Net5.0 to test matrix --- .github/workflows/test.yml | 2 +- S7.sln | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 226aa6f..fa94a91 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [windows-latest, ubuntu-latest, macos-latest] - test-framework: [netcoreapp3.1] + test-framework: [netcoreapp3.1, net5.0] include: - os: ubuntu-latest installSnap7: true diff --git a/S7.sln b/S7.sln index 2ae0e52..dbfaf5c 100644 --- a/S7.sln +++ b/S7.sln @@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject appveyor.yml = appveyor.yml README.md = README.md + .github\workflows\test.yml = .github\workflows\test.yml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "S7.Net.UnitTest", "S7.Net.UnitTest\S7.Net.UnitTest.csproj", "{303CCED6-9ABC-4899-A509-743341AAA804}" From fc9c33fdaf6bd350bdc973f709778f507f1dfbb8 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Sat, 5 Jun 2021 17:59:18 +0200 Subject: [PATCH 10/13] Add Net5.0 to test project as well. --- 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 e836b62..eb63e48 100644 --- a/S7.Net.UnitTest/S7.Net.UnitTest.csproj +++ b/S7.Net.UnitTest/S7.Net.UnitTest.csproj @@ -1,7 +1,7 @@  - net452;netcoreapp3.1 + net452;netcoreapp3.1;net5.0 true Properties\S7.Net.snk From 4541a7ebb77f89da95bb718be0ac938c16680e40 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Sat, 5 Jun 2021 18:11:06 +0200 Subject: [PATCH 11/13] Try adjusting the dotnet setup version. --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fa94a91..4bb5ea4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,6 +23,7 @@ jobs: installSnap7: true - os: windows-latest test-framework: net452 + fail-fast: false steps: - uses: actions/checkout@v2 @@ -42,7 +43,7 @@ jobs: - name: Setup Dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '3.1.x' # SDK Version to use; x will use the latest version of the 3.1 channel + dotnet-version: '5.0.x' # SDK Version to use; x will use the latest version of the 3.1 channel - name: Restore run: dotnet restore S7.Net.UnitTest From bcde65120c4be54b24e1a1226c1878f5668c2235 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Sat, 5 Jun 2021 18:37:35 +0200 Subject: [PATCH 12/13] Github Actions Net5.0 (#1) * Try integrating dotnet setup version into build matrix. * Another attempt * change matrix. * don't add dotnet-sdk as main matrix variable * remove test framework as well. * or maybe not * Fix copy paste mixup --- .github/workflows/test.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4bb5ea4..f0b2569 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,11 +18,30 @@ jobs: test-framework: [netcoreapp3.1, net5.0] include: - os: ubuntu-latest + test-framework: netcoreapp3.1 installSnap7: true + dotnet-sdk: '3.1.x' + - os: ubuntu-latest + test-framework: net5.0 + installSnap7: true + dotnet-sdk: '5.0.x' - os: macos-latest + test-framework: netcoreapp3.1 installSnap7: true + dotnet-sdk: '3.1.x' + - os: macos-latest + test-framework: net5.0 + installSnap7: true + dotnet-sdk: '5.0.x' + - os: windows-latest + test-framework: netcoreapp3.1 + dotnet-sdk: '3.1.x' + - os: windows-latest + test-framework: net5.0 + dotnet-sdk: '5.0.x' - os: windows-latest test-framework: net452 + dotnet-sdk: '5.0.x' fail-fast: false steps: @@ -43,7 +62,7 @@ jobs: - name: Setup Dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '5.0.x' # SDK Version to use; x will use the latest version of the 3.1 channel + dotnet-version: ${{ matrix.dotnet-sdk }} - name: Restore run: dotnet restore S7.Net.UnitTest From 5c24e801fddfac1783456240882ffaa56bb9dc6b Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Sat, 5 Jun 2021 18:50:28 +0200 Subject: [PATCH 13/13] Add Actions Nuget Cache (#2) * Try out action cache * fix syntax error * Now lets see what happens if it hits the cache --- .github/workflows/test.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f0b2569..0d13810 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,9 +63,18 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: ${{ matrix.dotnet-sdk }} + + - name: Nuget Cache + uses: actions/cache@v2 + 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') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.test-framework }}-nuget - name: Restore run: dotnet restore S7.Net.UnitTest - name: Test - run: dotnet test --no-restore --nologo --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }} \ No newline at end of file + run: dotnet test --no-restore --nologo --verbosity normal --logger GitHubActions --framework ${{ matrix.test-framework }}