From d3ff29bd4c26c35c8cf963955a7105349177e0c1 Mon Sep 17 00:00:00 2001 From: SeppPenner Date: Tue, 2 Jul 2024 17:56:21 +0200 Subject: [PATCH] Nearly fixed https://github.com/SeppPenner/SparkplugNet/issues/96. --- .../SparkplugPayloadConverterTestVersionB.cs | 120 +++++++++++++++++- .../Core/Extensions/DataTypeExtensions.cs | 45 +++++++ src/SparkplugNet/VersionB/PayloadConverter.cs | 2 +- 3 files changed, 160 insertions(+), 7 deletions(-) diff --git a/src/SparkplugNet.Tests/Payloads/SparkplugPayloadConverterTestVersionB.cs b/src/SparkplugNet.Tests/Payloads/SparkplugPayloadConverterTestVersionB.cs index b7d7039..0bb3fef 100644 --- a/src/SparkplugNet.Tests/Payloads/SparkplugPayloadConverterTestVersionB.cs +++ b/src/SparkplugNet.Tests/Payloads/SparkplugPayloadConverterTestVersionB.cs @@ -1909,7 +1909,10 @@ public void TestPropertySetConversion() [TestMethod] public void TestConvertVersionBPayloadFromProtoWithNegativeValues() { - var value = -2; + var value1 = -1; + var value2 = -2; + var value3 = -3; + var value4 = -4; var timestamp = new DateTimeOffset(2019, 1, 1, 0, 0, 0, TimeSpan.Zero); var bodyData = new byte[] { 1, 2, 3, 4 }; var metrics = new List @@ -1922,17 +1925,68 @@ public void TestConvertVersionBPayloadFromProtoWithNegativeValues() IsHistorical = true, IsTransient = true, IsNull = false, + DataType = (uint?)VersionBData.DataType.Int8, + IntValue = unchecked((uint)value1) + }, + new() + { + Name = "Test2", + Timestamp = (ulong)timestamp.ToUnixTimeMilliseconds(), + Alias = 3, + IsHistorical = true, + IsTransient = true, + IsNull = false, DataType = (uint?)VersionBData.DataType.Int16, - IntValue = unchecked((uint)value) + IntValue = unchecked((uint)value2) + }, + new() + { + Name = "Test3", + Timestamp = (ulong)timestamp.ToUnixTimeMilliseconds(), + Alias = 4, + IsHistorical = true, + IsTransient = true, + IsNull = false, + DataType = (uint?)VersionBData.DataType.Int32, + IntValue = unchecked((uint)value3) + }, + new() + { + Name = "Test4", + Timestamp = (ulong)timestamp.ToUnixTimeMilliseconds(), + Alias = 5, + IsHistorical = true, + IsTransient = true, + IsNull = false, + DataType = (uint?)VersionBData.DataType.Int64, + LongValue = unchecked((ulong)value4) } }; var convertedMetrics = new List { - new("Test1", VersionBData.DataType.Int16, (short)-2, timestamp) + new("Test1", VersionBData.DataType.Int8, (sbyte)value1, timestamp) { Alias = 2, IsHistorical = true, IsTransient = true + }, + new("Test2", VersionBData.DataType.Int16, (short)value2, timestamp) + { + Alias = 3, + IsHistorical = true, + IsTransient = true + }, + new("Test3", VersionBData.DataType.Int32, (int)value3, timestamp) + { + Alias = 4, + IsHistorical = true, + IsTransient = true + }, + new("Test4", VersionBData.DataType.Int64, (long)value4, timestamp) + { + Alias = 5, + IsHistorical = true, + IsTransient = true } }; var oldPayload = new VersionBProtoBufPayload @@ -1965,17 +2019,38 @@ public void TestConvertVersionBPayloadFromProtoWithNegativeValues() [TestMethod] public void TestConvertVersionBPayloadToProtoWithNegativeValues() { - var value = -1; + var value1 = -1; + var value2 = -2; + var value3 = -3; + var value4 = -4; var timestamp = new DateTimeOffset(2019, 1, 1, 0, 0, 0, TimeSpan.Zero); var bodyData = new byte[] { 1, 2, 3, 4 }; var metrics = new List { - new("Test1", VersionBData.DataType.Int8, (sbyte)-1, timestamp) + new("Test1", VersionBData.DataType.Int8, (sbyte)value1, timestamp) { Alias = 1, IsHistorical = true, IsTransient = true + }, + new("Test2", VersionBData.DataType.Int16, (short)value2, timestamp) + { + Alias = 2, + IsHistorical = true, + IsTransient = true + }, + new("Test3", VersionBData.DataType.Int32, (int)value3, timestamp) + { + Alias = 3, + IsHistorical = true, + IsTransient = true + }, + new("Test4", VersionBData.DataType.Int64, (long)value4, timestamp) + { + Alias = 4, + IsHistorical = true, + IsTransient = true } }; var convertedMetrics = new List @@ -1989,7 +2064,40 @@ public void TestConvertVersionBPayloadToProtoWithNegativeValues() IsTransient = true, IsNull = false, DataType = (uint?)VersionBProtoBuf.DataType.Int8, - IntValue = unchecked((uint)value) + IntValue = unchecked((uint)value1) + }, + new() + { + Name = "Test2", + Timestamp = (ulong)timestamp.ToUnixTimeMilliseconds(), + Alias = 2, + IsHistorical = true, + IsTransient = true, + IsNull = false, + DataType = (uint?)VersionBProtoBuf.DataType.Int16, + IntValue = unchecked((uint)value2) + }, + new() + { + Name = "Test3", + Timestamp = (ulong)timestamp.ToUnixTimeMilliseconds(), + Alias = 3, + IsHistorical = true, + IsTransient = true, + IsNull = false, + DataType = (uint?)VersionBProtoBuf.DataType.Int32, + IntValue = unchecked((uint)value3) + }, + new() + { + Name = "Test4", + Timestamp = (ulong)timestamp.ToUnixTimeMilliseconds(), + Alias = 4, + IsHistorical = true, + IsTransient = true, + IsNull = false, + DataType = (uint?)VersionBProtoBuf.DataType.Int64, + LongValue = unchecked((ulong)value4) } }; var oldPayload = new VersionBData.Payload diff --git a/src/SparkplugNet/Core/Extensions/DataTypeExtensions.cs b/src/SparkplugNet/Core/Extensions/DataTypeExtensions.cs index 4292c55..858a50d 100644 --- a/src/SparkplugNet/Core/Extensions/DataTypeExtensions.cs +++ b/src/SparkplugNet/Core/Extensions/DataTypeExtensions.cs @@ -27,6 +27,51 @@ internal static class DataTypeExtensions return default; } + // Check whether we need to convert from signed to unsigned. + var needToConvertFromSignedToUnsigned = + (objValue is sbyte sbyteVal && sbyteVal < 0) || + (objValue is short shortVal && shortVal < 0) || + (objValue is int intVal && intVal < 0) || + (objValue is long longVal && longVal < 0); + + if (needToConvertFromSignedToUnsigned) + { + return unchecked((T)objValue); + } + + // Check whether we need to convert from unsigned to signed. + // Check for uint values first. + if (objValue is uint uintVal) + { + var needToConvertFromUnsignedIntToSigned = + (uintVal > sbyte.MaxValue && typeof(T) == typeof(sbyte)) || + (uintVal > short.MaxValue && typeof(T) == typeof(short)) || + (uintVal > int.MaxValue && typeof(T) == typeof(int)); + + if (needToConvertFromUnsignedIntToSigned) + { + var intValue = unchecked((int)uintVal); + return (T)Convert.ChangeType(intValue, typeof(T)); + } + } + + // Check for ulong values second. + if (objValue is ulong ulongVal) + { + var needToConvertFromUnsignedLongToSigned = + (ulongVal > (ulong)sbyte.MaxValue && typeof(T) == typeof(sbyte)) || + (ulongVal > (ulong)short.MaxValue && typeof(T) == typeof(short)) || + (ulongVal > int.MaxValue && typeof(T) == typeof(int)) || + (ulongVal > long.MaxValue && typeof(T) == typeof(long)); + + if (needToConvertFromUnsignedLongToSigned) + { + var intValue = unchecked((long)ulongVal); + return (T)Convert.ChangeType(intValue, typeof(T)); + } + } + + // Regular case. return objValue is T valueAsT ? valueAsT : (T)Convert.ChangeType(objValue, typeof(T)); diff --git a/src/SparkplugNet/VersionB/PayloadConverter.cs b/src/SparkplugNet/VersionB/PayloadConverter.cs index d9d6c1e..fcc2242 100644 --- a/src/SparkplugNet/VersionB/PayloadConverter.cs +++ b/src/SparkplugNet/VersionB/PayloadConverter.cs @@ -221,7 +221,7 @@ public static VersionBProtoBuf.ProtoBufPayload.Metric ConvertVersionBMetric(Metr case VersionBDataTypeEnum.Int64: case VersionBDataTypeEnum.UInt32: case VersionBDataTypeEnum.UInt64: - protoMetric.LongValue = metric.Value is not null ? metric.Value.ConvertOrDefaultTo() : default; + protoMetric.LongValue = metric.Value.ConvertOrDefaultTo(); break; case VersionBDataTypeEnum.DateTime: if (metric.Value is null)