diff --git a/src/Asv.Mavlink.Shell/Resources/csharp.tpl b/src/Asv.Mavlink.Shell/Resources/csharp.tpl index 1aef82cc..63ebdd61 100644 --- a/src/Asv.Mavlink.Shell/Resources/csharp.tpl +++ b/src/Asv.Mavlink.Shell/Resources/csharp.tpl @@ -482,9 +482,9 @@ namespace Asv.Mavlink.V2.{{ Namespace }} {%- if field.IsEnum -%} {%- if field.IsArray -%} {%- if field.IsTheLargestArrayInMessage -%} - public {{ field.EnumCamelCaseName }}[] {{ field.CamelCaseName }} { get; set; } = new {{ field.Type }}[{{ field.ArrayLength }}]; + public {{ field.EnumCamelCaseName }}[] {{ field.CamelCaseName }} { get; set; } = new {{ field.EnumCamelCaseName }}[{{ field.ArrayLength }}]; {%- else -%} - public {{ field.EnumCamelCaseName }}[] {{ field.CamelCaseName }} { get; } = new {{ field.Type }}[{{ field.ArrayLength }}]; + public {{ field.EnumCamelCaseName }}[] {{ field.CamelCaseName }} { get; } = new {{ field.EnumCamelCaseName }}[{{ field.ArrayLength }}]; {%- endif -%} {%- else -%} public {{ field.EnumCamelCaseName }} {{ field.CamelCaseName }} { get; set; } diff --git a/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrExTests.cs b/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrExTests.cs index 60638513..15f0e5f0 100644 --- a/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrExTests.cs +++ b/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrExTests.cs @@ -7,17 +7,18 @@ using Asv.Common; using Asv.Mavlink.V2.AsvSdr; using Asv.Mavlink.V2.Common; +using DynamicData; using DynamicData.Binding; using Xunit; using Xunit.Abstractions; -namespace Asv.Mavlink.Test.AsvSdr; +namespace Asv.Mavlink.Test; public class AsvSdrExTests { private readonly ITestOutputHelper _testOutputHelper; - private async Task<(AsvSdrClientEx, AsvSdrServerEx)> SetUpConnection() + private async Task<(IAsvSdrClientEx, IAsvSdrServerEx)> SetUpConnection() { var link = new VirtualMavlinkConnection(); var mavlinkClientIdentity = new MavlinkClientIdentity() { SystemId = 1, ComponentId = 1, TargetSystemId = 2, TargetComponentId = 2 }; @@ -25,13 +26,15 @@ public class AsvSdrExTests var commandClient = new CommandClient(link.Client, mavlinkClientIdentity, new PacketSequenceCalculator(), new CommandProtocolConfig()); var asvSdrClient = new AsvSdrClient(link.Client, mavlinkClientIdentity, new PacketSequenceCalculator()); var asvSdrClientEx = new AsvSdrClientEx(asvSdrClient, heartBeatClient, commandClient, new AsvSdrClientExConfig()); - + + var pkt = new PacketSequenceCalculator(); var mavlinkServerIdentity = new MavlinkServerIdentity() { SystemId = 2, ComponentId = 2 }; - var heartBeatServer = new HeartbeatServer(link.Server, new PacketSequenceCalculator(), mavlinkServerIdentity, new MavlinkHeartbeatServerConfig(), Scheduler.Default); - var commandServer = new CommandServer(link.Server, new PacketSequenceCalculator(), mavlinkServerIdentity, Scheduler.Default); + var heartBeatServer = new HeartbeatServer(link.Server, pkt, mavlinkServerIdentity, new MavlinkHeartbeatServerConfig(), Scheduler.Default); + var commandServer = new CommandServer(link.Server, pkt, mavlinkServerIdentity, Scheduler.Default); var commandLongServerEx = new CommandLongServerEx(commandServer); - var asvSdrServer = new AsvSdrServer(link.Server, mavlinkServerIdentity, new AsvSdrServerConfig(), new PacketSequenceCalculator(), Scheduler.Default); - var asvSdrServerEx = new AsvSdrServerEx(asvSdrServer, heartBeatServer, commandLongServerEx); + var status = new StatusTextServer(link.Server, pkt, mavlinkServerIdentity, new StatusTextLoggerConfig(), Scheduler.Default); + var asvSdrServer = new AsvSdrServer(link.Server, mavlinkServerIdentity, new AsvSdrServerConfig(), pkt, Scheduler.Default); + var asvSdrServerEx = new AsvSdrServerEx(asvSdrServer,status, heartBeatServer, commandLongServerEx); heartBeatServer.Start(); asvSdrServer.Start(); @@ -54,16 +57,17 @@ public async Task Check_For_AsvCustomMode_Set(AsvSdrCustomMode customMode) { var (asvSdrClientEx, asvSdrServerEx) = await SetUpConnection(); - asvSdrServerEx.SetMode = (mode, hz, rate, ratio, cancel) => + asvSdrServerEx.SetMode = (mode, hz, rate, ratio, refPower, cancel) => { Assert.Equal(customMode, mode); Assert.Equal(11223U, hz); Assert.Equal(1, rate); Assert.Equal(1U, ratio); + Assert.Equal(-90, refPower); return Task.FromResult(MavResult.MavResultAccepted); }; - var result = await asvSdrClientEx.SetMode(customMode, 11223, 1, 1, CancellationToken.None); + var result = await asvSdrClientEx.SetMode(customMode, 11223, 1, 1, -90, CancellationToken.None); Assert.Equal(MavResult.MavResultAccepted, result); } @@ -78,9 +82,9 @@ public async Task Check_For_MavResult_Value(MavResult mavResult) { var (asvSdrClientEx, asvSdrServerEx) = await SetUpConnection(); - asvSdrServerEx.SetMode = (_, _, _, _, _) => Task.FromResult(mavResult); + asvSdrServerEx.SetMode = (_, _, _, _, _,_) => Task.FromResult(mavResult); - var result = await asvSdrClientEx.SetMode(AsvSdrCustomMode.AsvSdrCustomModeLlz, 11223, 1, 1, CancellationToken.None); + var result = await asvSdrClientEx.SetMode(AsvSdrCustomMode.AsvSdrCustomModeLlz, 11223, 1, 1, -90.5f, CancellationToken.None); Assert.Equal(mavResult, result); } @@ -769,4 +773,174 @@ public async Task Check_Stop_Mission_Behaviour() await client.StopMission(); } -} \ No newline at end of file + + + [Fact] + public async Task Client_call_start_calibration_server_respond() + { + var (client, server) = await SetUpConnection(); + server.StartCalibration = (cancel) => Task.FromResult(MavResult.MavResultAccepted); + await client.StartCalibrationAndCheckResult(); + + } + + [Fact] + public async Task Client_call_unsupported_commands() + { + var (client, server) = await SetUpConnection(); + await Assert.ThrowsAsync(async () => + { + await client.StartCalibrationAndCheckResult(); + }); + await Assert.ThrowsAsync(async () => + { + await client.StopCalibrationAndCheckResult(); + }); + } + + [Fact] + public async Task Client_read_calibration_table_list() + { + var (client, server) = await SetUpConnection(); + var date = DateTime.Now; + server.Base.Set(_ => _.CalibTableCount = 2); + server.ReadCalibrationTableInfo = index => + { + return index switch + { + 0 => new CalibrationTableInfo { Name = "Table 1", Size = 10, Updated = date, }, + 1 => new CalibrationTableInfo { Name = "Table 2", Size = 20, Updated = date, }, + _ => throw new Exception("Invalid table index") + }; + }; + var status = await client.Base.Status.FirstAsync(); + await client.ReadCalibrationTableList(); + var sub = client.CalibrationTables.Bind(out var list).Subscribe(); + Assert.Equal(2, list.Count); + Assert.Equal("Table 1", list[0].Name); + Assert.Equal("Table 2", list[1].Name); + Assert.Equal(10, list[0].RemoteRowCount.Value); + Assert.Equal(20, list[1].RemoteRowCount.Value); + Assert.Equal(0, list[0].Index); + Assert.Equal(1, list[1].Index); + Assert.True(date - list[0].Updated.Value < TimeSpan.FromMilliseconds(100)); + } + [Fact] + public async Task Client_download_calibration_table() + { + var (client, server) = await SetUpConnection(); + var date = DateTime.Now; + server.Base.Set(_ => _.CalibTableCount = 2); + server.ReadCalibrationTableInfo = index => + { + return index switch + { + 0 => new CalibrationTableInfo { Name = "Table 1", Size = 10, Updated = date, }, + 1 => new CalibrationTableInfo { Name = "Table 2", Size = 20, Updated = date, }, + _ => throw new Exception("Invalid table index") + }; + }; + var status = await client.Base.Status.FirstAsync(); + await client.ReadCalibrationTableList(); + var sub = client.CalibrationTables.Bind(out var list).Subscribe(); + Assert.Equal(2, list.Count); + Assert.Equal("Table 1", list[0].Name); + Assert.Equal("Table 2", list[1].Name); + Assert.Equal(10, list[0].RemoteRowCount.Value); + Assert.Equal(20, list[1].RemoteRowCount.Value); + Assert.Equal(0, list[0].Index); + Assert.Equal(1, list[1].Index); + Assert.True(date - list[0].Updated.Value < TimeSpan.FromSeconds(1)); + } + + [Theory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + public async Task Client_read_calibration_table_rows(ushort count) + { + var (client, server) = await SetUpConnection(); + var name = "Table 1"; + var date = DateTime.Now; + server.Base.Set(_ => _.CalibTableCount = 1); + server.ReadCalibrationTableInfo = index => + { + return index switch + { + 0 => new CalibrationTableInfo { Name = name, Size = count, Updated = date, }, + _ => throw new Exception("Invalid table index") + }; + }; + server.ReadCalibrationTableRow = (tableIndex, row) => + { + if (tableIndex != 0) throw new Exception("Invalid table index"); + return new CalibrationTableRow(row,row*0.1f,row*20,row*10); + }; + var status = await client.Base.Status.FirstAsync(); + await client.ReadCalibrationTableList(); + var sub = client.CalibrationTables.Bind(out var list).Subscribe(); + var table = await client.GetCalibrationTable(name); + Assert.NotNull(table); + Assert.Equal(name, table.Name); + Assert.Equal(count, table.RemoteRowCount.Value); + Assert.Equal(0, table.Index); + Assert.True(date - table.Updated.Value < TimeSpan.FromMilliseconds(100)); + var items = await table.Download(); + Assert.Equal(count, items.Length); + for (int i = 0; i < count; i++) + { + Assert.Equal((ulong)i, items[i].FrequencyHz); + Assert.Equal(i*0.1f, items[i].RefPower); + Assert.Equal(i*20, items[i].RefValue); + Assert.Equal(i*10, items[i].MeasuredValue); + } + } + + + + [Fact] + public async Task Client_upload_and_download_with_server_error() + { + var (client, server) = await SetUpConnection(); + var name = "Table 1"; + var date = DateTime.Now; + + server.Base.Set(_ => _.CalibTableCount = 1); + server.ReadCalibrationTableInfo = index => + { + return index switch + { + 0 => new CalibrationTableInfo { Name = name, Size = 10, Updated = date, }, + _ => throw new Exception("Invalid table index") + }; + }; + server.WriteCalibrationTable = (tableIndex, items) => + { + throw new Exception("FATAL"); + }; + server.ReadCalibrationTableRow = (tableIndex, row) => + { + throw new Exception("FATAL"); + }; + + + + + var status = await client.Base.Status.FirstAsync(); + await client.ReadCalibrationTableList(); + var sub = client.CalibrationTables.Bind(out var list).Subscribe(); + var table = await client.GetCalibrationTable(name); + Assert.NotNull(table); + await Assert.ThrowsAsync(async () => + { + var resultEmpty = await table.Download(); + }); + await Assert.ThrowsAsync(async () => + { + await table.Upload(new CalibrationTableRow[]{new CalibrationTableRow(100,0,0,0) }); + }); + + + } + +} diff --git a/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrHelperTest.cs b/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrHelperTest.cs index e30bc98d..7a96784b 100644 --- a/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrHelperTest.cs +++ b/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrHelperTest.cs @@ -9,15 +9,15 @@ public class AsvSdrHelperTest { [Theory] - [InlineData(AsvSdrCustomMode.AsvSdrCustomModeLlz, 0, 0, 0)] - [InlineData(AsvSdrCustomMode.AsvSdrCustomModeGp, 330000001, 5, 1)] - public void Check_mission_conversion_for_set_mode(AsvSdrCustomMode mode, ulong frequencyHz, float recordRate, uint sendingThinningRatio) + [InlineData(AsvSdrCustomMode.AsvSdrCustomModeLlz, 0, 0, 0, -10)] + [InlineData(AsvSdrCustomMode.AsvSdrCustomModeGp, 330000001, 5, 1,-60)] + public void Check_mission_conversion_for_set_mode(AsvSdrCustomMode mode, ulong frequencyHz, float recordRate, uint sendingThinningRatio, float refPower) { var input = new MissionItemIntPayload(); var buffer = new byte[input.GetByteSize()]; var inputSpan = new Span(buffer); - AsvSdrHelper.SetArgsForSdrSetMode(input, mode, frequencyHz, recordRate, sendingThinningRatio); + AsvSdrHelper.SetArgsForSdrSetMode(input, mode, frequencyHz, recordRate, sendingThinningRatio,refPower); input.Serialize(ref inputSpan); @@ -27,34 +27,36 @@ public void Check_mission_conversion_for_set_mode(AsvSdrCustomMode mode, ulong f var serverItem = AsvSdrHelper.Convert(output); - AsvSdrHelper.GetArgsForSdrSetMode(serverItem, out var modeOut, out var frequencyHzOut, out var recordRateOut, out var sendingThinningRatioOut); + AsvSdrHelper.GetArgsForSdrSetMode(serverItem, out var modeOut, out var frequencyHzOut, out var recordRateOut, out var sendingThinningRatioOut, out var outRefPower); Assert.Equal(mode, modeOut); Assert.Equal(frequencyHz, frequencyHzOut); Assert.Equal(recordRate, recordRateOut); Assert.Equal(sendingThinningRatio, sendingThinningRatioOut); + Assert.Equal(refPower, outRefPower); } [Theory] - [InlineData(AsvSdrCustomMode.AsvSdrCustomModeLlz, 0, 0, 0)] - [InlineData(AsvSdrCustomMode.AsvSdrCustomModeGp, 330000001, 5, 1)] - public void Check_command_conversion_for_set_mode(AsvSdrCustomMode mode, ulong frequencyHz, float recordRate, uint sendingThinningRatio) + [InlineData(AsvSdrCustomMode.AsvSdrCustomModeLlz, 0, 0, 0, -10)] + [InlineData(AsvSdrCustomMode.AsvSdrCustomModeGp, 330000001, 5, 1,-60)] + public void Check_command_conversion_for_set_mode(AsvSdrCustomMode mode, ulong frequencyHz, float recordRate, uint sendingThinningRatio, float refPower) { var input = new CommandLongPayload(); var buffer = new byte[input.GetByteSize()]; var inputSpan = new Span(buffer); - AsvSdrHelper.SetArgsForSdrSetMode(input, mode, frequencyHz, recordRate, sendingThinningRatio); + AsvSdrHelper.SetArgsForSdrSetMode(input, mode, frequencyHz, recordRate, sendingThinningRatio, refPower); input.Serialize(ref inputSpan); var output = new CommandLongPayload(); var outputSpan = new ReadOnlySpan(buffer); output.Deserialize(ref outputSpan); - AsvSdrHelper.GetArgsForSdrSetMode(output, out var modeOut, out var frequencyHzOut, out var recordRateOut, out var sendingThinningRatioOut); + AsvSdrHelper.GetArgsForSdrSetMode(output, out var modeOut, out var frequencyHzOut, out var recordRateOut, out var sendingThinningRatioOut, out var outRefPower); Assert.Equal(mode, modeOut); Assert.Equal(frequencyHz, frequencyHzOut); Assert.Equal(recordRate, recordRateOut); Assert.Equal(sendingThinningRatio, sendingThinningRatioOut); + Assert.Equal(refPower, outRefPower); } [Theory] diff --git a/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrTest.cs b/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrTest.cs index 6382f68b..30bb2fc1 100644 --- a/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrTest.cs +++ b/src/Asv.Mavlink.Test/Microservices/AsvSdr/AsvSdrTest.cs @@ -19,19 +19,33 @@ public AsvSdrTest(ITestOutputHelper output) _output = output; } - [Fact] - public void Server_Sdr_Set_Argument_Null_Exception() + private void SetupClientServer(out IAsvSdrServer serverSdr, out IAsvSdrClient clientSdr) { var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), + serverSdr = new AsvSdrServer(link.Server, + new MavlinkServerIdentity + { + ComponentId = 2, + SystemId = 2 + }, new AsvSdrServerConfig(), new PacketSequenceCalculator(), Scheduler.Default); - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), + clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity + { + SystemId = 13, + ComponentId = 13, + TargetSystemId = 2, + TargetComponentId = 2 + }, new PacketSequenceCalculator()); serverSdr.Start(); + } + + [Fact] + public void Server_Sdr_Set_Argument_Null_Exception() + { + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -42,16 +56,7 @@ public void Server_Sdr_Set_Argument_Null_Exception() [Fact] public void Server_Sdr_Send_Record_Argument_Null_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -66,16 +71,7 @@ public void Server_Sdr_Send_Record_Argument_Null_Exception() [InlineData(AsvSdrCustomMode.AsvSdrCustomModeVor)] public void Server_Sdr_Send_Record_Data_Argument_Null_Exception(AsvSdrCustomMode customMode) { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -86,16 +82,7 @@ public void Server_Sdr_Send_Record_Data_Argument_Null_Exception(AsvSdrCustomMode [Fact] public void Server_Sdr_Send_Record_Data_Idle_Argument_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -106,16 +93,7 @@ public void Server_Sdr_Send_Record_Data_Idle_Argument_Exception() [Fact] public void Server_Sdr_Create_Record_Data_Idle_Argument_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -126,16 +104,7 @@ public void Server_Sdr_Create_Record_Data_Idle_Argument_Exception() [Fact] public void Server_Sdr_Send_Record_Data_Response_Argument_Null_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -146,16 +115,7 @@ public void Server_Sdr_Send_Record_Data_Response_Argument_Null_Exception() [Fact] public void Server_Sdr_Send_Record_Tag_Delete_Response_Argument_Null_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -166,16 +126,7 @@ public void Server_Sdr_Send_Record_Tag_Delete_Response_Argument_Null_Exception() [Fact] public void Server_Sdr_Send_Record_Tag_Argument_Null_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -186,16 +137,7 @@ public void Server_Sdr_Send_Record_Tag_Argument_Null_Exception() [Fact] public void Server_Sdr_Send_Record_Tag_Response_Argument_Null_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -206,16 +148,7 @@ public void Server_Sdr_Send_Record_Tag_Response_Argument_Null_Exception() [Fact] public void Server_Sdr_Send_Record_Delete_Response_Argument_Null_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -226,16 +159,7 @@ public void Server_Sdr_Send_Record_Delete_Response_Argument_Null_Exception() [Fact] public void Server_Sdr_Send_Record_Response_Argument_Null_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -246,16 +170,7 @@ public void Server_Sdr_Send_Record_Response_Argument_Null_Exception() [Fact] public void Server_Sdr_Send_Record_Response_Fail_Result_Code_Argument_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -268,17 +183,7 @@ public void Server_Sdr_Send_Record_Response_Fail_Result_Code_Argument_Exception( [InlineData(AsvSdrRequestAck.AsvSdrRequestAckFail)] public void Server_Sdr_Send_Record_Response_Fail_Null_Reference_Exception(AsvSdrRequestAck requestAck) { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); - + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { serverSdr.SendRecordResponseFail(null, requestAck); @@ -288,16 +193,7 @@ public void Server_Sdr_Send_Record_Response_Fail_Null_Reference_Exception(AsvSdr [Fact] public void Server_Sdr_Send_Record_Response_Success_Null_Reference_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -308,16 +204,7 @@ public void Server_Sdr_Send_Record_Response_Success_Null_Reference_Exception() [Fact] public void Server_Sdr_Send_Record_Tag_Response_Fail_Result_Code_Argument_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -330,16 +217,7 @@ public void Server_Sdr_Send_Record_Tag_Response_Fail_Result_Code_Argument_Except [InlineData(AsvSdrRequestAck.AsvSdrRequestAckFail)] public void Server_Sdr_Send_Record_Tag_Response_Fail_Null_Reference_Exception(AsvSdrRequestAck requestAck) { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -350,16 +228,7 @@ public void Server_Sdr_Send_Record_Tag_Response_Fail_Null_Reference_Exception(As [Fact] public void Server_Sdr_Send_Record_Tag_Response_Success_Null_Reference_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -370,16 +239,7 @@ public void Server_Sdr_Send_Record_Tag_Response_Success_Null_Reference_Exception [Fact] public void Server_Sdr_Send_Record_Delete_Response_Fail_Result_Code_Argument_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -392,16 +252,7 @@ public void Server_Sdr_Send_Record_Delete_Response_Fail_Result_Code_Argument_Exc [InlineData(AsvSdrRequestAck.AsvSdrRequestAckFail)] public void Server_Sdr_Send_Record_Delete_Response_Fail_Null_Reference_Exception(AsvSdrRequestAck requestAck) { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -412,16 +263,7 @@ public void Server_Sdr_Send_Record_Delete_Response_Fail_Null_Reference_Exception [Fact] public void Server_Sdr_Send_Record_Delete_Response_Success_Null_Reference_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -432,16 +274,7 @@ public void Server_Sdr_Send_Record_Delete_Response_Success_Null_Reference_Except [Fact] public void Server_Sdr_Send_Record_Tag_Delete_Response_Fail_Result_Code_Argument_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -454,16 +287,7 @@ public void Server_Sdr_Send_Record_Tag_Delete_Response_Fail_Result_Code_Argument [InlineData(AsvSdrRequestAck.AsvSdrRequestAckFail)] public void Server_Sdr_Send_Record_Tag_Delete_Response_Fail_Null_Reference_Exception(AsvSdrRequestAck requestAck) { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -474,16 +298,7 @@ public void Server_Sdr_Send_Record_Tag_Delete_Response_Fail_Null_Reference_Excep [Fact] public void Server_Sdr_Send_Record_Tag_Delete_Response_Success_Null_Reference_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -494,16 +309,7 @@ public void Server_Sdr_Send_Record_Tag_Delete_Response_Success_Null_Reference_Ex [Fact] public void Server_Sdr_Send_Record_Data_Response_Fail_Result_Code_Argument_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -516,16 +322,7 @@ public void Server_Sdr_Send_Record_Data_Response_Fail_Result_Code_Argument_Excep [InlineData(AsvSdrRequestAck.AsvSdrRequestAckFail)] public void Server_Sdr_Send_Record_Data_Response_Fail_Null_Reference_Exception(AsvSdrRequestAck requestAck) { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -536,16 +333,7 @@ public void Server_Sdr_Send_Record_Data_Response_Fail_Null_Reference_Exception(A [Fact] public void Server_Sdr_Send_Record_Data_Response_Success_Null_Reference_Exception() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity(), new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity(), - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); Assert.Throws(() => { @@ -556,26 +344,7 @@ public void Server_Sdr_Send_Record_Data_Response_Success_Null_Reference_Exceptio [Fact] public async Task Client_Get_Record_List_And_Check() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity - { - ComponentId = 2, - SystemId = 2 - }, new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity - { - SystemId = 13, - ComponentId = 13, - TargetSystemId = 2, - TargetComponentId = 2 - }, - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); var requestId = 0; @@ -604,26 +373,7 @@ await serverSdr.SendRecordResponse(__ => [Fact] public async Task Client_Get_Record_Data_List_And_Check() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity - { - ComponentId = 2, - SystemId = 2 - }, new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity - { - SystemId = 13, - ComponentId = 13, - TargetSystemId = 2, - TargetComponentId = 2 - }, - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); var recordGuid = Guid.NewGuid(); @@ -657,26 +407,7 @@ await serverSdr.SendRecordDataResponse(__ => [Fact] public async Task Client_Get_Record_Tag_List_And_Check() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity - { - ComponentId = 2, - SystemId = 2 - }, new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity - { - SystemId = 13, - ComponentId = 13, - TargetSystemId = 2, - TargetComponentId = 2 - }, - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); var recordGuid = Guid.NewGuid(); @@ -706,26 +437,7 @@ await serverSdr.SendRecordTagResponse(__ => [Fact] public async Task Client_Record_Delete_And_Check() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity - { - ComponentId = 2, - SystemId = 2 - }, new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity - { - SystemId = 13, - ComponentId = 13, - TargetSystemId = 2, - TargetComponentId = 2 - }, - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); var recordGuid = Guid.NewGuid(); @@ -755,26 +467,7 @@ await serverSdr.SendRecordDeleteResponse(__ => [Fact] public async Task Client_Record_Tag_Delete_And_CheckClient_Record_Tag_Delete_And_Check() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity - { - ComponentId = 2, - SystemId = 2 - }, new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity - { - SystemId = 13, - ComponentId = 13, - TargetSystemId = 2, - TargetComponentId = 2 - }, - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); var recordGuid = Guid.NewGuid(); var tagGuid = Guid.NewGuid(); @@ -807,26 +500,7 @@ await serverSdr.SendRecordTagDeleteResponse(__ => [Fact] public async Task Server_Send_Record_Data_Llz_And_Check() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity - { - ComponentId = 2, - SystemId = 2 - }, new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity - { - SystemId = 13, - ComponentId = 13, - TargetSystemId = 2, - TargetComponentId = 2 - }, - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); AsvSdrRecordDataLlzPayload payload = null; @@ -958,26 +632,7 @@ await serverSdr.SendRecordData(AsvSdrCustomMode.AsvSdrCustomModeLlz, payload => [Fact] public async Task Server_Send_Record_And_Check() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity - { - ComponentId = 2, - SystemId = 2 - }, new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity - { - SystemId = 13, - ComponentId = 13, - TargetSystemId = 2, - TargetComponentId = 2 - }, - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); var record = new AsvSdrRecordPayload(); @@ -1013,26 +668,7 @@ await serverSdr.SendRecord( _ => [Fact] public async Task Server_Send_Record_Tag_And_Check() { - var link = new VirtualMavlinkConnection(); - - var serverSdr = new AsvSdrServer(link.Server, - new MavlinkServerIdentity - { - ComponentId = 2, - SystemId = 2 - }, new AsvSdrServerConfig(), - new PacketSequenceCalculator(), Scheduler.Default); - - var clientSdr = new AsvSdrClient(link.Client, new MavlinkClientIdentity - { - SystemId = 13, - ComponentId = 13, - TargetSystemId = 2, - TargetComponentId = 2 - }, - new PacketSequenceCalculator()); - - serverSdr.Start(); + SetupClientServer(out var serverSdr, out var clientSdr); var record = new AsvSdrRecordTagPayload(); @@ -1053,6 +689,168 @@ await serverSdr.SendRecordTag(_ => Assert.Equal(AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64, record.TagType); } + + + + [Theory] + [InlineData(AsvSdrRequestAck.AsvSdrRequestAckOk)] + [InlineData(AsvSdrRequestAck.AsvSdrRequestAckFail)] + [InlineData(AsvSdrRequestAck.AsvSdrRequestAckInProgress)] + [InlineData(AsvSdrRequestAck.AsvSdrRequestAckNotSupported)] + public async Task Server_send_acc_and_client_recv_it(AsvSdrRequestAck orgign) + { + SetupClientServer(out var serverSdr, out var clientSdr); + var tcs = new TaskCompletionSource(); + + using var sub1 = clientSdr.OnCalibrationAcc.Subscribe(args => + { + Assert.Equal(100, args.RequestId); + Assert.Equal( orgign,args.Result); + tcs.SetResult(); + }); + await serverSdr.SendCalibrationAcc(100, orgign); + await tcs.Task.WaitAsync(TimeSpan.FromMilliseconds(500)); + } + [Fact] + public async Task Client_request_calibration_table_and_server_respond() + { + SetupClientServer(out var serverSdr, out var clientSdr); + var date = DateTime.Now; + using var sub1 = serverSdr.OnCalibrationTableReadRequest.Subscribe(args => + { + Assert.Equal(100, args.TableIndex); + serverSdr.SendCalibrationTableReadResponse(res => + { + res.TableIndex = 100; + res.RowCount = 10; + res.CreatedUnixUs = MavlinkTypesHelper.ToUnixTimeUs(date); + MavlinkTypesHelper.SetString(res.TableName, "TEST"); + }); + }); + var result = await clientSdr.ReadCalibrationTable(100); + Assert.Equal(100, result.TableIndex); + Assert.Equal(10, result.RowCount); + // date is not equal because of precision + Assert.True((date - MavlinkTypesHelper.FromUnixTimeUs(result.CreatedUnixUs)).TotalMilliseconds < 100); + Assert.Equal("TEST", MavlinkTypesHelper.GetString(result.TableName)); - + } + + [Fact] + public async Task Client_request_calibration_table_and_server_respond_error() + { + SetupClientServer(out var serverSdr, out var clientSdr); + using var sub1 = serverSdr.OnCalibrationTableReadRequest.Subscribe(args => + { + Assert.Equal(100, args.TableIndex); + serverSdr.SendCalibrationAcc(args.RequestId, AsvSdrRequestAck.AsvSdrRequestAckFail); + }); + await Assert.ThrowsAsync(async () => + { + await clientSdr.ReadCalibrationTable(100); + }); + } + + [Fact] + public async Task Client_request_calibration_row_and_server_respond() + { + SetupClientServer(out var serverSdr, out var clientSdr); + using var sub1 = serverSdr.OnCalibrationTableRowReadRequest.Subscribe(args => + { + Assert.Equal(100, args.TableIndex); + Assert.Equal(10, args.RowIndex); + serverSdr.SendCalibrationTableRowReadResponse(res => + { + res.TableIndex = 100; + res.RowIndex = 10; + res.MeasuredValue = 100; + res.RefFreq = 200U; + res.RefValue = 300.5f; + res.RefPower = 400.0f; + }); + }); + var result = await clientSdr.ReadCalibrationTableRow(100, 10); + Assert.Equal(100, result.MeasuredValue); + Assert.Equal(200U, result.RefFreq); + Assert.Equal(300.5f, result.RefValue); + Assert.Equal(400.0f, result.RefPower); + Assert.Equal(100, result.TableIndex); + Assert.Equal(10, result.RowIndex); + + } + [Fact] + public async Task Client_request_calibration_row_and_server_respond_error() + { + SetupClientServer(out var serverSdr, out var clientSdr); + using var sub1 = serverSdr.OnCalibrationTableRowReadRequest.Subscribe(args => + { + Assert.Equal(100, args.TableIndex); + Assert.Equal(10, args.RowIndex); + serverSdr.SendCalibrationAcc(args.RequestId, AsvSdrRequestAck.AsvSdrRequestAckFail); + }); + await Assert.ThrowsAsync(async () => + { + await clientSdr.ReadCalibrationTableRow(100, 10); + }); + } + + [Fact] + public async Task Client_request_calibration_upload_start_and_server_recv() + { + SetupClientServer(out var serverSdr, out var clientSdr); + var date = DateTime.Now; + var tcs = new TaskCompletionSource(); + + + + using var sub1 = serverSdr.OnCalibrationTableUploadStart.Subscribe(args => + { + Assert.Equal(100, args.Payload.TableIndex); + Assert.Equal(10, args.Payload.RowCount); + Assert.Equal(200, args.Payload.RequestId); + Assert.True((date - MavlinkTypesHelper.FromUnixTimeUs(args.Payload.CreatedUnixUs)).TotalMilliseconds < 100); + tcs.SetResult(); + }); + await clientSdr.SendCalibrationTableRowUploadStart(args => + { + args.TableIndex = 100; + args.RowCount = 10; + args.RequestId = 200; + args.CreatedUnixUs = MavlinkTypesHelper.ToUnixTimeUs(date); + }); + await tcs.Task.WaitAsync(TimeSpan.FromMilliseconds(500)); + + } + + [Fact] + public async Task Server_callback_read_calibraion_row_and_client_respond() + { + SetupClientServer(out var serverSdr, out var clientSdr); + + using var sub1 = clientSdr.OnCalibrationTableRowUploadCallback.Subscribe(args => + { + Assert.Equal(100, args.TableIndex); + Assert.Equal(10, args.RowIndex); + Assert.Equal(200, args.RequestId); + clientSdr.SendCalibrationTableRowUploadItem(res => + { + res.TableIndex = 100; + res.RowIndex = 10; + res.MeasuredValue = 100; + res.RefFreq = 200U; + res.RefValue = 300.5f; + res.RefPower = 400.0f; + res.TargetComponent = clientSdr.Identity.TargetComponentId; + res.TargetSystem = clientSdr.Identity.TargetSystemId; + }).Wait(); + }); + var result = await serverSdr.CallCalibrationTableUploadReadCallback(clientSdr.Identity.SystemId, + clientSdr.Identity.ComponentId, 200, 100, 10); + Assert.Equal(100, result.MeasuredValue); + Assert.Equal(200U, result.FrequencyHz); + Assert.Equal(400.0f, result.RefPower); + Assert.Equal(300.5f, result.RefValue); + + } + } \ No newline at end of file diff --git a/src/Asv.Mavlink/Devices/Sdr/Server/SdrServerDevice.cs b/src/Asv.Mavlink/Devices/Sdr/Server/SdrServerDevice.cs index 0395aacd..a4df45a1 100644 --- a/src/Asv.Mavlink/Devices/Sdr/Server/SdrServerDevice.cs +++ b/src/Asv.Mavlink/Devices/Sdr/Server/SdrServerDevice.cs @@ -16,9 +16,6 @@ public class SdrServerDeviceConfig : ServerDeviceConfig public class SdrServerDevice:ServerDevice, ISdrServerDevice { - public static Logger Logger = LogManager.GetCurrentClassLogger(); - - public SdrServerDevice(IMavlinkV2Connection connection, IPacketSequenceCalculator seq, MavlinkServerIdentity identity, SdrServerDeviceConfig config, IScheduler scheduler, IEnumerable paramList, @@ -31,7 +28,8 @@ public SdrServerDevice(IMavlinkV2Connection connection, var sdr = new AsvSdrServer(connection, identity, config.Sdr,seq, scheduler).DisposeItWith(Disposable); var cmd = new CommandServer(connection, seq, identity, scheduler).DisposeItWith(Disposable); CommandLongEx = new CommandLongServerEx(cmd).DisposeItWith(Disposable); - SdrEx = new AsvSdrServerEx(sdr, Heartbeat, CommandLongEx).DisposeItWith(Disposable); + + SdrEx = new AsvSdrServerEx(sdr, StatusText, Heartbeat, CommandLongEx).DisposeItWith(Disposable); var paramsBase = new ParamsServer(connection, seq, identity, scheduler).DisposeItWith(Disposable); Params = new ParamsServerEx(paramsBase,StatusText,paramList,encoding,paramStore,config.Params).DisposeItWith(Disposable); var mission = new MissionServer(connection, identity, seq, scheduler).DisposeItWith(Disposable); diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrException.cs b/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrException.cs new file mode 100644 index 00000000..48c23f4b --- /dev/null +++ b/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Asv.Mavlink; + + +public class AsvSdrException : Exception +{ + public AsvSdrException() + { + } + + public AsvSdrException(string message) : base(message) + { + } + + public AsvSdrException(string message, Exception inner) : base(message, inner) + { + } +} diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrHelper.cs b/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrHelper.cs index 1d001b07..40c5ab06 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrHelper.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/AsvSdrHelper.cs @@ -20,6 +20,22 @@ public static class AsvSdrHelper private const string RecordTagNameRegexString = "^[A-Za-z][A-Za-z0-9_\\- +]{2,16}$"; private static readonly Regex RecordTagNameRegex = new(RecordNameRegexString, RegexOptions.Compiled); + + public const int CalibrationTableNameMaxLength = 28; + private const string CalibrationTableNameRegexString = "^[A-Za-z][A-Za-z0-9_\\- +]{2,28}$"; + private static readonly Regex CalibrationTableNameRegex = new(CalibrationTableNameRegexString, RegexOptions.Compiled); + + public static void CheckCalibrationTableName(string name) + { + if (name.IsNullOrWhiteSpace()) throw new Exception("Record name is empty"); + if (name.Length > CalibrationTableNameMaxLength) + throw new Exception($"Record name is too long. Max length is {CalibrationTableNameMaxLength}"); + if (CalibrationTableNameRegex.IsMatch(name) == false) + throw new ArgumentException( + $"Record name '{name}' not match regex '{CalibrationTableNameRegexString}')"); + } + + public static void CheckRecordName(string name) { if (name.IsNullOrWhiteSpace()) throw new Exception("Record name is empty"); @@ -151,7 +167,7 @@ public static void SetTagValueAsString(byte[] rawValue, AsvSdrRecordTagType type #region Set mode public static void SetArgsForSdrSetMode(CommandLongPayload item, AsvSdrCustomMode mode, ulong frequencyHz, - float recordRate, uint sendingThinningRatio) + float recordRate, uint sendingThinningRatio, float referencePowerDbm) { var freqArray = BitConverter.GetBytes(frequencyHz); item.Command = (V2.Common.MavCmd)MavCmd.MavCmdAsvSdrSetMode; @@ -160,12 +176,12 @@ public static void SetArgsForSdrSetMode(CommandLongPayload item, AsvSdrCustomMod item.Param3 = BitConverter.ToSingle(freqArray, 4); item.Param4 = recordRate; item.Param5 = BitConverter.ToSingle(BitConverter.GetBytes(sendingThinningRatio)); - item.Param6 = Single.NaN; + item.Param6 = referencePowerDbm; item.Param7 = Single.NaN; } public static void SetArgsForSdrSetMode(MissionItemIntPayload payload, AsvSdrCustomMode mode, ulong frequencyHz, - float recordRate, uint sendingThinningRatio) + float recordRate, uint sendingThinningRatio, float referencePowerDbm) { var freqArray = BitConverter.GetBytes(frequencyHz); payload.Command = (V2.Common.MavCmd)MavCmd.MavCmdAsvSdrSetMode; @@ -174,11 +190,11 @@ public static void SetArgsForSdrSetMode(MissionItemIntPayload payload, AsvSdrCus payload.Param3 = BitConverter.ToSingle(freqArray, 4); payload.Param4 = recordRate; payload.X = BitConverter.ToInt32(BitConverter.GetBytes(sendingThinningRatio)); - payload.Y = 0; + payload.Y = BitConverter.ToInt32(BitConverter.GetBytes(referencePowerDbm)); payload.Z = Single.NaN; } public static void GetArgsForSdrSetMode(CommandLongPayload item, out AsvSdrCustomMode mode, out ulong frequencyHz, - out float recordRate, out uint sendingThinningRatio) + out float recordRate, out uint sendingThinningRatio, out float referencePowerDbm) { if (item.Command != (V2.Common.MavCmd)MavCmd.MavCmdAsvSdrSetMode) throw new ArgumentException($"Command {item.Command} is not {MavCmd.MavCmdAsvSdrSetMode}"); @@ -189,10 +205,11 @@ public static void GetArgsForSdrSetMode(CommandLongPayload item, out AsvSdrCusto frequencyHz = BitConverter.ToUInt64(freqArray,0); recordRate = item.Param4; sendingThinningRatio = BitConverter.ToUInt32(BitConverter.GetBytes(item.Param5)); + referencePowerDbm = item.Param6; } public static void GetArgsForSdrSetMode(ServerMissionItem item, out AsvSdrCustomMode mode, out ulong frequencyHz, - out float recordRate, out uint sendingThinningRatio) + out float recordRate, out uint sendingThinningRatio, out float referencePowerDbm) { if (item.Command != (V2.Common.MavCmd)MavCmd.MavCmdAsvSdrSetMode) throw new ArgumentException($"Command {item.Command} is not {MavCmd.MavCmdAsvSdrSetMode}"); @@ -203,6 +220,7 @@ public static void GetArgsForSdrSetMode(ServerMissionItem item, out AsvSdrCustom frequencyHz = BitConverter.ToUInt64(freqArray,0); recordRate = item.Param4; sendingThinningRatio = BitConverter.ToUInt32(BitConverter.GetBytes(item.X)); + referencePowerDbm = BitConverter.ToSingle(BitConverter.GetBytes(item.Y)); } #endregion @@ -518,6 +536,41 @@ public static void GetArgsForSdrWaitVehicleWaypoint(ServerMissionItem item,out u index = (ushort)BitConverter.ToUInt32(BitConverter.GetBytes(item.Param1)); } + public static void SetArgsForSdrStartCalibration(CommandLongPayload item) + { + item.Command = (V2.Common.MavCmd)MavCmd.MavCmdAsvSdrStartCalibration; + item.Param1 = Single.NaN; + item.Param2 = Single.NaN; + item.Param3 = Single.NaN; + item.Param4 = Single.NaN; + item.Param5 = Single.NaN; + item.Param6 = Single.NaN; + item.Param7 = Single.NaN; + } + + public static void GetArgsForSdrStartCalibration(CommandLongPayload item) + { + if (item.Command != (V2.Common.MavCmd)MavCmd.MavCmdAsvSdrStartCalibration) + throw new ArgumentException($"Command {item.Command} is not {MavCmd.MavCmdAsvSdrStartCalibration}"); + } + + public static void SetArgsForSdrStopCalibration(CommandLongPayload item) + { + item.Command = (V2.Common.MavCmd)MavCmd.MavCmdAsvSdrStopCalibration; + item.Param1 = Single.NaN; + item.Param2 = Single.NaN; + item.Param3 = Single.NaN; + item.Param4 = Single.NaN; + item.Param5 = Single.NaN; + item.Param6 = Single.NaN; + item.Param7 = Single.NaN; + } + public static void GetArgsForSdrStopCalibration(CommandLongPayload item) + { + if (item.Command != (V2.Common.MavCmd)MavCmd.MavCmdAsvSdrStopCalibration) + throw new ArgumentException($"Command {item.Command} is not {MavCmd.MavCmdAsvSdrStopCalibration}"); + } + #endregion public static ServerMissionItem Convert(MissionItemIntPacket input) @@ -542,4 +595,6 @@ public static ServerMissionItem Convert(MissionItemIntPayload input) MissionType = input.MissionType }; } + + } \ No newline at end of file diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/CalibrationTableRow.cs b/src/Asv.Mavlink/Microservices/AsvSdr/CalibrationTableRow.cs new file mode 100644 index 00000000..92fef0cd --- /dev/null +++ b/src/Asv.Mavlink/Microservices/AsvSdr/CalibrationTableRow.cs @@ -0,0 +1,42 @@ +using System; +using Asv.Mavlink.V2.AsvSdr; + +namespace Asv.Mavlink; + +public class CalibrationTableInfo +{ + public string Name { get; set; } + public DateTime Updated { get; set; } + public ushort Size { get; set; } +} + +public class CalibrationTableRow +{ + public CalibrationTableRow(AsvSdrCalibTableRowPayload result) + { + FrequencyHz = result.RefFreq; + RefPower = result.RefPower; + RefValue = result.RefValue; + MeasuredValue = result.MeasuredValue; + } + + public CalibrationTableRow(ulong frequencyHz, float refPower, float refValue, float measuredValue) + { + FrequencyHz = frequencyHz; + RefPower = refPower; + RefValue = refValue; + MeasuredValue = measuredValue; + } + public ulong FrequencyHz { get; } + public float RefPower { get; } + public float RefValue { get; } + public float MeasuredValue { get; } + + public void Fill(AsvSdrCalibTableRowPayload args) + { + args.RefFreq = FrequencyHz; + args.RefPower = RefPower; + args.RefValue = RefValue; + args.MeasuredValue = MeasuredValue; + } +} \ No newline at end of file diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/Client/AsvSdrClient.cs b/src/Asv.Mavlink/Microservices/AsvSdr/Client/AsvSdrClient.cs index b9058c07..71500963 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/Client/AsvSdrClient.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/Client/AsvSdrClient.cs @@ -41,9 +41,21 @@ public AsvSdrClient(IMavlinkV2Connection connection, MavlinkClientIdentity ident OnDeleteRecord = InternalFilter().Select(_ => (new Guid(_.Payload.RecordGuid),_.Payload)) .Publish().RefCount(); + + OnRecordData = InternalFilteredVehiclePackets.Where(x=>dataPacketsHashSet.Contains(x.MessageId)); + OnCalibrationTableRowUploadCallback = InternalFilter().Select(_ => _.Payload) + .Publish().RefCount(); + OnCalibrationAcc = InternalFilter().Select(_ => _.Payload) + .Publish().RefCount(); + + OnCalibrationTable = InternalFilter().Select(_ => _.Payload) + .Publish().RefCount(); + OnSignal = InternalFilter().Select(x=>x.Payload).Publish().RefCount(); + + } public IRxValue Status => _status; @@ -63,7 +75,7 @@ public Task GetRecordList(ushort skip, ushort count },_=>_.Payload.RequestId == id,resultGetter:_=>_.Payload,cancel: cancel); } - private ushort GenerateRequestIndex() + public ushort GenerateRequestIndex() { return (ushort)(Interlocked.Increment(ref _requestCounter)%ushort.MaxValue); } @@ -127,5 +139,104 @@ public Task GetRecordDataList(Guid recordId, ui } public IObservable> OnRecordData { get; } + public IObservable OnCalibrationTable { get; } + + #region Calibration + public async Task ReadCalibrationTable(ushort tableIndex, CancellationToken cancel = default) + { + var id = GenerateRequestIndex(); + var result = await InternalCall<(AsvSdrCalibTablePayload,AsvSdrCalibAccPayload), AsvSdrCalibTableReadPacket>( + arg => + { + arg.Payload.TargetComponent = _identity.TargetComponentId; + arg.Payload.TargetSystem = _identity.TargetSystemId; + arg.Payload.RequestId = id; + arg.Payload.TableIndex = tableIndex; + }, (IPacketV2 input, out (AsvSdrCalibTablePayload, AsvSdrCalibAccPayload) tuple) => + { + switch (input.MessageId) + { + case AsvSdrCalibTablePacket.PacketMessageId when input is AsvSdrCalibTablePacket packet2 && packet2.Payload.TableIndex == tableIndex: + tuple = (packet2.Payload, default); + return true; + case AsvSdrCalibAccPacket.PacketMessageId when input is AsvSdrCalibAccPacket packet && packet.Payload.RequestId == id: + tuple = (default, packet.Payload); + return true; + default: + tuple = default; + return false; + } + }, cancel:cancel).ConfigureAwait(false); + if (result.Item2 != null) + { + throw new AsvSdrException($"Error to read calibration table {tableIndex}: {result.Item2.Result:G}"); + } + if (result.Item1 == null) + { + throw new AsvSdrException($"Error to read calibration table {tableIndex}: no response"); + } + return result.Item1; + } + + public async Task ReadCalibrationTableRow(ushort tableIndex, ushort rowIndex, CancellationToken cancel = default) + { + var id = GenerateRequestIndex(); + var result = await InternalCall<(AsvSdrCalibTableRowPayload,AsvSdrCalibAccPayload), AsvSdrCalibTableRowReadPacket>( + arg => + { + arg.Payload.TargetComponent = _identity.TargetComponentId; + arg.Payload.TargetSystem = _identity.TargetSystemId; + arg.Payload.RequestId = id; + arg.Payload.TableIndex = tableIndex; + arg.Payload.RowIndex = rowIndex; + }, (IPacketV2 input, out (AsvSdrCalibTableRowPayload, AsvSdrCalibAccPayload) tuple) => + { + switch (input.MessageId) + { + case AsvSdrCalibTableRowPacket.PacketMessageId when input is AsvSdrCalibTableRowPacket packet2 + && packet2.Payload.TableIndex == tableIndex + && packet2.Payload.RowIndex == rowIndex: + tuple = (packet2.Payload, default); + return true; + case AsvSdrCalibAccPacket.PacketMessageId when input is AsvSdrCalibAccPacket packet && packet.Payload.RequestId == id: + tuple = (default, packet.Payload); + return true; + default: + tuple = default; + return false; + } + }, cancel:cancel).ConfigureAwait(false); + if (result.Item2 != null) + { + throw new AsvSdrException($"Error to read calibration table {tableIndex}: {result.Item2.Result:G}"); + } + if (result.Item1 == null) + { + throw new AsvSdrException($"Error to read calibration table {tableIndex}: no response"); + } + return result.Item1; + } + + public Task SendCalibrationTableRowUploadStart(Action argsFill, CancellationToken cancel = default) + { + var id = GenerateRequestIndex(); + return InternalSend(_ => + { + _.Payload.TargetComponent = _identity.TargetComponentId; + _.Payload.TargetSystem = _identity.TargetSystemId; + _.Payload.RequestId = id; + argsFill(_.Payload); + },cancel: cancel); + } + + public IObservable OnCalibrationTableRowUploadCallback { get; } + public IObservable OnCalibrationAcc { get; } + public Task SendCalibrationTableRowUploadItem(Action argsFill, CancellationToken cancel = default) + { + return InternalSend(_ => { argsFill(_.Payload); },cancel: cancel); + } + + #endregion + public IObservable OnSignal { get; } } \ No newline at end of file diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientCalibrationTable.cs b/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientCalibrationTable.cs new file mode 100644 index 00000000..4183368f --- /dev/null +++ b/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientCalibrationTable.cs @@ -0,0 +1,137 @@ +using System; +using System.Reactive; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; +using Asv.Common; +using Asv.Mavlink.V2.AsvSdr; +using NLog; + +namespace Asv.Mavlink; + +public class AsvSdrClientCalibrationTable:DisposableOnceWithCancel +{ + private readonly IAsvSdrClient _ifc; + private readonly RxValue _remoteRowCount; + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private readonly TimeSpan _deviceUploadTimeout; + private readonly RxValue _updated; + + public AsvSdrClientCalibrationTable(AsvSdrCalibTablePayload payload, IAsvSdrClient ifc, TimeSpan deviceUploadTimeout) + { + _ifc = ifc; + _deviceUploadTimeout = deviceUploadTimeout; + _remoteRowCount = new RxValue(payload.RowCount).DisposeItWith(Disposable); + _updated = new RxValue(MavlinkTypesHelper.FromUnixTimeUs(payload.CreatedUnixUs)).DisposeItWith(Disposable); + Name = MavlinkTypesHelper.GetString(payload.TableName); + Index = payload.TableIndex; + } + public string Name { get; } + public ushort Index { get; } + + public IRxValue RemoteRowCount => _remoteRowCount; + public IRxEditableValue Updated => _updated; + + internal void Update(AsvSdrCalibTablePayload payload) + { + var name = MavlinkTypesHelper.GetString(payload.TableName); + if (payload.TableIndex!= Index) throw new ArgumentException($"Invalid table index. Expected: {Index}, actual: {payload.TableIndex}"); + if (name != Name) throw new ArgumentException($"Invalid table name. Expected: {Name}, actual: {name}"); + _updated.OnNext(MavlinkTypesHelper.FromUnixTimeUs(payload.CreatedUnixUs)); + _remoteRowCount.OnNext(payload.RowCount); + } + + public async Task Download(IProgress progress = null,CancellationToken cancel = default) + { + progress ??= new Progress(); + progress.Report(0); + var info = await _ifc.ReadCalibrationTable(Index, cancel).ConfigureAwait(false); + var count = info.RowCount; + _remoteRowCount.OnNext(count); + var array = new CalibrationTableRow[count]; + for (ushort i = 0; i < count; i++) + { + progress.Report((double)(i+1)/count); + var result = await _ifc.ReadCalibrationTableRow(Index, i, cancel).ConfigureAwait(false); + array[i] = new CalibrationTableRow(result); + } + return array; + } + + public async Task Upload(CalibrationTableRow[] data, IProgress progress = null,CancellationToken cancel = default) + { + progress ??= new Progress(); + progress.Report(0); + Logger.Info($"Begin upload rows for '{Name}[{Index}]' table"); + + var reqId = _ifc.GenerateRequestIndex(); + + using var linkedCancel = CancellationTokenSource.CreateLinkedTokenSource(cancel, DisposeCancel); + var tcs = new TaskCompletionSource(); + await using var c1 = linkedCancel.Token.Register(() => tcs.TrySetCanceled()); + + var lastUpdateTime = DateTime.Now; + using var checkTimer = Observable.Timer(_deviceUploadTimeout, _deviceUploadTimeout) + .Subscribe(_ => + { + if (DateTime.Now - lastUpdateTime > _deviceUploadTimeout) + { + Logger.Warn($"'{Name}[{Index}]' table upload timeout"); + tcs.TrySetException(new Exception($"'{Name}[{Index}]' table upload timeout")); + } + }); + + using var sub1 = _ifc.OnCalibrationTableRowUploadCallback.Subscribe(req => + { + Logger.Debug($"Payload request '{Name}[{Index}]' row with index={req.RowIndex}"); + lastUpdateTime = DateTime.Now; + if (data.Length <= req.RowIndex) + { + tcs.TrySetException( + new AsvSdrException($"Requested mission item with index '{req.RowIndex}' not found in local store")); + return; + } + var value = data[req.RowIndex]; + progress?.Report((double)(req.RowIndex) / data.Length); + _ifc.SendCalibrationTableRowUploadItem(arg => + { + arg.RowIndex = req.RowIndex; + arg.TableIndex = Index; + arg.TargetComponent = _ifc.Identity.TargetComponentId; + arg.TargetSystem = _ifc.Identity.TargetSystemId; + arg.RefFreq = value.FrequencyHz; + arg.RefPower = value.RefPower; + arg.RefValue = value.RefValue; + arg.MeasuredValue = value.MeasuredValue; + }, cancel); + }); + + using var sub2 = _ifc.OnCalibrationAcc.Where(_ => _.RequestId == reqId).Subscribe(_ => + { + lastUpdateTime = DateTime.Now; + if (_.Result == AsvSdrRequestAck.AsvSdrRequestAckOk) + { + tcs.TrySetResult(Unit.Default); + } + else + { + tcs.TrySetException(new AsvSdrException($"Error to upload '{Name}[{Index}]' table to payload:{_.Result:G}")); + } + + }); + + await _ifc.SendCalibrationTableRowUploadStart(args => + { + args.TableIndex = Index; + args.RowCount = (ushort)data.Length; + args.RequestId = reqId; + args.CreatedUnixUs = MavlinkTypesHelper.ToUnixTimeUs(DateTime.Now); + + }, linkedCancel.Token).ConfigureAwait(false); + await tcs.Task.ConfigureAwait(false); + + } + + + +} \ No newline at end of file diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientEx.cs b/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientEx.cs index fb6225d2..d41c5e57 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientEx.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/AsvSdrClientEx.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Linq; using System.Reactive.Linq; @@ -27,6 +28,9 @@ public class AsvSdrClientEx : DisposableOnceWithCancel, IAsvSdrClientEx private readonly SourceCache _records; private readonly RxValue _isRecordStarted; private readonly RxValue _currentRecord; + private readonly RxValue _calibrationTableRemoteCount; + private readonly RxValue _calibrationState; + private readonly ISourceCache _calibrationTables; public AsvSdrClientEx(IAsvSdrClient client, IHeartbeatClient heartbeatClient, ICommandClient commandClient, AsvSdrClientExConfig config) { @@ -48,6 +52,10 @@ public AsvSdrClientEx(IAsvSdrClient client, IHeartbeatClient heartbeatClient, IC .DisposeItWith(Disposable); _isRecordStarted = new RxValue(false) .DisposeItWith(Disposable); + _calibrationTableRemoteCount = new RxValue() + .DisposeItWith(Disposable); + _calibrationState = new RxValue() + .DisposeItWith(Disposable); client.Status.Subscribe(_ => { @@ -56,6 +64,8 @@ public AsvSdrClientEx(IAsvSdrClient client, IHeartbeatClient heartbeatClient, IC var guid = new Guid(_.CurrentRecordGuid); _currentRecord.OnNext(guid); _isRecordStarted.OnNext(guid != Guid.Empty); + _calibrationTableRemoteCount.OnNext(_.CalibTableCount); + _calibrationState.OnNext(_.CalibState); }).DisposeItWith(Disposable); _records = new SourceCache(x => x.Id) @@ -75,9 +85,25 @@ public AsvSdrClientEx(IAsvSdrClient client, IHeartbeatClient heartbeatClient, IC updater.RemoveKey(_.Item1); value.Value.Dispose(); })).DisposeItWith(Disposable); - Records = _records.Connect().Transform(_=>(IAsvSdrClientRecord)_).RefCount(); + + _calibrationTables = new SourceCache(_=>_.Name) + .DisposeItWith(Disposable); + CalibrationTables = _calibrationTables.Connect().DisposeMany().RefCount(); + Base.OnCalibrationTable.Subscribe(payload=>_calibrationTables.Edit(updater => + { + var name = MavlinkTypesHelper.GetString(payload.TableName); + var value = updater.Lookup(name); + if (value.HasValue == false) + { + updater.AddOrUpdate(new AsvSdrClientCalibrationTable(payload, Base, _maxTimeToWaitForResponseForList)); + } + else + { + value.Value.Update(payload); + } + })).DisposeItWith(Disposable); } public IRxValue CurrentRecord => _currentRecord; @@ -123,11 +149,11 @@ public async Task DownloadRecordList(IProgress progress, Cancellat public IRxValue RecordsCount => _recordsCount; public IObservable> Records { get; } - public async Task SetMode(AsvSdrCustomMode mode, ulong frequencyHz, float recordRate, uint sendingThinningRatio, + public async Task SetMode(AsvSdrCustomMode mode, ulong frequencyHz, float recordRate, uint sendingThinningRatio, float referencePowerDbm, CancellationToken cancel) { using var cs = CancellationTokenSource.CreateLinkedTokenSource(DisposeCancel, cancel); - var result = await _commandClient.CommandLong(item => AsvSdrHelper.SetArgsForSdrSetMode(item, mode,frequencyHz,recordRate,sendingThinningRatio),cs.Token).ConfigureAwait(false); + var result = await _commandClient.CommandLong(item => AsvSdrHelper.SetArgsForSdrSetMode(item, mode,frequencyHz,recordRate,sendingThinningRatio,referencePowerDbm),cs.Token).ConfigureAwait(false); return result.Result; } @@ -173,4 +199,45 @@ public async Task StopMission(CancellationToken cancel = default) var result = await _commandClient.CommandLong(AsvSdrHelper.SetArgsForSdrStopMission, cs.Token).ConfigureAwait(false); return result.Result; } + + public async Task StartCalibration(CancellationToken cancel = default) + { + using var cs = CancellationTokenSource.CreateLinkedTokenSource(DisposeCancel, cancel); + var result = await _commandClient.CommandLong(AsvSdrHelper.SetArgsForSdrStartCalibration, cs.Token).ConfigureAwait(false); + return result.Result; + } + + public async Task StopCalibration(CancellationToken cancel = default) + { + using var cs = CancellationTokenSource.CreateLinkedTokenSource(DisposeCancel, cancel); + var result = await _commandClient.CommandLong(AsvSdrHelper.SetArgsForSdrStopCalibration, cs.Token).ConfigureAwait(false); + return result.Result; + } + + public IRxValue CalibrationTableRemoteCount => _calibrationTableRemoteCount; + public IRxValue CalibrationState => _calibrationState; + public async Task ReadCalibrationTableList(IProgress? progress = null, CancellationToken cancel = default) + { + progress ??= new Progress(); + var count = CalibrationTableRemoteCount.Value; + for (ushort i = 0; i < count; i++) + { + progress.Report(i); + await Base.ReadCalibrationTable(i,cancel).ConfigureAwait(false); + // no need to add result to cache, it will be added by OnCalibrationTable subscription in ctor + } + } + + public async Task GetCalibrationTable(string name, CancellationToken cancel = default) + { + var value = _calibrationTables.Lookup(name); + if (value.HasValue) return value.Value; + await ReadCalibrationTableList(null, cancel).ConfigureAwait(false); + value = _calibrationTables.Lookup(name); + return value.HasValue ? value.Value : null; + } + + + public IObservable> CalibrationTables { get; } + } \ No newline at end of file diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/IAsvSdrClientEx.cs b/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/IAsvSdrClientEx.cs index 1fb0bb3a..c64f1e0e 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/IAsvSdrClientEx.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/Client/Ex/IAsvSdrClientEx.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Threading; using System.Threading.Tasks; @@ -19,91 +20,111 @@ public interface IAsvSdrClientEx IRxValue IsRecordStarted { get; } Task DeleteRecord(Guid recordName, CancellationToken cancel = default); Task DownloadRecordList(IProgress progress = null, CancellationToken cancel = default); - Task SetMode(AsvSdrCustomMode mode, ulong frequencyHz, float recordRate, uint sendingThinningRatio, CancellationToken cancel = default); + Task SetMode(AsvSdrCustomMode mode, ulong frequencyHz, float recordRate, uint sendingThinningRatio, float refPower, CancellationToken cancel = default); Task StartRecord(string recordName, CancellationToken cancel = default); Task StopRecord(CancellationToken cancel = default); Task CurrentRecordSetTag(string tagName, AsvSdrRecordTagType type, byte[] rawValue , CancellationToken cancel = default); Task SystemControlAction(AsvSdrSystemControlAction action, CancellationToken cancel = default); Task StartMission(ushort missionIndex = 0, CancellationToken cancel = default); Task StopMission(CancellationToken cancel = default); -} - -public static class AsvSdrClientExHelper -{ - public static async Task StartMissionAndCheckResult(this IAsvSdrClientEx src, ushort missionIndex, CancellationToken cancel = default) + + #region Calibration + + Task StartCalibration(CancellationToken cancel = default); + Task StopCalibration(CancellationToken cancel = default); + IRxValue CalibrationState { get; } + IRxValue CalibrationTableRemoteCount { get; } + Task ReadCalibrationTableList(IProgress? progress = null,CancellationToken cancel = default); + Task GetCalibrationTable(string name, CancellationToken cancel = default); + IObservable> CalibrationTables { get; } + + #endregion + + public async Task StartCalibrationAndCheckResult( CancellationToken cancel = default) + { + var result = await StartCalibration(cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Start calibration failed. Result: {result}"); + } + + public async Task StopCalibrationAndCheckResult( CancellationToken cancel = default) { - var result = await src.StartMission(missionIndex, cancel).ConfigureAwait(false); - if (result != MavResult.MavResultAccepted) throw new Exception($"Start mission '{missionIndex:g}' failed. Result: {result}"); + var result = await StopCalibration(cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Stop calibration failed. Result: {result}"); } - public static async Task StopMissionAndCheckResult(this IAsvSdrClientEx src, CancellationToken cancel = default) + + public async Task StartMissionAndCheckResult( ushort missionIndex, CancellationToken cancel = default) { - var result = await src.StopMission(cancel).ConfigureAwait(false); - if (result != MavResult.MavResultAccepted) throw new Exception($"Stop mission failed. Result: {result}"); + var result = await StartMission(missionIndex, cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Start mission '{missionIndex:g}' failed. Result: {result}"); } - public static async Task SystemControlActionCheckResult(this IAsvSdrClientEx src, AsvSdrSystemControlAction action, CancellationToken cancel = default) + public async Task StopMissionAndCheckResult( CancellationToken cancel = default) { - var result = await src.SystemControlAction(action, cancel).ConfigureAwait(false); - if (result != MavResult.MavResultAccepted) throw new Exception($"System control action {action:G} failed. Result: {result}"); + var result = await StopMission(cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Stop mission failed. Result: {result}"); } - public static async Task SetModeAndCheckResult(this IAsvSdrClientEx src, AsvSdrCustomMode mode, ulong frequencyHz, float recordRate, uint sendingThinningRatio, CancellationToken cancel) + public async Task SystemControlActionCheckResult( AsvSdrSystemControlAction action, CancellationToken cancel = default) { - var result = await src.SetMode(mode, frequencyHz, recordRate, sendingThinningRatio, cancel).ConfigureAwait(false); - if (result != MavResult.MavResultAccepted) throw new Exception($"Set mode failed. Result: {result}"); + var result = await SystemControlAction(action, cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"System control action {action:G} failed. Result: {result}"); + } + public async Task SetModeAndCheckResult( AsvSdrCustomMode mode, ulong frequencyHz, float recordRate, uint sendingThinningRatio, float refPower, CancellationToken cancel) + { + var result = await SetMode(mode, frequencyHz, recordRate, sendingThinningRatio, refPower, cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Set mode failed. Result: {result}"); } - public static async Task StartRecordAndCheckResult(this IAsvSdrClientEx src, string recordName, CancellationToken cancel) + public async Task StartRecordAndCheckResult( string recordName, CancellationToken cancel) { - var result = await src.StartRecord(recordName, cancel).ConfigureAwait(false); - if (result != MavResult.MavResultAccepted) throw new Exception($"Start record failed. Result: {result}"); + var result = await StartRecord(recordName, cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Start record failed. Result: {result}"); } - public static async Task StopRecordAndCheckResult(this IAsvSdrClientEx src, CancellationToken cancel) + public async Task StopRecordAndCheckResult( CancellationToken cancel) { - var result = await src.StopRecord(cancel).ConfigureAwait(false); - if (result != MavResult.MavResultAccepted) throw new Exception($"Stop record failed. Result: {result}"); + var result = await StopRecord(cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Stop record failed. Result: {result}"); } - public static async Task CurrentRecordSetTagAndCheckResult(this IAsvSdrClientEx src, string tagName, string value, CancellationToken cancel) + public async Task CurrentRecordSetTagAndCheckResult( string tagName, string value, CancellationToken cancel) { - var result = await src.CurrentRecordSetTag(tagName, value, cancel).ConfigureAwait(false); - if (result != MavResult.MavResultAccepted) throw new Exception($"Set tag failed. Result: {result}"); + var result = await CurrentRecordSetTag(tagName, value, cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Set tag failed. Result: {result}"); } - public static async Task CurrentRecordSetTagAndCheckResult(this IAsvSdrClientEx src, string tagName, ulong value, CancellationToken cancel) + public async Task CurrentRecordSetTagAndCheckResult( string tagName, ulong value, CancellationToken cancel) { - var result = await src.CurrentRecordSetTag(tagName, value, cancel).ConfigureAwait(false); - if (result != MavResult.MavResultAccepted) throw new Exception($"Set tag failed. Result: {result}"); + var result = await CurrentRecordSetTag(tagName, value, cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Set tag failed. Result: {result}"); } - public static async Task CurrentRecordSetTagAndCheckResult(this IAsvSdrClientEx src, string tagName, long value, CancellationToken cancel) + public async Task CurrentRecordSetTagAndCheckResult( string tagName, long value, CancellationToken cancel) { - var result = await src.CurrentRecordSetTag(tagName, value, cancel).ConfigureAwait(false); - if (result != MavResult.MavResultAccepted) throw new Exception($"Set tag failed. Result: {result}"); + var result = await CurrentRecordSetTag(tagName, value, cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Set tag failed. Result: {result}"); } - public static async Task CurrentRecordSetTagAndCheckResult(this IAsvSdrClientEx src, string tagName, double value, CancellationToken cancel) + public async Task CurrentRecordSetTagAndCheckResult( string tagName, double value, CancellationToken cancel) { - var result = await src.CurrentRecordSetTag(tagName, value, cancel).ConfigureAwait(false); - if (result != MavResult.MavResultAccepted) throw new Exception($"Set tag failed. Result: {result}"); + var result = await CurrentRecordSetTag(tagName, value, cancel).ConfigureAwait(false); + if (result != MavResult.MavResultAccepted) throw new AsvSdrException($"Set tag failed. Result: {result}"); } - public static Task CurrentRecordSetTag(this IAsvSdrClientEx src, string tagName, string value, CancellationToken cancel) + public Task CurrentRecordSetTag( string tagName, string value, CancellationToken cancel) { if (value.Length > AsvSdrHelper.RecordTagValueLength) - throw new Exception($"Tag string value is too long. Max length is {AsvSdrHelper.RecordTagValueLength}"); + throw new ArgumentException($"Tag string value is too long. Max length is {AsvSdrHelper.RecordTagValueLength}"); var nameArray = new byte[AsvSdrHelper.RecordTagValueLength]; MavlinkTypesHelper.SetString(nameArray,value); - return src.CurrentRecordSetTag(tagName, AsvSdrRecordTagType.AsvSdrRecordTagTypeString8, nameArray, cancel); + return CurrentRecordSetTag(tagName, AsvSdrRecordTagType.AsvSdrRecordTagTypeString8, nameArray, cancel); } - public static Task CurrentRecordSetTag(this IAsvSdrClientEx src, string tagName, ulong value, CancellationToken cancel) + public Task CurrentRecordSetTag( string tagName, ulong value, CancellationToken cancel) { - return src.CurrentRecordSetTag(tagName, AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64, BitConverter.GetBytes(value), cancel); + return CurrentRecordSetTag(tagName, AsvSdrRecordTagType.AsvSdrRecordTagTypeUint64, BitConverter.GetBytes(value), cancel); } - public static Task CurrentRecordSetTag(this IAsvSdrClientEx src, string tagName, long value, CancellationToken cancel) + public Task CurrentRecordSetTag( string tagName, long value, CancellationToken cancel) { - return src.CurrentRecordSetTag(tagName, AsvSdrRecordTagType.AsvSdrRecordTagTypeInt64, BitConverter.GetBytes(value), cancel); + return CurrentRecordSetTag(tagName, AsvSdrRecordTagType.AsvSdrRecordTagTypeInt64, BitConverter.GetBytes(value), cancel); } - public static Task CurrentRecordSetTag(this IAsvSdrClientEx src, string tagName, double value, CancellationToken cancel) + public Task CurrentRecordSetTag( string tagName, double value, CancellationToken cancel) { - return src.CurrentRecordSetTag(tagName, AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64, BitConverter.GetBytes(value), cancel); + return CurrentRecordSetTag(tagName, AsvSdrRecordTagType.AsvSdrRecordTagTypeReal64, BitConverter.GetBytes(value), cancel); } - -} \ No newline at end of file +} diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/Client/IAsvSdrClient.cs b/src/Asv.Mavlink/Microservices/AsvSdr/Client/IAsvSdrClient.cs index b3207f56..6b1efab3 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/Client/IAsvSdrClient.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/Client/IAsvSdrClient.cs @@ -10,18 +10,45 @@ namespace Asv.Mavlink; /// public interface IAsvSdrClient { + MavlinkClientIdentity Identity { get; } + ushort GenerateRequestIndex(); + IRxValue Status { get; } - IObservable<(Guid,AsvSdrRecordPayload)> OnRecord { get; } + + IObservable OnSignal { get; } + + #region Records Task GetRecordList(ushort startIndex, ushort stopIndex, CancellationToken cancel=default); - IObservable<(TagId,AsvSdrRecordTagPayload)> OnRecordTag { get; } - IObservable<(Guid,AsvSdrRecordDeleteResponsePayload)> OnDeleteRecord { get; } + IObservable<(Guid,AsvSdrRecordPayload)> OnRecord { get; } Task DeleteRecord(Guid recordId, CancellationToken cancel = default); + IObservable<(Guid,AsvSdrRecordDeleteResponsePayload)> OnDeleteRecord { get; } + #endregion + + #region Tags Task GetRecordTagList(Guid recordId, ushort skip, ushort count, CancellationToken cancel = default); - IObservable<(TagId, AsvSdrRecordTagDeleteResponsePayload)> OnDeleteRecordTag { get; } + IObservable<(TagId,AsvSdrRecordTagPayload)> OnRecordTag { get; } Task DeleteRecordTag(TagId tag, CancellationToken cancel = default); + IObservable<(TagId, AsvSdrRecordTagDeleteResponsePayload)> OnDeleteRecordTag { get; } + #endregion + + #region Record data Task GetRecordDataList(Guid recordId, uint skip, uint count, CancellationToken cancel = default); IObservable> OnRecordData { get; } - IObservable OnSignal { get; } + #endregion + + + #region Calibration + + IObservable OnCalibrationTable { get; } + Task ReadCalibrationTable(ushort tableIndex, CancellationToken cancel=default); + Task ReadCalibrationTableRow(ushort tableId, ushort rowIndex, CancellationToken cancel = default); + Task SendCalibrationTableRowUploadStart(Action argsFill, CancellationToken cancel = default); + IObservable OnCalibrationTableRowUploadCallback { get; } + IObservable OnCalibrationAcc { get; } + Task SendCalibrationTableRowUploadItem(Action argsFill, CancellationToken cancel = default); + + #endregion + } public static class AsvSdrClientExtensions diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/Server/AsvSdrServer.cs b/src/Asv.Mavlink/Microservices/AsvSdr/Server/AsvSdrServer.cs index 127f62fd..5e38fd06 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/Server/AsvSdrServer.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/Server/AsvSdrServer.cs @@ -38,6 +38,15 @@ public AsvSdrServer(IMavlinkV2Connection connection, .Select(_ => _.Payload).Publish().RefCount(); OnRecordDataRequest = InternalFilter(_=>_.Payload.TargetSystem,_=>_.Payload.TargetComponent) .Select(_ => _.Payload).Publish().RefCount(); + + OnCalibrationTableReadRequest = InternalFilter(_=>_.Payload.TargetSystem,_=>_.Payload.TargetComponent) + .Select(_ => _.Payload).Publish().RefCount(); + OnCalibrationTableRowReadRequest = InternalFilter(_=>_.Payload.TargetSystem,_=>_.Payload.TargetComponent) + .Select(_ => _.Payload).Publish().RefCount(); + OnCalibrationTableUploadStart = + InternalFilter(_ => _.Payload.TargetSystem, + _ => _.Payload.TargetComponent) + .Publish().RefCount(); } public void Start() @@ -115,9 +124,56 @@ public IPacketV2 CreateRecordData(AsvSdrCustomMode mode) return Connection.CreatePacketByMessageId((int)mode); } + #region Calibration + public Task SendSignal(Action setValueCallback, CancellationToken cancel = default) { return InternalSend(setValueCallback,cancel); } + + public Task SendCalibrationAcc(ushort reqId, AsvSdrRequestAck resultCode, + CancellationToken cancel = default) + { + return InternalSend(args => + { + args.Payload.Result = resultCode; + args.Payload.RequestId = reqId; + }, cancel); + } + + + public IObservable OnCalibrationTableReadRequest { get; } + public Task SendCalibrationTableReadResponse(Action setValueCallback, CancellationToken cancel = default) + { + if (setValueCallback == null) throw new ArgumentNullException(nameof(setValueCallback)); + return InternalSend(args => setValueCallback(args.Payload), cancel); + } + + public IObservable OnCalibrationTableRowReadRequest { get; } + public Task SendCalibrationTableRowReadResponse(Action setValueCallback, CancellationToken cancel = default) + { + if (setValueCallback == null) throw new ArgumentNullException(nameof(setValueCallback)); + return InternalSend(args => + { + setValueCallback(args.Payload); + }, cancel); + } + + public IObservable OnCalibrationTableUploadStart { get; } + public Task CallCalibrationTableUploadReadCallback(byte targetSysId, byte targetCompId, ushort reqId, ushort tableIndex, ushort rowIndex , CancellationToken cancel = default) + { + return InternalCall( + x => + { + x.Payload.TableIndex = tableIndex; + x.Payload.RequestId = reqId; + x.Payload.TargetComponent = targetCompId; + x.Payload.TargetSystem = targetSysId; + x.Payload.RowIndex = rowIndex; + },p=>p.Payload.TargetSystem, p=>p.Payload.TargetComponent,p=> p.Payload.RowIndex == rowIndex && p.Payload.TableIndex == tableIndex, _=>new CalibrationTableRow(_.Payload) , cancel:DisposeCancel); + + } + + #endregion } } \ No newline at end of file diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/Server/Ex/AsvSdrServerEx.cs b/src/Asv.Mavlink/Microservices/AsvSdr/Server/Ex/AsvSdrServerEx.cs index 12b084d6..e2d4a424 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/Server/Ex/AsvSdrServerEx.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/Server/Ex/AsvSdrServerEx.cs @@ -1,4 +1,6 @@ +#nullable enable using System; +using System.Collections.Generic; using System.Globalization; using System.Reactive.Linq; using System.Threading; @@ -8,6 +10,7 @@ using Asv.Mavlink.V2.AsvSdr; using Asv.Mavlink.V2.Common; using Asv.Mavlink.V2.Minimal; +using NLog; using MavCmd = Asv.Mavlink.V2.Common.MavCmd; using MavType = Asv.Mavlink.V2.Minimal.MavType; @@ -15,12 +18,16 @@ namespace Asv.Mavlink; public class AsvSdrServerEx : DisposableOnceWithCancel, IAsvSdrServerEx { + private readonly IStatusTextServer _status; private double _signalSendingFlag; + private int _calibrationTableUploadFlag; + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - public AsvSdrServerEx(IAsvSdrServer server,IHeartbeatServer heartbeat, ICommandServerEx commands) + public AsvSdrServerEx(IAsvSdrServer server, IStatusTextServer status, IHeartbeatServer heartbeat, ICommandServerEx commands) { if (heartbeat == null) throw new ArgumentNullException(nameof(heartbeat)); if (commands == null) throw new ArgumentNullException(nameof(commands)); + _status = status ?? throw new ArgumentNullException(nameof(status)); Base = server ?? throw new ArgumentNullException(nameof(server)); #region Heartbeat @@ -46,8 +53,8 @@ public AsvSdrServerEx(IAsvSdrServer server,IHeartbeatServer heartbeat, ICommandS { if (SetMode == null) return new CommandResult(MavResult.MavResultUnsupported); using var cs = CancellationTokenSource.CreateLinkedTokenSource(DisposeCancel, cancel); - AsvSdrHelper.GetArgsForSdrSetMode(args.Payload, out var mode, out var freq, out var rate, out var sendingThinningRatio); - var result = await SetMode(mode,freq, rate,sendingThinningRatio, cs.Token).ConfigureAwait(false); + AsvSdrHelper.GetArgsForSdrSetMode(args.Payload, out var mode, out var freq, out var rate, out var sendingThinningRatio, out var referencePower); + var result = await SetMode(mode,freq, rate,sendingThinningRatio, referencePower, cs.Token).ConfigureAwait(false); return new CommandResult(result); }; commands[(MavCmd)V2.AsvSdr.MavCmd.MavCmdAsvSdrStartRecord] = async (id,args, cancel) => @@ -98,6 +105,116 @@ public AsvSdrServerEx(IAsvSdrServer server,IHeartbeatServer heartbeat, ICommandS var result = await StopMission(cs.Token).ConfigureAwait(false); return new CommandResult(result); }; + + commands[(MavCmd)V2.AsvSdr.MavCmd.MavCmdAsvSdrStartCalibration] = async (id, args, cancel) => + { + if (StartCalibration == null) return new CommandResult(MavResult.MavResultUnsupported); + using var cs = CancellationTokenSource.CreateLinkedTokenSource(DisposeCancel, cancel); + AsvSdrHelper.SetArgsForSdrStartCalibration(args.Payload); + var result = await StartCalibration(cs.Token).ConfigureAwait(false); + return new CommandResult(result); + }; + commands[(MavCmd)V2.AsvSdr.MavCmd.MavCmdAsvSdrStopCalibration] = async (id, args, cancel) => + { + if (StopCalibration == null) return new CommandResult(MavResult.MavResultUnsupported); + using var cs = CancellationTokenSource.CreateLinkedTokenSource(DisposeCancel, cancel); + AsvSdrHelper.SetArgsForSdrStopCalibration(args.Payload); + var result = await StopCalibration(cs.Token).ConfigureAwait(false); + return new CommandResult(result); + }; + + Base.OnCalibrationTableReadRequest.Subscribe(OnCalibrationReadTable).DisposeItWith(Disposable); + Base.OnCalibrationTableRowReadRequest.Subscribe(OnCalibrationReadTableRow).DisposeItWith(Disposable); + Base.OnCalibrationTableUploadStart.Subscribe(OnCalibrationTableUploadStart).DisposeItWith(Disposable); + } + + private async void OnCalibrationTableUploadStart(AsvSdrCalibTableUploadStartPacket args) + { + if (WriteCalibrationTable == null) + { + await Base.SendCalibrationAcc(args.Payload.RequestId, AsvSdrRequestAck.AsvSdrRequestAckNotSupported).ConfigureAwait(false); + return; + } + if (Interlocked.CompareExchange(ref _calibrationTableUploadFlag, 1, 0) != 0) + { + await Base.SendCalibrationAcc(args.Payload.RequestId, AsvSdrRequestAck.AsvSdrRequestAckInProgress).ConfigureAwait(false); + Logger.Warn($"Calibration table upload already in progress"); + return; + } + try + { + _status.Info($"Upload calibration [{args.Payload.TableIndex}] started"); + var rows = new CalibrationTableRow[args.Payload.RowCount]; + for (ushort i = 0; i < args.Payload.RowCount; i++) + { + var row = await Base.CallCalibrationTableUploadReadCallback(args.SystemId,args.ComponentId,args.Payload.RequestId,args.Payload.TableIndex,i,DisposeCancel).ConfigureAwait(false); + rows[i] = row; + } + WriteCalibrationTable(args.Payload.TableIndex, rows); + _status.Info($"Upload calibration [{args.Payload.TableIndex}] completed"); + await Base.SendCalibrationAcc(args.Payload.RequestId, AsvSdrRequestAck.AsvSdrRequestAckOk).ConfigureAwait(false); + } + catch (Exception e) + { + _status.Info($"Upload calibration [{args.Payload.TableIndex}] error"); + Logger.Error(e,$"Upload calibration [{args.Payload.TableIndex}] error:{e.Message}"); + await Base.SendCalibrationAcc(args.Payload.RequestId, AsvSdrRequestAck.AsvSdrRequestAckFail).ConfigureAwait(false); + } + finally + { + Interlocked.Exchange(ref _calibrationTableUploadFlag, 0); + } + + } + private async void OnCalibrationReadTableRow(AsvSdrCalibTableRowReadPayload args) + { + if (ReadCalibrationTableRow == null) + { + await Base.SendCalibrationAcc(args.RequestId, AsvSdrRequestAck.AsvSdrRequestAckNotSupported).ConfigureAwait(false); + return; + } + try + { + var info = ReadCalibrationTableRow(args.TableIndex,args.RowIndex); + await Base.SendCalibrationTableRowReadResponse(res => + { + res.RowIndex = args.RowIndex; + res.TableIndex = args.TableIndex; + res.TargetComponent = 0; + res.TargetSystem = 0; + info.Fill(res); + }).ConfigureAwait(false); + } + catch (Exception e) + { + await Base.SendCalibrationAcc(args.RequestId, AsvSdrRequestAck.AsvSdrRequestAckFail).ConfigureAwait(false); + } + } + + + + private void OnCalibrationReadTable(AsvSdrCalibTableReadPayload args) + { + if (ReadCalibrationTableInfo == null) + { + Base.SendCalibrationAcc(args.RequestId, AsvSdrRequestAck.AsvSdrRequestAckNotSupported); + return; + } + try + { + var info = ReadCalibrationTableInfo(args.TableIndex); + Base.SendCalibrationTableReadResponse(res => + { + res.RowCount = info.Size; + res.TableIndex = args.TableIndex; + MavlinkTypesHelper.SetString(res.TableName, info.Name); + res.CreatedUnixUs = MavlinkTypesHelper.ToUnixTimeUs(info.Updated); + }); + } + catch (Exception e) + { + Base.SendCalibrationAcc(args.RequestId, AsvSdrRequestAck.AsvSdrRequestAckFail); + } } public SetModeDelegate SetMode { get; set; } @@ -107,6 +224,12 @@ public AsvSdrServerEx(IAsvSdrServer server,IHeartbeatServer heartbeat, ICommandS public SystemControlActionDelegate SystemControlAction { get; set; } public StartMissionDelegate StartMission { get; set; } public StopMissionDelegate StopMission { get; set; } + public StartCalibrationDelegate StartCalibration { get; set; } + public StopCalibrationDelegate StopCalibration { get; set; } + public ReadCalibrationTableInfoDelegate? ReadCalibrationTableInfo { get; set; } + public ReadCalibrationTableRowDelegate? ReadCalibrationTableRow { get; set; } + public WriteCalibrationDelegate? WriteCalibrationTable { get; set; } + public async Task SendSignal(ulong unixTime, string name, ReadOnlyMemory signal, AsvSdrSignalFormat format, CancellationToken cancel = default) { diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/Server/Ex/IAsvSdrServerEx.cs b/src/Asv.Mavlink/Microservices/AsvSdr/Server/Ex/IAsvSdrServerEx.cs index 95c7a6c8..325ba240 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/Server/Ex/IAsvSdrServerEx.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/Server/Ex/IAsvSdrServerEx.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Threading; using System.Threading.Tasks; @@ -8,7 +9,7 @@ namespace Asv.Mavlink; -public delegate Task SetModeDelegate(AsvSdrCustomMode mode, ulong frequencyHz, float recordRate,uint sendingThinningRatio, CancellationToken cancel); +public delegate Task SetModeDelegate(AsvSdrCustomMode mode, ulong frequencyHz, float recordRate,uint sendingThinningRatio, float referencePower, CancellationToken cancel); public delegate Task StartRecordDelegate(string recordName, CancellationToken cancel); @@ -21,6 +22,13 @@ namespace Asv.Mavlink; public delegate Task StartMissionDelegate(ushort missionIndex, CancellationToken cancel); public delegate Task StopMissionDelegate(CancellationToken cancel); +public delegate Task StartCalibrationDelegate(CancellationToken cancel); +public delegate Task StopCalibrationDelegate(CancellationToken cancel); +public delegate CalibrationTableInfo ReadCalibrationTableInfoDelegate(ushort tableIndex); +public delegate CalibrationTableRow ReadCalibrationTableRowDelegate(ushort tableIndex, ushort rowIndex); +public delegate void WriteCalibrationDelegate(ushort tableIndex, CalibrationTableRow[] items); + + public interface IAsvSdrServerEx { IAsvSdrServer Base { get; } @@ -32,5 +40,11 @@ public interface IAsvSdrServerEx SystemControlActionDelegate SystemControlAction { set; } StartMissionDelegate StartMission { set; } StopMissionDelegate StopMission { set; } + + StartCalibrationDelegate StartCalibration { set; } + StopCalibrationDelegate StopCalibration { set; } + ReadCalibrationTableInfoDelegate? ReadCalibrationTableInfo { set; } + ReadCalibrationTableRowDelegate? ReadCalibrationTableRow { get; set; } + WriteCalibrationDelegate? WriteCalibrationTable { get; set; } Task SendSignal(ulong unixTime, string name, ReadOnlyMemory signal, AsvSdrSignalFormat format, CancellationToken cancel = default); } \ No newline at end of file diff --git a/src/Asv.Mavlink/Microservices/AsvSdr/Server/IAsvSdrServer.cs b/src/Asv.Mavlink/Microservices/AsvSdr/Server/IAsvSdrServer.cs index e686bfbf..4859c2f0 100644 --- a/src/Asv.Mavlink/Microservices/AsvSdr/Server/IAsvSdrServer.cs +++ b/src/Asv.Mavlink/Microservices/AsvSdr/Server/IAsvSdrServer.cs @@ -24,48 +24,57 @@ public interface IAsvSdrServer Task SendRecordData(AsvSdrCustomMode mode, Action setValueCallback, CancellationToken cancel = default); IPacketV2 CreateRecordData(AsvSdrCustomMode mode); Task SendSignal(Action setValueCallback, CancellationToken cancel = default); - } - - public static class AsvSdrServerHelper - { - public static Task SendRecordResponseFail(this IAsvSdrServer src, AsvSdrRecordRequestPayload request, + + #region Calibration + + Task SendCalibrationAcc(ushort reqId, AsvSdrRequestAck resultCode , CancellationToken cancel = default); + IObservable OnCalibrationTableReadRequest { get; } + Task SendCalibrationTableReadResponse(Action setValueCallback, CancellationToken cancel = default); + IObservable OnCalibrationTableRowReadRequest { get; } + Task SendCalibrationTableRowReadResponse(Action setValueCallback, CancellationToken cancel = default); + IObservable OnCalibrationTableUploadStart { get; } + Task CallCalibrationTableUploadReadCallback(byte targetSysId, byte targetCompId, ushort reqId, ushort tableIndex, ushort rowIndex , CancellationToken cancel = default); + + #endregion + + public Task SendRecordResponseFail( AsvSdrRecordRequestPayload request, AsvSdrRequestAck resultCode) { if (resultCode == AsvSdrRequestAck.AsvSdrRequestAckOk) throw new ArgumentException("Result code must be not success"); - return src.SendRecordResponse(x => + return SendRecordResponse(x => { x.ItemsCount = 0; x.RequestId = request.RequestId; x.Result = resultCode; }, CancellationToken.None); } - public static Task SendRecordResponseSuccess(this IAsvSdrServer src, AsvSdrRecordRequestPayload request, + public Task SendRecordResponseSuccess( AsvSdrRecordRequestPayload request, ushort recordsCount) { - return src.SendRecordResponse(x => + return SendRecordResponse(x => { x.ItemsCount = recordsCount; x.RequestId = request.RequestId; x.Result = AsvSdrRequestAck.AsvSdrRequestAckOk; }, CancellationToken.None); } - public static Task SendRecordTagResponseFail(this IAsvSdrServer src, AsvSdrRecordTagRequestPayload request, + public Task SendRecordTagResponseFail( AsvSdrRecordTagRequestPayload request, AsvSdrRequestAck resultCode) { if (resultCode == AsvSdrRequestAck.AsvSdrRequestAckOk) throw new ArgumentException("Result code must be not success"); - return src.SendRecordTagResponse(x => + return SendRecordTagResponse(x => { x.ItemsCount = 0; x.RequestId = request.RequestId; x.Result = resultCode; }, CancellationToken.None); } - public static Task SendRecordTagResponseSuccess(this IAsvSdrServer src, AsvSdrRecordTagRequestPayload request, + public Task SendRecordTagResponseSuccess( AsvSdrRecordTagRequestPayload request, ushort recordsCount) { - return src.SendRecordTagResponse(x => + return SendRecordTagResponse(x => { x.ItemsCount = recordsCount; x.RequestId = request.RequestId; @@ -73,21 +82,21 @@ public static Task SendRecordTagResponseSuccess(this IAsvSdrServer src, AsvSdrRe }, CancellationToken.None); } - public static Task SendRecordDeleteResponseFail(this IAsvSdrServer src, AsvSdrRecordDeleteRequestPayload request, + public Task SendRecordDeleteResponseFail( AsvSdrRecordDeleteRequestPayload request, AsvSdrRequestAck resultCode) { if (resultCode == AsvSdrRequestAck.AsvSdrRequestAckOk) throw new ArgumentException("Result code must be not success"); - return src.SendRecordDeleteResponse(x => + return SendRecordDeleteResponse(x => { request.RecordGuid.CopyTo(x.RecordGuid, 0); x.RequestId = request.RequestId; x.Result = resultCode; }, CancellationToken.None); } - public static Task SendRecordDeleteResponseSuccess(this IAsvSdrServer src, AsvSdrRecordDeleteRequestPayload request) + public Task SendRecordDeleteResponseSuccess( AsvSdrRecordDeleteRequestPayload request) { - return src.SendRecordDeleteResponse(x => + return SendRecordDeleteResponse(x => { request.RecordGuid.CopyTo(x.RecordGuid, 0); x.RequestId = request.RequestId; @@ -95,12 +104,12 @@ public static Task SendRecordDeleteResponseSuccess(this IAsvSdrServer src, AsvSd }, CancellationToken.None); } - public static Task SendRecordTagDeleteResponseFail(this IAsvSdrServer src, AsvSdrRecordTagDeleteRequestPayload request, + public Task SendRecordTagDeleteResponseFail( AsvSdrRecordTagDeleteRequestPayload request, AsvSdrRequestAck resultCode) { if (resultCode == AsvSdrRequestAck.AsvSdrRequestAckOk) throw new ArgumentException("Result code must be not success"); - return src.SendRecordTagDeleteResponse(x => + return SendRecordTagDeleteResponse(x => { request.RecordGuid.CopyTo(x.RecordGuid, 0); request.TagGuid.CopyTo(x.TagGuid, 0); @@ -109,10 +118,10 @@ public static Task SendRecordTagDeleteResponseFail(this IAsvSdrServer src, AsvSd }, CancellationToken.None); } - public static Task SendRecordTagDeleteResponseSuccess(this IAsvSdrServer src, AsvSdrRecordTagDeleteRequestPayload request) + public Task SendRecordTagDeleteResponseSuccess( AsvSdrRecordTagDeleteRequestPayload request) { - return src.SendRecordTagDeleteResponse(x => + return SendRecordTagDeleteResponse(x => { request.RecordGuid.CopyTo(x.RecordGuid, 0); request.TagGuid.CopyTo(x.TagGuid, 0); @@ -121,12 +130,12 @@ public static Task SendRecordTagDeleteResponseSuccess(this IAsvSdrServer src, As }, CancellationToken.None); } - public static Task SendRecordDataResponseFail(this IAsvSdrServer src, AsvSdrRecordDataRequestPayload request, + public Task SendRecordDataResponseFail( AsvSdrRecordDataRequestPayload request, AsvSdrRequestAck resultCode) { if (resultCode == AsvSdrRequestAck.AsvSdrRequestAckOk) throw new ArgumentException("Result code must be not success"); - return src.SendRecordDataResponse(x => + return SendRecordDataResponse(x => { request.RecordGuid.CopyTo(x.RecordGuid, 0); x.ItemsCount = 0; @@ -135,10 +144,10 @@ public static Task SendRecordDataResponseFail(this IAsvSdrServer src, AsvSdrReco }, CancellationToken.None); } - public static Task SendRecordDataResponseSuccess(this IAsvSdrServer src, AsvSdrRecordDataRequestPayload request, + public Task SendRecordDataResponseSuccess( AsvSdrRecordDataRequestPayload request, uint count) { - return src.SendRecordDataResponse(x => + return SendRecordDataResponse(x => { request.RecordGuid.CopyTo(x.RecordGuid, 0); x.ItemsCount = count; @@ -147,4 +156,5 @@ public static Task SendRecordDataResponseSuccess(this IAsvSdrServer src, AsvSdrR }, CancellationToken.None); } } + } \ No newline at end of file diff --git a/src/Asv.Mavlink/Microservices/MavlinkMicroserviceClient.cs b/src/Asv.Mavlink/Microservices/MavlinkMicroserviceClient.cs index 18013dae..faeba5fa 100644 --- a/src/Asv.Mavlink/Microservices/MavlinkMicroserviceClient.cs +++ b/src/Asv.Mavlink/Microservices/MavlinkMicroserviceClient.cs @@ -35,6 +35,8 @@ public override string ToString() } + public delegate bool FilterDelegate(IPacketV2 inputPacket, out TResult result); + public abstract class MavlinkMicroserviceClient:DisposableOnceWithCancel { private readonly string _ifcLogName; @@ -88,7 +90,7 @@ private bool FilterVehicle(IPacketV2 packetV2) if (Identity.TargetComponentId != packetV2.ComponentId) return false; return true; } - protected MavlinkClientIdentity Identity { get; } + public MavlinkClientIdentity Identity { get; } protected IPacketSequenceCalculator Sequence { get; } @@ -110,6 +112,67 @@ protected Task InternalSend(Action fillPacket, Cancell return Connection.Send(packet, cancel); } + protected async Task InternalSendAndWaitAnswer(IPacketV2 packet, + CancellationToken cancel, FilterDelegate filterAndResultGetter, int timeoutMs = 1000) + { + if (filterAndResultGetter == null) throw new ArgumentNullException(nameof(filterAndResultGetter)); + Logger.Trace($"{LogSend} call {packet.Name}"); + using var linkedCancel = CancellationTokenSource.CreateLinkedTokenSource(cancel, DisposeCancel); + linkedCancel.CancelAfter(timeoutMs); + var tcs = new TaskCompletionSource(); + await using var c1 = linkedCancel.Token.Register(() => tcs.TrySetCanceled()); + + using var subscribe = InternalFilteredVehiclePackets.Subscribe(x=> + { + if (filterAndResultGetter(x, out var result) == true) + { + tcs.TrySetResult(result); + } + }); + packet.Sequence = Sequence.GetNextSequenceNumber(); + packet.ComponentId = Identity.ComponentId; + packet.SystemId = Identity.SystemId; + await Connection.Send(packet, linkedCancel.Token).ConfigureAwait(false); + var result = await tcs.Task.ConfigureAwait(false); + Logger.Trace($"{LogRecv} ok {packet.Name}<=={result}"); + return result; + } + + protected async Task InternalCall( + Action fillPacket, FilterDelegate filterAndResultGetter, int attemptCount = 5, + Action? fillOnConfirmation = null, int timeoutMs = 1000, CancellationToken cancel = default) + where TPacketSend : IPacketV2, new() + { + var packet = new TPacketSend(); + fillPacket(packet); + byte currentAttempt = 0; + var name = packet.Name; + while (currentAttempt < attemptCount) + { + if (currentAttempt != 0) + { + fillOnConfirmation?.Invoke(packet, currentAttempt); + Logger.Warn($"{LogSend} replay {currentAttempt} {name}"); + } + ++currentAttempt; + try + { + return await InternalSendAndWaitAnswer(packet, cancel, filterAndResultGetter, timeoutMs).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + if (cancel.IsCancellationRequested) + { + throw; + } + } + } + var msg = $"{LogSend} Timeout to execute '{name}' with {attemptCount} x {timeoutMs} ms'"; + Logger.Error(msg); + throw new TimeoutException(msg); + } + + protected async Task InternalSendAndWaitAnswer(IPacketV2 packet, CancellationToken cancel, Func? filter = null, int timeoutMs = 1000) where TAnswerPacket : IPacketV2, new() diff --git a/src/Asv.Mavlink/Microservices/Missions/Client/Ex/MissionClientEx.cs b/src/Asv.Mavlink/Microservices/Missions/Client/Ex/MissionClientEx.cs index 11beeba2..902afec1 100644 --- a/src/Asv.Mavlink/Microservices/Missions/Client/Ex/MissionClientEx.cs +++ b/src/Asv.Mavlink/Microservices/Missions/Client/Ex/MissionClientEx.cs @@ -100,7 +100,7 @@ public async Task Upload(CancellationToken cancel, Action progress = nul tcs.TrySetException(new Exception($"Mission upload timeout")); } }); - _client.OnMissionRequest.Subscribe(req => + using var sub1 = _client.OnMissionRequest.Subscribe(req => { Logger.Debug($"UAV request {req.Seq} item"); lastUpdateTime = DateTime.Now; @@ -114,9 +114,9 @@ public async Task Upload(CancellationToken cancel, Action progress = nul } _client.WriteMissionItem(item.Value, cancel); - } , cancel); + } ); - _client.OnMissionAck.Subscribe(_ => + using var sub2 =_client.OnMissionAck.Subscribe(_ => { lastUpdateTime = DateTime.Now; if (_.Type == MavMissionResult.MavMissionAccepted) @@ -128,7 +128,7 @@ public async Task Upload(CancellationToken cancel, Action progress = nul tcs.TrySetException(new Exception($"Error to upload mission to vehicle:{_.Type:G}")); } - } , cancel); + }); await _client.MissionSetCount((ushort)_missionSource.Count, cancel).ConfigureAwait(false); await tcs.Task.ConfigureAwait(false); diff --git a/src/Asv.Mavlink/Protocol/Dialects/asv_sdr.xml b/src/Asv.Mavlink/Protocol/Dialects/asv_sdr.xml index 09485306..a1855ee4 100644 --- a/src/Asv.Mavlink/Protocol/Dialects/asv_sdr.xml +++ b/src/Asv.Mavlink/Protocol/Dialects/asv_sdr.xml @@ -27,7 +27,7 @@ Frequency in Hz, 4-7 bytes of uint_64, ignored for IDLE mode (uint32). Record rate in Hz, ignored for IDLE mode (float). Sending data thinning ratio. Each specified amount of recorded data will be skipped before it is sent over the communication channel. (uint32). - Empty. + Estimated reference power in dBm. Needed to tune the internal amplifiers and filters (float). Empty. @@ -110,6 +110,26 @@ Empty. Empty. + + Start calibration process. Can't be used in the mission protocol for SDR payloads. + Empty. + Empty. + Empty. + Empty. + Empty. + Empty. + Empty. + + + Stop calibration process. Can't be used in the mission protocol for SDR payloads. + Empty. + Empty. + Empty. + Empty. + Empty. + Empty. + Empty. + State of the current mission (unit8_t). @@ -164,11 +184,11 @@ - - - - - + + + + + @@ -184,6 +204,9 @@ Command error. + + Not supported command. + @@ -195,7 +218,19 @@ Request system shutdown [!WRAP_TO_V2_EXTENSION_PACKET!] - + + + Status of calibration process. + + Calibration not supported by device. Commands MAV_CMD_ASV_SDR_START_CALIBRATION and MAV_CMD_ASV_SDR_STOP_CALIBRATION not supported. + + + Normal measure mode. Calibration table USED for measures. + + + Calibration progress started. Table NOT! used for calculating values. All measures send as raw values. + + SDR signal transmition data type @@ -221,8 +256,11 @@ Record name, terminated by NULL if the length is less than 28 human-readable chars and WITHOUT null termination (NULL) byte if the length is exactly 28 chars - applications have to provide 28+1 bytes storage if the name is stored as string. If the data is currently not being recorded, than return null; Mission state. Current mission index. + + Calibration status. + Number of calibration tables. - + Request list of ASV_SDR_RECORD from the system/component.[!WRAP_TO_V2_EXTENSION_PACKET!] System ID @@ -242,12 +280,14 @@ Record GUID. Generated by payload after the start of recording. Record name, terminated by NULL if the length is less than 28 human-readable chars and WITHOUT null termination (NULL) byte if the length is exactly 28 chars - applications have to provide 28+1 bytes storage if the name is stored as string. Record data type(it is also possible to know the type of data inside the record by cast enum to int). - Frequency in Hz. + Reference frequency in Hz, specified by MAV_CMD_ASV_SDR_SET_MODE command. Created timestamp (UNIX epoch time). Record duration in sec. Tag items count. Data items count. Total data size of record with all data items and tags. + + Reference power in dBm, specified by MAV_CMD_ASV_SDR_SET_MODE command. Request to delete ASV_SDR_RECORD items from the system/component.[!WRAP_TO_V2_EXTENSION_PACKET!] @@ -262,7 +302,7 @@ Specifies the unique number of the original request. This allows the response to be matched to the correct request. Specifies the GUID of the record that was deleted. - + Request list of ASV_SDR_RECORD_TAG from the system/component.[!WRAP_TO_V2_EXTENSION_PACKET!] System ID @@ -294,14 +334,14 @@ Record GUID. Tag GUID. - + Response for ASV_SDR_RECORD_TAG_DELETE_REQUEST.[!WRAP_TO_V2_EXTENSION_PACKET!] Result code. Specifies the unique number of the original request. This allows the response to be matched to the correct request. Record GUID. Tag GUID. - + Request list of ASV_SDR_RECORD_DATA_* items from the system/component.[!WRAP_TO_V2_EXTENSION_PACKET!] System ID @@ -319,8 +359,66 @@ Record data type(it is also possible to know the type of data inside the record by cast enum to int). Number of items ASV_SDR_RECORD_DATA_* for transmition after this request with success result code (depended from request). - - + + + + Response for ASV_SDR_CALIB_* requests. Result from ASV_SDR_CALIB_TABLE_READ, ASV_SDR_CALIB_TABLE_ROW_READ, ASV_SDR_CALIB_TABLE_UPLOAD_START messages.[!WRAP_TO_V2_EXTENSION_PACKET!] + Specifies the unique number of the original request. This allows the response to be matched to the correct request. + Result code. + + + Request to read ASV_SDR_CALIB_TABLE from the system/component. If success, device send ASV_SDR_CALIB_TABLE or ASV_SDR_CALIB_ACC, when error occured.[!WRAP_TO_V2_EXTENSION_PACKET!] + System ID + Component ID + Table index. + Specifies the unique number of the original request. This allows the response to be matched to the correct request. + + + Calibration table info.[!WRAP_TO_V2_EXTENSION_PACKET!] + Table index. + Table name, terminated by NULL if the length is less than 28 human-readable chars and WITHOUT null termination (NULL) byte if the length is exactly 28 chars - applications have to provide 28+1 bytes storage if the name is stored as string. + Specifies the number of ROWs in the table. + Updated timestamp (UNIX epoch time). + + + Request to read ASV_SDR_CALIB_TABLE_ROW from the system/component. If success, device send ASV_SDR_CALIB_TABLE_ROW or ASV_SDR_CALIB_ACC, when error occured.[!WRAP_TO_V2_EXTENSION_PACKET!] + System ID. + Component ID. + Specifies the unique number of the original request. This allows the response to be matched to the correct request. + Table index. + ROW index. + + + Calibration ROW content.[!WRAP_TO_V2_EXTENSION_PACKET!] + System ID. + Component ID. + Table index. + ROW index. + Reference frequency in Hz. + Reference power in dBm. + Reference value. + Measured value. + + + Start uploading process. After that payload must send ASV_SDR_CALIB_TABLE_UPLOAD_READ_CALLBACK to client for reading table rows row_count times. Process end by payload with ASV_SDR_CALIB_ACC. [!WRAP_TO_V2_EXTENSION_PACKET!] + System ID. + Component ID. + Table index. + Specifies the unique number of the original request. This allows the response to be matched to the correct request. + Specifies the number of ROWs in the table. + Current timestamp (UNIX epoch time). + + + Read ASV_SDR_CALIB_TABLE_ROW callback from payload server to client. [!WRAP_TO_V2_EXTENSION_PACKET!] + System ID. + Component ID. + Specifies the unique number of the original request from ASV_SDR_CALIB_TABLE_UPLOAD_START. This allows the response to be matched to the correct request. + Table index. + ROW index. + + + + Raw signal data for visualization.[!WRAP_TO_V2_EXTENSION_PACKET!] Signal name, terminated by NULL if the length is less than 8 human-readable chars and WITHOUT null termination (NULL) byte if the length is exactly 16 chars - applications have to provide 8+1 bytes storage if the ID is stored as string Timestamp (UNIX epoch time) for current set of measures. diff --git a/src/Asv.Mavlink/Protocol/Messages/asv_gbs.cs b/src/Asv.Mavlink/Protocol/Messages/asv_gbs.cs index d1a4cf7a..99425de3 100644 --- a/src/Asv.Mavlink/Protocol/Messages/asv_gbs.cs +++ b/src/Asv.Mavlink/Protocol/Messages/asv_gbs.cs @@ -25,6 +25,7 @@ using System; using System.Text; using Asv.Mavlink.V2.Common; +using Asv.Mavlink.V2.Minimal; using Asv.IO; namespace Asv.Mavlink.V2.AsvGbs diff --git a/src/Asv.Mavlink/Protocol/Messages/asv_sdr.cs b/src/Asv.Mavlink/Protocol/Messages/asv_sdr.cs index 8cb0d4c2..53e9b0a6 100644 --- a/src/Asv.Mavlink/Protocol/Messages/asv_sdr.cs +++ b/src/Asv.Mavlink/Protocol/Messages/asv_sdr.cs @@ -25,6 +25,7 @@ using System; using System.Text; using Asv.Mavlink.V2.Common; +using Asv.Mavlink.V2.Minimal; using Asv.IO; namespace Asv.Mavlink.V2.AsvSdr @@ -47,6 +48,13 @@ public static void RegisterAsvSdrDialect(this IPacketDecoder src.Register(()=>new AsvSdrRecordTagDeleteResponsePacket()); src.Register(()=>new AsvSdrRecordDataRequestPacket()); src.Register(()=>new AsvSdrRecordDataResponsePacket()); + src.Register(()=>new AsvSdrCalibAccPacket()); + src.Register(()=>new AsvSdrCalibTableReadPacket()); + src.Register(()=>new AsvSdrCalibTablePacket()); + src.Register(()=>new AsvSdrCalibTableRowReadPacket()); + src.Register(()=>new AsvSdrCalibTableRowPacket()); + src.Register(()=>new AsvSdrCalibTableUploadStartPacket()); + src.Register(()=>new AsvSdrCalibTableUploadReadCallbackPacket()); src.Register(()=>new AsvSdrSignalRawPacket()); src.Register(()=>new AsvSdrRecordDataLlzPacket()); src.Register(()=>new AsvSdrRecordDataGpPacket()); @@ -80,7 +88,7 @@ public enum MavCmd:uint /// Param 3 - Frequency in Hz, 4-7 bytes of uint_64, ignored for IDLE mode (uint32). /// Param 4 - Record rate in Hz, ignored for IDLE mode (float). /// Param 5 - Sending data thinning ratio. Each specified amount of recorded data will be skipped before it is sent over the communication channel. (uint32). - /// Param 6 - Empty. + /// Param 6 - Estimated reference power in dBm. Needed to tune the internal amplifiers and filters (float). /// Param 7 - Empty. /// MAV_CMD_ASV_SDR_SET_MODE /// @@ -122,7 +130,7 @@ public enum MavCmd:uint /// MavCmdAsvSdrSetRecordTag = 13103, /// - /// Send shutdown or reboot command. Can be used in the mission protocol for SDR payloads. + /// Send shutdown or reboot command. Can't be used in the mission protocol for SDR payloads. /// Param 1 - ASV_SDR_SYSTEM_CONTROL_ACTION (uint32). /// Param 2 - Empty. /// Param 3 - Empty. @@ -134,7 +142,7 @@ public enum MavCmd:uint /// MavCmdAsvSdrSystemControlAction = 13104, /// - /// Waiting for a vehicle mission waypoint point. Can be used in the mission protocol for SDR payloads. + /// Waiting for a vehicle mission waypoint point. Only used in the mission protocol for SDR payloads. /// Param 1 - Waypoint index (uint32). /// Param 2 - Empty. /// Param 3 - Empty. @@ -146,7 +154,7 @@ public enum MavCmd:uint /// MavCmdAsvSdrWaitVehicleWaypoint = 13105, /// - /// Waiting a certain amount of time. Can be used in the mission protocol for SDR payloads. + /// Waiting a certain amount of time. Only used in the mission protocol for SDR payloads. /// Param 1 - Delay in ms (uint32). /// Param 2 - Empty. /// Param 3 - Empty. @@ -181,6 +189,30 @@ public enum MavCmd:uint /// MAV_CMD_ASV_SDR_STOP_MISSION /// MavCmdAsvSdrStopMission = 13108, + /// + /// Start calibration process. Can't be used in the mission protocol for SDR payloads. + /// Param 1 - Empty. + /// Param 2 - Empty. + /// Param 3 - Empty. + /// Param 4 - Empty. + /// Param 5 - Empty. + /// Param 6 - Empty. + /// Param 7 - Empty. + /// MAV_CMD_ASV_SDR_START_CALIBRATION + /// + MavCmdAsvSdrStartCalibration = 13109, + /// + /// Stop calibration process. Can't be used in the mission protocol for SDR payloads. + /// Param 1 - Empty. + /// Param 2 - Empty. + /// Param 3 - Empty. + /// Param 4 - Empty. + /// Param 5 - Empty. + /// Param 6 - Empty. + /// Param 7 - Empty. + /// MAV_CMD_ASV_SDR_STOP_CALIBRATION + /// + MavCmdAsvSdrStopCalibration = 13110, } /// @@ -303,6 +335,11 @@ public enum AsvSdrRequestAck:uint /// ASV_SDR_REQUEST_ACK_FAIL /// AsvSdrRequestAckFail = 2, + /// + /// Not supported command. + /// ASV_SDR_REQUEST_ACK_NOT_SUPPORTED + /// + AsvSdrRequestAckNotSupported = 3, } /// @@ -323,6 +360,29 @@ public enum AsvSdrSystemControlAction:uint AsvSdrSystemControlActionShutdown = 1, } + /// + /// Status of calibration process. + /// ASV_SDR_CALIB_STATE + /// + public enum AsvSdrCalibState:uint + { + /// + /// Calibration not supported by device. Commands MAV_CMD_ASV_SDR_START_CALIBRATION and MAV_CMD_ASV_SDR_STOP_CALIBRATION not supported. + /// ASV_SDR_CALIB_STATE_NOT_SUPPORTED + /// + AsvSdrCalibStateNotSupported = 0, + /// + /// Normal measure mode. Calibration table USED for measures. + /// ASV_SDR_CALIB_STATE_OK + /// + AsvSdrCalibStateOk = 1, + /// + /// Calibration progress started. Table NOT! used for calculating values. All measures send as raw values. + /// ASV_SDR_CALIB_STATE_PROGRESS + /// + AsvSdrCalibStateProgress = 2, + } + /// /// SDR signal transmition data type /// ASV_SDR_SIGNAL_FORMAT @@ -372,8 +432,8 @@ public class AsvSdrOutStatusPacket: PacketV2 /// public class AsvSdrOutStatusPayload : IPayload { - public byte GetMaxByteSize() => 66; // Sum of byte sized of all fields (include extended) - public byte GetMinByteSize() => 66; // of byte sized of fields (exclude extended) + public byte GetMaxByteSize() => 69; // Sum of byte sized of all fields (include extended) + public byte GetMinByteSize() => 69; // of byte sized of fields (exclude extended) public int GetByteSize() { var sum = 0; @@ -385,6 +445,8 @@ public int GetByteSize() sum+= 1; // CurrentRecordMode sum+=CurrentRecordName.Length; //CurrentRecordName sum+= 1; // MissionState + sum+= 1; // CalibState + sum+=2; //CalibTableCount return (byte)sum; } @@ -404,7 +466,7 @@ public void Deserialize(ref ReadOnlySpan buffer) CurrentRecordGuid[i] = (byte)BinSerialize.ReadByte(ref buffer); } CurrentRecordMode = (AsvSdrCustomMode)BinSerialize.ReadByte(ref buffer); - arraySize = /*ArrayLength*/28 - Math.Max(0,((/*PayloadByteSize*/66 - payloadSize - /*ExtendedFieldsLength*/0)/1 /*FieldTypeByteSize*/)); + arraySize = /*ArrayLength*/28 - Math.Max(0,((/*PayloadByteSize*/69 - payloadSize - /*ExtendedFieldsLength*/3)/1 /*FieldTypeByteSize*/)); CurrentRecordName = new char[arraySize]; unsafe { @@ -417,6 +479,12 @@ public void Deserialize(ref ReadOnlySpan buffer) buffer = buffer.Slice(arraySize); MissionState = (AsvSdrMissionState)BinSerialize.ReadByte(ref buffer); + // extended field 'CalibState' can be empty + if (buffer.IsEmpty) return; + CalibState = (AsvSdrCalibState)BinSerialize.ReadByte(ref buffer); + // extended field 'CalibTableCount' can be empty + if (buffer.IsEmpty) return; + CalibTableCount = BinSerialize.ReadUShort(ref buffer); } @@ -442,7 +510,9 @@ public void Serialize(ref Span buffer) buffer = buffer.Slice(CurrentRecordName.Length); BinSerialize.WriteByte(ref buffer,(byte)MissionState); - /* PayloadByteSize = 66 */; + BinSerialize.WriteByte(ref buffer,(byte)CalibState); + BinSerialize.WriteUShort(ref buffer,CalibTableCount); + /* PayloadByteSize = 69 */; } @@ -490,6 +560,16 @@ public void Serialize(ref Span buffer) /// OriginName: mission_state, Units: , IsExtended: false /// public AsvSdrMissionState MissionState { get; set; } + /// + /// Calibration status. + /// OriginName: calib_state, Units: , IsExtended: true + /// + public AsvSdrCalibState CalibState { get; set; } + /// + /// Number of calibration tables. + /// OriginName: calib_table_count, Units: , IsExtended: true + /// + public ushort CalibTableCount { get; set; } } /// /// Request list of ASV_SDR_RECORD from the system/component.[!WRAP_TO_V2_EXTENSION_PACKET!] @@ -668,8 +748,8 @@ public class AsvSdrRecordPacket: PacketV2 /// public class AsvSdrRecordPayload : IPayload { - public byte GetMaxByteSize() => 78; // Sum of byte sized of all fields (include extended) - public byte GetMinByteSize() => 78; // of byte sized of fields (exclude extended) + public byte GetMaxByteSize() => 82; // Sum of byte sized of all fields (include extended) + public byte GetMinByteSize() => 82; // of byte sized of fields (exclude extended) public int GetByteSize() { var sum = 0; @@ -682,6 +762,7 @@ public int GetByteSize() sum+=2; //TagCount sum+=RecordGuid.Length; //RecordGuid sum+=RecordName.Length; //RecordName + sum+=4; //RefPower return (byte)sum; } @@ -703,7 +784,7 @@ public void Deserialize(ref ReadOnlySpan buffer) { RecordGuid[i] = (byte)BinSerialize.ReadByte(ref buffer); } - arraySize = /*ArrayLength*/28 - Math.Max(0,((/*PayloadByteSize*/78 - payloadSize - /*ExtendedFieldsLength*/0)/1 /*FieldTypeByteSize*/)); + arraySize = /*ArrayLength*/28 - Math.Max(0,((/*PayloadByteSize*/82 - payloadSize - /*ExtendedFieldsLength*/4)/1 /*FieldTypeByteSize*/)); RecordName = new char[arraySize]; unsafe { @@ -715,6 +796,9 @@ public void Deserialize(ref ReadOnlySpan buffer) } buffer = buffer.Slice(arraySize); + // extended field 'RefPower' can be empty + if (buffer.IsEmpty) return; + RefPower = BinSerialize.ReadFloat(ref buffer); } @@ -741,7 +825,8 @@ public void Serialize(ref Span buffer) } buffer = buffer.Slice(RecordName.Length); - /* PayloadByteSize = 78 */; + BinSerialize.WriteFloat(ref buffer,RefPower); + /* PayloadByteSize = 82 */; } @@ -749,7 +834,7 @@ public void Serialize(ref Span buffer) /// - /// Frequency in Hz. + /// Reference frequency in Hz, specified by MAV_CMD_ASV_SDR_SET_MODE command. /// OriginName: frequency, Units: , IsExtended: false /// public ulong Frequency { get; set; } @@ -794,6 +879,11 @@ public void Serialize(ref Span buffer) /// public char[] RecordName { get; set; } = new char[28]; public byte GetRecordNameMaxItemsCount() => 28; + /// + /// Reference power in dBm, specified by MAV_CMD_ASV_SDR_SET_MODE command. + /// OriginName: ref_power, Units: , IsExtended: true + /// + public float RefPower { get; set; } } /// /// Request to delete ASV_SDR_RECORD items from the system/component.[!WRAP_TO_V2_EXTENSION_PACKET!] @@ -1380,7 +1470,7 @@ public void Serialize(ref Span buffer) /// public class AsvSdrRecordTagDeleteResponsePacket: PacketV2 { - public const int PacketMessageId = 13116; + public const int PacketMessageId = 13114; public override int MessageId => PacketMessageId; public override byte GetCrcEtra() => 100; public override bool WrapToV2Extension => true; @@ -1673,12 +1763,629 @@ public void Serialize(ref Span buffer) public byte GetRecordGuidMaxItemsCount() => 16; } /// + /// Response for ASV_SDR_CALIB_* requests. Result from ASV_SDR_CALIB_TABLE_READ, ASV_SDR_CALIB_TABLE_ROW_READ, ASV_SDR_CALIB_TABLE_UPLOAD_START messages.[!WRAP_TO_V2_EXTENSION_PACKET!] + /// ASV_SDR_CALIB_ACC + /// + public class AsvSdrCalibAccPacket: PacketV2 + { + public const int PacketMessageId = 13124; + public override int MessageId => PacketMessageId; + public override byte GetCrcEtra() => 136; + public override bool WrapToV2Extension => true; + + public override AsvSdrCalibAccPayload Payload { get; } = new AsvSdrCalibAccPayload(); + + public override string Name => "ASV_SDR_CALIB_ACC"; + } + + /// + /// ASV_SDR_CALIB_ACC + /// + public class AsvSdrCalibAccPayload : IPayload + { + public byte GetMaxByteSize() => 3; // Sum of byte sized of all fields (include extended) + public byte GetMinByteSize() => 3; // of byte sized of fields (exclude extended) + public int GetByteSize() + { + var sum = 0; + sum+=2; //RequestId + sum+= 1; // Result + return (byte)sum; + } + + + + public void Deserialize(ref ReadOnlySpan buffer) + { + RequestId = BinSerialize.ReadUShort(ref buffer); + Result = (AsvSdrRequestAck)BinSerialize.ReadByte(ref buffer); + + } + + public void Serialize(ref Span buffer) + { + BinSerialize.WriteUShort(ref buffer,RequestId); + BinSerialize.WriteByte(ref buffer,(byte)Result); + /* PayloadByteSize = 3 */; + } + + + + + + /// + /// Specifies the unique number of the original request. This allows the response to be matched to the correct request. + /// OriginName: request_id, Units: , IsExtended: false + /// + public ushort RequestId { get; set; } + /// + /// Result code. + /// OriginName: result, Units: , IsExtended: false + /// + public AsvSdrRequestAck Result { get; set; } + } + /// + /// Request to read ASV_SDR_CALIB_TABLE from the system/component. If success, device send ASV_SDR_CALIB_TABLE or ASV_SDR_CALIB_ACC, when error occured.[!WRAP_TO_V2_EXTENSION_PACKET!] + /// ASV_SDR_CALIB_TABLE_READ + /// + public class AsvSdrCalibTableReadPacket: PacketV2 + { + public const int PacketMessageId = 13125; + public override int MessageId => PacketMessageId; + public override byte GetCrcEtra() => 8; + public override bool WrapToV2Extension => true; + + public override AsvSdrCalibTableReadPayload Payload { get; } = new AsvSdrCalibTableReadPayload(); + + public override string Name => "ASV_SDR_CALIB_TABLE_READ"; + } + + /// + /// ASV_SDR_CALIB_TABLE_READ + /// + public class AsvSdrCalibTableReadPayload : IPayload + { + public byte GetMaxByteSize() => 6; // Sum of byte sized of all fields (include extended) + public byte GetMinByteSize() => 6; // of byte sized of fields (exclude extended) + public int GetByteSize() + { + var sum = 0; + sum+=2; //TableIndex + sum+=2; //RequestId + sum+=1; //TargetSystem + sum+=1; //TargetComponent + return (byte)sum; + } + + + + public void Deserialize(ref ReadOnlySpan buffer) + { + TableIndex = BinSerialize.ReadUShort(ref buffer); + RequestId = BinSerialize.ReadUShort(ref buffer); + TargetSystem = (byte)BinSerialize.ReadByte(ref buffer); + TargetComponent = (byte)BinSerialize.ReadByte(ref buffer); + + } + + public void Serialize(ref Span buffer) + { + BinSerialize.WriteUShort(ref buffer,TableIndex); + BinSerialize.WriteUShort(ref buffer,RequestId); + BinSerialize.WriteByte(ref buffer,(byte)TargetSystem); + BinSerialize.WriteByte(ref buffer,(byte)TargetComponent); + /* PayloadByteSize = 6 */; + } + + + + + + /// + /// Table index. + /// OriginName: table_index, Units: , IsExtended: false + /// + public ushort TableIndex { get; set; } + /// + /// Specifies the unique number of the original request. This allows the response to be matched to the correct request. + /// OriginName: request_id, Units: , IsExtended: false + /// + public ushort RequestId { get; set; } + /// + /// System ID + /// OriginName: target_system, Units: , IsExtended: false + /// + public byte TargetSystem { get; set; } + /// + /// Component ID + /// OriginName: target_component, Units: , IsExtended: false + /// + public byte TargetComponent { get; set; } + } + /// + /// Calibration table info.[!WRAP_TO_V2_EXTENSION_PACKET!] + /// ASV_SDR_CALIB_TABLE + /// + public class AsvSdrCalibTablePacket: PacketV2 + { + public const int PacketMessageId = 13126; + public override int MessageId => PacketMessageId; + public override byte GetCrcEtra() => 194; + public override bool WrapToV2Extension => true; + + public override AsvSdrCalibTablePayload Payload { get; } = new AsvSdrCalibTablePayload(); + + public override string Name => "ASV_SDR_CALIB_TABLE"; + } + + /// + /// ASV_SDR_CALIB_TABLE + /// + public class AsvSdrCalibTablePayload : IPayload + { + public byte GetMaxByteSize() => 40; // Sum of byte sized of all fields (include extended) + public byte GetMinByteSize() => 40; // of byte sized of fields (exclude extended) + public int GetByteSize() + { + var sum = 0; + sum+=8; //CreatedUnixUs + sum+=2; //TableIndex + sum+=2; //RowCount + sum+=TableName.Length; //TableName + return (byte)sum; + } + + + + public void Deserialize(ref ReadOnlySpan buffer) + { + var arraySize = 0; + var payloadSize = buffer.Length; + CreatedUnixUs = BinSerialize.ReadULong(ref buffer); + TableIndex = BinSerialize.ReadUShort(ref buffer); + RowCount = BinSerialize.ReadUShort(ref buffer); + arraySize = /*ArrayLength*/28 - Math.Max(0,((/*PayloadByteSize*/40 - payloadSize - /*ExtendedFieldsLength*/0)/1 /*FieldTypeByteSize*/)); + TableName = new char[arraySize]; + unsafe + { + fixed (byte* bytePointer = buffer) + fixed (char* charPointer = TableName) + { + Encoding.ASCII.GetChars(bytePointer, arraySize, charPointer, TableName.Length); + } + } + buffer = buffer.Slice(arraySize); + + + } + + public void Serialize(ref Span buffer) + { + BinSerialize.WriteULong(ref buffer,CreatedUnixUs); + BinSerialize.WriteUShort(ref buffer,TableIndex); + BinSerialize.WriteUShort(ref buffer,RowCount); + unsafe + { + fixed (byte* bytePointer = buffer) + fixed (char* charPointer = TableName) + { + Encoding.ASCII.GetBytes(charPointer, TableName.Length, bytePointer, TableName.Length); + } + } + buffer = buffer.Slice(TableName.Length); + + /* PayloadByteSize = 40 */; + } + + + + + + /// + /// Updated timestamp (UNIX epoch time). + /// OriginName: created_unix_us, Units: us, IsExtended: false + /// + public ulong CreatedUnixUs { get; set; } + /// + /// Table index. + /// OriginName: table_index, Units: , IsExtended: false + /// + public ushort TableIndex { get; set; } + /// + /// Specifies the number of ROWs in the table. + /// OriginName: row_count, Units: , IsExtended: false + /// + public ushort RowCount { get; set; } + /// + /// Table name, terminated by NULL if the length is less than 28 human-readable chars and WITHOUT null termination (NULL) byte if the length is exactly 28 chars - applications have to provide 28+1 bytes storage if the name is stored as string. + /// OriginName: table_name, Units: , IsExtended: false + /// + public char[] TableName { get; set; } = new char[28]; + public byte GetTableNameMaxItemsCount() => 28; + } + /// + /// Request to read ASV_SDR_CALIB_TABLE_ROW from the system/component. If success, device send ASV_SDR_CALIB_TABLE_ROW or ASV_SDR_CALIB_ACC, when error occured.[!WRAP_TO_V2_EXTENSION_PACKET!] + /// ASV_SDR_CALIB_TABLE_ROW_READ + /// + public class AsvSdrCalibTableRowReadPacket: PacketV2 + { + public const int PacketMessageId = 13127; + public override int MessageId => PacketMessageId; + public override byte GetCrcEtra() => 2; + public override bool WrapToV2Extension => true; + + public override AsvSdrCalibTableRowReadPayload Payload { get; } = new AsvSdrCalibTableRowReadPayload(); + + public override string Name => "ASV_SDR_CALIB_TABLE_ROW_READ"; + } + + /// + /// ASV_SDR_CALIB_TABLE_ROW_READ + /// + public class AsvSdrCalibTableRowReadPayload : IPayload + { + public byte GetMaxByteSize() => 8; // Sum of byte sized of all fields (include extended) + public byte GetMinByteSize() => 8; // of byte sized of fields (exclude extended) + public int GetByteSize() + { + var sum = 0; + sum+=2; //RequestId + sum+=2; //TableIndex + sum+=2; //RowIndex + sum+=1; //TargetSystem + sum+=1; //TargetComponent + return (byte)sum; + } + + + + public void Deserialize(ref ReadOnlySpan buffer) + { + RequestId = BinSerialize.ReadUShort(ref buffer); + TableIndex = BinSerialize.ReadUShort(ref buffer); + RowIndex = BinSerialize.ReadUShort(ref buffer); + TargetSystem = (byte)BinSerialize.ReadByte(ref buffer); + TargetComponent = (byte)BinSerialize.ReadByte(ref buffer); + + } + + public void Serialize(ref Span buffer) + { + BinSerialize.WriteUShort(ref buffer,RequestId); + BinSerialize.WriteUShort(ref buffer,TableIndex); + BinSerialize.WriteUShort(ref buffer,RowIndex); + BinSerialize.WriteByte(ref buffer,(byte)TargetSystem); + BinSerialize.WriteByte(ref buffer,(byte)TargetComponent); + /* PayloadByteSize = 8 */; + } + + + + + + /// + /// Specifies the unique number of the original request. This allows the response to be matched to the correct request. + /// OriginName: request_id, Units: , IsExtended: false + /// + public ushort RequestId { get; set; } + /// + /// Table index. + /// OriginName: table_index, Units: , IsExtended: false + /// + public ushort TableIndex { get; set; } + /// + /// ROW index. + /// OriginName: row_index, Units: , IsExtended: false + /// + public ushort RowIndex { get; set; } + /// + /// System ID. + /// OriginName: target_system, Units: , IsExtended: false + /// + public byte TargetSystem { get; set; } + /// + /// Component ID. + /// OriginName: target_component, Units: , IsExtended: false + /// + public byte TargetComponent { get; set; } + } + /// + /// Calibration ROW content.[!WRAP_TO_V2_EXTENSION_PACKET!] + /// ASV_SDR_CALIB_TABLE_ROW + /// + public class AsvSdrCalibTableRowPacket: PacketV2 + { + public const int PacketMessageId = 13128; + public override int MessageId => PacketMessageId; + public override byte GetCrcEtra() => 103; + public override bool WrapToV2Extension => true; + + public override AsvSdrCalibTableRowPayload Payload { get; } = new AsvSdrCalibTableRowPayload(); + + public override string Name => "ASV_SDR_CALIB_TABLE_ROW"; + } + + /// + /// ASV_SDR_CALIB_TABLE_ROW + /// + public class AsvSdrCalibTableRowPayload : IPayload + { + public byte GetMaxByteSize() => 26; // Sum of byte sized of all fields (include extended) + public byte GetMinByteSize() => 26; // of byte sized of fields (exclude extended) + public int GetByteSize() + { + var sum = 0; + sum+=8; //RefFreq + sum+=4; //RefPower + sum+=4; //RefValue + sum+=4; //MeasuredValue + sum+=2; //TableIndex + sum+=2; //RowIndex + sum+=1; //TargetSystem + sum+=1; //TargetComponent + return (byte)sum; + } + + + + public void Deserialize(ref ReadOnlySpan buffer) + { + RefFreq = BinSerialize.ReadULong(ref buffer); + RefPower = BinSerialize.ReadFloat(ref buffer); + RefValue = BinSerialize.ReadFloat(ref buffer); + MeasuredValue = BinSerialize.ReadFloat(ref buffer); + TableIndex = BinSerialize.ReadUShort(ref buffer); + RowIndex = BinSerialize.ReadUShort(ref buffer); + TargetSystem = (byte)BinSerialize.ReadByte(ref buffer); + TargetComponent = (byte)BinSerialize.ReadByte(ref buffer); + + } + + public void Serialize(ref Span buffer) + { + BinSerialize.WriteULong(ref buffer,RefFreq); + BinSerialize.WriteFloat(ref buffer,RefPower); + BinSerialize.WriteFloat(ref buffer,RefValue); + BinSerialize.WriteFloat(ref buffer,MeasuredValue); + BinSerialize.WriteUShort(ref buffer,TableIndex); + BinSerialize.WriteUShort(ref buffer,RowIndex); + BinSerialize.WriteByte(ref buffer,(byte)TargetSystem); + BinSerialize.WriteByte(ref buffer,(byte)TargetComponent); + /* PayloadByteSize = 26 */; + } + + + + + + /// + /// Reference frequency in Hz. + /// OriginName: ref_freq, Units: , IsExtended: false + /// + public ulong RefFreq { get; set; } + /// + /// Reference power in dBm. + /// OriginName: ref_power, Units: , IsExtended: false + /// + public float RefPower { get; set; } + /// + /// Reference value. + /// OriginName: ref_value, Units: , IsExtended: false + /// + public float RefValue { get; set; } + /// + /// Measured value. + /// OriginName: measured_value, Units: , IsExtended: false + /// + public float MeasuredValue { get; set; } + /// + /// Table index. + /// OriginName: table_index, Units: , IsExtended: false + /// + public ushort TableIndex { get; set; } + /// + /// ROW index. + /// OriginName: row_index, Units: , IsExtended: false + /// + public ushort RowIndex { get; set; } + /// + /// System ID. + /// OriginName: target_system, Units: , IsExtended: false + /// + public byte TargetSystem { get; set; } + /// + /// Component ID. + /// OriginName: target_component, Units: , IsExtended: false + /// + public byte TargetComponent { get; set; } + } + /// + /// Start uploading process. After that payload must send ASV_SDR_CALIB_TABLE_UPLOAD_READ_CALLBACK to client for reading table rows row_count times. Process end by payload with ASV_SDR_CALIB_ACC. [!WRAP_TO_V2_EXTENSION_PACKET!] + /// ASV_SDR_CALIB_TABLE_UPLOAD_START + /// + public class AsvSdrCalibTableUploadStartPacket: PacketV2 + { + public const int PacketMessageId = 13129; + public override int MessageId => PacketMessageId; + public override byte GetCrcEtra() => 40; + public override bool WrapToV2Extension => true; + + public override AsvSdrCalibTableUploadStartPayload Payload { get; } = new AsvSdrCalibTableUploadStartPayload(); + + public override string Name => "ASV_SDR_CALIB_TABLE_UPLOAD_START"; + } + + /// + /// ASV_SDR_CALIB_TABLE_UPLOAD_START + /// + public class AsvSdrCalibTableUploadStartPayload : IPayload + { + public byte GetMaxByteSize() => 16; // Sum of byte sized of all fields (include extended) + public byte GetMinByteSize() => 16; // of byte sized of fields (exclude extended) + public int GetByteSize() + { + var sum = 0; + sum+=8; //CreatedUnixUs + sum+=2; //TableIndex + sum+=2; //RequestId + sum+=2; //RowCount + sum+=1; //TargetSystem + sum+=1; //TargetComponent + return (byte)sum; + } + + + + public void Deserialize(ref ReadOnlySpan buffer) + { + CreatedUnixUs = BinSerialize.ReadULong(ref buffer); + TableIndex = BinSerialize.ReadUShort(ref buffer); + RequestId = BinSerialize.ReadUShort(ref buffer); + RowCount = BinSerialize.ReadUShort(ref buffer); + TargetSystem = (byte)BinSerialize.ReadByte(ref buffer); + TargetComponent = (byte)BinSerialize.ReadByte(ref buffer); + + } + + public void Serialize(ref Span buffer) + { + BinSerialize.WriteULong(ref buffer,CreatedUnixUs); + BinSerialize.WriteUShort(ref buffer,TableIndex); + BinSerialize.WriteUShort(ref buffer,RequestId); + BinSerialize.WriteUShort(ref buffer,RowCount); + BinSerialize.WriteByte(ref buffer,(byte)TargetSystem); + BinSerialize.WriteByte(ref buffer,(byte)TargetComponent); + /* PayloadByteSize = 16 */; + } + + + + + + /// + /// Current timestamp (UNIX epoch time). + /// OriginName: created_unix_us, Units: us, IsExtended: false + /// + public ulong CreatedUnixUs { get; set; } + /// + /// Table index. + /// OriginName: table_index, Units: , IsExtended: false + /// + public ushort TableIndex { get; set; } + /// + /// Specifies the unique number of the original request. This allows the response to be matched to the correct request. + /// OriginName: request_id, Units: , IsExtended: false + /// + public ushort RequestId { get; set; } + /// + /// Specifies the number of ROWs in the table. + /// OriginName: row_count, Units: , IsExtended: false + /// + public ushort RowCount { get; set; } + /// + /// System ID. + /// OriginName: target_system, Units: , IsExtended: false + /// + public byte TargetSystem { get; set; } + /// + /// Component ID. + /// OriginName: target_component, Units: , IsExtended: false + /// + public byte TargetComponent { get; set; } + } + /// + /// Read ASV_SDR_CALIB_TABLE_ROW callback from payload server to client. [!WRAP_TO_V2_EXTENSION_PACKET!] + /// ASV_SDR_CALIB_TABLE_UPLOAD_READ_CALLBACK + /// + public class AsvSdrCalibTableUploadReadCallbackPacket: PacketV2 + { + public const int PacketMessageId = 13130; + public override int MessageId => PacketMessageId; + public override byte GetCrcEtra() => 156; + public override bool WrapToV2Extension => true; + + public override AsvSdrCalibTableUploadReadCallbackPayload Payload { get; } = new AsvSdrCalibTableUploadReadCallbackPayload(); + + public override string Name => "ASV_SDR_CALIB_TABLE_UPLOAD_READ_CALLBACK"; + } + + /// + /// ASV_SDR_CALIB_TABLE_UPLOAD_READ_CALLBACK + /// + public class AsvSdrCalibTableUploadReadCallbackPayload : IPayload + { + public byte GetMaxByteSize() => 8; // Sum of byte sized of all fields (include extended) + public byte GetMinByteSize() => 8; // of byte sized of fields (exclude extended) + public int GetByteSize() + { + var sum = 0; + sum+=2; //RequestId + sum+=2; //TableIndex + sum+=2; //RowIndex + sum+=1; //TargetSystem + sum+=1; //TargetComponent + return (byte)sum; + } + + + + public void Deserialize(ref ReadOnlySpan buffer) + { + RequestId = BinSerialize.ReadUShort(ref buffer); + TableIndex = BinSerialize.ReadUShort(ref buffer); + RowIndex = BinSerialize.ReadUShort(ref buffer); + TargetSystem = (byte)BinSerialize.ReadByte(ref buffer); + TargetComponent = (byte)BinSerialize.ReadByte(ref buffer); + + } + + public void Serialize(ref Span buffer) + { + BinSerialize.WriteUShort(ref buffer,RequestId); + BinSerialize.WriteUShort(ref buffer,TableIndex); + BinSerialize.WriteUShort(ref buffer,RowIndex); + BinSerialize.WriteByte(ref buffer,(byte)TargetSystem); + BinSerialize.WriteByte(ref buffer,(byte)TargetComponent); + /* PayloadByteSize = 8 */; + } + + + + + + /// + /// Specifies the unique number of the original request from ASV_SDR_CALIB_TABLE_UPLOAD_START. This allows the response to be matched to the correct request. + /// OriginName: request_id, Units: , IsExtended: false + /// + public ushort RequestId { get; set; } + /// + /// Table index. + /// OriginName: table_index, Units: , IsExtended: false + /// + public ushort TableIndex { get; set; } + /// + /// ROW index. + /// OriginName: row_index, Units: , IsExtended: false + /// + public ushort RowIndex { get; set; } + /// + /// System ID. + /// OriginName: target_system, Units: , IsExtended: false + /// + public byte TargetSystem { get; set; } + /// + /// Component ID. + /// OriginName: target_component, Units: , IsExtended: false + /// + public byte TargetComponent { get; set; } + } + /// /// Raw signal data for visualization.[!WRAP_TO_V2_EXTENSION_PACKET!] /// ASV_SDR_SIGNAL_RAW /// public class AsvSdrSignalRawPacket: PacketV2 { - public const int PacketMessageId = 13130; + public const int PacketMessageId = 13134; public override int MessageId => PacketMessageId; public override byte GetCrcEtra() => 27; public override bool WrapToV2Extension => true;