13 Commits

Author SHA1 Message Date
793a088c5b Update editorconfig 2025-08-08 17:52:06 +02:00
b216304482 Added missing snapshot tests 2025-08-07 16:42:54 +02:00
e87649ff43 Added basic information for docs 2025-08-07 16:21:10 +02:00
11295ea247 Added automatic documentation generation on Release 2025-08-07 16:20:33 +02:00
799a014b15 Moving structure as preparation for docs 2025-08-06 21:08:55 +02:00
885079ae70 Fixed wrong R/W timeout handling on tcp listener handling. v0.4.2 2025-02-07 17:05:23 +01:00
17fc216658 Bump to v0.4.1 2025-02-06 17:04:38 +01:00
885231466b Fix of #7 tried to set DualMode on IPv4 network 2025-02-06 17:02:52 +01:00
5b8a2a8af1 Changed UnitTests for further improvements on testing 2025-02-05 20:46:13 +01:00
980dab22f3 Small .NET optimizations 2025-02-03 22:29:42 +01:00
9270f49519 CLI writing to error 2025-02-03 22:28:59 +01:00
241a9d114c Async optimization 2025-02-03 22:28:31 +01:00
9283b04971 Provide license as file on NuGet.
This will provide the file also in the NuGet package and can be seen when looking into projects dependencies.
2025-02-03 22:26:06 +01:00
130 changed files with 1714 additions and 1640 deletions

View File

@@ -91,6 +91,7 @@ dotnet_naming_style.prefix_underscore.required_prefix = _
[*.cs] [*.cs]
csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:suggestion csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:suggestion
csharp_style_prefer_primary_constructors = false
# Only use "var" when it's obvious what the variable type is # Only use "var" when it's obvious what the variable type is
csharp_style_var_for_built_in_types = false:warning csharp_style_var_for_built_in_types = false:warning
@@ -157,3 +158,6 @@ csharp_space_between_square_brackets = false
[*.{xml,csproj,targets,props,json,yml}] [*.{xml,csproj,targets,props,json,yml}]
indent_size = 2 indent_size = 2
indent_style = space indent_style = space
[*.{json,yml}]
end_of_line = lf

3
.gitignore vendored
View File

@@ -2,6 +2,9 @@
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons. ## files generated by popular Visual Studio add-ons.
docs/api
docs/_site
# User-specific files # User-specific files
*.suo *.suo
*.user *.user

View File

@@ -22,8 +22,7 @@ build-debug:
script: script:
- shopt -s globstar - shopt -s globstar
- mkdir ./artifacts - mkdir ./artifacts
- dotnet restore --no-cache --force - dotnet build -c Debug --nologo
- dotnet build -c Debug --nologo --no-restore --no-incremental
- mv ./**/*.nupkg ./artifacts/ - mv ./**/*.nupkg ./artifacts/
- mv ./**/*.snupkg ./artifacts/ - mv ./**/*.snupkg ./artifacts/
artifacts: artifacts:
@@ -65,7 +64,7 @@ deploy-debug:
tags: tags:
- docker - docker
- lnx - lnx
- 64bit - server
rules: rules:
- if: $CI_COMMIT_TAG == null - if: $CI_COMMIT_TAG == null
script: script:
@@ -84,8 +83,7 @@ build-release:
script: script:
- shopt -s globstar - shopt -s globstar
- mkdir ./artifacts - mkdir ./artifacts
- dotnet restore --no-cache --force - dotnet build -c Release --nologo
- dotnet build -c Release --nologo --no-restore --no-incremental
- mv ./**/*.nupkg ./artifacts/ - mv ./**/*.nupkg ./artifacts/
- mv ./**/*.snupkg ./artifacts/ - mv ./**/*.snupkg ./artifacts/
artifacts: artifacts:
@@ -104,7 +102,6 @@ test-release:
- amd64 - amd64
rules: rules:
- if: $CI_COMMIT_TAG != null - if: $CI_COMMIT_TAG != null
coverage: /Branch coverage[\s\S].+%/
before_script: before_script:
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools - dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
script: script:
@@ -127,8 +124,34 @@ deploy-release:
tags: tags:
- docker - docker
- lnx - lnx
- 64bit - server
rules: rules:
- if: $CI_COMMIT_TAG != null - if: $CI_COMMIT_TAG != null
script: script:
- dotnet nuget push -k $NUGET_APIKEY -s https://api.nuget.org/v3/index.json --skip-duplicate artifacts/*.nupkg - dotnet nuget push -k $NUGET_APIKEY -s https://api.nuget.org/v3/index.json --skip-duplicate artifacts/*.nupkg
publish-docs:
variables:
DOCFX_SOURCE_REPOSITORY_URL: "https://github.com/AM-WD/AMWD.Protocols.Modbus"
stage: deploy
tags:
- docker
- lnx
- server
rules:
- if: $CI_COMMIT_TAG != null
before_script:
- apt-get update
- apt-get -y install zip unzip
- dotnet tool install docfx --tool-path /dotnet-tools
script:
# Build the docs
- dotnet build -c Release --nologo
- /dotnet-tools/docfx metadata docs/docfx.json
- /dotnet-tools/docfx build docs/docfx.json
# Deploy the docs
- cd docs/_site
- zip -r ../docs.zip *
- curl --user "$DOCS_DEPLOY_USER:$DOCS_DEPLOY_PASS" -F docs=modbus -F dump=@../docs.zip "$DOCS_DEPLOY_URL"

View File

@@ -1,30 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<PackageId>AMWD.Protocols.Modbus.Tcp</PackageId>
<AssemblyName>amwd-modbus-tcp</AssemblyName>
<RootNamespace>AMWD.Protocols.Modbus.Tcp</RootNamespace>
<Product>Modbus TCP Protocol</Product>
<Description>Implementation of the Modbus protocol communicating via TCP.</Description>
<PackageTags>Modbus Protocol Network TCP LAN</PackageTags>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(SolutionDir)/AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs" Link="Extensions/ArrayExtensions.cs" />
<Compile Include="$(SolutionDir)/AMWD.Protocols.Modbus.Common/Extensions/ReaderWriterLockSlimExtensions.cs" Link="Extensions/ReaderWriterLockSlimExtensions.cs" />
<Compile Include="$(SolutionDir)/AMWD.Protocols.Modbus.Common/Utils/AsyncQueue.cs" Link="Utils/AsyncQueue.cs" />
<Compile Include="$(SolutionDir)/AMWD.Protocols.Modbus.Common/Utils/RequestQueueItem.cs" Link="Utils/RequestQueueItem.cs" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Common\AMWD.Protocols.Modbus.Common.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,32 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<CollectCoverage>true</CollectCoverage>
<CoverletOutputFormat>Cobertura</CoverletOutputFormat>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="MSTest.TestAdapter" Version="3.7.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.7.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Common\AMWD.Protocols.Modbus.Common.csproj" />
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Serial\AMWD.Protocols.Modbus.Serial.csproj" />
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Tcp\AMWD.Protocols.Modbus.Tcp.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,5 +0,0 @@
global using System;
global using System.Linq;
global using AMWD.Protocols.Modbus.Common;
global using AMWD.Protocols.Modbus.Common.Contracts;
global using Microsoft.VisualStudio.TestTools.UnitTesting;

View File

@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.8.34525.116 VisualStudioVersion = 17.8.34525.116
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Common", "AMWD.Protocols.Modbus.Common\AMWD.Protocols.Modbus.Common.csproj", "{2B7689D8-9E56-4DEB-B40E-F70DB4A6F250}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Common", "src\AMWD.Protocols.Modbus.Common\AMWD.Protocols.Modbus.Common.csproj", "{2B7689D8-9E56-4DEB-B40E-F70DB4A6F250}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C43172F-63F3-455A-A5FC-CAE7492A969B}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C43172F-63F3-455A-A5FC-CAE7492A969B}"
EndProject EndProject
@@ -29,15 +29,30 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{C8065AE3
Directory.Build.props = Directory.Build.props Directory.Build.props = Directory.Build.props
EndProjectSection EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Tests", "AMWD.Protocols.Modbus.Tests\AMWD.Protocols.Modbus.Tests.csproj", "{146070C4-E922-4F5A-AD6F-9A899186E26E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Tests", "test\AMWD.Protocols.Modbus.Tests\AMWD.Protocols.Modbus.Tests.csproj", "{146070C4-E922-4F5A-AD6F-9A899186E26E}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Tcp", "AMWD.Protocols.Modbus.Tcp\AMWD.Protocols.Modbus.Tcp.csproj", "{8C888A84-CD09-4087-B5DA-67708ABBABA2}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Tcp", "src\AMWD.Protocols.Modbus.Tcp\AMWD.Protocols.Modbus.Tcp.csproj", "{8C888A84-CD09-4087-B5DA-67708ABBABA2}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Serial", "AMWD.Protocols.Modbus.Serial\AMWD.Protocols.Modbus.Serial.csproj", "{D966826F-EE6C-4BC0-9185-C2A9A50FD586}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Serial", "src\AMWD.Protocols.Modbus.Serial\AMWD.Protocols.Modbus.Serial.csproj", "{D966826F-EE6C-4BC0-9185-C2A9A50FD586}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliClient", "CliClient\CliClient.csproj", "{B0E53462-B0ED-4685-8AA5-948DC160EE27}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliClient", "tool\CliClient\CliClient.csproj", "{B0E53462-B0ED-4685-8AA5-948DC160EE27}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliProxy", "CliProxy\CliProxy.csproj", "{AC922E80-E9B6-493D-B1D1-752527E883ED}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliProxy", "tool\CliProxy\CliProxy.csproj", "{AC922E80-E9B6-493D-B1D1-752527E883ED}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
src\Directory.Build.props = src\Directory.Build.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0C2CC421-9808-4CA2-BEA8-11493467DBCE}"
ProjectSection(SolutionItems) = preProject
test\Directory.Build.props = test\Directory.Build.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tool", "tool", "{3429CE19-211E-4AFA-9629-D7E1A360B7AC}"
ProjectSection(SolutionItems) = preProject
tool\Directory.Build.props = tool\Directory.Build.props
EndProjectSection
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -74,9 +89,15 @@ Global
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{2B7689D8-9E56-4DEB-B40E-F70DB4A6F250} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{A5A9AEA2-3AFF-4536-9FF9-34663DA4D0AD} = {0C43172F-63F3-455A-A5FC-CAE7492A969B} {A5A9AEA2-3AFF-4536-9FF9-34663DA4D0AD} = {0C43172F-63F3-455A-A5FC-CAE7492A969B}
{2ED08B2B-1F72-4E1E-9586-1DC6BEFD7BA7} = {0C43172F-63F3-455A-A5FC-CAE7492A969B} {2ED08B2B-1F72-4E1E-9586-1DC6BEFD7BA7} = {0C43172F-63F3-455A-A5FC-CAE7492A969B}
{C8065AE3-BA87-49AC-8100-C85D6DF7E436} = {0C43172F-63F3-455A-A5FC-CAE7492A969B} {C8065AE3-BA87-49AC-8100-C85D6DF7E436} = {0C43172F-63F3-455A-A5FC-CAE7492A969B}
{146070C4-E922-4F5A-AD6F-9A899186E26E} = {0C2CC421-9808-4CA2-BEA8-11493467DBCE}
{8C888A84-CD09-4087-B5DA-67708ABBABA2} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{D966826F-EE6C-4BC0-9185-C2A9A50FD586} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{B0E53462-B0ED-4685-8AA5-948DC160EE27} = {3429CE19-211E-4AFA-9629-D7E1A360B7AC}
{AC922E80-E9B6-493D-B1D1-752527E883ED} = {3429CE19-211E-4AFA-9629-D7E1A360B7AC}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E4FD8EF0-3594-4994-BE80-5FADA5EE17B4} SolutionGuid = {E4FD8EF0-3594-4994-BE80-5FADA5EE17B4}

View File

@@ -7,7 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
_nothing changed yet_ ### Added
- New automatic documentation generation using docfx.
- Additional articles for the documentation.
### Changed
- Reorganized folder structure to allow documentation generation.
## [v0.4.2] (2025-02-07)
### Fixed
- Fixing issue with R/W timeouts while processing client requests on the `ModbusTcpProxy`.
## [v0.4.1] (2025-02-06)
### Changed
- Async methods do not return on captured context anymore (`Task.ConfigureAwait(false)`).
### Fixed
- Set `Socket.DualMode` on IPv4 network address is not allowed (`ModbusTcpProxy`).
## [v0.4.0] (2025-01-29) ## [v0.4.0] (2025-01-29)
@@ -77,7 +102,9 @@ So this tag is only here for documentation purposes of the NuGet Gallery.
[Unreleased]: https://github.com/AM-WD/AMWD.Protocols.Modbus/compare/v0.4.0...HEAD [Unreleased]: https://github.com/AM-WD/AMWD.Protocols.Modbus/compare/v0.4.2...HEAD
[v0.4.2]: https://github.com/AM-WD/AMWD.Protocols.Modbus/compare/v0.4.1...v0.4.2
[v0.4.1]: https://github.com/AM-WD/AMWD.Protocols.Modbus/compare/v0.4.0...v0.4.1
[v0.4.0]: https://github.com/AM-WD/AMWD.Protocols.Modbus/compare/v0.3.2...v0.4.0 [v0.4.0]: https://github.com/AM-WD/AMWD.Protocols.Modbus/compare/v0.3.2...v0.4.0
[v0.3.2]: https://github.com/AM-WD/AMWD.Protocols.Modbus/compare/v0.3.1...v0.3.2 [v0.3.2]: https://github.com/AM-WD/AMWD.Protocols.Modbus/compare/v0.3.1...v0.3.2
[v0.3.1]: https://github.com/AM-WD/AMWD.Protocols.Modbus/compare/v0.3.0...v0.3.1 [v0.3.1]: https://github.com/AM-WD/AMWD.Protocols.Modbus/compare/v0.3.0...v0.3.1

View File

@@ -1,33 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>modbus-client</AssemblyName>
<RootNamespace>AMWD.Protocols.Modbus.CliClient</RootNamespace>
<Product>Modbus CLI client</Product>
<Description>Small CLI client for Modbus communication.</Description>
<IsPackable>false</IsPackable>
<SignAssembly>false</SignAssembly>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<None Remove="$(SolutionDir)/package-icon.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Serial\AMWD.Protocols.Modbus.Serial.csproj" />
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Tcp\AMWD.Protocols.Modbus.Tcp.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,33 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>modbus-proxy</AssemblyName>
<RootNamespace>AMWD.Protocols.Modbus.CliProxy</RootNamespace>
<Product>Modbus CLI proxy</Product>
<Description>Small CLI proxy to forward messages.</Description>
<IsPackable>false</IsPackable>
<SignAssembly>false</SignAssembly>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<None Remove="$(SolutionDir)/package-icon.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Serial\AMWD.Protocols.Modbus.Serial.csproj" />
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Tcp\AMWD.Protocols.Modbus.Tcp.csproj" />
</ItemGroup>
</Project>

View File

