Fixes for SerialRtuProxy
- Adding UnitTests - Fixing some bugs - Updating UnitTest dependencies
This commit is contained in:
@@ -20,10 +20,10 @@ build-debug:
|
|||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG == null
|
- if: $CI_COMMIT_TAG == null
|
||||||
script:
|
script:
|
||||||
- dotnet restore --no-cache --force
|
|
||||||
- dotnet build -c Debug --nologo --no-restore --no-incremental
|
|
||||||
- shopt -s globstar
|
- shopt -s globstar
|
||||||
- mkdir ./artifacts
|
- mkdir ./artifacts
|
||||||
|
- dotnet restore --no-cache --force
|
||||||
|
- dotnet build -c Debug --nologo --no-restore --no-incremental
|
||||||
- mv ./**/*.nupkg ./artifacts/
|
- mv ./**/*.nupkg ./artifacts/
|
||||||
- mv ./**/*.snupkg ./artifacts/
|
- mv ./**/*.snupkg ./artifacts/
|
||||||
artifacts:
|
artifacts:
|
||||||
@@ -42,10 +42,20 @@ test-debug:
|
|||||||
- 64bit
|
- 64bit
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG == null
|
- if: $CI_COMMIT_TAG == null
|
||||||
coverage: '/Total[^|]*\|[^|]*\|\s*([0-9.%]+)/'
|
coverage: /Branch coverage[\s\S].+%/
|
||||||
|
before_script:
|
||||||
|
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
|
||||||
script:
|
script:
|
||||||
- dotnet restore --no-cache --force
|
- dotnet test -c Debug --nologo /p:CoverletOutputFormat=Cobertura
|
||||||
- dotnet test -c Debug --nologo --no-restore
|
- /dotnet-tools/reportgenerator "-reports:${CI_PROJECT_DIR}/**/coverage.cobertura.xml" "-targetdir:/reports" -reportType:TextSummary
|
||||||
|
after_script:
|
||||||
|
- cat /reports/Summary.txt
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
coverage_report:
|
||||||
|
coverage_format: cobertura
|
||||||
|
path: ./**/coverage.cobertura.xml
|
||||||
|
|
||||||
deploy-debug:
|
deploy-debug:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
@@ -72,17 +82,17 @@ build-release:
|
|||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG != null
|
- if: $CI_COMMIT_TAG != null
|
||||||
script:
|
script:
|
||||||
- dotnet restore --no-cache --force
|
|
||||||
- dotnet build -c Release --nologo --no-restore --no-incremental
|
|
||||||
- shopt -s globstar
|
- shopt -s globstar
|
||||||
- mkdir ./artifacts
|
- mkdir ./artifacts
|
||||||
|
- dotnet restore --no-cache --force
|
||||||
|
- dotnet build -c Release --nologo --no-restore --no-incremental
|
||||||
- mv ./**/*.nupkg ./artifacts/
|
- mv ./**/*.nupkg ./artifacts/
|
||||||
- mv ./**/*.snupkg ./artifacts/
|
- mv ./**/*.snupkg ./artifacts/
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- artifacts/*.nupkg
|
- artifacts/*.nupkg
|
||||||
- artifacts/*.snupkg
|
- artifacts/*.snupkg
|
||||||
expire_in: 1 days
|
expire_in: 7 days
|
||||||
|
|
||||||
test-release:
|
test-release:
|
||||||
stage: test
|
stage: test
|
||||||
@@ -94,10 +104,20 @@ test-release:
|
|||||||
- amd64
|
- amd64
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG != null
|
- if: $CI_COMMIT_TAG != null
|
||||||
coverage: '/Total[^|]*\|[^|]*\|\s*([0-9.%]+)/'
|
coverage: /Branch coverage[\s\S].+%/
|
||||||
|
before_script:
|
||||||
|
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
|
||||||
script:
|
script:
|
||||||
- dotnet restore --no-cache --force
|
- dotnet test -c Release --nologo /p:CoverletOutputFormat=Cobertura
|
||||||
- dotnet test -c Release --nologo --no-restore
|
- /dotnet-tools/reportgenerator "-reports:${CI_PROJECT_DIR}/**/coverage.cobertura.xml" "-targetdir:/reports" -reportType:TextSummary
|
||||||
|
after_script:
|
||||||
|
- cat /reports/Summary.txt
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
coverage_report:
|
||||||
|
coverage_format: cobertura
|
||||||
|
path: ./**/coverage.cobertura.xml
|
||||||
|
|
||||||
deploy-release:
|
deploy-release:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ namespace AMWD.Protocols.Modbus.Common.Utils
|
|||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
internal VirtualProtocol TypedProtocol => Protocol as VirtualProtocol;
|
internal VirtualProtocol TypedProtocol
|
||||||
|
=> Protocol as VirtualProtocol;
|
||||||
|
|
||||||
#endregion Properties
|
#endregion Properties
|
||||||
|
|
||||||
|
|||||||
@@ -618,7 +618,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
var responseBytes = new List<byte>();
|
var responseBytes = new List<byte>();
|
||||||
responseBytes.AddRange(requestBytes.Take(8));
|
responseBytes.AddRange(requestBytes.Take(2));
|
||||||
|
|
||||||
ushort firstAddress = requestBytes.GetBigEndianUInt16(2);
|
ushort firstAddress = requestBytes.GetBigEndianUInt16(2);
|
||||||
ushort count = requestBytes.GetBigEndianUInt16(4);
|
ushort count = requestBytes.GetBigEndianUInt16(4);
|
||||||
@@ -646,6 +646,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
HighByte = requestBytes[baseOffset + i * 2],
|
HighByte = requestBytes[baseOffset + i * 2],
|
||||||
LowByte = requestBytes[baseOffset + i * 2 + 1]
|
LowByte = requestBytes[baseOffset + i * 2 + 1]
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
bool isSuccess = await Client.WriteMultipleHoldingRegistersAsync(requestBytes[0], list, cancellationToken);
|
bool isSuccess = await Client.WriteMultipleHoldingRegistersAsync(requestBytes[0], list, cancellationToken);
|
||||||
if (isSuccess)
|
if (isSuccess)
|
||||||
@@ -659,7 +660,6 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
|
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
responseBytes[1] |= 0x80;
|
responseBytes[1] |= 0x80;
|
||||||
@@ -671,6 +671,9 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
|
|
||||||
private async Task<byte[]> HandleEncapsulatedInterfaceAsync(byte[] requestBytes, CancellationToken cancellationToken)
|
private async Task<byte[]> HandleEncapsulatedInterfaceAsync(byte[] requestBytes, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
if (requestBytes.Length < 7)
|
||||||
|
return null;
|
||||||
|
|
||||||
var responseBytes = new List<byte>();
|
var responseBytes = new List<byte>();
|
||||||
responseBytes.AddRange(requestBytes.Take(2));
|
responseBytes.AddRange(requestBytes.Take(2));
|
||||||
|
|
||||||
@@ -702,7 +705,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var res = await Client.ReadDeviceIdentificationAsync(requestBytes[6], category, firstObject, cancellationToken);
|
var deviceInfo = await Client.ReadDeviceIdentificationAsync(requestBytes[0], category, firstObject, cancellationToken);
|
||||||
|
|
||||||
var bodyBytes = new List<byte>();
|
var bodyBytes = new List<byte>();
|
||||||
|
|
||||||
@@ -711,31 +714,20 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
|
|
||||||
// Conformity
|
// Conformity
|
||||||
bodyBytes.Add((byte)category);
|
bodyBytes.Add((byte)category);
|
||||||
if (res.IsIndividualAccessAllowed)
|
if (deviceInfo.IsIndividualAccessAllowed)
|
||||||
bodyBytes[2] |= 0x80;
|
bodyBytes[2] |= 0x80;
|
||||||
|
|
||||||
// More, NextId, NumberOfObjects
|
// More, NextId, NumberOfObjects
|
||||||
bodyBytes.AddRange(new byte[3]);
|
bodyBytes.AddRange(new byte[3]);
|
||||||
|
|
||||||
int maxObjectId;
|
int maxObjectId = category switch
|
||||||
switch (category)
|
|
||||||
{
|
{
|
||||||
case ModbusDeviceIdentificationCategory.Basic:
|
ModbusDeviceIdentificationCategory.Basic => 0x02,
|
||||||
maxObjectId = 0x02;
|
ModbusDeviceIdentificationCategory.Regular => 0x06,
|
||||||
break;
|
ModbusDeviceIdentificationCategory.Extended => 0xFF,
|
||||||
|
// Individual
|
||||||
case ModbusDeviceIdentificationCategory.Regular:
|
_ => requestBytes[4],
|
||||||
maxObjectId = 0x06;
|
};
|
||||||
break;
|
|
||||||
|
|
||||||
case ModbusDeviceIdentificationCategory.Extended:
|
|
||||||
maxObjectId = 0xFF;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // Individual
|
|
||||||
maxObjectId = requestBytes[4];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte numberOfObjects = 0;
|
byte numberOfObjects = 0;
|
||||||
for (int i = requestBytes[4]; i <= maxObjectId; i++)
|
for (int i = requestBytes[4]; i <= maxObjectId; i++)
|
||||||
@@ -744,7 +736,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
if (0x07 <= i && i <= 0x7F)
|
if (0x07 <= i && i <= 0x7F)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
byte[] objBytes = GetDeviceObject((byte)i, res);
|
byte[] objBytes = GetDeviceObject((byte)i, deviceInfo);
|
||||||
|
|
||||||
// We need to split the response if it would exceed the max ADU size
|
// We need to split the response if it would exceed the max ADU size
|
||||||
if (responseBytes.Count + bodyBytes.Count + objBytes.Length > RtuProtocol.MAX_ADU_LENGTH)
|
if (responseBytes.Count + bodyBytes.Count + objBytes.Length > RtuProtocol.MAX_ADU_LENGTH)
|
||||||
@@ -754,7 +746,8 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
|
|
||||||
bodyBytes[5] = numberOfObjects;
|
bodyBytes[5] = numberOfObjects;
|
||||||
responseBytes.AddRange(bodyBytes);
|
responseBytes.AddRange(bodyBytes);
|
||||||
return [.. responseBytes];
|
|
||||||
|
return ReturnResponse(responseBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyBytes.AddRange(objBytes);
|
bodyBytes.AddRange(objBytes);
|
||||||
@@ -782,7 +775,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
{
|
{
|
||||||
case ModbusDeviceIdentificationObject.VendorName:
|
case ModbusDeviceIdentificationObject.VendorName:
|
||||||
{
|
{
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.VendorName);
|
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.VendorName ?? "");
|
||||||
result.Add((byte)bytes.Length);
|
result.Add((byte)bytes.Length);
|
||||||
result.AddRange(bytes);
|
result.AddRange(bytes);
|
||||||
}
|
}
|
||||||
@@ -790,7 +783,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
|
|
||||||
case ModbusDeviceIdentificationObject.ProductCode:
|
case ModbusDeviceIdentificationObject.ProductCode:
|
||||||
{
|
{
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.ProductCode);
|
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.ProductCode ?? "");
|
||||||
result.Add((byte)bytes.Length);
|
result.Add((byte)bytes.Length);
|
||||||
result.AddRange(bytes);
|
result.AddRange(bytes);
|
||||||
}
|
}
|
||||||
@@ -798,7 +791,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
|
|
||||||
case ModbusDeviceIdentificationObject.MajorMinorRevision:
|
case ModbusDeviceIdentificationObject.MajorMinorRevision:
|
||||||
{
|
{
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.MajorMinorRevision);
|
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.MajorMinorRevision ?? "");
|
||||||
result.Add((byte)bytes.Length);
|
result.Add((byte)bytes.Length);
|
||||||
result.AddRange(bytes);
|
result.AddRange(bytes);
|
||||||
}
|
}
|
||||||
@@ -806,7 +799,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
|
|
||||||
case ModbusDeviceIdentificationObject.VendorUrl:
|
case ModbusDeviceIdentificationObject.VendorUrl:
|
||||||
{
|
{
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.VendorUrl);
|
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.VendorUrl ?? "");
|
||||||
result.Add((byte)bytes.Length);
|
result.Add((byte)bytes.Length);
|
||||||
result.AddRange(bytes);
|
result.AddRange(bytes);
|
||||||
}
|
}
|
||||||
@@ -814,7 +807,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
|
|
||||||
case ModbusDeviceIdentificationObject.ProductName:
|
case ModbusDeviceIdentificationObject.ProductName:
|
||||||
{
|
{
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.ProductName);
|
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.ProductName ?? "");
|
||||||
result.Add((byte)bytes.Length);
|
result.Add((byte)bytes.Length);
|
||||||
result.AddRange(bytes);
|
result.AddRange(bytes);
|
||||||
}
|
}
|
||||||
@@ -822,7 +815,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
|
|
||||||
case ModbusDeviceIdentificationObject.ModelName:
|
case ModbusDeviceIdentificationObject.ModelName:
|
||||||
{
|
{
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.ModelName);
|
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.ModelName ?? "");
|
||||||
result.Add((byte)bytes.Length);
|
result.Add((byte)bytes.Length);
|
||||||
result.AddRange(bytes);
|
result.AddRange(bytes);
|
||||||
}
|
}
|
||||||
@@ -830,7 +823,7 @@ namespace AMWD.Protocols.Modbus.Serial
|
|||||||
|
|
||||||
case ModbusDeviceIdentificationObject.UserApplicationName:
|
case ModbusDeviceIdentificationObject.UserApplicationName:
|
||||||
{
|
{
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.UserApplicationName);
|
byte[] bytes = Encoding.UTF8.GetBytes(deviceIdentification.UserApplicationName ?? "");
|
||||||
result.Add((byte)bytes.Length);
|
result.Add((byte)bytes.Length);
|
||||||
result.AddRange(bytes);
|
result.AddRange(bytes);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -24,9 +24,9 @@ namespace AMWD.Protocols.Modbus.Serial.Utils
|
|||||||
|
|
||||||
public SerialPortWrapper()
|
public SerialPortWrapper()
|
||||||
{
|
{
|
||||||
_serialPort.DataReceived += OnDataReceived;
|
_serialPort.DataReceived += (sender, e) => DataReceived?.Invoke(this, e);
|
||||||
_serialPort.PinChanged += OnPinChanged;
|
_serialPort.PinChanged += (sender, e) => PinChanged?.Invoke(this, e);
|
||||||
_serialPort.ErrorReceived += OnErrorReceived;
|
_serialPort.ErrorReceived += (sender, e) => ErrorReceived?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Constructor
|
#endregion Constructor
|
||||||
@@ -42,15 +42,6 @@ namespace AMWD.Protocols.Modbus.Serial.Utils
|
|||||||
/// <inheritdoc cref="SerialPort.ErrorReceived"/>
|
/// <inheritdoc cref="SerialPort.ErrorReceived"/>
|
||||||
public virtual event SerialErrorReceivedEventHandler ErrorReceived;
|
public virtual event SerialErrorReceivedEventHandler ErrorReceived;
|
||||||
|
|
||||||
private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
|
|
||||||
=> DataReceived?.Invoke(sender, e);
|
|
||||||
|
|
||||||
private void OnPinChanged(object sender, SerialPinChangedEventArgs e)
|
|
||||||
=> PinChanged?.Invoke(sender, e);
|
|
||||||
|
|
||||||
private void OnErrorReceived(object sender, SerialErrorReceivedEventArgs e)
|
|
||||||
=> ErrorReceived?.Invoke(sender, e);
|
|
||||||
|
|
||||||
#endregion Events
|
#endregion Events
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|||||||
@@ -6,24 +6,21 @@
|
|||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<IsTestProject>true</IsTestProject>
|
<IsTestProject>true</IsTestProject>
|
||||||
<CollectCoverage>true</CollectCoverage>
|
<CollectCoverage>true</CollectCoverage>
|
||||||
|
<CoverletOutputFormat>Cobertura</CoverletOutputFormat>
|
||||||
|
|
||||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
<PackageReference Include="coverlet.msbuild" Version="6.0.4">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||||
<PackageReference Include="Moq" Version="4.20.72" />
|
<PackageReference Include="Moq" Version="4.20.72" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="3.6.4" />
|
<PackageReference Include="MSTest.TestAdapter" Version="3.7.2" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
|
<PackageReference Include="MSTest.TestFramework" Version="3.7.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
23
AMWD.Protocols.Modbus.Tests/Helper.cs
Normal file
23
AMWD.Protocols.Modbus.Tests/Helper.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace AMWD.Protocols.Modbus.Tests
|
||||||
|
{
|
||||||
|
internal static class Helper
|
||||||
|
{
|
||||||
|
public static T CreateInstance<T>(params object[] args)
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
|
||||||
|
object instance = type.Assembly.CreateInstance(
|
||||||
|
typeName: type.FullName,
|
||||||
|
ignoreCase: false,
|
||||||
|
bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic,
|
||||||
|
binder: null,
|
||||||
|
args: args,
|
||||||
|
culture: null,
|
||||||
|
activationAttributes: null);
|
||||||
|
|
||||||
|
return (T)instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2006
AMWD.Protocols.Modbus.Tests/Serial/ModbusRtuProxyTest.cs
Normal file
2006
AMWD.Protocols.Modbus.Tests/Serial/ModbusRtuProxyTest.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
|||||||
|
DeviceIdentification
|
||||||
|
VendorName: VendorName
|
||||||
|
ProductCode: ProductCode
|
||||||
|
MajorMinorRevision: MajorMinorRevision
|
||||||
|
VendorUrl:
|
||||||
|
ProductName:
|
||||||
|
ModelName:
|
||||||
|
UserApplicationName:
|
||||||
|
IsIndividualAccessAllowed: False
|
||||||
Reference in New Issue
Block a user