From 1e842656c362ada4138602b41e48770304a26b28 Mon Sep 17 00:00:00 2001 From: Alexey <248997@gmail.com> Date: Wed, 8 Nov 2023 16:39:09 +0500 Subject: [PATCH] feat(sdr): add utility methods for SDR tags and tests Added multiple utility methods to handle AsvSdrRecordTagTypes in AsvSdrHelper.cs. These methods include PrintTag, PrintTagValue, GetTagValueAsUInt64, SetTagValueAsUInt64, GetTagValueAsInt64, SetTagValueAsInt64, GetTagValueAsReal64, SetTagValueAsReal64, GetTagValueAsString, and SetTagValueAsString. These methods provide a unified way to manage SDR tag values, handle various data types and improve code robustness by performing necessary checks to prevent incorrect usage. Mavlink endpoint 'AsvSdrClientRecordTag.cs' was refactored to use these new utility methods. The changes are justified by enhancing the capacity of handling SDR tags and simplifying the client's tasks in managing these data types. Also added corresponding tests for these new utility methods in AsvSdrHelperTest.cs to ensure their correct functionality. Tests include setting and getting tag value for all supported types (ulong, long, double, string). These added tests provide validation that new utility methods function as expected. A method was added in MavlinkTypesHelper.cs for setting and getting string value from byte arrays. Testing for these methods was also included. Reducing code repetition and enhancing code robustness lead to sturdier and easily maintainable codebase. --- .../Microservices/AsvSdr/AsvSdrHelperTest.cs | 50 ++++++++++ .../Microservices/AsvSdr/AsvSdrHelper.cs | 94 +++++++++++++++++++ .../AsvSdr/Client/Ex/AsvSdrClientRecordTag.cs | 20 +--- src/Asv.Mavlink/Tools/MavlinkTypesHelper.cs | 26 +++++ 4 files changed, 175 insertions(+), 15 deletions(-) diff --git a/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrHelperTest.cs b/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrHelperTest.cs index 17d44269..e30bc98d 100644 --- a/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrHelperTest.cs +++ b/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrHelperTest.cs @@ -261,6 +261,56 @@ public void Check_mission_conversion_for_wait_vehicle(ushort index) AsvSdrHelper.GetArgsForSdrWaitVehicleWaypoint(outputServerItem, out var indexOut); Assert.Equal(index,indexOut); } + [Theory] + [InlineData(ulong.MaxValue)] + [InlineData(ulong.MinValue)] + [InlineData(1234567789)] + public void Check_set_and_get_value_from_byte_array_uint64(ulong value) + { + var data = new byte[AsvSdrHelper.RecordTagValueLength]; + AsvSdrHelper.SetTagValueAsUInt64(data, AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64, value); + var valueOut = AsvSdrHelper.GetTagValueAsUInt64(data, AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64); + Assert.Equal(value,valueOut); + } + [Theory] + [InlineData(0)] + [InlineData(123456789)] + [InlineData(long.MaxValue)] + [InlineData(long.MinValue)] + public void Check_set_and_get_value_from_byte_array_int64(long value) + { + var data = new byte[AsvSdrHelper.RecordTagValueLength]; + AsvSdrHelper.SetTagValueAsInt64(data, AsvSdrRecordTagType.AsvSdrRecordTagTypeInt64, value); + var valueOut = AsvSdrHelper.GetTagValueAsInt64(data, AsvSdrRecordTagType.AsvSdrRecordTagTypeInt64); + Assert.Equal(value,valueOut); + } + [Theory] + [InlineData(0)] + [InlineData(double.Epsilon)] + [InlineData(double.NaN)] + [InlineData(12345.45)] + public void Check_set_and_get_value_from_byte_array_double(double value) + { + var data = new byte[AsvSdrHelper.RecordTagValueLength]; + AsvSdrHelper.SetTagValueAsReal64(data, AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64, value); + var valueOut = AsvSdrHelper.GetTagValueAsReal64(data, AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64); + Assert.Equal(value,valueOut); + } + + [Theory] + [InlineData("")] + [InlineData("12345678")] + [InlineData("1234")] + [InlineData("A")] + public void Check_set_and_get_value_from_byte_array_string8(string value) + { + var data = new byte[AsvSdrHelper.RecordTagValueLength]; + AsvSdrHelper.SetTagValueAsString(data, AsvSdrRecordTagType.AsvSdrRecordTagTypeString8, value); + var valueOut = AsvSdrHelper.GetTagValueAsString(data, AsvSdrRecordTagType.AsvSdrRecordTagTypeString8); + Assert.Equal(value,valueOut); + } + + diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrHelper.cs b/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrHelper.cs index e03591d3..1d001b07 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrHelper.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrHelper.cs @@ -30,6 +30,8 @@ public static void CheckRecordName(string name) $"Record name '{name}' not match regex '{RecordNameRegexString}')"); } + #region Tags + public static void CheckTagName(string name) { if (name.IsNullOrWhiteSpace()) throw new Exception("Tag name is empty"); @@ -40,6 +42,98 @@ public static void CheckTagName(string name) $"Tag name '{name}' not match regex '{RecordTagNameRegexString}')"); } + public static string PrintTag(string tagName,AsvSdrRecordTagType type, byte[] value) + { + return $"{tagName}:{PrintTagValue(type, value)}"; + } + public static string PrintTagValue(AsvSdrRecordTagType type, byte[] rawValue) + { + if (rawValue.Length != RecordTagValueLength) + throw new ArgumentException(nameof(rawValue), $"Tag value array must be {RecordTagValueLength} bytes length"); + return type switch + { + AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64 => $"{BitConverter.ToUInt64(rawValue, 0)}", + AsvSdrRecordTagType.AsvSdrRecordTagTypeInt64 => $"{BitConverter.ToInt64(rawValue, 0)}", + AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64 => $"{BitConverter.ToDouble(rawValue, 0)}", + AsvSdrRecordTagType.AsvSdrRecordTagTypeString8 => $"{MavlinkTypesHelper.GetString(rawValue)}", + _ => throw new ArgumentOutOfRangeException() + }; + } + + public static ulong GetTagValueAsUInt64(ReadOnlySpan rawValue, AsvSdrRecordTagType type) + { + if (rawValue.Length != RecordTagValueLength) + throw new ArgumentException($"Tag value array must be {RecordTagValueLength} bytes length", nameof(rawValue)); + if (type != AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64) + throw new ArgumentException($"Tag type must be {AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64}", nameof(type)); + return BitConverter.ToUInt64(rawValue); + } + public static void SetTagValueAsUInt64(Span rawValue, AsvSdrRecordTagType type, ulong value) + { + if (rawValue.Length != RecordTagValueLength) + throw new ArgumentException($"Tag value array must be {RecordTagValueLength} bytes length", nameof(rawValue)); + if (type != AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64) + throw new ArgumentException($"Tag type must be {AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64}", nameof(type)); + if (BitConverter.TryWriteBytes(rawValue, value) == false) + throw new ArgumentException($"Can't write value {value} to tag value array", nameof(value)); + + } + public static long GetTagValueAsInt64(ReadOnlySpan rawValue, AsvSdrRecordTagType type) + { + if (rawValue.Length != RecordTagValueLength) + throw new ArgumentException($"Tag value array must be {RecordTagValueLength} bytes length", nameof(rawValue)); + if (type != AsvSdrRecordTagType.AsvSdrRecordTagTypeInt64) + throw new ArgumentException($"Tag type must be {AsvSdrRecordTagType.AsvSdrRecordTagTypeInt64}", nameof(type)); + return BitConverter.ToInt64(rawValue); + } + + public static void SetTagValueAsInt64(Span rawValue, AsvSdrRecordTagType type, long value) + { + if (rawValue.Length != RecordTagValueLength) + throw new ArgumentException($"Tag value array must be {RecordTagValueLength} bytes length", nameof(rawValue)); + if (type != AsvSdrRecordTagType.AsvSdrRecordTagTypeInt64) + throw new ArgumentException($"Tag type must be {AsvSdrRecordTagType.AsvSdrRecordTagTypeInt64}", nameof(type)); + if (BitConverter.TryWriteBytes(rawValue, value) == false) + throw new ArgumentException($"Can't write value {value} to tag value array", nameof(value)); + } + public static double GetTagValueAsReal64(ReadOnlySpan rawValue, AsvSdrRecordTagType type) + { + if (rawValue.Length != RecordTagValueLength) + throw new ArgumentException($"Tag value array must be {RecordTagValueLength} bytes length", nameof(rawValue)); + if (type != AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64) + throw new ArgumentException($"Tag type must be {AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64}", nameof(type)); + return BitConverter.ToDouble(rawValue); + } + + public static void SetTagValueAsReal64(Span rawValue, AsvSdrRecordTagType type, double value) + { + if (rawValue.Length != RecordTagValueLength) + throw new ArgumentException($"Tag value array must be {RecordTagValueLength} bytes length", nameof(rawValue)); + if (type != AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64) + throw new ArgumentException($"Tag type must be {AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64}", nameof(type)); + if (BitConverter.TryWriteBytes(rawValue, value) == false) + throw new ArgumentException($"Can't write value {value} to tag value array", nameof(value)); + } + + public static string GetTagValueAsString(byte[] rawValue, AsvSdrRecordTagType type) + { + if (rawValue.Length != RecordTagValueLength) + throw new ArgumentException($"Tag value array must be {RecordTagValueLength} bytes length", nameof(rawValue)); + if (type != AsvSdrRecordTagType.AsvSdrRecordTagTypeString8) + throw new ArgumentException($"Tag type must be {AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64}", nameof(type)); + return MavlinkTypesHelper.GetString(rawValue); + } + + public static void SetTagValueAsString(byte[] rawValue, AsvSdrRecordTagType type, string value) + { + if (rawValue.Length != RecordTagValueLength) + throw new ArgumentException($"Tag value array must be {RecordTagValueLength} bytes length", nameof(rawValue)); + if (type != AsvSdrRecordTagType.AsvSdrRecordTagTypeString8) + throw new ArgumentException($"Tag type must be {AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64}", nameof(type)); + MavlinkTypesHelper.SetString(rawValue, value); + } + + #endregion public static readonly ListDataFileFormat FileFormat = new() { diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientRecordTag.cs b/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientRecordTag.cs index 8dc7475c..c24a2528 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientRecordTag.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientRecordTag.cs @@ -19,22 +19,12 @@ internal AsvSdrClientRecordTag(TagId id, AsvSdrRecordTagPayload payload) public AsvSdrRecordTagType Type { get; } - public ulong GetUint64() => BitConverter.ToUInt64(RawValue,0); - public long GetInt64() => BitConverter.ToInt64(RawValue,0); - public double GetReal64() => BitConverter.ToDouble(RawValue,0); - public string GetString() => MavlinkTypesHelper.GetString(RawValue); + public ulong GetUint64() => AsvSdrHelper.GetTagValueAsUInt64(RawValue, Type); + public long GetInt64() => AsvSdrHelper.GetTagValueAsInt64(RawValue, Type); + public double GetReal64() => AsvSdrHelper.GetTagValueAsReal64(RawValue,Type); + public string GetString() => AsvSdrHelper.GetTagValueAsString(RawValue,Type); - public override string ToString() - { - return Type switch - { - AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64 => $"{Name}:{GetUint64()}", - AsvSdrRecordTagType.AsvSdrRecordTagTypeInt64 => $"{Name}:{GetInt64()}", - AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64 => $"{Name}:{GetReal64()}", - AsvSdrRecordTagType.AsvSdrRecordTagTypeString8 => $"{Name}:{GetString()}", - _ => throw new ArgumentOutOfRangeException() - }; - } + public override string ToString() => AsvSdrHelper.PrintTag(Name, Type, RawValue); public void CopyTo(AsvSdrRecordTagPayload dest) { diff --git a/src/Asv.Mavlink/Tools/MavlinkTypesHelper.cs b/src/Asv.Mavlink/Tools/MavlinkTypesHelper.cs index 9c45a88a..a5a30a6b 100644 --- a/src/Asv.Mavlink/Tools/MavlinkTypesHelper.cs +++ b/src/Asv.Mavlink/Tools/MavlinkTypesHelper.cs @@ -66,6 +66,20 @@ public static void SetString(byte[] data,string value) } Encoding.ASCII.GetBytes(value,0,value.Length, data, 0); } + + public static void SetString(Span data,string value) + { + if (data == null) throw new ArgumentNullException(nameof(data)); + if (value.IsNullOrWhiteSpace()) + { + for (var i = 0; i < data.Length; i++) + { + data[i] = (byte)'\0'; + } + return; + } + Encoding.ASCII.GetBytes(value,data); + } public static void SetString(char[] data,string value) { if (data == null) throw new ArgumentNullException(nameof(data)); @@ -101,6 +115,18 @@ public static string GetString(byte[] data) } return sb.ToString(); } + + public static string GetString(ReadOnlySpan data) + { + if (data == null) throw new ArgumentNullException(nameof(data)); + var sb = new StringBuilder(data.Length); + foreach (var _ in data) + { + if (_ == '\0') break; + sb.Append((char)_); + } + return sb.ToString(); + } public static GeoPoint FromInt32ToGeoPoint(int lat, int lon,float alt) {