@@ -3,59 +3,18 @@
<LangVersion>12.0</LangVersion> <LangVersion>12.0</LangVersion>
<NrtRevisionFormat>{semvertag:main}{!:-dev}</NrtRevisionFormat> <NrtRevisionFormat>{semvertag:main}{!:-dev}</NrtRevisionFormat>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/AM-WD/AMWD.Protocols.Modbus.git</RepositoryUrl>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<EmbedUntrackedSources>false</EmbedUntrackedSources>
<PackageIcon>package-icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Title>Modbus Protocol for .NET</Title> <Title>Modbus Protocol for .NET</Title>
<Company>AM.WD</Company> <Company>AM.WD</Company>
<Authors>Andreas Müller</Authors> <Authors>Andreas Müller</Authors>
<Copyright>© {copyright:2018-} AM.WD</Copyright> <Copyright>© {copyright:2018-} AM.WD</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>$(SolutionDir)/AMWD.Protocols.Modbus.snk</AssemblyOriginatorKeyFile> <PublicKey>0024000004800000940000000602000000240000525341310004000001000100adcc4f9f5bb3ac73cb30661f6f35772b8f90a74412925764a960af06ef125bdcec05ed1d139503d5203fb72aa3fa74bab58e82ac2a6cd4b650f8cbf7086a71bc2dfc67e95b8d26d776d60856acf3121f831529b1a4dee91b34ac84f95f71a1165b7783edb591929ba2a684100c92bbed8859c7266fb507f6f55bb6f7fcac80b4</PublicKey>
<MoqPublicKey>0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7</MoqPublicKey>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(GITLAB_CI)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup Condition="'$(SignAssembly)' != 'true'">
<InternalsVisibleTo Include="AMWD.Protocols.Modbus.Tests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup Condition="'$(SignAssembly)' == 'true'">
<InternalsVisibleTo Include="AMWD.Protocols.Modbus.Tests" PublicKey="0024000004800000940000000602000000240000525341310004000001000100adcc4f9f5bb3ac73cb30661f6f35772b8f90a74412925764a960af06ef125bdcec05ed1d139503d5203fb72aa3fa74bab58e82ac2a6cd4b650f8cbf7086a71bc2dfc67e95b8d26d776d60856acf3121f831529b1a4dee91b34ac84f95f71a1165b7783edb591929ba2a684100c92bbed8859c7266fb507f6f55bb6f7fcac80b4" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" PublicKey="0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" />
</ItemGroup>
<ItemGroup Condition="'$(GITLAB_CI)' == 'true'">
<SourceLinkGitLabHost Include="$(CI_SERVER_HOST)" Version="$(CI_SERVER_VERSION)" />
<PackageReference Include="Microsoft.SourceLink.GitLab" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="$(SolutionDir)/package-icon.png" Pack="true" PackagePath="/" /> <PackageReference Include="AMWD.NetRevisionTask" Version="1.3.0">
</ItemGroup>
<ItemGroup>
<PackageReference Include="AMWD.NetRevisionTask" Version="1.2.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@@ -2,9 +2,12 @@
Here you can find a basic implementation of the Modbus protocol. Here you can find a basic implementation of the Modbus protocol.
![NuGet Version](https://shields.io/nuget/v/AMWD.Protocols.Modbus.Common?style=flat&logo=nuget)
![Test Coverage](https://git.am-wd.de/am-wd/amwd.protocols.modbus/badges/main/coverage.svg?style=flat)
## Overview ## Overview
The project is divided into four parts. The project is divided into multiple parts.
To be mentioned at the beginning: To be mentioned at the beginning:
Only the clients are build very modular to fit any requirement reached on the first implementation back in 2018 ([see here]). Only the clients are build very modular to fit any requirement reached on the first implementation back in 2018 ([see here]).
@@ -35,14 +38,13 @@ It uses a specific TCP connection implementation and plugs all things from the C
--- ---
Published under [MIT License] (see [choose a license]) Published under [MIT License] (see [choose a license])
[![Buy me a Coffee](https://shields.am-wd.de/badge/PayPal-Buy_me_a_Coffee-yellow?style=flat&logo=paypal)](https://link.am-wd.de/donate) [![Buy me a Coffee](https://shields.io/badge/PayPal-Buy_me_a_Coffee-yellow?style=flat&logo=paypal)](https://link.am-wd.de/donate)
[![built with Codeium](https://codeium.com/badges/main)](https://link.am-wd.de/codeium)
[see here]: https://github.com/andreasAMmueller/Modbus [see here]: https://github.com/andreasAMmueller/Modbus
[Common]: AMWD.Protocols.Modbus.Common/README.md [Common]: src/AMWD.Protocols.Modbus.Common/README.md
[Serial]: AMWD.Protocols.Modbus.Serial/README.md [Serial]: src/AMWD.Protocols.Modbus.Serial/README.md
[TCP]: AMWD.Protocols.Modbus.Tcp/README.md [TCP]: src/AMWD.Protocols.Modbus.Tcp/README.md
[MIT License]: LICENSE.txt [MIT License]: LICENSE.txt
[choose a license]: https://choosealicense.com/licenses/mit/ [choose a license]: https://choosealicense.com/licenses/mit/

View File

@@ -0,0 +1,106 @@
# Getting Started
To begin, you need at least the [Common] package.
In this package you'll find everything you need to implement you own client as the package contains the protocol implementations (`TCP`, `RTU` and `ASCII`).
The [`ModbusClientBase`](~/api/AMWD.Protocols.Modbus.Common.Contracts.ModbusClientBase.yml) is the place, where most of the magic happens.
In this base client you have all known (and implemented) methods to request a device.
The Protocol implementations are the other magic place to be, as there the request will be converted into bits and bytes, before they get transfered.
## Using a TCP client
To use a TCP Modbus client, you need the [Common] package and the [TCP] package installed.
```cs
using AMWD.Protocols.Modbus.Common;
using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Common.Protocols;
using AMWD.Protocols.Modbus.Tcp;
namespace ConsoleApp;
internal class Program
{
string hostname = "modbus-device.internal";
int port = 502;
byte unitId = 5;
ushort startAddress = 19000;
ushort count = 2;
using var client = new ModbusTcpClient(hostname, port);
await client.ConnectAsync(CancellationToken.None);
var holdingRegisters = await client.ReadHoldingRegistersAsync(unitId, startAddress, count);
float voltage = holdingRegisters.GetSingle();
Console.WriteLine($"The voltage of the device #{unitId} between L1 and N is {voltage:N2}V.");
}
```
This will automatically create a TCP client using the TCP protocol.
If you want to change the protocol sent over TCP, you can specify it:
```cs
// [...] other code
using var client = new ModbusTcpClient(hostname, port)
{
Protocol = new RtuProtocol()
};
// [...] other code
```
## Using a Serial client
To use a Serial Modbus client, you need the [Common] package and the [Serial] package installed.
```cs
using AMWD.Protocols.Modbus.Common;
using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Common.Protocols;
using AMWD.Protocols.Modbus.Serial;
namespace ConsoleApp;
internal class Program
{
string serialPort = "/dev/ttyUSB0";
byte unitId = 5;
ushort startAddress = 19000;
ushort count = 2;
using var client = new ModbusSerialClient(serialPort);
await client.ConnectAsync(CancellationToken.None);
var holdingRegisters = await client.ReadHoldingRegistersAsync(unitId, startAddress, count);
float voltage = holdingRegisters.GetSingle();
Console.WriteLine($"The voltage of the device #{unitId} between L1 and N is {voltage:N2}V.");
}
```
This will automatically create a Serial client using the RTU protocol.
If you want to change the protocol sent over serial line, you can specify it:
```cs
// [...] other code
using var client = new ModbusSerialClient(serialPort)
{
Protocol = new AsciiProtocol()
};
// [...] other code
```
[Common]: https://www.nuget.org/packages/AMWD.Protocols.Modbus.Common
[Serial]: https://www.nuget.org/packages/AMWD.Protocols.Modbus.Serial
[TCP]: https://www.nuget.org/packages/AMWD.Protocols.Modbus.Tcp

View File

@@ -0,0 +1,20 @@
# Introduction
During my training, I came into contact with the Modbus protocol.
The implementation I developed at that time was very cumbersome and rigid.
There were huge inheritance hierarchies and the design was very confusing.
In 2018, I wanted to do better and completely redesigned the library.
After changing companies, this library could be integrated and tested under real-world conditions.
This quickly led to new challenges and some specific requirements were implemented.
This was the first time that both TCP and the RTU protocol were fully implemented.
However, the structure of the library also revealed problems and was too rigid for the requirements.
Therefore, in 2024, there was a new development from scratch, which now exists and has already been tested by some eager people THANK YOU SO MUCH!
The focus is, of course, on the development of the protocol and the clients. However, a server implementation (TCP/RTU) is also available.
For detailed changes of the current development, see the [CHANGELOG].
[CHANGELOG]: https://github.com/AM-WD/AMWD.Protocols.Modbus/blob/main/CHANGELOG.md

8
docs/articles/toc.yml Normal file
View File

@@ -0,0 +1,8 @@
- name: Introduction
href: introduction.md
- name: Getting Started
href: getting-started.md
- name: GitHub
href: https://github.com/AM-WD/AMWD.Protocols.Modbus
- name: NuGet
href: https://www.nuget.org/packages?q=AMWD.Protocols.Modbus

63
docs/docfx.json Normal file
View File

@@ -0,0 +1,63 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json",
"metadata": [
{
"src": [
{
"src": "../",
"files": [
"src/AMWD.Protocols.Modbus.Common/bin/Release/netstandard2.0/amwd-modbus-common.dll",
"src/AMWD.Protocols.Modbus.Serial/bin/Release/netstandard2.0/amwd-modbus-serial.dll",
"src/AMWD.Protocols.Modbus.Tcp/bin/Release/netstandard2.0/amwd-modbus-tcp.dll"
]
}
],
"dest": "api",
"outputFormat": "apiPage"
}
],
"build": {
"content": [
{
"files": [ "**/*.{md,yml}" ],
"exclude": [ "_site/**", "obj/**" ]
}
],
"resource": [
{
"files": [ "images/**" ],
"exclude": [ "_site/**", "obj/**" ]
}
],
"output": "_site",
"template": [ "default", "modern", "templates/amwd" ],
"postProcessors": ["ExtractSearchIndex"],
"globalMetadata": {
"_appName": "Protocol for .NET",
"_appTitle": "Modbus Protocol for .NET",
"_appFooter": "<span>&copy; AM.WD &mdash; Docs generated using <a href=\"https://dotnet.github.io/docfx\" target=\"_blank\">docfx</a>.</span>",
"_appLogoPath": "images/logo.svg",
"_appFaviconPath": "images/favicon.ico",
"_disableBreadcrumb": true,
"_disableContribution": true,
"_enableSearch": true,
"_enableNewTab": true,
"pdf": false
},
"markdownEngineName": "markdig",
"markdownEngineProperties": {
"alerts": {
"TODO": "alert alert-secondary"
}
},
"sitemap": {
"baseUrl": "https://docs.am-wd.de/modbus",
"priority": 0.5,
"changefreq": "weekly"
},
"noLangKeyword": false,
"keepFileLink": false,
"cleanupCacheHistory": false,
"disableGitFeatures": true
}
}

BIN
docs/images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

168
docs/images/logo.svg Normal file
View File

@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="103.59954"
height="35"
viewBox="0 0 172.52591 58.286041"
version="1.1"
id="svg17631"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
sodipodi:docname="Logo of Modbus.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview17633"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.64052329"
inkscape:cx="352.0559"
inkscape:cy="108.50503"
inkscape:window-width="1850"
inkscape:window-height="1136"
inkscape:window-x="70"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs17628">
<clipPath
id="clip1">
<path
d="M 54,12.761719 H 76 V 34 H 54 Z m 0,0"
id="path619" />
</clipPath>
<clipPath
id="clip2">
<path
d="M 71,73 H 93 V 94.441406 H 71 Z m 0,0"
id="path622" />
</clipPath>
<clipPath
id="clip3">
<path
d="M 32.160156,35 H 54 V 57 H 32.160156 Z m 0,0"
id="path625" />
</clipPath>
<clipPath
id="clip4">
<path
d="m 247,46 h 26.92578 V 67 H 247 Z m 0,0"
id="path628" />
</clipPath>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-11.970621,-119.59261)">
<path
style="fill:#f5911c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 61.396335,149.27146 c 0,11.04421 -8.9452,20.00055 -19.983825,20.00055 -11.035839,0 -19.983816,-8.95634 -19.983816,-20.00055 0,-11.04698 8.947977,-20.00054 19.983816,-20.00054 11.038625,0 19.983825,8.95356 19.983825,20.00054"
id="path944" />
<path
style="fill:#fdbf0c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 64.409645,132.37902 c 0.14496,4.13949 -3.09137,7.61274 -7.22807,7.7577 -4.13669,0.14503 -7.60717,-3.09416 -7.75212,-7.23365 -0.14217,-4.13947 3.09416,-7.61275 7.22807,-7.7577 4.1367,-0.14504 7.60718,3.09415 7.75212,7.23365"
id="path946" />
<path
style="fill:#fdbf0c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 33.038765,164.56666 c 0.144956,4.13949 -3.091374,7.61274 -7.22807,7.7577 -4.136699,0.14503 -7.607177,-3.09416 -7.752129,-7.23365 -0.144956,-4.13949 3.091375,-7.61275 7.228071,-7.7577 4.136698,-0.14504 7.607177,3.09415 7.752128,7.23365"
id="path948" />
<g
clip-path="url(#clip1)"
clip-rule="nonzero"
id="g952"
transform="matrix(0.7136082,0,0,0.7136082,-10.979154,110.48574)">
<path
style="fill:#fdbf0c;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 69.574219,13.992188 c 5.121093,2.726562 7.066406,9.089843 4.339843,14.214843 -2.722656,5.125 -9.078124,7.070313 -14.199218,4.34375 -5.121094,-2.722656 -7.066406,-9.085937 -4.34375,-14.210937 2.722656,-5.125 9.082031,-7.074219 14.203125,-4.347656"
id="path950" />
</g>
<g
clip-path="url(#clip2)"
clip-rule="nonzero"
id="g956"
transform="matrix(0.7136082,0,0,0.7136082,-10.979154,110.48574)">
<path
style="fill:#fdbf0c;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 86.625,74.648438 c 5.121094,2.722656 7.0625,9.085937 4.339844,14.214843 -2.722656,5.121094 -9.078125,7.070313 -14.199219,4.34375 -5.121094,-2.722656 -7.066406,-9.089843 -4.34375,-14.214843 2.722656,-5.125 9.082031,-7.070313 14.203125,-4.34375"
id="path954" />
</g>
<g
clip-path="url(#clip3)"
clip-rule="nonzero"
id="g960"
transform="matrix(0.7136082,0,0,0.7136082,-10.979154,110.48574)">
<path
style="fill:#fdbf0c;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 37.097656,36.910156 c 4.917969,-3.074218 11.398438,-1.578125 14.472656,3.34375 3.074219,4.925782 1.578126,11.40625 -3.34375,14.484375 -4.917968,3.074219 -11.394531,1.578125 -14.46875,-3.34375 -3.074218,-4.921875 -1.578124,-11.40625 3.339844,-14.484375"
id="path958" />
</g>
<path
style="fill:#fdbf0c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 59.032505,147.92229 c 3.5095,-2.19377 8.13401,-1.12616 10.3278,2.38613 2.191,3.51229 1.12338,8.13959 -2.38613,10.33618 -3.5095,2.19377 -8.13123,1.12617 -10.32501,-2.38613 -2.19379,-3.51229 -1.12617,-8.13959 2.38334,-10.33618"
id="path962" />
<path
style="fill:#008cc7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 86.963565,148.26516 c -0.76378,0.61047 -1.06761,1.52758 -1.22094,2.44188 -0.15333,0.91711 -0.30662,2.14084 0,2.60077 0.30385,0.45436 1.83143,0.61046 1.83143,0.61046 h 3.5095 c 0,0 1.68087,0 2.44467,-0.45993 0.76377,-0.45715 0.91429,-1.37703 1.21814,-2.5952 0.30941,-1.22373 0.15333,-1.98751 0,-2.44467 -0.1505,-0.45993 -1.67809,-0.61047 -1.67809,-0.61047 h -3.96667 c 0,0 -1.37424,-0.15333 -2.13804,0.45716 z m 2.13804,-4.20359 h 5.03708 c 0,0 3.66282,-0.22858 5.03706,1.37704 1.374245,1.60562 0.68574,3.89418 0.45716,5.4998 -0.22858,1.60283 -0.68573,3.89418 -2.28856,5.27122 -1.60283,1.37425 -4.50186,1.45231 -5.64755,1.6781 -1.14567,0.23137 -6.94653,-0.0753 -6.94653,-0.0753 0,0 -2.67324,-0.0752 -4.35133,-1.8342 -0.65228,-0.68295 0,-3.43702 0.45716,-5.4998 0.45994,-2.05999 1.37425,-4.35132 2.7485,-5.27122 1.37425,-0.91709 5.49701,-1.14567 5.49701,-1.14567"
id="path964" />
<path
style="fill:#008cc7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 107.37108,148.28468 c -0.76377,0.61047 -1.07039,1.52756 -1.22093,2.44467 -0.15333,0.91709 -0.30383,2.13803 0,2.59518 0.30385,0.45996 1.8314,0.61327 1.8314,0.61327 h 3.50951 c 0,0 1.68088,0 2.44466,-0.45996 0.7638,-0.45715 0.91432,-1.37424 1.22094,-2.59518 0.30385,-1.22373 0.1505,-1.98751 0,-2.44467 -0.1505,-0.45995 -1.68089,-0.61326 -1.68089,-0.61326 h -3.96664 c 0,0 -1.37704,-0.1505 -2.13805,0.45995 z m 11.29229,-9.59748 h 4.73323 l -3.97222,19.09738 h -4.73323 l 0.15616,-1.9875 c 0,0 -0.092,0.61882 -1.8342,1.22373 -2.15755,0.74984 -3.8245,0.85298 -3.8245,0.85298 l -4.72485,-0.24252 c 0,0 -1.67531,0 -2.44188,-1.22093 -0.76101,-1.22373 -0.30385,-4.12556 0,-5.34929 0.30662,-1.22373 1.12338,-4.97295 2.7485,-6.11027 1.15404,-0.80838 2.44187,-1.0704 3.20567,-1.0704 0.76378,0 5.03706,0 5.03706,0 0,0 2.13805,0 2.74852,0.76378 0.61324,0.76378 1.0676,1.52756 1.0676,1.52756 l 1.8342,-7.48452"
id="path966" />
<path
style="fill:#008cc7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 130.42119,147.97804 c -0.76378,0.61047 -1.06763,1.52758 -1.22094,2.44467 -0.15333,0.91711 -0.30384,2.13805 0,2.59798 0.30383,0.45715 1.83141,0.61046 1.83141,0.61046 h 3.51229 c 0,0 1.6781,0 2.44188,-0.45993 0.76099,-0.45715 0.91709,-1.37424 1.22094,-2.59518 0.30383,-1.22373 0.1505,-1.98753 0,-2.44467 -0.15333,-0.45716 -1.6781,-0.61326 -1.6781,-0.61326 h -3.96945 c 0,0 -1.37425,-0.1505 -2.13803,0.45993 z m -2.44746,9.34661 -5.03706,0.15333 4.12277,-19.09459 h 4.73044 l -1.6781,7.63783 c 0,0 1.22094,-1.22652 2.59519,-1.68089 1.37147,-0.45993 4.57992,-0.61047 4.57992,-0.61047 0,0 3.51229,-0.15333 4.88375,0.7638 1.37425,0.9143 2.13802,1.83418 1.06763,5.95974 -1.06763,4.12276 -1.7422,5.2322 -2.89904,6.10748 -0.87807,0.669 -3.66281,1.37703 -5.80365,1.37703 -2.13246,0 -4.27328,0.1505 -6.10469,-2.59797 l -0.45716,1.98473"
id="path968" />
<path
style="fill:#008cc7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 146.64742,144.14798 h 5.03706 l -1.37425,6.45593 c -0.17558,1.02859 -0.87807,2.89066 0.68574,3.30601 1.98471,0.52964 5.94023,0.6272 7.19183,-1.87601 0.64393,-1.28783 2.04884,-7.88593 2.04884,-7.88593 h 4.73044 l -2.74851,13.6366 h -4.88654 l 0.76377,-2.59797 c 0,0 -0.91429,0.76378 -2.13523,1.37424 -1.22373,0.61327 -2.59521,1.53037 -5.80086,1.37704 -3.20845,-0.15333 -5.64755,-1.37704 -5.3437,-3.66837 0.30383,-2.29136 1.83141,-10.12154 1.83141,-10.12154"
id="path970" />
<g
clip-path="url(#clip4)"
clip-rule="nonzero"
id="g974"
transform="matrix(0.7136082,0,0,0.7136082,-10.979154,110.48574)">
<path
style="fill:#008cc7;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 267.16016,52.363281 h 6.63281 c 0,0 0.63281,-2.773437 -0.85156,-3.851562 -2.12891,-1.542969 -3.64063,-1.925781 -6.41797,-1.925781 -2.78516,0 -7.70313,0 -7.70313,0 0,0 -2.46484,0.08984 -5.13281,1.070312 -1.90625,0.699219 -3.42578,2.140625 -3.85156,5.136719 -0.42969,2.996093 0,4.710937 2.99218,5.351562 2.9961,0.644531 7.0625,0 7.0625,0 0,0 5.5586,-0.210937 6.41797,0.859375 0.85157,1.070313 0.42578,1.710938 -0.85937,2.351563 -1.28516,0.640625 -3.63281,0.855469 -5.5586,0.855469 -1.92578,0 -4.70703,0 -5.13671,-0.855469 -0.42579,-0.855469 -0.42579,-1.5 -0.42579,-1.5 h -6.41796 c 0,0 -1.07032,3 -0.21485,3.855469 0.85938,0.855468 0.85938,2.570312 8.98438,2.785156 8.1289,0.210937 8.14453,-0.234375 9.84375,-0.429688 1.69922,-0.199218 4.48437,-1.285156 5.5625,-3.640625 1.0664,-2.351562 1.37109,-5.894531 0.85547,-6.636719 -0.95704,-1.367187 -3.21094,-1.925781 -5.13672,-1.925781 -1.92578,0 -9.62891,-0.214843 -9.62891,-0.214843 0,0 -2.13672,0.214843 -1.92187,-1.285157 0.21093,-1.5 2.99609,-1.5 4.92187,-1.5 1.92188,0 5.13281,0.214844 5.77344,0.859375 0.64062,0.640625 0.21094,0.640625 0.21094,0.640625"
id="path972" />
</g>
<path
style="fill:#2bb34a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 40.481474,142.70405 c 0.730335,1.32964 1.87601,0.10033 1.87601,0.10033 l -1.485754,4.06981 -3.336677,-2.75966 c 0,0 1.608406,0.47945 1.569382,-1.03697 -0.04181,-1.51641 -1.970785,-8.63018 -2.701119,-9.95706 -0.730333,-1.32964 -1.876009,-0.10316 -1.876009,-0.10316 l 1.485753,-4.06701 3.336677,2.75687 c 0,0 -1.608406,-0.47666 -1.566592,1.03975 0.03902,1.51364 1.967998,8.62741 2.698329,9.95708"
id="path976" />
<path
style="fill:#2bb34a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 46.577808,165.27748 c 0.730337,1.32964 1.876007,0.10316 1.876007,0.10316 l -1.48575,4.06979 -3.336678,-2.75966 c 0,0 1.608406,0.47667 1.56938,-1.03974 -0.04181,-1.51364 -1.970786,-8.62741 -2.701119,-9.95708 -0.727546,-1.32685 -1.87601,-0.10033 -1.87601,-0.10033 l 1.485756,-4.06699 3.336675,2.75687 c 0,0 -1.608406,-0.47947 -1.56938,1.03696 0.04181,1.51641 1.970785,8.63018 2.701119,9.95706"
id="path978" />
<path
style="fill:#2bb34a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 46.608472,145.07624 c -0.786085,1.29618 0.850193,1.67807 0.850193,1.67807 l -4.264921,0.74428 0.719184,-4.27049 c 0,0 0.390254,1.6335 1.683669,0.84182 1.290628,-0.79444 6.481008,-6.02106 7.264308,-7.32004 0.78608,-1.29621 -0.8502,-1.6781 -0.8502,-1.6781 l 4.26493,-0.74428 -0.71919,4.27049 c 0,0 -0.39025,-1.63349 -1.68088,-0.83902 -1.29341,0.79165 -6.4838,6.02106 -7.267093,7.31727"
id="path980" />
<path
style="fill:#2bb34a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 30.123007,161.6481 c -0.786085,1.29621 0.847408,1.6781 0.847408,1.6781 l -4.262136,0.74707 0.719184,-4.2733 c 0,0 0.390254,1.63349 1.680882,0.84184 1.290628,-0.79445 6.48101,-6.02107 7.267095,-7.32007 0.786084,-1.2962 -0.850196,-1.6753 -0.850196,-1.6753 l 4.262136,-0.74705 -0.719184,4.27049 c 0,0 -0.387466,-1.63349 -1.680882,-0.84184 -1.290626,0.79446 -6.481012,6.02385 -7.264307,7.32006"
id="path982" />
<path
style="fill:#2bb34a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 47.617555,151.57117 c -1.516416,-0.0307 -1.02581,1.57497 -1.02581,1.57497 l -2.779169,-3.31996 4.055859,-1.51083 c 0,0 -1.218152,1.15402 0.11429,1.87601 1.33244,0.72197 8.4518,2.60633 9.96543,2.63979 1.51642,0.0307 1.0286,-1.57775 1.0286,-1.57775 l 2.77638,3.32274 -4.05586,1.51085 c 0,0 1.21815,-1.15405 -0.11429,-1.87601 -1.33244,-0.72199 -8.4518,-2.60635 -9.96543,-2.63981"
id="path984" />
<path
style="fill:#2bb34a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 25.03576,145.56963 c -1.516418,-0.0334 -1.028599,1.57495 -1.028599,1.57495 l -2.77917,-3.32274 4.055858,-1.51085 c 0,0 -1.21815,1.15405 0.11429,1.87601 1.335227,0.72197 8.451798,2.60913 9.968214,2.63979 1.51363,0.0334 1.025811,-1.57495 1.025811,-1.57495 l 2.776384,3.32273 -4.05586,1.51085 c 0,0 1.22094,-1.15404 -0.111498,-1.8788 -1.332442,-0.71917 -8.451798,-2.60633 -9.965428,-2.63699"
id="path986" />
<path
style="fill:#008cc7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.713609"
d="m 44.501097,157.93789 h 5.954168 l 2.97708,-13.51952 5.03707,13.29094 h 5.03708 l 10.76544,-13.29094 -2.7485,13.51952 h 5.49422 l 4.35134,-19.01932 h -9.15983 l -9.61698,11.68534 -4.34856,-11.91671 h -9.61977 l -4.122758,19.25069"
id="path988" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

26
docs/index.md Normal file
View File

@@ -0,0 +1,26 @@
---
_layout: landing
---
# Modbus Protocol for .NET
This library implements the basic Modbus protocol specified at [modbus.org](https://modbus.org/tech.php).
The aim was to include all necessary steps to have a fully working client, which is capable of the common protocol versions.
## NuGet packages
Here is an overview of the latest packages.
| Package Url | Version | Short description |
|-------------|---------|-------------------|
| [AMWD.Protocols.Modbus.Common] | ![NuGet Version: Common](https://img.shields.io/nuget/v/AMWD.Protocols.Modbus.Common?style=flat-square&logo=nuget) | Common data for Modbus protocol. |
| [AMWD.Protocols.Modbus.Serial] | ![NuGet Version: Serial](https://img.shields.io/nuget/v/AMWD.Protocols.Modbus.Serial?style=flat-square&logo=nuget) | Implementation of the Modbus protocol communicating via serial line using RTU or ASCII encoding. |
| [AMWD.Protocols.Modbus.Tcp] | ![NuGet Version: TCP](https://img.shields.io/nuget/v/AMWD.Protocols.Modbus.Tcp?style=flat-square&logo=nuget) | Implementation of the Modbus protocol communicating via TCP. |
[AMWD.Protocols.Modbus.Common]: https://www.nuget.org/packages/AMWD.Protocols.Modbus.Common
[AMWD.Protocols.Modbus.Serial]: https://www.nuget.org/packages/AMWD.Protocols.Modbus.Serial
[AMWD.Protocols.Modbus.Tcp]: https://www.nuget.org/packages/AMWD.Protocols.Modbus.Tcp

3
docs/templates/amwd/public/main.css vendored Normal file
View File

@@ -0,0 +1,3 @@
#logo {
margin-right: 8px;
}

4
docs/toc.yml Normal file
View File

@@ -0,0 +1,4 @@
- name: API
href: api/
- name: Articles
href: articles/

View File

@@ -1,8 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<PackageId>AMWD.Protocols.Modbus.Common</PackageId> <PackageId>AMWD.Protocols.Modbus.Common</PackageId>
<AssemblyName>amwd-modbus-common</AssemblyName> <AssemblyName>amwd-modbus-common</AssemblyName>
<RootNamespace>AMWD.Protocols.Modbus.Common</RootNamespace> <RootNamespace>AMWD.Protocols.Modbus.Common</RootNamespace>
@@ -12,8 +10,4 @@
<PackageTags>Modbus Protocol</PackageTags> <PackageTags>Modbus Protocol</PackageTags>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup>
</Project> </Project>

View File

@@ -10,19 +10,27 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
/// <summary> /// <summary>
/// Base implementation of a Modbus client. /// Base implementation of a Modbus client.
/// </summary> /// </summary>
public abstract class ModbusClientBase : IDisposable /// <remarks>
/// Initializes a new instance of the <see cref="ModbusClientBase"/> class with a specific <see cref="IModbusConnection"/>.
/// </remarks>
/// <param name="connection">The <see cref="IModbusConnection"/> responsible for invoking the requests.</param>
/// <param name="disposeConnection">
/// <see langword="true"/> if the connection should be disposed of by Dispose(),
/// <see langword="false"/> otherwise if you inted to reuse the connection.
/// </param>
public abstract class ModbusClientBase(IModbusConnection connection, bool disposeConnection) : IDisposable
{ {
private bool _isDisposed; private bool _isDisposed;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the connection should be disposed of by <see cref="Dispose()"/>. /// Gets or sets a value indicating whether the connection should be disposed of by <see cref="Dispose()"/>.
/// </summary> /// </summary>
protected readonly bool disposeConnection; protected readonly bool disposeConnection = disposeConnection;
/// <summary> /// <summary>
/// Gets or sets the <see cref="IModbusConnection"/> responsible for invoking the requests. /// Gets or sets the <see cref="IModbusConnection"/> responsible for invoking the requests.
/// </summary> /// </summary>
protected readonly IModbusConnection connection; protected readonly IModbusConnection connection = connection ?? throw new ArgumentNullException(nameof(connection));
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ModbusClientBase"/> class with a specific <see cref="IModbusConnection"/>. /// Initializes a new instance of the <see cref="ModbusClientBase"/> class with a specific <see cref="IModbusConnection"/>.
@@ -32,20 +40,6 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
: this(connection, true) : this(connection, true)
{ } { }
/// <summary>
/// Initializes a new instance of the <see cref="ModbusClientBase"/> class with a specific <see cref="IModbusConnection"/>.
/// </summary>
/// <param name="connection">The <see cref="IModbusConnection"/> responsible for invoking the requests.</param>
/// <param name="disposeConnection">
/// <see langword="true"/> if the connection should be disposed of by Dispose(),
/// <see langword="false"/> otherwise if you inted to reuse the connection.
/// </param>
public ModbusClientBase(IModbusConnection connection, bool disposeConnection)
{
this.connection = connection ?? throw new ArgumentNullException(nameof(connection));
this.disposeConnection = disposeConnection;
}
/// <summary> /// <summary>
/// Gets or sets the protocol type to use. /// Gets or sets the protocol type to use.
/// </summary> /// </summary>
@@ -67,7 +61,7 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
Assertions(); Assertions();
var request = Protocol.SerializeReadCoils(unitId, startAddress, count); var request = Protocol.SerializeReadCoils(unitId, startAddress, count);
var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken); var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
Protocol.ValidateResponse(request, response); Protocol.ValidateResponse(request, response);
// The protocol processes complete bytes from the response. // The protocol processes complete bytes from the response.
@@ -92,7 +86,7 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
Assertions(); Assertions();
var request = Protocol.SerializeReadDiscreteInputs(unitId, startAddress, count); var request = Protocol.SerializeReadDiscreteInputs(unitId, startAddress, count);
var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken); var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
Protocol.ValidateResponse(request, response); Protocol.ValidateResponse(request, response);
// The protocol processes complete bytes from the response. // The protocol processes complete bytes from the response.
@@ -117,7 +111,7 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
Assertions(); Assertions();
var request = Protocol.SerializeReadHoldingRegisters(unitId, startAddress, count); var request = Protocol.SerializeReadHoldingRegisters(unitId, startAddress, count);
var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken); var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
Protocol.ValidateResponse(request, response); Protocol.ValidateResponse(request, response);
var holdingRegisters = Protocol.DeserializeReadHoldingRegisters(response).ToList(); var holdingRegisters = Protocol.DeserializeReadHoldingRegisters(response).ToList();
@@ -140,7 +134,7 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
Assertions(); Assertions();
var request = Protocol.SerializeReadInputRegisters(unitId, startAddress, count); var request = Protocol.SerializeReadInputRegisters(unitId, startAddress, count);
var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken); var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
Protocol.ValidateResponse(request, response); Protocol.ValidateResponse(request, response);
var inputRegisters = Protocol.DeserializeReadInputRegisters(response).ToList(); var inputRegisters = Protocol.DeserializeReadInputRegisters(response).ToList();
@@ -184,7 +178,7 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
do do
{ {
var request = Protocol.SerializeReadDeviceIdentification(unitId, category, requestObjectId); var request = Protocol.SerializeReadDeviceIdentification(unitId, category, requestObjectId);
var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken); var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
Protocol.ValidateResponse(request, response); Protocol.ValidateResponse(request, response);
result = Protocol.DeserializeReadDeviceIdentification(response); result = Protocol.DeserializeReadDeviceIdentification(response);
@@ -247,7 +241,7 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
Assertions(); Assertions();
var request = Protocol.SerializeWriteSingleCoil(unitId, coil); var request = Protocol.SerializeWriteSingleCoil(unitId, coil);
var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken); var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
Protocol.ValidateResponse(request, response); Protocol.ValidateResponse(request, response);
var result = Protocol.DeserializeWriteSingleCoil(response); var result = Protocol.DeserializeWriteSingleCoil(response);
@@ -268,7 +262,7 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
Assertions(); Assertions();
var request = Protocol.SerializeWriteSingleHoldingRegister(unitId, register); var request = Protocol.SerializeWriteSingleHoldingRegister(unitId, register);
var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken); var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
Protocol.ValidateResponse(request, response); Protocol.ValidateResponse(request, response);
var result = Protocol.DeserializeWriteSingleHoldingRegister(response); var result = Protocol.DeserializeWriteSingleHoldingRegister(response);
@@ -289,7 +283,7 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
Assertions(); Assertions();
var request = Protocol.SerializeWriteMultipleCoils(unitId, coils); var request = Protocol.SerializeWriteMultipleCoils(unitId, coils);
var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken); var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
Protocol.ValidateResponse(request, response); Protocol.ValidateResponse(request, response);
var (firstAddress, count) = Protocol.DeserializeWriteMultipleCoils(response); var (firstAddress, count) = Protocol.DeserializeWriteMultipleCoils(response);
@@ -309,7 +303,7 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
Assertions(); Assertions();
var request = Protocol.SerializeWriteMultipleHoldingRegisters(unitId, registers); var request = Protocol.SerializeWriteMultipleHoldingRegisters(unitId, registers);
var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken); var response = await connection.InvokeAsync(request, Protocol.CheckResponseComplete, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
Protocol.ValidateResponse(request, response); Protocol.ValidateResponse(request, response);
var (firstAddress, count) = Protocol.DeserializeWriteMultipleHoldingRegisters(response); var (firstAddress, count) = Protocol.DeserializeWriteMultipleHoldingRegisters(response);

View File

@@ -10,6 +10,22 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
/// </summary> /// </summary>
public class RtuProtocol : IModbusProtocol public class RtuProtocol : IModbusProtocol
{ {
#region Fields
private static readonly byte[] _readFunctionCodes = [
(byte)ModbusFunctionCode.ReadCoils,
(byte)ModbusFunctionCode.ReadDiscreteInputs,
(byte)ModbusFunctionCode.ReadHoldingRegisters,
(byte)ModbusFunctionCode.ReadInputRegisters];
private static readonly byte[] _writeFunctionCodes = [
(byte)ModbusFunctionCode.WriteSingleCoil,
(byte)ModbusFunctionCode.WriteSingleRegister,
(byte)ModbusFunctionCode.WriteMultipleCoils,
(byte)ModbusFunctionCode.WriteMultipleRegisters];
#endregion Fields
#region Constants #region Constants
/// <summary> /// <summary>
@@ -627,7 +643,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
// - 0x03 Read Holding Registers // - 0x03 Read Holding Registers
// - 0x04 Read Input Registers // - 0x04 Read Input Registers
// do have a "following bytes" at position 3 // do have a "following bytes" at position 3
if (new[] { 0x01, 0x02, 0x03, 0x04 }.Contains(responseBytes[1])) if (_readFunctionCodes.Contains(responseBytes[1]))
{ {
// Unit ID, Function Code, ByteCount, 2x CRC and length of ByteCount // Unit ID, Function Code, ByteCount, 2x CRC and length of ByteCount
if (responseBytes.Count < 5 + responseBytes[2]) if (responseBytes.Count < 5 + responseBytes[2])
@@ -638,7 +654,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
// - 0x06 Write Single Register // - 0x06 Write Single Register
// - 0x0F Write Multiple Coils // - 0x0F Write Multiple Coils
// - 0x10 Write Multiple Registers // - 0x10 Write Multiple Registers
if (new[] { 0x05, 0x06, 0x0F, 0x10 }.Contains(responseBytes[1])) if (_writeFunctionCodes.Contains(responseBytes[1]))
{ {
// Write Single => Unit ID, Function code, 2x Address, 2x Value, 2x CRC // Write Single => Unit ID, Function code, 2x Address, 2x Value, 2x CRC
// Write Multi => Unit ID, Function code, 2x Address, 2x QuantityWritten, 2x CRC // Write Multi => Unit ID, Function code, 2x Address, 2x QuantityWritten, 2x CRC
@@ -715,13 +731,13 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
if (isError) if (isError)
throw new ModbusException("Remote Error") { ErrorCode = (ModbusErrorCode)response[2] }; throw new ModbusException("Remote Error") { ErrorCode = (ModbusErrorCode)response[2] };
if (new[] { 0x01, 0x02, 0x03, 0x04 }.Contains(fnCode)) if (_readFunctionCodes.Contains(fnCode))
{ {
if (response.Count != 5 + response[2]) if (response.Count != 5 + response[2])
throw new ModbusException("Number of following bytes does not match."); throw new ModbusException("Number of following bytes does not match.");
} }
if (new[] { 0x05, 0x06, 0x0F, 0x10 }.Contains(fnCode)) if (_writeFunctionCodes.Contains(fnCode))
{ {
if (response.Count != 8) if (response.Count != 8)
throw new ModbusException("Number of bytes does not match."); throw new ModbusException("Number of bytes does not match.");

View File

@@ -71,7 +71,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
public DeviceIdentificationRaw DeserializeReadDeviceIdentification(IReadOnlyList<byte> response) public DeviceIdentificationRaw DeserializeReadDeviceIdentification(IReadOnlyList<byte> response)
{ {
if (!_devices.TryGetValue(response[0], out var device)) if (!_devices.TryGetValue(response[0], out var _))
throw new TimeoutException("Device not found."); throw new TimeoutException("Device not found.");
var result = new DeviceIdentificationRaw var result = new DeviceIdentificationRaw

View File

@@ -1,8 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<PackageId>AMWD.Protocols.Modbus.Serial</PackageId> <PackageId>AMWD.Protocols.Modbus.Serial</PackageId>
<AssemblyName>amwd-modbus-serial</AssemblyName> <AssemblyName>amwd-modbus-serial</AssemblyName>
<RootNamespace>AMWD.Protocols.Modbus.Serial</RootNamespace> <RootNamespace>AMWD.Protocols.Modbus.Serial</RootNamespace>
@@ -13,14 +11,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="$(SolutionDir)/AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs" Link="Extensions/ArrayExtensions.cs" /> <Compile Include="../AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs" Link="Extensions/ArrayExtensions.cs" />
<Compile Include="$(SolutionDir)/AMWD.Protocols.Modbus.Common/Extensions/ReaderWriterLockSlimExtensions.cs" Link="Extensions/ReaderWriterLockSlimExtensions.cs" /> <Compile Include="../AMWD.Protocols.Modbus.Common/Extensions/ReaderWriterLockSlimExtensions.cs" Link="Extensions/ReaderWriterLockSlimExtensions.cs" />
<Compile Include="$(SolutionDir)/AMWD.Protocols.Modbus.Common/Utils/AsyncQueue.cs" Link="Utils/AsyncQueue.cs" /> <Compile Include="../AMWD.Protocols.Modbus.Common/Utils/AsyncQueue.cs" Link="Utils/AsyncQueue.cs" />
<Compile Include="$(SolutionDir)/AMWD.Protocols.Modbus.Common/Utils/RequestQueueItem.cs" Link="Utils/RequestQueueItem.cs" /> <Compile Include="../AMWD.Protocols.Modbus.Common/Utils/RequestQueueItem.cs" Link="Utils/RequestQueueItem.cs" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
@@ -36,7 +30,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Common\AMWD.Protocols.Modbus.Common.csproj" /> <ProjectReference Include="..\AMWD.Protocols.Modbus.Common\AMWD.Protocols.Modbus.Common.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -192,17 +192,16 @@ namespace AMWD.Protocols.Modbus.Serial
public Task StopAsync(CancellationToken cancellationToken = default) public Task StopAsync(CancellationToken cancellationToken = default)
{ {
Assertions(); Assertions();
return StopAsyncInternal(cancellationToken); StopAsyncInternal();
return Task.CompletedTask;
} }
private Task StopAsyncInternal(CancellationToken cancellationToken) private void StopAsyncInternal()
{ {
_stopCts?.Cancel(); _stopCts?.Cancel();
_serialPort.Close(); _serialPort.Close();
_serialPort.DataReceived -= OnDataReceived; _serialPort.DataReceived -= OnDataReceived;
return Task.CompletedTask;
} }
/// <summary> /// <summary>
@@ -215,7 +214,7 @@ namespace AMWD.Protocols.Modbus.Serial
_isDisposed = true; _isDisposed = true;
StopAsyncInternal(CancellationToken.None).Wait(); StopAsyncInternal();
_serialPort.Dispose(); _serialPort.Dispose();
_stopCts?.Dispose(); _stopCts?.Dispose();
@@ -332,7 +331,7 @@ namespace AMWD.Protocols.Modbus.Serial
responseBytes.AddRange(requestBytes.Take(2)); responseBytes.AddRange(requestBytes.Take(2));
try try
{ {
var coils = await Client.ReadCoilsAsync(unitId, firstAddress, count, cancellationToken); var coils = await Client.ReadCoilsAsync(unitId, firstAddress, count, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
byte[] values = new byte[(int)Math.Ceiling(coils.Count / 8.0)]; byte[] values = new byte[(int)Math.Ceiling(coils.Count / 8.0)];
for (int i = 0; i < coils.Count; i++) for (int i = 0; i < coils.Count; i++)
@@ -371,7 +370,7 @@ namespace AMWD.Protocols.Modbus.Serial
responseBytes.AddRange(requestBytes.Take(2)); responseBytes.AddRange(requestBytes.Take(2));
try try
{ {
var discreteInputs = await Client.ReadDiscreteInputsAsync(unitId, firstAddress, count, cancellationToken); var discreteInputs = await Client.ReadDiscreteInputsAsync(unitId, firstAddress, count, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
byte[] values = new byte[(int)Math.Ceiling(discreteInputs.Count / 8.0)]; byte[] values = new byte[(int)Math.Ceiling(discreteInputs.Count / 8.0)];
for (int i = 0; i < discreteInputs.Count; i++) for (int i = 0; i < discreteInputs.Count; i++)
@@ -410,7 +409,7 @@ namespace AMWD.Protocols.Modbus.Serial
responseBytes.AddRange(requestBytes.Take(2)); responseBytes.AddRange(requestBytes.Take(2));
try try
{ {
var holdingRegisters = await Client.ReadHoldingRegistersAsync(unitId, firstAddress, count, cancellationToken); var holdingRegisters = await Client.ReadHoldingRegistersAsync(unitId, firstAddress, count, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
byte[] values = new byte[holdingRegisters.Count * 2]; byte[] values = new byte[holdingRegisters.Count * 2];
for (int i = 0; i < holdingRegisters.Count; i++) for (int i = 0; i < holdingRegisters.Count; i++)
@@ -444,7 +443,7 @@ namespace AMWD.Protocols.Modbus.Serial
responseBytes.AddRange(requestBytes.Take(2)); responseBytes.AddRange(requestBytes.Take(2));
try try
{ {
var inputRegisters = await Client.ReadInputRegistersAsync(unitId, firstAddress, count, cancellationToken); var inputRegisters = await Client.ReadInputRegistersAsync(unitId, firstAddress, count, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
byte[] values = new byte[count * 2]; byte[] values = new byte[count * 2];
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
@@ -492,7 +491,7 @@ namespace AMWD.Protocols.Modbus.Serial
LowByte = requestBytes[5], LowByte = requestBytes[5],
}; };
bool isSuccess = await Client.WriteSingleCoilAsync(requestBytes[0], coil, cancellationToken); bool isSuccess = await Client.WriteSingleCoilAsync(requestBytes[0], coil, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (isSuccess) if (isSuccess)
{ {
// Response is an echo of the request // Response is an echo of the request
@@ -531,7 +530,7 @@ namespace AMWD.Protocols.Modbus.Serial
LowByte = requestBytes[5] LowByte = requestBytes[5]
}; };
bool isSuccess = await Client.WriteSingleHoldingRegisterAsync(requestBytes[0], register, cancellationToken); bool isSuccess = await Client.WriteSingleHoldingRegisterAsync(requestBytes[0], register, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (isSuccess) if (isSuccess)
{ {
// Response is an echo of the request // Response is an echo of the request
@@ -591,7 +590,7 @@ namespace AMWD.Protocols.Modbus.Serial
}); });
} }
bool isSuccess = await Client.WriteMultipleCoilsAsync(requestBytes[0], coils, cancellationToken); bool isSuccess = await Client.WriteMultipleCoilsAsync(requestBytes[0], coils, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (isSuccess) if (isSuccess)
{ {
// Response is an echo of the request // Response is an echo of the request
@@ -648,7 +647,7 @@ namespace AMWD.Protocols.Modbus.Serial
}); });
} }
bool isSuccess = await Client.WriteMultipleHoldingRegistersAsync(requestBytes[0], list, cancellationToken); bool isSuccess = await Client.WriteMultipleHoldingRegistersAsync(requestBytes[0], list, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (isSuccess) if (isSuccess)
{ {
// Response is an echo of the request // Response is an echo of the request
@@ -705,7 +704,7 @@ namespace AMWD.Protocols.Modbus.Serial
try try
{ {
var deviceInfo = await Client.ReadDeviceIdentificationAsync(requestBytes[0], category, firstObject, cancellationToken); var deviceInfo = await Client.ReadDeviceIdentificationAsync(requestBytes[0], category, firstObject, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
var bodyBytes = new List<byte>(); var bodyBytes = new List<byte>();
@@ -855,5 +854,21 @@ namespace AMWD.Protocols.Modbus.Serial
} }
#endregion Request Handling #endregion Request Handling
/// <inheritdoc/>
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"RTU Proxy");
sb.AppendLine($" {nameof(PortName)}: {PortName}");
sb.AppendLine($" {nameof(BaudRate)}: {(int)BaudRate}");
sb.AppendLine($" {nameof(DataBits)}: {DataBits}");
sb.AppendLine($" {nameof(StopBits)}: {StopBits}");
sb.AppendLine($" {nameof(Parity)}: {Parity}");
sb.AppendLine($" {nameof(Client)}: {Client.GetType().Name}");
return sb.ToString();
}
} }
} }

View File

@@ -31,8 +31,7 @@ namespace AMWD.Protocols.Modbus.Serial
private readonly Task _processingTask; private readonly Task _processingTask;
private readonly AsyncQueue<RequestQueueItem> _requestQueue = new(); private readonly AsyncQueue<RequestQueueItem> _requestQueue = new();
// Only required to cover all logic branches on unit tests. private readonly bool _isLinux;
private bool _isUnitTest = false;
#endregion Fields #endregion Fields
@@ -41,6 +40,8 @@ namespace AMWD.Protocols.Modbus.Serial
/// </summary> /// </summary>
public ModbusSerialConnection(string portName) public ModbusSerialConnection(string portName)
{ {
_isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
if (string.IsNullOrWhiteSpace(portName)) if (string.IsNullOrWhiteSpace(portName))
throw new ArgumentNullException(nameof(portName)); throw new ArgumentNullException(nameof(portName));
@@ -268,7 +269,7 @@ namespace AMWD.Protocols.Modbus.Serial
try try
{ {
// Get next request to process // Get next request to process
var item = await _requestQueue.DequeueAsync(cancellationToken); var item = await _requestQueue.DequeueAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
// Remove registration => already removed from queue // Remove registration => already removed from queue
item.CancellationTokenRegistration.Dispose(); item.CancellationTokenRegistration.Dispose();
@@ -276,13 +277,13 @@ namespace AMWD.Protocols.Modbus.Serial
// Build combined cancellation token // Build combined cancellation token
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, item.CancellationTokenSource.Token); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, item.CancellationTokenSource.Token);
// Wait for exclusive access // Wait for exclusive access
await _portLock.WaitAsync(linkedCts.Token); await _portLock.WaitAsync(linkedCts.Token).ConfigureAwait(continueOnCapturedContext: false);
try try
{ {
// Ensure connection is up // Ensure connection is up
await AssertConnection(linkedCts.Token); await AssertConnection(linkedCts.Token);
await _serialPort.WriteAsync(item.Request, linkedCts.Token); await _serialPort.WriteAsync(item.Request, linkedCts.Token).ConfigureAwait(continueOnCapturedContext: false);
linkedCts.Token.ThrowIfCancellationRequested(); linkedCts.Token.ThrowIfCancellationRequested();
@@ -291,7 +292,7 @@ namespace AMWD.Protocols.Modbus.Serial
do do
{ {
int readCount = await _serialPort.ReadAsync(buffer, 0, buffer.Length, linkedCts.Token); int readCount = await _serialPort.ReadAsync(buffer, 0, buffer.Length, linkedCts.Token).ConfigureAwait(continueOnCapturedContext: false);
if (readCount < 1) if (readCount < 1)
throw new EndOfStreamException(); throw new EndOfStreamException();
@@ -322,7 +323,7 @@ namespace AMWD.Protocols.Modbus.Serial
_portLock.Release(); _portLock.Release();
_idleTimer.Change(IdleTimeout, Timeout.InfiniteTimeSpan); _idleTimer.Change(IdleTimeout, Timeout.InfiniteTimeSpan);
await Task.Delay(InterRequestDelay, cancellationToken); await Task.Delay(InterRequestDelay, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
} }
} }
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
@@ -353,7 +354,7 @@ namespace AMWD.Protocols.Modbus.Serial
_serialPort.Close(); _serialPort.Close();
_serialPort.ResetRS485DriverStateFlags(); _serialPort.ResetRS485DriverStateFlags();
if (DriverEnabledRS485 && (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || _isUnitTest)) if (DriverEnabledRS485 && _isLinux)
{ {
var flags = _serialPort.GetRS485DriverStateFlags(); var flags = _serialPort.GetRS485DriverStateFlags();
flags |= RS485Flags.Enabled; flags |= RS485Flags.Enabled;
@@ -361,7 +362,7 @@ namespace AMWD.Protocols.Modbus.Serial
_serialPort.ChangeRS485DriverStateFlags(flags); _serialPort.ChangeRS485DriverStateFlags(flags);
} }
using var connectTask = Task.Run(_serialPort.Open); using var connectTask = Task.Run(_serialPort.Open, cancellationToken);
if (await Task.WhenAny(connectTask, Task.Delay(ReadTimeout, cancellationToken)) == connectTask) if (await Task.WhenAny(connectTask, Task.Delay(ReadTimeout, cancellationToken)) == connectTask)
{ {
await connectTask; await connectTask;
@@ -379,7 +380,7 @@ namespace AMWD.Protocols.Modbus.Serial
try try
{ {
await Task.Delay(TimeSpan.FromSeconds(delay), cancellationToken); await Task.Delay(TimeSpan.FromSeconds(delay), cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
} }
catch catch
{ /* keep it quiet */ } { /* keep it quiet */ }

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>AMWD.Protocols.Modbus.Tcp</PackageId>
<AssemblyName>amwd-modbus-tcp</AssemblyName>
<RootNamespace>AMWD.Protocols.Modbus.Tcp</RootNamespace>
<Product>Modbus TCP Protocol</Product>
<Description>Implementation of the Modbus protocol communicating via TCP.</Description>
<PackageTags>Modbus Protocol Network TCP LAN</PackageTags>
</PropertyGroup>
<ItemGroup>
<Compile Include="../AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs" Link="Extensions/ArrayExtensions.cs" />
<Compile Include="../AMWD.Protocols.Modbus.Common/Extensions/ReaderWriterLockSlimExtensions.cs" Link="Extensions/ReaderWriterLockSlimExtensions.cs" />
<Compile Include="../AMWD.Protocols.Modbus.Common/Utils/AsyncQueue.cs" Link="Utils/AsyncQueue.cs" />
<Compile Include="../AMWD.Protocols.Modbus.Common/Utils/RequestQueueItem.cs" Link="Utils/RequestQueueItem.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AMWD.Protocols.Modbus.Common\AMWD.Protocols.Modbus.Common.csproj" />
</ItemGroup>
</Project>

View File

@@ -12,7 +12,7 @@ namespace System.IO
int offset = 0; int offset = 0;
do do
{ {
int count = await stream.ReadAsync(buffer, offset, expectedBytes - offset, cancellationToken); int count = await stream.ReadAsync(buffer, offset, expectedBytes - offset, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (count < 1) if (count < 1)
throw new EndOfStreamException(); throw new EndOfStreamException();
@@ -30,7 +30,7 @@ namespace System.IO
int offset = 0; int offset = 0;
do do
{ {
int count = await stream.ReadAsync(buffer, offset, expectedBytes - offset, cancellationToken); int count = await stream.ReadAsync(buffer, offset, expectedBytes - offset, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (count < 1) if (count < 1)
throw new EndOfStreamException(); throw new EndOfStreamException();

View File

@@ -0,0 +1,17 @@
using System.Threading.Tasks;
namespace AMWD.Protocols.Modbus.Tcp.Extensions
{
internal static class TaskExtensions
{
public static async void Forget(this Task task)
{
try
{
await task;
}
catch
{ /* keep it quiet */ }
}
}
}

View File

@@ -65,8 +65,12 @@ namespace AMWD.Protocols.Modbus.Tcp
get => _readTimeout; get => _readTimeout;
set set
{ {
#if NET8_0_OR_GREATER
ArgumentOutOfRangeException.ThrowIfLessThan(value, TimeSpan.Zero);
#else
if (value < TimeSpan.Zero) if (value < TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(value)); throw new ArgumentOutOfRangeException(nameof(value));
#endif
_readTimeout = value; _readTimeout = value;
@@ -81,8 +85,12 @@ namespace AMWD.Protocols.Modbus.Tcp
get => _writeTimeout; get => _writeTimeout;
set set
{ {
#if NET8_0_OR_GREATER
ArgumentOutOfRangeException.ThrowIfLessThan(value, TimeSpan.Zero);
#else
if (value < TimeSpan.Zero) if (value < TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(value)); throw new ArgumentOutOfRangeException(nameof(value));
#endif
_writeTimeout = value; _writeTimeout = value;
@@ -208,7 +216,7 @@ namespace AMWD.Protocols.Modbus.Tcp
try try
{ {
// Get next request to process // Get next request to process
var item = await _requestQueue.DequeueAsync(cancellationToken); var item = await _requestQueue.DequeueAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
// Remove registration => already removed from queue // Remove registration => already removed from queue
item.CancellationTokenRegistration.Dispose(); item.CancellationTokenRegistration.Dispose();
@@ -216,19 +224,19 @@ namespace AMWD.Protocols.Modbus.Tcp
// Build combined cancellation token // Build combined cancellation token
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, item.CancellationTokenSource.Token); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, item.CancellationTokenSource.Token);
// Wait for exclusive access // Wait for exclusive access
await _clientLock.WaitAsync(linkedCts.Token); await _clientLock.WaitAsync(linkedCts.Token).ConfigureAwait(continueOnCapturedContext: false);
try try
{ {
// Ensure connection is up // Ensure connection is up
await AssertConnection(linkedCts.Token); await AssertConnection(linkedCts.Token).ConfigureAwait(continueOnCapturedContext: false);
var stream = _tcpClient.GetStream(); var stream = _tcpClient.GetStream();
await stream.FlushAsync(linkedCts.Token); await stream.FlushAsync(linkedCts.Token);
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
await stream.WriteAsync(item.Request, linkedCts.Token); await stream.WriteAsync(item.Request, linkedCts.Token).ConfigureAwait(continueOnCapturedContext: false);
#else #else
await stream.WriteAsync(item.Request, 0, item.Request.Length, linkedCts.Token); await stream.WriteAsync(item.Request, 0, item.Request.Length, linkedCts.Token).ConfigureAwait(continueOnCapturedContext: false);
#endif #endif
linkedCts.Token.ThrowIfCancellationRequested(); linkedCts.Token.ThrowIfCancellationRequested();
@@ -239,9 +247,9 @@ namespace AMWD.Protocols.Modbus.Tcp
do do
{ {
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
int readCount = await stream.ReadAsync(buffer, linkedCts.Token); int readCount = await stream.ReadAsync(buffer, linkedCts.Token).ConfigureAwait(continueOnCapturedContext: false);
#else #else
int readCount = await stream.ReadAsync(buffer, 0, buffer.Length, linkedCts.Token); int readCount = await stream.ReadAsync(buffer, 0, buffer.Length, linkedCts.Token).ConfigureAwait(continueOnCapturedContext: false);
#endif #endif
if (readCount < 1) if (readCount < 1)
throw new EndOfStreamException(); throw new EndOfStreamException();
@@ -332,7 +340,7 @@ namespace AMWD.Protocols.Modbus.Tcp
try try
{ {
await Task.Delay(TimeSpan.FromSeconds(delay), cancellationToken); await Task.Delay(TimeSpan.FromSeconds(delay), cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
} }
catch catch
{ /* keep it quiet */ } { /* keep it quiet */ }
@@ -376,10 +384,9 @@ namespace AMWD.Protocols.Modbus.Tcp
try try
{ {
return Dns.GetHostAddresses(hostname) return [.. Dns.GetHostAddresses(hostname)
.Where(a => a.AddressFamily == AddressFamily.InterNetwork || a.AddressFamily == AddressFamily.InterNetworkV6) .Where(a => a.AddressFamily == AddressFamily.InterNetwork || a.AddressFamily == AddressFamily.InterNetworkV6)
.OrderBy(a => a.AddressFamily) // prefer IPv4 .OrderBy(a => a.AddressFamily)]; // prefer IPv4
.ToArray();
} }
catch catch
{ {

View File

@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using AMWD.Protocols.Modbus.Common; using AMWD.Protocols.Modbus.Common;
using AMWD.Protocols.Modbus.Common.Contracts; using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Common.Protocols; using AMWD.Protocols.Modbus.Common.Protocols;
using AMWD.Protocols.Modbus.Tcp.Extensions;
using AMWD.Protocols.Modbus.Tcp.Utils; using AMWD.Protocols.Modbus.Tcp.Utils;
namespace AMWD.Protocols.Modbus.Tcp namespace AMWD.Protocols.Modbus.Tcp
@@ -17,7 +18,12 @@ namespace AMWD.Protocols.Modbus.Tcp
/// <summary> /// <summary>
/// Implements a Modbus TCP server proxying all requests to a Modbus client of choice. /// Implements a Modbus TCP server proxying all requests to a Modbus client of choice.
/// </summary> /// </summary>
public class ModbusTcpProxy : IModbusProxy /// <remarks>
/// Initializes a new instance of the <see cref="ModbusTcpProxy"/> class.
/// </remarks>
/// <param name="client">The <see cref="ModbusClientBase"/> used to request the remote device, that should be proxied.</param>
/// <param name="listenAddress">An <see cref="IPAddress"/> to listen on.</param>
public class ModbusTcpProxy(ModbusClientBase client, IPAddress listenAddress) : IModbusProxy
{ {
#region Fields #region Fields
@@ -25,30 +31,17 @@ namespace AMWD.Protocols.Modbus.Tcp
private TimeSpan _readWriteTimeout = TimeSpan.FromSeconds(100); private TimeSpan _readWriteTimeout = TimeSpan.FromSeconds(100);
private TcpListenerWrapper _tcpListener; private readonly TcpListenerWrapper _tcpListener = new(listenAddress, 502);
private CancellationTokenSource _stopCts; private CancellationTokenSource _stopCts;
private Task _clientConnectTask = Task.CompletedTask; private Task _clientConnectTask = Task.CompletedTask;
private readonly SemaphoreSlim _clientListLock = new(1, 1); private readonly SemaphoreSlim _clientListLock = new(1, 1);
private readonly List<TcpClientWrapper> _clients = []; private readonly List<TcpClientWrapper> _clients = [];
private readonly List<Task> _clientTasks = [];
#endregion Fields #endregion Fields
#region Constructors #region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ModbusTcpProxy"/> class.
/// </summary>
/// <param name="client">The <see cref="ModbusClientBase"/> used to request the remote device, that should be proxied.</param>
/// <param name="listenAddress">An <see cref="IPAddress"/> to listen on.</param>
public ModbusTcpProxy(ModbusClientBase client, IPAddress listenAddress)
{
Client = client ?? throw new ArgumentNullException(nameof(client));
_tcpListener = new TcpListenerWrapper(listenAddress, 502);
}
#endregion Constructors #endregion Constructors
#region Properties #region Properties
@@ -56,7 +49,7 @@ namespace AMWD.Protocols.Modbus.Tcp
/// <summary> /// <summary>
/// Gets the Modbus client used to request the remote device, that should be proxied. /// Gets the Modbus client used to request the remote device, that should be proxied.
/// </summary> /// </summary>
public ModbusClientBase Client { get; } public ModbusClientBase Client { get; } = client ?? throw new ArgumentNullException(nameof(client));
/// <summary> /// <summary>
/// Gets the <see cref="IPAddress"/> to listen on. /// Gets the <see cref="IPAddress"/> to listen on.
@@ -115,7 +108,10 @@ namespace AMWD.Protocols.Modbus.Tcp
_stopCts?.Dispose(); _stopCts?.Dispose();
_stopCts = new CancellationTokenSource(); _stopCts = new CancellationTokenSource();
_tcpListener.Socket.DualMode = ListenAddress.AddressFamily == AddressFamily.InterNetworkV6; // Only allowed to set, if the socket is in the InterNetworkV6 address family.
// See: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.dualmode?view=netstandard-2.0#exceptions
if (ListenAddress.AddressFamily == AddressFamily.InterNetworkV6)
_tcpListener.Socket.DualMode = true;
_tcpListener.Start(); _tcpListener.Start();
_clientConnectTask = WaitForClientAsync(_stopCts.Token); _clientConnectTask = WaitForClientAsync(_stopCts.Token);
@@ -140,16 +136,7 @@ namespace AMWD.Protocols.Modbus.Tcp
try try
{ {
await Task.WhenAny(_clientConnectTask, Task.Delay(Timeout.Infinite, cancellationToken)); await Task.WhenAny(_clientConnectTask, Task.Delay(Timeout.Infinite, cancellationToken)).ConfigureAwait(continueOnCapturedContext: false);
}
catch (OperationCanceledException)
{
// Terminated
}
try
{
await Task.WhenAny(Task.WhenAll(_clientTasks), Task.Delay(Timeout.Infinite, cancellationToken));
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@@ -174,6 +161,7 @@ namespace AMWD.Protocols.Modbus.Tcp
_tcpListener.Dispose(); _tcpListener.Dispose();
_stopCts?.Dispose(); _stopCts?.Dispose();
GC.SuppressFinalize(this);
} }
private void Assertions() private void Assertions()
@@ -196,12 +184,13 @@ namespace AMWD.Protocols.Modbus.Tcp
{ {
try try
{ {
var client = await _tcpListener.AcceptTcpClientAsync(cancellationToken); var client = await _tcpListener.AcceptTcpClientAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
await _clientListLock.WaitAsync(cancellationToken); await _clientListLock.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
try try
{ {
_clients.Add(client); _clients.Add(client);
_clientTasks.Add(HandleClientAsync(client, cancellationToken)); // Can be ignored as it will terminate by itself on cancellation
HandleClientAsync(client, cancellationToken).Forget();
} }
finally finally
{ {
@@ -224,23 +213,32 @@ namespace AMWD.Protocols.Modbus.Tcp
{ {
var requestBytes = new List<byte>(); var requestBytes = new List<byte>();
// Waiting for next request
byte[] headerBytes = await stream.ReadExpectedBytesAsync(6, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
requestBytes.AddRange(headerBytes);
ushort length = headerBytes
.Skip(4).Take(2).ToArray()
.GetBigEndianUInt16();
// Waiting for the remaining required data
using (var cts = new CancellationTokenSource(ReadWriteTimeout)) using (var cts = new CancellationTokenSource(ReadWriteTimeout))
using (cancellationToken.Register(cts.Cancel)) using (cancellationToken.Register(cts.Cancel))
{ {
byte[] headerBytes = await stream.ReadExpectedBytesAsync(6, cts.Token); byte[] bodyBytes = await stream.ReadExpectedBytesAsync(length, cts.Token).ConfigureAwait(continueOnCapturedContext: false);
requestBytes.AddRange(headerBytes);
ushort length = headerBytes
.Skip(4).Take(2).ToArray()
.GetBigEndianUInt16();
byte[] bodyBytes = await stream.ReadExpectedBytesAsync(length, cts.Token);
requestBytes.AddRange(bodyBytes); requestBytes.AddRange(bodyBytes);
} }
byte[] responseBytes = await HandleRequestAsync([.. requestBytes], cancellationToken); byte[] responseBytes = await HandleRequestAsync([.. requestBytes], cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (responseBytes != null) if (responseBytes != null)
await stream.WriteAsync(responseBytes, 0, responseBytes.Length, cancellationToken); {
// Write response when available
using (var cts = new CancellationTokenSource(ReadWriteTimeout))
using (cancellationToken.Register(cts.Cancel))
{
await stream.WriteAsync(responseBytes, 0, responseBytes.Length, cts.Token).ConfigureAwait(continueOnCapturedContext: false);
}
}
} }
} }
catch catch
@@ -249,7 +247,7 @@ namespace AMWD.Protocols.Modbus.Tcp
} }
finally finally
{ {
await _clientListLock.WaitAsync(cancellationToken); await _clientListLock.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
try try
{ {
_clients.Remove(client); _clients.Remove(client);
@@ -324,7 +322,7 @@ namespace AMWD.Protocols.Modbus.Tcp
responseBytes.AddRange(requestBytes.Take(8)); responseBytes.AddRange(requestBytes.Take(8));
try try
{ {
var coils = await Client.ReadCoilsAsync(unitId, firstAddress, count, cancellationToken); var coils = await Client.ReadCoilsAsync(unitId, firstAddress, count, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
byte[] values = new byte[(int)Math.Ceiling(coils.Count / 8.0)]; byte[] values = new byte[(int)Math.Ceiling(coils.Count / 8.0)];
for (int i = 0; i < coils.Count; i++) for (int i = 0; i < coils.Count; i++)
@@ -363,7 +361,7 @@ namespace AMWD.Protocols.Modbus.Tcp
responseBytes.AddRange(requestBytes.Take(8)); responseBytes.AddRange(requestBytes.Take(8));
try try
{ {
var discreteInputs = await Client.ReadDiscreteInputsAsync(unitId, firstAddress, count, cancellationToken); var discreteInputs = await Client.ReadDiscreteInputsAsync(unitId, firstAddress, count, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
byte[] values = new byte[(int)Math.Ceiling(discreteInputs.Count / 8.0)]; byte[] values = new byte[(int)Math.Ceiling(discreteInputs.Count / 8.0)];
for (int i = 0; i < discreteInputs.Count; i++) for (int i = 0; i < discreteInputs.Count; i++)
@@ -402,7 +400,7 @@ namespace AMWD.Protocols.Modbus.Tcp
responseBytes.AddRange(requestBytes.Take(8)); responseBytes.AddRange(requestBytes.Take(8));
try try
{ {
var holdingRegisters = await Client.ReadHoldingRegistersAsync(unitId, firstAddress, count, cancellationToken); var holdingRegisters = await Client.ReadHoldingRegistersAsync(unitId, firstAddress, count, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
byte[] values = new byte[holdingRegisters.Count * 2]; byte[] values = new byte[holdingRegisters.Count * 2];
for (int i = 0; i < holdingRegisters.Count; i++) for (int i = 0; i < holdingRegisters.Count; i++)
@@ -436,7 +434,7 @@ namespace AMWD.Protocols.Modbus.Tcp
responseBytes.AddRange(requestBytes.Take(8)); responseBytes.AddRange(requestBytes.Take(8));
try try
{ {
var inputRegisters = await Client.ReadInputRegistersAsync(unitId, firstAddress, count, cancellationToken); var inputRegisters = await Client.ReadInputRegistersAsync(unitId, firstAddress, count, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
byte[] values = new byte[count * 2]; byte[] values = new byte[count * 2];
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
@@ -484,7 +482,7 @@ namespace AMWD.Protocols.Modbus.Tcp
LowByte = requestBytes[11], LowByte = requestBytes[11],
}; };
bool isSuccess = await Client.WriteSingleCoilAsync(requestBytes[6], coil, cancellationToken); bool isSuccess = await Client.WriteSingleCoilAsync(requestBytes[6], coil, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (isSuccess) if (isSuccess)
{ {
// Response is an echo of the request // Response is an echo of the request
@@ -524,7 +522,7 @@ namespace AMWD.Protocols.Modbus.Tcp
LowByte = requestBytes[11] LowByte = requestBytes[11]
}; };
bool isSuccess = await Client.WriteSingleHoldingRegisterAsync(requestBytes[6], register, cancellationToken); bool isSuccess = await Client.WriteSingleHoldingRegisterAsync(requestBytes[6], register, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (isSuccess) if (isSuccess)
{ {
// Response is an echo of the request // Response is an echo of the request
@@ -584,7 +582,7 @@ namespace AMWD.Protocols.Modbus.Tcp
}); });
} }
bool isSuccess = await Client.WriteMultipleCoilsAsync(requestBytes[6], coils, cancellationToken); bool isSuccess = await Client.WriteMultipleCoilsAsync(requestBytes[6], coils, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (isSuccess) if (isSuccess)
{ {
// Response is an echo of the request // Response is an echo of the request
@@ -641,7 +639,7 @@ namespace AMWD.Protocols.Modbus.Tcp
}); });
} }
bool isSuccess = await Client.WriteMultipleHoldingRegistersAsync(requestBytes[6], list, cancellationToken); bool isSuccess = await Client.WriteMultipleHoldingRegistersAsync(requestBytes[6], list, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (isSuccess) if (isSuccess)
{ {
// Response is an echo of the request // Response is an echo of the request
@@ -698,7 +696,7 @@ namespace AMWD.Protocols.Modbus.Tcp
try try
{ {
var deviceInfo = await Client.ReadDeviceIdentificationAsync(requestBytes[6], category, firstObject, cancellationToken); var deviceInfo = await Client.ReadDeviceIdentificationAsync(requestBytes[6], category, firstObject, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
var bodyBytes = new List<byte>(); var bodyBytes = new List<byte>();
@@ -761,7 +759,7 @@ namespace AMWD.Protocols.Modbus.Tcp
} }
} }
private byte[] GetDeviceObject(byte objectId, DeviceIdentification deviceIdentification) private static byte[] GetDeviceObject(byte objectId, DeviceIdentification deviceIdentification)
{ {
var result = new List<byte> { objectId }; var result = new List<byte> { objectId };
switch ((ModbusDeviceIdentificationObject)objectId) switch ((ModbusDeviceIdentificationObject)objectId)
@@ -851,5 +849,18 @@ namespace AMWD.Protocols.Modbus.Tcp
} }
#endregion Request Handling #endregion Request Handling
/// <inheritdoc/>
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"TCP Proxy");
sb.AppendLine($" {nameof(ListenAddress)}: {ListenAddress}");
sb.AppendLine($" {nameof(ListenPort)}: {ListenPort}");
sb.AppendLine($" {nameof(Client)}: {Client.GetType().Name}");
return sb.ToString();
}
} }
} }

View File

@@ -1,15 +1,13 @@
using System.Net; using System.Net;
using System.Net.Sockets;
namespace AMWD.Protocols.Modbus.Tcp.Utils namespace AMWD.Protocols.Modbus.Tcp.Utils
{ {
internal class IPEndPointWrapper /// <inheritdoc cref="IPEndPoint" />
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class IPEndPointWrapper(EndPoint endPoint)
{ {
private IPEndPoint _ipEndPoint; private readonly IPEndPoint _ipEndPoint = (IPEndPoint)endPoint;
public IPEndPointWrapper(EndPoint endPoint)
{
_ipEndPoint = (IPEndPoint)endPoint;
}
#region Properties #region Properties

View File

@@ -8,14 +8,9 @@ namespace AMWD.Protocols.Modbus.Tcp.Utils
{ {
/// <inheritdoc cref="NetworkStream" /> /// <inheritdoc cref="NetworkStream" />
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class NetworkStreamWrapper : IDisposable internal class NetworkStreamWrapper(NetworkStream stream) : IDisposable
{ {
private readonly NetworkStream _stream; private readonly NetworkStream _stream = stream;
public NetworkStreamWrapper(NetworkStream stream)
{
_stream = stream;
}
/// <inheritdoc cref="NetworkStream.Dispose" /> /// <inheritdoc cref="NetworkStream.Dispose" />
public virtual void Dispose() public virtual void Dispose()

View File

@@ -3,14 +3,11 @@ using System.Net.Sockets;
namespace AMWD.Protocols.Modbus.Tcp.Utils namespace AMWD.Protocols.Modbus.Tcp.Utils
{ {
internal class SocketWrapper : IDisposable /// <inheritdoc cref="Socket" />
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class SocketWrapper(Socket socket) : IDisposable
{ {
private Socket _socket; private readonly Socket _socket = socket;
public SocketWrapper(Socket socket)
{
_socket = socket;
}
/// <inheritdoc cref="Socket.DualMode" /> /// <inheritdoc cref="Socket.DualMode" />
public virtual bool DualMode public virtual bool DualMode

View File

@@ -3,6 +3,9 @@ using System.Net.Sockets;
namespace AMWD.Protocols.Modbus.Tcp.Utils namespace AMWD.Protocols.Modbus.Tcp.Utils
{ {
/// <summary>
/// Factory for creating <see cref="TcpClientWrapper"/> instances.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class TcpClientWrapperFactory internal class TcpClientWrapperFactory
{ {

View File

@@ -6,22 +6,18 @@ using System.Threading.Tasks;
namespace AMWD.Protocols.Modbus.Tcp.Utils namespace AMWD.Protocols.Modbus.Tcp.Utils
{ {
/// <inheritdoc cref="TcpListener" />
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class TcpListenerWrapper : IDisposable internal class TcpListenerWrapper(IPAddress localaddr, int port) : IDisposable
{ {
#region Fields #region Fields
private TcpListener _tcpListener; private readonly TcpListener _tcpListener = new(localaddr, port);
#endregion Fields #endregion Fields
#region Constructor #region Constructor
public TcpListenerWrapper(IPAddress localaddr, int port)
{
_tcpListener = new TcpListener(localaddr, port);
}
#endregion Constructor #endregion Constructor
#region Properties #region Properties

50
src/Directory.Build.props Normal file
View File

@@ -0,0 +1,50 @@
<Project>
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/AM-WD/AMWD.Protocols.Modbus.git</RepositoryUrl>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<EmbedUntrackedSources>false</EmbedUntrackedSources>
<PackageIcon>package-icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageProjectUrl>https://modbus.org/tech.php</PackageProjectUrl>
<AssemblyOriginatorKeyFile>../../AMWD.Protocols.Modbus.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition="'$(GITLAB_CI)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup Condition="'$(GITLAB_CI)' == 'true'">
<SourceLinkGitLabHost Include="$(CI_SERVER_HOST)" Version="$(CI_SERVER_VERSION)" />
<PackageReference Include="Microsoft.SourceLink.GitLab" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="../../package-icon.png" Pack="true" PackagePath="/" />
<None Include="../../LICENSE.txt" Pack="true" PackagePath="/" />
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="AMWD.Protocols.Modbus.Tests" PublicKey="$(PublicKey)" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" PublicKey="$(MoqPublicKey)" />
</ItemGroup>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)\..'))" />
</Project>

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup />
<ItemGroup>
<ProjectReference Include="..\..\src\AMWD.Protocols.Modbus.Serial\AMWD.Protocols.Modbus.Serial.csproj" />
<ProjectReference Include="..\..\src\AMWD.Protocols.Modbus.Tcp\AMWD.Protocols.Modbus.Tcp.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,8 +1,7 @@
using System.Collections.Generic; using System.Text;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Moq; using AMWD.Protocols.Modbus.Common.Contracts;
namespace AMWD.Protocols.Modbus.Tests.Common.Contracts namespace AMWD.Protocols.Modbus.Tests.Common.Contracts
{ {
@@ -105,19 +104,16 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Contracts
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowExceptionOnNullConnection() public void ShouldThrowExceptionOnNullConnection()
{ {
// Arrange // Arrange
IModbusConnection connection = null; IModbusConnection connection = null;
// Act // Act + Assert
new ModbusClientBaseWrapper(connection); Assert.ThrowsExactly<ArgumentNullException>(() => new ModbusClientBaseWrapper(connection));
// Assert - ArgumentNullException
} }
[DataTestMethod] [TestMethod]
[DataRow(true)] [DataRow(true)]
[DataRow(false)] [DataRow(false)]
public void ShouldAlsoDisposeConnection(bool disposeConnection) public void ShouldAlsoDisposeConnection(bool disposeConnection)
@@ -155,31 +151,25 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Contracts
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ObjectDisposedException))]
public async Task ShouldAssertDisposed() public async Task ShouldAssertDisposed()
{ {
// Arrange // Arrange
var client = GetClient(); var client = GetClient();
client.Dispose(); client.Dispose();
// Act // Act + Assert
await client.ReadCoilsAsync(UNIT_ID, START_ADDRESS, READ_COUNT); await Assert.ThrowsExactlyAsync<ObjectDisposedException>(() => client.ReadCoilsAsync(UNIT_ID, START_ADDRESS, READ_COUNT));
// Assert - ObjectDisposedException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public async Task ShouldAssertProtocolSet() public async Task ShouldAssertProtocolSet()
{ {
// Arrange // Arrange
var client = GetClient(); var client = GetClient();
client.Protocol = null; client.Protocol = null;
// Act // Act + Assert
await client.ReadCoilsAsync(UNIT_ID, START_ADDRESS, READ_COUNT); await Assert.ThrowsExactlyAsync<ArgumentNullException>(() => client.ReadCoilsAsync(UNIT_ID, START_ADDRESS, READ_COUNT));
// Assert - ArgumentNullException
} }
#endregion Common/Connection/Assertions #endregion Common/Connection/Assertions

View File

@@ -41,20 +41,16 @@
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullOnGetSingle() public void ShouldThrowNullOnGetSingle()
{ {
// Arrange // Arrange
HoldingRegister[] registers = null; HoldingRegister[] registers = null;
// Act // Act + Assert
registers.GetSingle(0); Assert.ThrowsExactly<ArgumentNullException>(() => registers.GetSingle(0));
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetSingleForLength() public void ShouldThrowArgumentOnGetSingleForLength()
{ {
// Arrange // Arrange
@@ -63,16 +59,13 @@
new() { Address = 101, HighByte = 0x01, LowByte = 0x02 } new() { Address = 101, HighByte = 0x01, LowByte = 0x02 }
}; };
// Act // Act + Assert
registers.GetSingle(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetSingle(0));
// Assert - ArgumentException
} }
[DataTestMethod] [TestMethod]
[DataRow(1)] [DataRow(1)]
[DataRow(-1)] [DataRow(-1)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeOnGetSingle(int startIndex) public void ShouldThrowArgumentOutOfRangeOnGetSingle(int startIndex)
{ {
// Arrange // Arrange
@@ -82,14 +75,11 @@
new() { Address = 100, HighByte = 0x03, LowByte = 0x04 } new() { Address = 100, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetSingle(startIndex); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => registers.GetSingle(startIndex));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetSingleForType() public void ShouldThrowArgumentOnGetSingleForType()
{ {
// Arrange // Arrange
@@ -99,10 +89,8 @@
new InputRegister { Address = 101, HighByte = 0x03, LowByte = 0x04 } new InputRegister { Address = 101, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetSingle(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetSingle(0));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
@@ -145,20 +133,16 @@
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullOnGetDouble() public void ShouldThrowNullOnGetDouble()
{ {
// Arrange // Arrange
HoldingRegister[] registers = null; HoldingRegister[] registers = null;
// Act // Act + Assert
registers.GetDouble(0); Assert.ThrowsExactly<ArgumentNullException>(() => registers.GetDouble(0));
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetDoubleForLength() public void ShouldThrowArgumentOnGetDoubleForLength()
{ {
// Arrange // Arrange
@@ -169,16 +153,13 @@
new() { Address = 102, HighByte = 0x7A, LowByte = 0xE1 } new() { Address = 102, HighByte = 0x7A, LowByte = 0xE1 }
}; };
// Act // Act + Assert
registers.GetDouble(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetDouble(0));
// Assert - ArgumentException
} }
[DataTestMethod] [TestMethod]
[DataRow(1)] [DataRow(1)]
[DataRow(-1)] [DataRow(-1)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeOnGetDouble(int startIndex) public void ShouldThrowArgumentOutOfRangeOnGetDouble(int startIndex)
{ {
// Arrange // Arrange
@@ -190,14 +171,11 @@
new() { Address = 103, HighByte = 0x47, LowByte = 0xAE } new() { Address = 103, HighByte = 0x47, LowByte = 0xAE }
}; };
// Act // Act + Assert
registers.GetDouble(startIndex); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => registers.GetDouble(startIndex));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetDoubleForType() public void ShouldThrowArgumentOnGetDoubleForType()
{ {
// Arrange // Arrange
@@ -209,10 +187,8 @@
new InputRegister { Address = 103, HighByte = 0x47, LowByte = 0xAE } new InputRegister { Address = 103, HighByte = 0x47, LowByte = 0xAE }
}; };
// Act // Act + Assert
registers.GetDouble(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetDouble(0));
// Assert - ArgumentException
} }
#endregion Modbus to value #endregion Modbus to value

View File

@@ -30,16 +30,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Extensions
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullOnGetBoolean() public void ShouldThrowNullOnGetBoolean()
{ {
// Arrange // Arrange
Coil coil = null; Coil coil = null;
// Act // Act + Assert
coil.GetBoolean(); Assert.ThrowsExactly<ArgumentNullException>(() => coil.GetBoolean());
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
@@ -95,35 +92,28 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Extensions
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullOnString() public void ShouldThrowNullOnString()
{ {
// Arrange // Arrange
HoldingRegister[] list = null; HoldingRegister[] list = null;
// Act // Act + Assert
list.GetString(2); Assert.ThrowsExactly<ArgumentNullException>(() => list.GetString(2));
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnStringForEmptyList() public void ShouldThrowArgumentOnStringForEmptyList()
{ {
// Arrange // Arrange
var registers = Array.Empty<HoldingRegister>(); var registers = Array.Empty<HoldingRegister>();
// Act // Act + Assert
registers.GetString(2); Assert.ThrowsExactly<ArgumentException>(() => registers.GetString(2));
// Assert - ArgumentException
} }
[DataTestMethod] [TestMethod]
[DataRow(1)] [DataRow(1)]
[DataRow(-1)] [DataRow(-1)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeOnString(int startIndex) public void ShouldThrowArgumentOutOfRangeOnString(int startIndex)
{ {
// Arrange // Arrange
@@ -133,14 +123,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Extensions
new() { Address = 2, HighByte = 67, LowByte = 0 } new() { Address = 2, HighByte = 67, LowByte = 0 }
}; };
// Act // Act + Assert
registers.GetString(2, startIndex); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => registers.GetString(2, startIndex));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnStringForMixedTypes() public void ShouldThrowArgumentOnStringForMixedTypes()
{ {
// Arrange // Arrange
@@ -150,10 +137,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Extensions
new InputRegister { Address = 2, HighByte = 67, LowByte = 0 } new InputRegister { Address = 2, HighByte = 67, LowByte = 0 }
}; };
// Act // Act + Assert
registers.GetString(2); Assert.ThrowsExactly<ArgumentException>(() => registers.GetString(2));
// Assert - ArgumentException
} }
#endregion Modbus to value #endregion Modbus to value
@@ -272,16 +257,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Extensions
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullOnGetString() public void ShouldThrowNullOnGetString()
{ {
// Arrange // Arrange
string str = null; string str = null;
// Act // Act + Assert
_ = str.ToRegisters(100).ToArray(); Assert.ThrowsExactly<ArgumentNullException>(() => str.ToRegisters(100).ToArray());
// Assert - ArgumentNullException
} }
#endregion Value to Modbus #endregion Value to Modbus

View File

@@ -32,31 +32,23 @@
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullForGetSByte() public void ShouldThrowNullForGetSByte()
{ {
// Arrange // Arrange
HoldingRegister register = null; HoldingRegister register = null;
// Act // Act + Assert
register.GetSByte(); Assert.ThrowsExactly<ArgumentNullException>(() => register.GetSByte());
// Assert - ArgumentNullException
Assert.Fail();
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentForGetSByte() public void ShouldThrowArgumentForGetSByte()
{ {
// Arrange // Arrange
var obj = new Coil(); var obj = new Coil();
// Act // Act + Assert
obj.GetSByte(); Assert.ThrowsExactly<ArgumentException>(() => obj.GetSByte());
// Assert - ArgumentException
Assert.Fail();
} }
[TestMethod] [TestMethod]
@@ -86,31 +78,23 @@
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullForGetInt16() public void ShouldThrowNullForGetInt16()
{ {
// Arrange // Arrange
HoldingRegister register = null; HoldingRegister register = null;
// Act // Act + Assert
register.GetInt16(); Assert.ThrowsExactly<ArgumentNullException>(() => register.GetInt16());
// Assert - ArgumentNullException
Assert.Fail();
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentForGetInt16() public void ShouldThrowArgumentForGetInt16()
{ {
// Arrange // Arrange
var obj = new Coil(); var obj = new Coil();
// Act // Act + Assert
obj.GetInt16(); Assert.ThrowsExactly<ArgumentException>(() => obj.GetInt16());
// Assert - ArgumentException
Assert.Fail();
} }
[TestMethod] [TestMethod]
@@ -149,21 +133,16 @@
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullOnGetInt32() public void ShouldThrowNullOnGetInt32()
{ {
// Arrange // Arrange
HoldingRegister[] registers = null; HoldingRegister[] registers = null;
// Act // Act + Assert
registers.GetInt32(0); Assert.ThrowsExactly<ArgumentNullException>(() => registers.GetInt32(0));
// Assert - ArgumentNullException
Assert.Fail();
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetInt32ForLength() public void ShouldThrowArgumentOnGetInt32ForLength()
{ {
// Arrange // Arrange
@@ -172,17 +151,13 @@
new HoldingRegister { Address = 101, HighByte = 0x01, LowByte = 0x02 } new HoldingRegister { Address = 101, HighByte = 0x01, LowByte = 0x02 }
}; };
// Act // Act + Assert
registers.GetInt32(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetInt32(0));
// Assert - ArgumentException
Assert.Fail();
} }
[DataTestMethod] [TestMethod]
[DataRow(1)] [DataRow(1)]
[DataRow(-1)] [DataRow(-1)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeOnGetInt32(int startIndex) public void ShouldThrowArgumentOutOfRangeOnGetInt32(int startIndex)
{ {
// Arrange // Arrange
@@ -192,15 +167,11 @@
new HoldingRegister { Address = 100, HighByte = 0x03, LowByte = 0x04 } new HoldingRegister { Address = 100, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetInt32(startIndex); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => registers.GetInt32(startIndex));
// Assert - ArgumentOutOfRangeException
Assert.Fail();
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetInt32ForType() public void ShouldThrowArgumentOnGetInt32ForType()
{ {
// Arrange // Arrange
@@ -210,11 +181,8 @@
new InputRegister { Address = 101, HighByte = 0x03, LowByte = 0x04 } new InputRegister { Address = 101, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetInt32(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetInt32(0));
// Assert - ArgumentException
Assert.Fail();
} }
[TestMethod] [TestMethod]
@@ -257,21 +225,16 @@
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullOnGetInt64() public void ShouldThrowNullOnGetInt64()
{ {
// Arrange // Arrange
HoldingRegister[] registers = null; HoldingRegister[] registers = null;
// Act // Act + Assert
registers.GetInt64(0); Assert.ThrowsExactly<ArgumentNullException>(() => registers.GetInt64(0));
// Assert - ArgumentNullException
Assert.Fail();
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetInt64ForLength() public void ShouldThrowArgumentOnGetInt64ForLength()
{ {
// Arrange // Arrange
@@ -282,17 +245,13 @@
new HoldingRegister { Address = 103, HighByte = 0x03, LowByte = 0x04 } new HoldingRegister { Address = 103, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetInt64(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetInt64(0));
// Assert - ArgumentException
Assert.Fail();
} }
[DataTestMethod] [TestMethod]
[DataRow(1)] [DataRow(1)]
[DataRow(-1)] [DataRow(-1)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeOnGetInt64(int startIndex) public void ShouldThrowArgumentOutOfRangeOnGetInt64(int startIndex)
{ {
// Arrange // Arrange
@@ -304,15 +263,11 @@
new HoldingRegister { Address = 103, HighByte = 0x03, LowByte = 0x04 } new HoldingRegister { Address = 103, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetInt64(startIndex); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => registers.GetInt64(startIndex));
// Assert - ArgumentOutOfRangeException
Assert.Fail();
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetInt64ForType() public void ShouldThrowArgumentOnGetInt64ForType()
{ {
// Arrange // Arrange
@@ -324,11 +279,8 @@
new InputRegister { Address = 103, HighByte = 0x03, LowByte = 0x04 } new InputRegister { Address = 103, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetInt64(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetInt64(0));
// Assert - ArgumentException
Assert.Fail();
} }
#endregion Modbus to value #endregion Modbus to value

View File

@@ -32,29 +32,23 @@
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullForGetByte() public void ShouldThrowNullForGetByte()
{ {
// Arrange // Arrange
HoldingRegister register = null; HoldingRegister register = null;
// Act // Act + Assert
register.GetByte(); Assert.ThrowsExactly<ArgumentNullException>(() => register.GetByte());
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentForGetByte() public void ShouldThrowArgumentForGetByte()
{ {
// Arrange // Arrange
var obj = new Coil(); var obj = new Coil();
// Act // Act + Assert
obj.GetByte(); Assert.ThrowsExactly<ArgumentException>(() => obj.GetByte());
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
@@ -84,29 +78,23 @@
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullForGetUInt16() public void ShouldThrowNullForGetUInt16()
{ {
// Arrange // Arrange
HoldingRegister register = null; HoldingRegister register = null;
// Act // Act + Assert
register.GetUInt16(); Assert.ThrowsExactly<ArgumentNullException>(() => register.GetUInt16());
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentForGetUInt16() public void ShouldThrowArgumentForGetUInt16()
{ {
// Arrange // Arrange
var obj = new Coil(); var obj = new Coil();
// Act // Act + Assert
obj.GetUInt16(); Assert.ThrowsExactly<ArgumentException>(() => obj.GetUInt16());
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
@@ -145,21 +133,16 @@
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullOnGetUInt32() public void ShouldThrowNullOnGetUInt32()
{ {
// Arrange // Arrange
HoldingRegister[] registers = null; HoldingRegister[] registers = null;
// Act // Act + Assert
registers.GetUInt32(0); Assert.ThrowsExactly<ArgumentNullException>(() => registers.GetUInt32(0));
// Assert - ArgumentNullException
Assert.Fail();
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetUInt32ForLength() public void ShouldThrowArgumentOnGetUInt32ForLength()
{ {
// Arrange // Arrange
@@ -168,16 +151,13 @@
new() { Address = 101, HighByte = 0x01, LowByte = 0x02 } new() { Address = 101, HighByte = 0x01, LowByte = 0x02 }
}; };
// Act // Act + Assert
registers.GetUInt32(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetUInt32(1));
// Assert - ArgumentException
} }
[DataTestMethod] [TestMethod]
[DataRow(1)] [DataRow(1)]
[DataRow(-1)] [DataRow(-1)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeOnGetUInt32(int startIndex) public void ShouldThrowArgumentOutOfRangeOnGetUInt32(int startIndex)
{ {
// Arrange // Arrange
@@ -187,14 +167,11 @@
new() { Address = 100, HighByte = 0x03, LowByte = 0x04 } new() { Address = 100, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetUInt32(startIndex); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => registers.GetUInt32(startIndex));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetUInt32ForType() public void ShouldThrowArgumentOnGetUInt32ForType()
{ {
// Arrange // Arrange
@@ -204,10 +181,8 @@
new InputRegister { Address = 101, HighByte = 0x03, LowByte = 0x04 } new InputRegister { Address = 101, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetUInt32(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetUInt32(0));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
@@ -250,21 +225,16 @@
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowNullOnGetUInt64() public void ShouldThrowNullOnGetUInt64()
{ {
// Arrange // Arrange
HoldingRegister[] registers = null; HoldingRegister[] registers = null;
// Act // Act + Assert
registers.GetUInt64(0); Assert.ThrowsExactly<ArgumentNullException>(() => registers.GetUInt64(0));
// Assert - ArgumentNullException
Assert.Fail();
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetUInt64ForLength() public void ShouldThrowArgumentOnGetUInt64ForLength()
{ {
// Arrange // Arrange
@@ -275,16 +245,13 @@
new() { Address = 103, HighByte = 0x03, LowByte = 0x04 } new() { Address = 103, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetUInt64(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetUInt64(0));
// Assert - ArgumentException
} }
[DataTestMethod] [TestMethod]
[DataRow(1)] [DataRow(1)]
[DataRow(-1)] [DataRow(-1)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeOnGetUInt64(int startIndex) public void ShouldThrowArgumentOutOfRangeOnGetUInt64(int startIndex)
{ {
// Arrange // Arrange
@@ -296,14 +263,11 @@
new() { Address = 103, HighByte = 0x03, LowByte = 0x04 } new() { Address = 103, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetUInt64(startIndex); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => registers.GetUInt64(startIndex));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentOnGetUInt64ForType() public void ShouldThrowArgumentOnGetUInt64ForType()
{ {
// Arrange // Arrange
@@ -315,10 +279,8 @@
new InputRegister { Address = 103, HighByte = 0x03, LowByte = 0x04 } new InputRegister { Address = 103, HighByte = 0x03, LowByte = 0x04 }
}; };
// Act // Act + Assert
registers.GetUInt64(0); Assert.ThrowsExactly<ArgumentException>(() => registers.GetUInt64(0));
// Assert - ArgumentException
} }
#endregion Modbus to value #endregion Modbus to value

View File

@@ -87,7 +87,7 @@
Assert.IsFalse(success); Assert.IsFalse(success);
} }
[DataTestMethod] [TestMethod]
[DataRow(0xFF)] [DataRow(0xFF)]
[DataRow(0x00)] [DataRow(0x00)]
public void ShouldPrintPrettyString(int highByte) public void ShouldPrintPrettyString(int highByte)

View File

@@ -87,7 +87,7 @@
Assert.IsFalse(success); Assert.IsFalse(success);
} }
[DataTestMethod] [TestMethod]
[DataRow(0xFF)] [DataRow(0xFF)]
[DataRow(0x00)] [DataRow(0x00)]
public void ShouldPrintPrettyString(int highByte) public void ShouldPrintPrettyString(int highByte)

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic; using AMWD.Protocols.Modbus.Common.Models;
using System.Reflection; using System.Reflection;
using AMWD.Protocols.Modbus.Common.Models;
namespace AMWD.Protocols.Modbus.Tests.Common.Models namespace AMWD.Protocols.Modbus.Tests.Common.Models
{ {

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Text;
using System.Text;
using AMWD.Protocols.Modbus.Common.Protocols; using AMWD.Protocols.Modbus.Common.Protocols;
namespace AMWD.Protocols.Modbus.Tests.Common.Protocols namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
@@ -29,32 +28,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
CollectionAssert.AreEqual(expectedBytes, bytes.ToArray()); CollectionAssert.AreEqual(expectedBytes, bytes.ToArray());
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(2001)] [DataRow(2001)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadCoils(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadCoils(int count)
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeReadCoils(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadCoils(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadCoils() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadCoils()
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeReadCoils(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadCoils(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -88,7 +81,6 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadCoils() public void ShouldThrowExceptionOnDeserializeReadCoils()
{ {
// Arrange // Arrange
@@ -98,10 +90,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
_ = protocol.DeserializeReadCoils(responseBytes); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadCoils(responseBytes));
// Assert - ModbusException
} }
#endregion Read Coils #endregion Read Coils
@@ -126,32 +116,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
CollectionAssert.AreEqual(expectedBytes, bytes.ToArray()); CollectionAssert.AreEqual(expectedBytes, bytes.ToArray());
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(2001)] [DataRow(2001)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadDiscreteInputs(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadDiscreteInputs(int count)
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeReadDiscreteInputs(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadDiscreteInputs(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadDiscreteInputs() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadDiscreteInputs()
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeReadDiscreteInputs(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadDiscreteInputs(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -185,7 +169,6 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadDiscreteInputs() public void ShouldThrowExceptionOnDeserializeReadDiscreteInputs()
{ {
// Arrange // Arrange
@@ -195,10 +178,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.DeserializeReadDiscreteInputs(responseBytes); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadDiscreteInputs(responseBytes));
// Assert - ModbusException
} }
#endregion Read Discrete Inputs #endregion Read Discrete Inputs
@@ -223,32 +204,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
CollectionAssert.AreEqual(expectedBytes, bytes.ToArray()); CollectionAssert.AreEqual(expectedBytes, bytes.ToArray());
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(2001)] [DataRow(2001)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadHoldingRegisters(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadHoldingRegisters(int count)
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeReadHoldingRegisters(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadHoldingRegisters(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadHoldingRegisters() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadHoldingRegisters()
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeReadHoldingRegisters(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadHoldingRegisters(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -276,7 +251,6 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadHoldingRegisters() public void ShouldThrowExceptionOnDeserializeReadHoldingRegisters()
{ {
// Arrange // Arrange
@@ -286,10 +260,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.DeserializeReadHoldingRegisters(responseBytes); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadHoldingRegisters(responseBytes));
// Assert - ModbusException
} }
#endregion Read Holding Registers #endregion Read Holding Registers
@@ -314,32 +286,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
CollectionAssert.AreEqual(expectedBytes, bytes.ToArray()); CollectionAssert.AreEqual(expectedBytes, bytes.ToArray());
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(2001)] [DataRow(2001)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadInputRegisters(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadInputRegisters(int count)
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeReadInputRegisters(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadInputRegisters(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadInputRegisters() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadInputRegisters()
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeReadInputRegisters(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadInputRegisters(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -367,7 +333,6 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadInputRegisters() public void ShouldThrowExceptionOnDeserializeReadInputRegisters()
{ {
// Arrange // Arrange
@@ -377,17 +342,15 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.DeserializeReadInputRegisters(responseBytes); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadInputRegisters(responseBytes));
// Assert - ModbusException
} }
#endregion Read Input Registers #endregion Read Input Registers
#region Read Device Identification #region Read Device Identification
[DataTestMethod] [TestMethod]
[DataRow(ModbusDeviceIdentificationCategory.Basic)] [DataRow(ModbusDeviceIdentificationCategory.Basic)]
[DataRow(ModbusDeviceIdentificationCategory.Regular)] [DataRow(ModbusDeviceIdentificationCategory.Regular)]
[DataRow(ModbusDeviceIdentificationCategory.Extended)] [DataRow(ModbusDeviceIdentificationCategory.Extended)]
@@ -410,19 +373,16 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeExceptionForCategoryOnSerializeReadDeviceIdentification() public void ShouldThrowOutOfRangeExceptionForCategoryOnSerializeReadDeviceIdentification()
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeReadDeviceIdentification(UNIT_ID, (ModbusDeviceIdentificationCategory)10, ModbusDeviceIdentificationObject.ProductCode); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadDeviceIdentification(UNIT_ID, (ModbusDeviceIdentificationCategory)10, ModbusDeviceIdentificationObject.ProductCode));
// Assert - ArgumentOutOfRangeException
} }
[DataTestMethod] [TestMethod]
[DataRow(false)] [DataRow(false)]
[DataRow(true)] [DataRow(true)]
public void ShouldDeserializeReadDeviceIdentification(bool moreAndIndividual) public void ShouldDeserializeReadDeviceIdentification(bool moreAndIndividual)
@@ -449,7 +409,6 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForMeiType() public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForMeiType()
{ {
// Arrange // Arrange
@@ -459,12 +418,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.DeserializeReadDeviceIdentification(responseBytes); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadDeviceIdentification(responseBytes));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForCategory() public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForCategory()
{ {
// Arrange // Arrange
@@ -474,8 +432,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.DeserializeReadDeviceIdentification(responseBytes); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadDeviceIdentification(responseBytes));
} }
#endregion Read Device Identification #endregion Read Device Identification
@@ -502,16 +460,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteSingleCoil() public void ShouldThrowArgumentNullOnSerializeWriteSingleCoil()
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeWriteSingleCoil(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteSingleCoil(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
@@ -557,16 +512,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteSingleHoldingRegister() public void ShouldThrowArgumentNullOnSerializeWriteSingleHoldingRegister()
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeWriteSingleHoldingRegister(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteSingleHoldingRegister(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
@@ -619,22 +571,18 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteMultipleCoils() public void ShouldThrowArgumentNullOnSerializeWriteMultipleCoils()
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(1969)] [DataRow(1969)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleCoils(int count) public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleCoils(int count)
{ {
// Arrange // Arrange
@@ -644,14 +592,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, coils); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, coils));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleCoils() public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleCoils()
{ {
// Arrange // Arrange
@@ -662,14 +607,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, coils); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, coils));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleCoils() public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleCoils()
{ {
// Arrange // Arrange
@@ -680,10 +622,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, coils); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, coils));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
@@ -732,22 +672,18 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteMultipleHoldingRegisters() public void ShouldThrowArgumentNullOnSerializeWriteMultipleHoldingRegisters()
{ {
// Arrange // Arrange
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(124)] [DataRow(124)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleHoldingRegisters(int count) public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleHoldingRegisters(int count)
{ {
// Arrange // Arrange
@@ -757,14 +693,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleHoldingRegisters() public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleHoldingRegisters()
{ {
// Arrange // Arrange
@@ -775,14 +708,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleHoldingRegisters() public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleHoldingRegisters()
{ {
// Arrange // Arrange
@@ -793,10 +723,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
@@ -863,7 +791,7 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
Assert.IsFalse(complete); Assert.IsFalse(complete);
} }
[DataTestMethod] [TestMethod]
[DataRow(0x01)] [DataRow(0x01)]
[DataRow(0x02)] [DataRow(0x02)]
[DataRow(0x03)] [DataRow(0x03)]
@@ -880,7 +808,7 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)); protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response));
} }
[DataTestMethod] [TestMethod]
[DataRow(0x05)] [DataRow(0x05)]
[DataRow(0x06)] [DataRow(0x06)]
[DataRow(0x0F)] [DataRow(0x0F)]
@@ -898,7 +826,6 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForMissingHeaderOnValidateResponse() public void ShouldThrowForMissingHeaderOnValidateResponse()
{ {
// Arrange // Arrange
@@ -907,12 +834,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
AddTrailer(ref response); AddTrailer(ref response);
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForMissingTrailerOnValidateResponse() public void ShouldThrowForMissingTrailerOnValidateResponse()
{ {
// Arrange // Arrange
@@ -920,12 +846,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
string response = $":{UNIT_ID:X2}010100"; string response = $":{UNIT_ID:X2}010100";
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForUnitIdOnValidateResponse() public void ShouldThrowForUnitIdOnValidateResponse()
{ {
// Arrange // Arrange
@@ -934,12 +859,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
AddTrailer(ref response); AddTrailer(ref response);
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForLrcOnValidateResponse() public void ShouldThrowForLrcOnValidateResponse()
{ {
// Arrange // Arrange
@@ -947,12 +871,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
string response = $":{UNIT_ID:X2}010001FF00XX\r\n"; string response = $":{UNIT_ID:X2}010001FF00XX\r\n";
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForFunctionCodeOnValidateResponse() public void ShouldThrowForFunctionCodeOnValidateResponse()
{ {
// Arrange // Arrange
@@ -961,12 +884,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
AddTrailer(ref response); AddTrailer(ref response);
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForErrorOnValidateResponse() public void ShouldThrowForErrorOnValidateResponse()
{ {
// Arrange // Arrange
@@ -975,16 +897,15 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
AddTrailer(ref response); AddTrailer(ref response);
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)));
} }
[DataTestMethod] [TestMethod]
[DataRow(0x01)] [DataRow(0x01)]
[DataRow(0x02)] [DataRow(0x02)]
[DataRow(0x03)] [DataRow(0x03)]
[DataRow(0x04)] [DataRow(0x04)]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForReadLengthOnValidateResponse(int fn) public void ShouldThrowForReadLengthOnValidateResponse(int fn)
{ {
// Arrange // Arrange
@@ -993,16 +914,15 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
AddTrailer(ref response); AddTrailer(ref response);
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)));
} }
[DataTestMethod] [TestMethod]
[DataRow(0x05)] [DataRow(0x05)]
[DataRow(0x06)] [DataRow(0x06)]
[DataRow(0x0F)] [DataRow(0x0F)]
[DataRow(0x10)] [DataRow(0x10)]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForWriteLengthOnValidateResponse(int fn) public void ShouldThrowForWriteLengthOnValidateResponse(int fn)
{ {
// Arrange // Arrange
@@ -1011,8 +931,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
AddTrailer(ref response); AddTrailer(ref response);
var protocol = new AsciiProtocol(); var protocol = new AsciiProtocol();
// Act // Act + Assert
protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(Encoding.ASCII.GetBytes(request), Encoding.ASCII.GetBytes(response)));
} }
[TestMethod] [TestMethod]
@@ -1028,63 +948,51 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
Assert.AreEqual("F7", lrc); Assert.AreEqual("F7", lrc);
} }
[DataTestMethod] [TestMethod]
[DataRow(null)] [DataRow(null)]
[DataRow("")] [DataRow("")]
[DataRow(" ")] [DataRow(" ")]
[DataRow("\t")] [DataRow("\t")]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullExceptionForMessageOnLrc(string msg) public void ShouldThrowArgumentNullExceptionForMessageOnLrc(string msg)
{ {
// Arrange // Arrange
// Act // Act + Assert
AsciiProtocol.LRC(msg); Assert.ThrowsExactly<ArgumentNullException>(() => AsciiProtocol.LRC(msg));
// Assert - ArgumentNullException
} }
[DataTestMethod] [TestMethod]
[DataRow(-1)] [DataRow(-1)]
[DataRow(4)] [DataRow(4)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeExceptionForStartOnLrc(int start) public void ShouldThrowArgumentOutOfRangeExceptionForStartOnLrc(int start)
{ {
// Arrange // Arrange
string msg = "0207"; string msg = "0207";
// Act // Act + Assert
AsciiProtocol.LRC(msg, start); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => AsciiProtocol.LRC(msg, start));
// Assert - ArgumentOutOfRangeException
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(5)] [DataRow(5)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeExceptionForLengthOnLrc(int length) public void ShouldThrowArgumentOutOfRangeExceptionForLengthOnLrc(int length)
{ {
// Arrange // Arrange
string msg = "0207"; string msg = "0207";
// Act // Act + Assert
AsciiProtocol.LRC(msg, 0, length); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => AsciiProtocol.LRC(msg, 0, length));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForMessageLengthOnLrc() public void ShouldThrowArgumentExceptionForMessageLengthOnLrc()
{ {
// Arrange // Arrange
string msg = "0207"; string msg = "0207";
// Act // Act + Assert
AsciiProtocol.LRC(msg); Assert.ThrowsExactly<ArgumentException>(() => AsciiProtocol.LRC(msg));
// Assert - ArgumentException
} }
#endregion Validation #endregion Validation

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Reflection;
using System.Reflection;
using AMWD.Protocols.Modbus.Common.Protocols; using AMWD.Protocols.Modbus.Common.Protocols;
namespace AMWD.Protocols.Modbus.Tests.Common.Protocols namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
@@ -52,32 +51,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
// CRC check will be ignored // CRC check will be ignored
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(2001)] [DataRow(2001)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadCoils(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadCoils(int count)
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeReadCoils(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadCoils(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadCoils() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadCoils()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeReadCoils(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadCoils(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -106,16 +99,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadCoils() public void ShouldThrowExceptionOnDeserializeReadCoils()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.DeserializeReadCoils([0x00, 0x01, 0x00, 0x00, 0x00, 0x08, UNIT_ID, 0x01, 0x02, 0xCD, 0x6B, 0x05, 0x00, 0x00]); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadCoils([0x00, 0x01, 0x00, 0x00, 0x00, 0x08, UNIT_ID, 0x01, 0x02, 0xCD, 0x6B, 0x05, 0x00, 0x00]));
// Assert - ModbusException
} }
#endregion Read Coils #endregion Read Coils
@@ -163,32 +153,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
// CRC check will be ignored // CRC check will be ignored
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(2001)] [DataRow(2001)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadDiscreteInputs(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadDiscreteInputs(int count)
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeReadDiscreteInputs(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadDiscreteInputs(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadDiscreteInputs() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadDiscreteInputs()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeReadDiscreteInputs(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadDiscreteInputs(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -217,16 +201,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadDiscreteInputs() public void ShouldThrowExceptionOnDeserializeReadDiscreteInputs()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.DeserializeReadDiscreteInputs([0x00, 0x01, 0x00, 0x00, 0x00, 0x08, UNIT_ID, 0x02, 0x02, 0xCD, 0x6B, 0x05, 0x00, 0x00]); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadDiscreteInputs([0x00, 0x01, 0x00, 0x00, 0x00, 0x08, UNIT_ID, 0x02, 0x02, 0xCD, 0x6B, 0x05, 0x00, 0x00]));
// Assert - ModbusException
} }
#endregion Read Discrete Inputs #endregion Read Discrete Inputs
@@ -274,32 +255,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
// CRC check will be ignored // CRC check will be ignored
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(126)] [DataRow(126)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadHoldingRegisters(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadHoldingRegisters(int count)
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeReadHoldingRegisters(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadHoldingRegisters(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadHoldingRegisters() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadHoldingRegisters()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeReadHoldingRegisters(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadHoldingRegisters(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -323,16 +298,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadHoldingRegisters() public void ShouldThrowExceptionOnDeserializeReadHoldingRegisters()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.DeserializeReadHoldingRegisters([0x00, 0x01, 0x00, 0x00, 0x00, 0x07, UNIT_ID, 0x03, 0x04, 0x02, 0x2B, 0x00, 0x00]); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadHoldingRegisters([0x00, 0x01, 0x00, 0x00, 0x00, 0x08, UNIT_ID, 0x03, 0x04, 0x02, 0x2B, 0x00, 0x00]));
// Assert - ModbusException
} }
#endregion Read Holding Registers #endregion Read Holding Registers
@@ -380,32 +352,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
// CRC check will be ignored // CRC check will be ignored
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(126)] [DataRow(126)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadInputRegisters(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadInputRegisters(int count)
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeReadInputRegisters(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadInputRegisters(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadInputRegisters() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadInputRegisters()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeReadInputRegisters(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadInputRegisters(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -429,23 +395,20 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadInputRegisters() public void ShouldThrowExceptionOnDeserializeReadInputRegisters()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.DeserializeReadInputRegisters([0x00, 0x01, 0x00, 0x00, 0x00, 0x07, UNIT_ID, 0x04, 0x04, 0x02, 0x2B, 0x00, 0x00]); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadInputRegisters([0x00, 0x01, 0x00, 0x00, 0x00, 0x08, UNIT_ID, 0x04, 0x04, 0x02, 0x2B, 0x00, 0x00]));
// Assert - ModbusException
} }
#endregion Read Input Registers #endregion Read Input Registers
#region Read Device Identification #region Read Device Identification
[DataTestMethod] [TestMethod]
[DataRow(ModbusDeviceIdentificationCategory.Basic)] [DataRow(ModbusDeviceIdentificationCategory.Basic)]
[DataRow(ModbusDeviceIdentificationCategory.Regular)] [DataRow(ModbusDeviceIdentificationCategory.Regular)]
[DataRow(ModbusDeviceIdentificationCategory.Extended)] [DataRow(ModbusDeviceIdentificationCategory.Extended)]
@@ -493,19 +456,16 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeExceptionForCategoryOnSerializeReadDeviceIdentification() public void ShouldThrowOutOfRangeExceptionForCategoryOnSerializeReadDeviceIdentification()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeReadDeviceIdentification(UNIT_ID, (ModbusDeviceIdentificationCategory)10, ModbusDeviceIdentificationObject.ProductCode); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadDeviceIdentification(UNIT_ID, (ModbusDeviceIdentificationCategory)10, ModbusDeviceIdentificationObject.ProductCode));
// Assert - ArgumentOutOfRangeException
} }
[DataTestMethod] [TestMethod]
[DataRow(false)] [DataRow(false)]
[DataRow(true)] [DataRow(true)]
public void ShouldDeserializeReadDeviceIdentification(bool moreAndIndividual) public void ShouldDeserializeReadDeviceIdentification(bool moreAndIndividual)
@@ -529,27 +489,25 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForMeiType() public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForMeiType()
{ {
// Arrange // Arrange
byte[] response = [0x00, 0x01, 0x00, 0x00, 0x00, 0x05, UNIT_ID, 0x2B, 0x0D, 0x00, 0x00]; byte[] response = [0x00, 0x01, 0x00, 0x00, 0x00, 0x05, UNIT_ID, 0x2B, 0x0D, 0x00, 0x00];
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.DeserializeReadDeviceIdentification(response); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadDeviceIdentification(response));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForCategory() public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForCategory()
{ {
// Arrange // Arrange
byte[] response = [0x00, 0x01, 0x00, 0x00, 0x00, 0x06, UNIT_ID, 0x2B, 0x0E, 0x08, 0x00, 0x00]; byte[] response = [0x00, 0x01, 0x00, 0x00, 0x00, 0x06, UNIT_ID, 0x2B, 0x0E, 0x08, 0x00, 0x00];
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.DeserializeReadDeviceIdentification(response); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadDeviceIdentification(response));
} }
#endregion Read Device Identification #endregion Read Device Identification
@@ -600,16 +558,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteSingleCoil() public void ShouldThrowArgumentNullOnSerializeWriteSingleCoil()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeWriteSingleCoil(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteSingleCoil(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
@@ -676,16 +631,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteSingleHoldingRegister() public void ShouldThrowArgumentNullOnSerializeWriteSingleHoldingRegister()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeWriteSingleHoldingRegister(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteSingleHoldingRegister(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
@@ -765,22 +717,18 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteMultipleCoils() public void ShouldThrowArgumentNullOnSerializeWriteMultipleCoils()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(1969)] [DataRow(1969)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleCoils(int count) public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleCoils(int count)
{ {
// Arrange // Arrange
@@ -790,14 +738,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, coils); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, coils));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleCoils() public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleCoils()
{ {
// Arrange // Arrange
@@ -808,14 +753,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, coils); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, coils));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleCoils() public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleCoils()
{ {
// Arrange // Arrange
@@ -826,10 +768,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, coils); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, coils));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
@@ -908,22 +848,18 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteMultipleHoldingRegisters() public void ShouldThrowArgumentNullOnSerializeWriteMultipleHoldingRegisters()
{ {
// Arrange // Arrange
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(124)] [DataRow(124)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleHoldingRegisters(int count) public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleHoldingRegisters(int count)
{ {
// Arrange // Arrange
@@ -933,14 +869,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleHoldingRegisters() public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleHoldingRegisters()
{ {
// Arrange // Arrange
@@ -951,14 +884,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleHoldingRegisters() public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleHoldingRegisters()
{ {
// Arrange // Arrange
@@ -969,10 +899,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
@@ -1062,10 +990,9 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
protocol.ValidateResponse(request, response); protocol.ValidateResponse(request, response);
} }
[DataTestMethod] [TestMethod]
[DataRow(0x00, 0x00)] [DataRow(0x00, 0x00)]
[DataRow(0x01, 0x01)] [DataRow(0x01, 0x01)]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForTransactionIdOnValidateResponse(int hi, int lo) public void ShouldThrowForTransactionIdOnValidateResponse(int hi, int lo)
{ {
// Arrange // Arrange
@@ -1074,14 +1001,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[DataTestMethod] [TestMethod]
[DataRow(0x00, 0x01)] [DataRow(0x00, 0x01)]
[DataRow(0x01, 0x00)] [DataRow(0x01, 0x00)]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForProtocolIdOnValidateResponse(int hi, int lo) public void ShouldThrowForProtocolIdOnValidateResponse(int hi, int lo)
{ {
// Arrange // Arrange
@@ -1090,12 +1016,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForFollowingBytesOnValidateResponse() public void ShouldThrowForFollowingBytesOnValidateResponse()
{ {
// Arrange // Arrange
@@ -1104,12 +1029,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForUnitIdOnValidateResponse() public void ShouldThrowForUnitIdOnValidateResponse()
{ {
// Arrange // Arrange
@@ -1118,12 +1042,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForFunctionCodeOnValidateResponse() public void ShouldThrowForFunctionCodeOnValidateResponse()
{ {
// Arrange // Arrange
@@ -1132,12 +1055,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForModbusErrorOnValidateResponse() public void ShouldThrowForModbusErrorOnValidateResponse()
{ {
// Arrange // Arrange
@@ -1146,14 +1068,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[DataTestMethod] [TestMethod]
[DataRow(0x59, 0x6C)] [DataRow(0x59, 0x6C)]
[DataRow(0x58, 0x6B)] [DataRow(0x58, 0x6B)]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForCrcOnValidateResponse(int hi, int lo) public void ShouldThrowForCrcOnValidateResponse(int hi, int lo)
{ {
// Arrange // Arrange
@@ -1161,8 +1082,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
byte[] response = [0x00, 0x01, 0x00, 0x00, 0x00, 0x06, UNIT_ID, 0x01, 0x01, 0x00, (byte)hi, (byte)lo]; byte[] response = [0x00, 0x01, 0x00, 0x00, 0x00, 0x06, UNIT_ID, 0x01, 0x01, 0x00, (byte)hi, (byte)lo];
var protocol = new RtuOverTcpProtocol(); var protocol = new RtuOverTcpProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
#endregion Validation #endregion Validation

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Text;
using System.Text;
using AMWD.Protocols.Modbus.Common.Protocols; using AMWD.Protocols.Modbus.Common.Protocols;
namespace AMWD.Protocols.Modbus.Tests.Common.Protocols namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
@@ -40,32 +39,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
// CRC check will be ignored // CRC check will be ignored
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(2001)] [DataRow(2001)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadCoils(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadCoils(int count)
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeReadCoils(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadCoils(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadCoils() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadCoils()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeReadCoils(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadCoils(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -94,16 +87,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadCoils() public void ShouldThrowExceptionOnDeserializeReadCoils()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
_ = protocol.DeserializeReadCoils([UNIT_ID, 0x01, 0x02, 0xCD, 0x6B, 0x05, 0x00, 0x00]); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadCoils([UNIT_ID, 0x01, 0x02, 0xCD, 0x6B, 0x05, 0x00, 0x00]));
// Assert - ModbusException
} }
#endregion Read Coils #endregion Read Coils
@@ -139,32 +129,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
// CRC check will be ignored // CRC check will be ignored
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(2001)] [DataRow(2001)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadDiscreteInputs(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadDiscreteInputs(int count)
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeReadDiscreteInputs(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadDiscreteInputs(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadDiscreteInputs() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadDiscreteInputs()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeReadDiscreteInputs(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadDiscreteInputs(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -193,16 +177,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadDiscreteInputs() public void ShouldThrowExceptionOnDeserializeReadDiscreteInputs()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
_ = protocol.DeserializeReadDiscreteInputs([UNIT_ID, 0x02, 0x02, 0xCD, 0x6B, 0x05, 0x00, 0x00]); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadDiscreteInputs([UNIT_ID, 0x02, 0x02, 0xCD, 0x6B, 0x05, 0x00, 0x00]));
// Assert - ModbusException
} }
#endregion Read Discrete Inputs #endregion Read Discrete Inputs
@@ -238,32 +219,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
// CRC check will be ignored // CRC check will be ignored
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(126)] [DataRow(126)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadHoldingRegisters(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadHoldingRegisters(int count)
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeReadHoldingRegisters(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadHoldingRegisters(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadHoldingRegisters() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadHoldingRegisters()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeReadHoldingRegisters(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadHoldingRegisters(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -287,16 +262,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadHoldingRegisters() public void ShouldThrowExceptionOnDeserializeReadHoldingRegisters()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.DeserializeReadHoldingRegisters([UNIT_ID, 0x03, 0x04, 0x02, 0x2B, 0x00, 0x00]); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadHoldingRegisters([UNIT_ID, 0x03, 0x04, 0x02, 0x2B, 0x00, 0x00]));
// Assert - ModbusException
} }
#endregion Read Holding Registers #endregion Read Holding Registers
@@ -332,32 +304,26 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
// CRC check will be ignored // CRC check will be ignored
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(126)] [DataRow(126)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeReadInputRegisters(int count) public void ShouldThrowOutOfRangeForCountOnSerializeReadInputRegisters(int count)
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeReadInputRegisters(UNIT_ID, 19, (ushort)count); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadInputRegisters(UNIT_ID, 19, (ushort)count));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadInputRegisters() public void ShouldThrowOutOfRangeForStartingAddressOnSerializeReadInputRegisters()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeReadInputRegisters(UNIT_ID, ushort.MaxValue, 2); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadInputRegisters(UNIT_ID, ushort.MaxValue, 2));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
@@ -381,23 +347,20 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadInputRegisters() public void ShouldThrowExceptionOnDeserializeReadInputRegisters()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.DeserializeReadInputRegisters([UNIT_ID, 0x04, 0x04, 0x02, 0x2B, 0x00, 0x00]); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadInputRegisters([UNIT_ID, 0x04, 0x04, 0x02, 0x2B, 0x00, 0x00]));
// Assert - ModbusException
} }
#endregion Read Input Registers #endregion Read Input Registers
#region Read Device Identification #region Read Device Identification
[DataTestMethod] [TestMethod]
[DataRow(ModbusDeviceIdentificationCategory.Basic)] [DataRow(ModbusDeviceIdentificationCategory.Basic)]
[DataRow(ModbusDeviceIdentificationCategory.Regular)] [DataRow(ModbusDeviceIdentificationCategory.Regular)]
[DataRow(ModbusDeviceIdentificationCategory.Extended)] [DataRow(ModbusDeviceIdentificationCategory.Extended)]
@@ -433,19 +396,16 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeExceptionForCategoryOnSerializeReadDeviceIdentification() public void ShouldThrowOutOfRangeExceptionForCategoryOnSerializeReadDeviceIdentification()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeReadDeviceIdentification(UNIT_ID, (ModbusDeviceIdentificationCategory)10, ModbusDeviceIdentificationObject.ProductCode); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeReadDeviceIdentification(UNIT_ID, (ModbusDeviceIdentificationCategory)10, ModbusDeviceIdentificationObject.ProductCode));
// Assert - ArgumentOutOfRangeException
} }
[DataTestMethod] [TestMethod]
[DataRow(false)] [DataRow(false)]
[DataRow(true)] [DataRow(true)]
public void ShouldDeserializeReadDeviceIdentification(bool moreAndIndividual) public void ShouldDeserializeReadDeviceIdentification(bool moreAndIndividual)
@@ -469,27 +429,25 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForMeiType() public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForMeiType()
{ {
// Arrange // Arrange
byte[] response = [UNIT_ID, 0x2B, 0x0D, 0x00, 0x00]; byte[] response = [UNIT_ID, 0x2B, 0x0D, 0x00, 0x00];
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.DeserializeReadDeviceIdentification(response); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadDeviceIdentification(response));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForCategory() public void ShouldThrowExceptionOnDeserializeReadDeviceIdentificationForCategory()
{ {
// Arrange // Arrange
byte[] response = [UNIT_ID, 0x2B, 0x0E, 0x08, 0x00, 0x00]; byte[] response = [UNIT_ID, 0x2B, 0x0E, 0x08, 0x00, 0x00];
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.DeserializeReadDeviceIdentification(response); Assert.ThrowsExactly<ModbusException>(() => protocol.DeserializeReadDeviceIdentification(response));
} }
#endregion Read Device Identification #endregion Read Device Identification
@@ -528,16 +486,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteSingleCoil() public void ShouldThrowArgumentNullOnSerializeWriteSingleCoil()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeWriteSingleCoil(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteSingleCoil(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
@@ -592,16 +547,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteSingleHoldingRegister() public void ShouldThrowArgumentNullOnSerializeWriteSingleHoldingRegister()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeWriteSingleHoldingRegister(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteSingleHoldingRegister(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[TestMethod] [TestMethod]
@@ -669,22 +621,18 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteMultipleCoils() public void ShouldThrowArgumentNullOnSerializeWriteMultipleCoils()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(1969)] [DataRow(1969)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleCoils(int count) public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleCoils(int count)
{ {
// Arrange // Arrange
@@ -694,14 +642,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, coils); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, coils));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleCoils() public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleCoils()
{ {
// Arrange // Arrange
@@ -712,14 +657,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, coils); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, coils));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleCoils() public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleCoils()
{ {
// Arrange // Arrange
@@ -730,10 +672,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleCoils(UNIT_ID, coils); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleCoils(UNIT_ID, coils));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
@@ -800,22 +740,18 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldThrowArgumentNullOnSerializeWriteMultipleHoldingRegisters() public void ShouldThrowArgumentNullOnSerializeWriteMultipleHoldingRegisters()
{ {
// Arrange // Arrange
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, null); Assert.ThrowsExactly<ArgumentNullException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, null));
// Assert - ArgumentNullException
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(124)] [DataRow(124)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleHoldingRegisters(int count) public void ShouldThrowOutOfRangeForCountOnSerializeWriteMultipleHoldingRegisters(int count)
{ {
// Arrange // Arrange
@@ -825,14 +761,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers));
// Assert - ArgumentOutOfRangeException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleHoldingRegisters() public void ShouldThrowArgumentExceptionForDuplicateEntryOnSerializeMultipleHoldingRegisters()
{ {
// Arrange // Arrange
@@ -843,14 +776,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleHoldingRegisters() public void ShouldThrowArgumentExceptionForGapInAddressOnSerializeMultipleHoldingRegisters()
{ {
// Arrange // Arrange
@@ -861,10 +791,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
}; };
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers); Assert.ThrowsExactly<ArgumentException>(() => protocol.SerializeWriteMultipleHoldingRegisters(UNIT_ID, registers));
// Assert - ArgumentException
} }
[TestMethod] [TestMethod]
@@ -928,7 +856,7 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
Assert.IsTrue(complete); Assert.IsTrue(complete);
} }
[DataTestMethod] [TestMethod]
[DataRow(0x01)] // Read Coils [DataRow(0x01)] // Read Coils
[DataRow(0x02)] // Read Discrete Inputs [DataRow(0x02)] // Read Discrete Inputs
[DataRow(0x03)] // Read Holding Registers [DataRow(0x03)] // Read Holding Registers
@@ -946,7 +874,7 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
Assert.IsFalse(complete); Assert.IsFalse(complete);
} }
[DataTestMethod] [TestMethod]
[DataRow(0x01)] // Read Coils [DataRow(0x01)] // Read Coils
[DataRow(0x02)] // Read Discrete Inputs [DataRow(0x02)] // Read Discrete Inputs
[DataRow(0x03)] // Read Holding Registers [DataRow(0x03)] // Read Holding Registers
@@ -964,7 +892,7 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
Assert.IsTrue(complete); Assert.IsTrue(complete);
} }
[DataTestMethod] [TestMethod]
[DataRow(0x05)] // Write Single Coil [DataRow(0x05)] // Write Single Coil
[DataRow(0x06)] // Write Single Register [DataRow(0x06)] // Write Single Register
[DataRow(0x0F)] // Write Multiple Coils [DataRow(0x0F)] // Write Multiple Coils
@@ -982,7 +910,7 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
Assert.IsFalse(complete); Assert.IsFalse(complete);
} }
[DataTestMethod] [TestMethod]
[DataRow(0x05)] // Write Single Coil [DataRow(0x05)] // Write Single Coil
[DataRow(0x06)] // Write Single Register [DataRow(0x06)] // Write Single Register
[DataRow(0x0F)] // Write Multiple Coils [DataRow(0x0F)] // Write Multiple Coils
@@ -1070,7 +998,7 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
Assert.IsTrue(complete); Assert.IsTrue(complete);
} }
[DataTestMethod] [TestMethod]
[DataRow(0x01)] [DataRow(0x01)]
[DataRow(0x02)] [DataRow(0x02)]
[DataRow(0x03)] [DataRow(0x03)]
@@ -1087,7 +1015,7 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
protocol.ValidateResponse(request, response); protocol.ValidateResponse(request, response);
} }
[DataTestMethod] [TestMethod]
[DataRow(0x05)] [DataRow(0x05)]
[DataRow(0x06)] [DataRow(0x06)]
[DataRow(0x0F)] [DataRow(0x0F)]
@@ -1105,7 +1033,6 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForUnitIdOnValidateResponse() public void ShouldThrowForUnitIdOnValidateResponse()
{ {
// Arrange // Arrange
@@ -1114,14 +1041,13 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[DataTestMethod] [TestMethod]
[DataRow(0x57, 0x6C)] [DataRow(0x57, 0x6C)]
[DataRow(0x58, 0x6B)] [DataRow(0x58, 0x6B)]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForCrcOnValidateResponse(int hi, int lo) public void ShouldThrowForCrcOnValidateResponse(int hi, int lo)
{ {
// Arrange // Arrange
@@ -1129,12 +1055,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
byte[] response = [UNIT_ID, 0x01, 0x01, 0x00, (byte)hi, (byte)lo]; byte[] response = [UNIT_ID, 0x01, 0x01, 0x00, (byte)hi, (byte)lo];
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForFunctionCodeOnValidateResponse() public void ShouldThrowForFunctionCodeOnValidateResponse()
{ {
// Arrange // Arrange
@@ -1143,12 +1068,11 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForErrorOnValidateResponse() public void ShouldThrowForErrorOnValidateResponse()
{ {
// Arrange // Arrange
@@ -1157,16 +1081,15 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[DataTestMethod] [TestMethod]
[DataRow(0x01)] [DataRow(0x01)]
[DataRow(0x02)] [DataRow(0x02)]
[DataRow(0x03)] [DataRow(0x03)]
[DataRow(0x04)] [DataRow(0x04)]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForReadLengthOnValidateResponse(int fn) public void ShouldThrowForReadLengthOnValidateResponse(int fn)
{ {
// Arrange // Arrange
@@ -1175,16 +1098,15 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[DataTestMethod] [TestMethod]
[DataRow(0x05)] [DataRow(0x05)]
[DataRow(0x06)] [DataRow(0x06)]
[DataRow(0x0F)] [DataRow(0x0F)]
[DataRow(0x10)] [DataRow(0x10)]
[ExpectedException(typeof(ModbusException))]
public void ShouldThrowForWriteLengthOnValidateResponse(int fn) public void ShouldThrowForWriteLengthOnValidateResponse(int fn)
{ {
// Arrange // Arrange
@@ -1193,8 +1115,8 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
SetCrc(response); SetCrc(response);
var protocol = new RtuProtocol(); var protocol = new RtuProtocol();
// Act // Act + Assert
protocol.ValidateResponse(request, response); Assert.ThrowsExactly<ModbusException>(() => protocol.ValidateResponse(request, response));
} }
[TestMethod] [TestMethod]
@@ -1214,46 +1136,39 @@ namespace AMWD.Protocols.Modbus.Tests.Common.Protocols
Assert.AreEqual(0x12, crc[1]); Assert.AreEqual(0x12, crc[1]);
} }
[DataTestMethod] [TestMethod]
[DataRow(null)] [DataRow(null)]
[DataRow(new byte[0])] [DataRow(new byte[0])]
[ExpectedException(typeof(ArgumentNullException))]
public void ShuldThrowArgumentNullExceptionForBytesOnCrc16(byte[] bytes) public void ShuldThrowArgumentNullExceptionForBytesOnCrc16(byte[] bytes)
{ {
// Act // Arrange
_ = RtuProtocol.CRC16(bytes);
// Assert - ArgumentNullException // Act + Assert
Assert.ThrowsExactly<ArgumentNullException>(() => RtuProtocol.CRC16(bytes));
} }
[DataTestMethod] [TestMethod]
[DataRow(-1)] [DataRow(-1)]
[DataRow(10)] [DataRow(10)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeForStartOnCrc16(int start) public void ShouldThrowArgumentOutOfRangeForStartOnCrc16(int start)
{ {
// Arrange // Arrange
byte[] bytes = Encoding.UTF8.GetBytes("0123456789"); byte[] bytes = Encoding.UTF8.GetBytes("0123456789");
// Act // Act + Assert
_ = RtuProtocol.CRC16(bytes, start); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => RtuProtocol.CRC16(bytes, start));
// Assert - ArgumentOutOfRangeException
} }
[DataTestMethod] [TestMethod]
[DataRow(0)] [DataRow(0)]
[DataRow(11)] [DataRow(11)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void ShouldThrowArgumentOutOfRangeForLengthOnCrc16(int length) public void ShouldThrowArgumentOutOfRangeForLengthOnCrc16(int length)
{ {
// Arrange // Arrange
byte[] bytes = Encoding.UTF8.GetBytes("0123456789"); byte[] bytes = Encoding.UTF8.GetBytes("0123456789");
// Act // Act + Assert
_ = RtuProtocol.CRC16(bytes, 0, length); Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => RtuProtocol.CRC16(bytes, 0, length));
// Assert - ArgumentOutOfRangeException
} }
#endregion Validation #endregion Validation

Some files were not shown because too many files have changed in this diff Show More