diff --git a/.github/workflows/BuildForWindows.yml b/.github/workflows/BuildForWindows.yml
new file mode 100644
index 00000000..ea1ba210
--- /dev/null
+++ b/.github/workflows/BuildForWindows.yml
@@ -0,0 +1,91 @@
+name: Build and Release
+
+on:
+ push:
+ tags:
+ - "v*"
+jobs:
+ build-and-release:
+ runs-on: windows-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: 8.0.x
+
+ - name: Add NuGet source
+ env:
+ NUGET_USERNAME: ${{ secrets.USER_NAME }}
+ NUGET_PASSWORD: ${{ secrets.GIHUB_NUGET_AUTH_TOKEN }}
+ run: |
+ echo "" > nuget.config
+ echo "" >> nuget.config
+ echo '' >> nuget.config
+ echo '' >> nuget.config
+ echo "" >> nuget.config
+ echo "" >> nuget.config
+ echo "" >> nuget.config
+ echo '' >> nuget.config
+ echo '' >> nuget.config
+ echo "" >> nuget.config
+ echo "" >> nuget.config
+ echo "" >> nuget.config
+
+ - name: Install dependencies
+ run: |
+ dotnet restore ./src/Asv.Drones.Gui.Api/Asv.Drones.Gui.Api.csproj
+ dotnet restore ./src/Asv.Drones.Gui/Asv.Drones.Gui.csproj
+ dotnet restore ./src/Asv.Drones.Gui.Desktop/Asv.Drones.Gui.Desktop.csproj
+
+ - name: Build
+ run: |
+ dotnet build ./src/Asv.Drones.Gui.Api/Asv.Drones.Gui.Api.csproj --configuration Release --no-restore
+ dotnet build ./src/Asv.Drones.Gui/Asv.Drones.Gui.csproj --configuration Release --no-restore
+ dotnet build ./src/Asv.Drones.Gui.Desktop/Asv.Drones.Gui.Desktop.csproj --configuration Release --no-restore
+
+ - name: Set version variable
+ env:
+ TAG: ${{ github.ref_name }}
+ run: echo "VERSION=${TAG#v}" >> $GITHUB_ENV
+
+
+ # here you must define path to your .csproj
+ - name: Publish project for installer
+ run: dotnet publish ./src/Asv.Drones.Gui.Desktop/Asv.Drones.Gui.Desktop.csproj -c Release -o ./publish/app
+
+ - name: Install NSIS
+ run: |
+ choco install nsis
+
+ #here you must define path to your .nsi file (it is used for installer setup and creation)
+ - name: Create EXE installer
+ run: makensis ./win-64-install.nsi
+
+ - name: List output files
+ run: Get-ChildItem -Path ./publish/app -Force
+
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GIHUB_NUGET_AUTH_TOKEN }}
+ RELEASE_BODY: ${{ steps.create-release-notes.outputs.release-notes }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: Release ${{ github.ref }}
+ draft: false
+ prerelease: ${{ contains(github.ref, 'alpha') }}
+
+
+ - name: Upload Release Asset
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GIHUB_NUGET_AUTH_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./AsvDronesGuiInstaller.exe
+ asset_name: asv-drones-${{ github.ref_name }}-setup-windows-64.exe
+ asset_content_type: application/vnd.microsoft.portable-executable
\ No newline at end of file
diff --git a/.github/workflows/ReleaseDeployAction.yml b/.github/workflows/ReleaseDeployAction.yml
deleted file mode 100644
index 906cce48..00000000
--- a/.github/workflows/ReleaseDeployAction.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-# Name of the workflow
-name: Release
-
-# Run on every commit tag which begins with "v" (e.g., "v0.1.4")
-on:
- push:
- tags:
- - "v*"
-
-# Automatically create a GitHub Release, with release details specified (the relevant commits)
-jobs:
- release:
- name: "Release"
- runs-on: "windows-latest"
-
- steps:
- - uses: "asv-soft/action-automatic-release@updated20"
- with:
- repo_token: "${{ secrets.GIHUB_NUGET_AUTH_TOKEN }}"
- prerelease: false
- files: |
- *Setup.exe
- *.zip
diff --git a/src/.run/Win-x64.run.xml b/src/.run/Win-x64.run.xml
index 2c32d50b..699e6f6d 100644
--- a/src/.run/Win-x64.run.xml
+++ b/src/.run/Win-x64.run.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/src/Asv.Drones.Gui.Api/Asv.Drones.Gui.Api.csproj b/src/Asv.Drones.Gui.Api/Asv.Drones.Gui.Api.csproj
index cc25d583..3ca893e1 100644
--- a/src/Asv.Drones.Gui.Api/Asv.Drones.Gui.Api.csproj
+++ b/src/Asv.Drones.Gui.Api/Asv.Drones.Gui.Api.csproj
@@ -81,4 +81,9 @@
+
+
+ ..\..\..\..\Users\Havok\.nuget\packages\asv.avalonia.toolkit\0.1.7\lib\net8.0\Asv.Avalonia.Toolkit.dll
+
+
diff --git a/src/Asv.Drones.Gui.Api/CompatibilitySuppressions.xml b/src/Asv.Drones.Gui.Api/CompatibilitySuppressions.xml
index 277a2ac9..e1f16b60 100644
--- a/src/Asv.Drones.Gui.Api/CompatibilitySuppressions.xml
+++ b/src/Asv.Drones.Gui.Api/CompatibilitySuppressions.xml
@@ -1,9 +1,16 @@
+
+ CP0002
+ M:Asv.Drones.Gui.Api.IMavlinkDevicesService.GetRfsaByFullId(System.UInt16)
+ lib/net8.0/Asv.Drones.Gui.Api.dll
+ lib/net8.0/Asv.Drones.Gui.Api.dll
+ true
+
CP0006
- P:Asv.Drones.Gui.Api.ILocalizationService.Accuracy
+ M:Asv.Drones.Gui.Api.IMavlinkDevicesService.GetRfsaByFullId(System.UInt16)
lib/net8.0/Asv.Drones.Gui.Api.dll
lib/net8.0/Asv.Drones.Gui.Api.dll
true
diff --git a/src/Asv.Drones.Gui.Api/RS.Designer.cs b/src/Asv.Drones.Gui.Api/RS.Designer.cs
index 6fe140e4..4c612d55 100644
--- a/src/Asv.Drones.Gui.Api/RS.Designer.cs
+++ b/src/Asv.Drones.Gui.Api/RS.Designer.cs
@@ -86,6 +86,15 @@ public static string Anchor_Editor_Action_Paste {
}
}
+ ///
+ /// Looks up a localized string similar to Actions.
+ ///
+ public static string AnchorsEditorView_TextBlock_Actions {
+ get {
+ return ResourceManager.GetString("AnchorsEditorView_TextBlock_Actions", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Altitude.
///
diff --git a/src/Asv.Drones.Gui.Api/RS.resx b/src/Asv.Drones.Gui.Api/RS.resx
index c1d9ae68..add23c66 100644
--- a/src/Asv.Drones.Gui.Api/RS.resx
+++ b/src/Asv.Drones.Gui.Api/RS.resx
@@ -190,4 +190,7 @@
Paste
+
+ Actions
+
\ No newline at end of file
diff --git a/src/Asv.Drones.Gui.Api/RS.ru.resx b/src/Asv.Drones.Gui.Api/RS.ru.resx
index 5c30337d..4f73ccca 100644
--- a/src/Asv.Drones.Gui.Api/RS.ru.resx
+++ b/src/Asv.Drones.Gui.Api/RS.ru.resx
@@ -186,4 +186,7 @@
Вставить
+
+ Действия
+
\ No newline at end of file
diff --git a/src/Asv.Drones.Gui.Api/Services/LogService/ILogService.cs b/src/Asv.Drones.Gui.Api/Services/LogService/ILogService.cs
index e66d11ea..29aace0d 100644
--- a/src/Asv.Drones.Gui.Api/Services/LogService/ILogService.cs
+++ b/src/Asv.Drones.Gui.Api/Services/LogService/ILogService.cs
@@ -1,5 +1,16 @@
+using ReactiveUI;
+
namespace Asv.Drones.Gui.Api;
+public static class LogHelper
+{
+ public static IDisposable CatchToLog(this ReactiveCommand cmd, ILogService log, string sender)
+ {
+ return cmd.ThrownExceptions.Subscribe(ex=>log.Error(sender, ex.Message,ex));
+ }
+}
+
+
public interface ILogService
{
IObservable OnMessage { get; }
@@ -7,14 +18,19 @@ public interface ILogService
IEnumerable LoadItemsFromLogFile();
void DeleteLogFile();
+ public IDisposable CatchToLog( ReactiveCommand cmd, string sender)
+ {
+ return cmd.ThrownExceptions.Subscribe(ex=>Error(sender, ex.Message,ex));
+ }
+
public void Fatal(string sender, string message,
- Exception ex = default)
+ Exception? ex = default)
{
SaveMessage(new LogMessage(DateTime.Now, LogMessageType.Fatal, sender, message, ex?.Message));
}
public void Error(string sender, string message,
- Exception ex = default)
+ Exception? ex = default)
{
SaveMessage(new LogMessage(DateTime.Now, LogMessageType.Error, sender, message, ex?.Message));
}
diff --git a/src/Asv.Drones.Gui.Api/Services/Mavlink/IMavlinkDevicesService.cs b/src/Asv.Drones.Gui.Api/Services/Mavlink/IMavlinkDevicesService.cs
index f5a0ce84..3298fb13 100644
--- a/src/Asv.Drones.Gui.Api/Services/Mavlink/IMavlinkDevicesService.cs
+++ b/src/Asv.Drones.Gui.Api/Services/Mavlink/IMavlinkDevicesService.cs
@@ -54,12 +54,13 @@ public interface IMavlinkDevicesService
/// Id of searched vehicle
/// Vehicle object
IVehicleClient? GetVehicleByFullId(ushort id);
-
IObservable> BaseStations { get; }
IGbsClientDevice? GetGbsByFullId(ushort id);
IObservable> Payloads { get; }
ISdrClientDevice? GetPayloadsByFullId(ushort id);
IObservable> AdsbDevices { get; }
IAdsbClientDevice? GetAdsbVehicleByFullId(ushort id);
+ IObservable> RfsaDevices { get; }
+ IRfsaClientDevice? GetRfsaByFullId(ushort id);
}
}
\ No newline at end of file
diff --git a/src/Asv.Drones.Gui.Api/Services/Mavlink/MavlinkHelper.cs b/src/Asv.Drones.Gui.Api/Services/Mavlink/MavlinkHelper.cs
index 419266a1..fff284de 100644
--- a/src/Asv.Drones.Gui.Api/Services/Mavlink/MavlinkHelper.cs
+++ b/src/Asv.Drones.Gui.Api/Services/Mavlink/MavlinkHelper.cs
@@ -54,7 +54,8 @@ public static MaterialIconKind GetIcon(DeviceClass type)
DeviceClass.SdrPayload => MaterialIconKind.Radio,
DeviceClass.GbsRtk => MaterialIconKind.RouterWireless,
DeviceClass.Adsb => MaterialIconKind.Radar,
- _ => MaterialIconKind.Navigation,
+ DeviceClass.Rfsa => MaterialIconKind.Waveform,
+ _ => MaterialIconKind.HelpNetworkOutline,
};
}
}
diff --git a/src/Asv.Drones.Gui.Api/Services/Plugins/ILocalPluginInfo.cs b/src/Asv.Drones.Gui.Api/Services/Plugins/ILocalPluginInfo.cs
index a879b434..30bd2e87 100644
--- a/src/Asv.Drones.Gui.Api/Services/Plugins/ILocalPluginInfo.cs
+++ b/src/Asv.Drones.Gui.Api/Services/Plugins/ILocalPluginInfo.cs
@@ -1,3 +1,5 @@
+using Asv.Common;
+
namespace Asv.Drones.Gui.Api;
public interface ILocalPluginInfo : IPluginSpecification
@@ -21,6 +23,7 @@ public interface IPluginSearchInfo : IPluginSpecification
public interface IPluginSpecification
{
+ SemVersion ApiVersion { get; }
string PackageId { get; }
string? Title { get; }
public string? Description { get; }
diff --git a/src/Asv.Drones.Gui.Api/Services/Plugins/IPluginManager.cs b/src/Asv.Drones.Gui.Api/Services/Plugins/IPluginManager.cs
index 7dc6a6b9..a4a2f377 100644
--- a/src/Asv.Drones.Gui.Api/Services/Plugins/IPluginManager.cs
+++ b/src/Asv.Drones.Gui.Api/Services/Plugins/IPluginManager.cs
@@ -1,3 +1,4 @@
+using Asv.Common;
using DynamicData;
namespace Asv.Drones.Gui.Api;
@@ -31,6 +32,7 @@ Task Install(IPluginServerInfo source, string packageId, string version, IProgre
void CancelUninstall(ILocalPluginInfo pluginInfo);
IEnumerable Installed { get; }
bool IsInstalled(string packageId, out ILocalPluginInfo? info);
+ SemVersion ApiVersion { get; }
}
public class SearchQuery
diff --git a/src/Asv.Drones.Gui.Api/Services/Plugins/NullPluginManager.cs b/src/Asv.Drones.Gui.Api/Services/Plugins/NullPluginManager.cs
index e9ac553b..a8b3875a 100644
--- a/src/Asv.Drones.Gui.Api/Services/Plugins/NullPluginManager.cs
+++ b/src/Asv.Drones.Gui.Api/Services/Plugins/NullPluginManager.cs
@@ -1,4 +1,6 @@
-namespace Asv.Drones.Gui.Api;
+using Asv.Common;
+
+namespace Asv.Drones.Gui.Api;
public class NullPluginManager : IPluginManager
{
@@ -43,4 +45,6 @@ public bool IsInstalled(string packageId, out ILocalPluginInfo? info)
info = null;
return false;
}
+
+ public SemVersion ApiVersion { get; } = new(0);
}
\ No newline at end of file
diff --git a/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreEntryViewModel.cs b/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreEntryViewModel.cs
index 88860e84..07976470 100644
--- a/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreEntryViewModel.cs
+++ b/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreEntryViewModel.cs
@@ -78,7 +78,7 @@ public HierarchicalStoreEntryViewModel()
[Reactive] public string Description { get; set; }
- protected virtual void OnError(HierarchicalStoreEntryAction action, Exception exception)
+ protected virtual void OnError(HierarchicalStoreEntryAction action, Exception? exception)
{
}
@@ -161,7 +161,7 @@ public HierarchicalStoreEntryViewModel(Node, TKey>
ParentId = node.Item.ParentId;
}
- protected override void OnError(HierarchicalStoreEntryAction action, Exception ex)
+ protected override void OnError(HierarchicalStoreEntryAction action, Exception? ex)
{
base.OnError(action, ex);
_log.Error("Store", $"Error to '{action}' entry", ex);
diff --git a/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreView.axaml b/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreView.axaml
index 528127c4..0578b0c0 100644
--- a/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreView.axaml
+++ b/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreView.axaml
@@ -74,24 +74,27 @@
Text="{Binding SearchText}" />
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreViewModel.cs b/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreViewModel.cs
index 20b077e5..0029507f 100644
--- a/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreViewModel.cs
+++ b/src/Asv.Drones.Gui.Api/Tools/Controls/HierarchicalStore/HierarchicalStoreViewModel.cs
@@ -172,7 +172,7 @@ protected virtual void CreateNewFolderImpl()
{
}
- protected virtual void OnError(HierarchicalStoreAction action, Exception ex)
+ protected virtual void OnError(HierarchicalStoreAction action, Exception? ex)
{
}
@@ -236,7 +236,7 @@ public HierarchicalStoreViewModel(Uri id, IHierarchicalStore store,
.Subscribe(x => x?.Refresh()).DisposeItWith(Disposable);
}
- protected override void OnError(HierarchicalStoreAction action, Exception ex)
+ protected override void OnError(HierarchicalStoreAction action, Exception? ex)
{
_log.Error(DisplayName, $"Error to '{action:G}'", ex);
}
diff --git a/src/Asv.Drones.Gui.Api/Tools/Controls/Map/Actions/Ruler/MapRulerActionViewModel.cs b/src/Asv.Drones.Gui.Api/Tools/Controls/Map/Actions/Ruler/MapRulerActionViewModel.cs
index e02afc62..88c2faf7 100644
--- a/src/Asv.Drones.Gui.Api/Tools/Controls/Map/Actions/Ruler/MapRulerActionViewModel.cs
+++ b/src/Asv.Drones.Gui.Api/Tools/Controls/Map/Actions/Ruler/MapRulerActionViewModel.cs
@@ -121,7 +121,6 @@ public RulerStartAnchor(Uri id, GeoPoint startPoint) : base(id)
OffsetX = OffsetXEnum.Center;
OffsetY = OffsetYEnum.Bottom;
StrokeThickness = 1;
- BaseStrokeThickness = 1;
IconBrush = Brushes.Indigo;
Stroke = Brushes.White;
IsVisible = true;
@@ -139,8 +138,7 @@ public RulerStopAnchor(Uri id, RulerStartAnchor start, ILocalizationService loc,
BaseSize = 48;
OffsetX = OffsetXEnum.Center;
OffsetY = OffsetYEnum.Bottom;
- StrokeThickness = 1;
- BaseStrokeThickness = 1;
+ StrokeThickness = 1;
IconBrush = Brushes.Indigo;
Stroke = Brushes.White;
IsVisible = true;
diff --git a/src/Asv.Drones.Gui.Api/Tools/Controls/Map/MapPageView.axaml b/src/Asv.Drones.Gui.Api/Tools/Controls/Map/MapPageView.axaml
index 9e44dcfb..71af4022 100644
--- a/src/Asv.Drones.Gui.Api/Tools/Controls/Map/MapPageView.axaml
+++ b/src/Asv.Drones.Gui.Api/Tools/Controls/Map/MapPageView.axaml
@@ -63,7 +63,7 @@
-
+
@@ -72,7 +72,7 @@
-
+
@@ -81,8 +81,8 @@
-
+ CornerRadius="{DynamicResource ControlCornerRadius}" IsVisible="{Binding !!MapActions.Count}">
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 0,0,0,0
- 0,0,0,0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Asv.Drones.Gui.Api/Tools/Controls/Map/Widgets/AnchorEditor/AnchorsEditorViewModel.cs b/src/Asv.Drones.Gui.Api/Tools/Controls/Map/Widgets/AnchorEditor/AnchorsEditorViewModel.cs
index 8f79ea2d..c8e24c76 100644
--- a/src/Asv.Drones.Gui.Api/Tools/Controls/Map/Widgets/AnchorEditor/AnchorsEditorViewModel.cs
+++ b/src/Asv.Drones.Gui.Api/Tools/Controls/Map/Widgets/AnchorEditor/AnchorsEditorViewModel.cs
@@ -249,6 +249,7 @@ protected override void InternalAfterMapInit(IMap context)
context.WhenValueChanged(_ => _.SelectedItem)
.Subscribe(_ =>
{
+ Actions = null;
if (_prevAnchor != null && _prevAnchor.IsInEditMode)
{
UpdateLatitude(Latitude);
@@ -261,7 +262,7 @@ protected override void InternalAfterMapInit(IMap context)
if (_ != null)
{
- Actions = _.Actions;
+ if (_.Actions is not null) Actions = _.Actions.OrderByDescending(_=>_.Title.Length);
_prevAnchor = _;
IsVisible = true;
IsEditable = _.IsEditable;
diff --git a/src/Asv.Drones.Gui.Api/Tools/Controls/Params/ParamItemViewModel.cs b/src/Asv.Drones.Gui.Api/Tools/Controls/Params/ParamItemViewModel.cs
index edfd87da..54b28b74 100644
--- a/src/Asv.Drones.Gui.Api/Tools/Controls/Params/ParamItemViewModel.cs
+++ b/src/Asv.Drones.Gui.Api/Tools/Controls/Params/ParamItemViewModel.cs
@@ -193,12 +193,12 @@ public ParamItemViewModel(Uri id, IParamItem paramItem, ILogService log) : base(
.DisposeItWith(Disposable);
}
- private void OnWriteError(Exception ex)
+ private void OnWriteError(Exception? ex)
{
_log.Error("Params", $"Write {Name} error", ex);
}
- private void OnReadError(Exception ex)
+ private void OnReadError(Exception? ex)
{
_log.Error("Params", $"Read {Name} error", ex);
}
diff --git a/src/Asv.Drones.Gui.Api/Tools/Controls/Params/ParamPageViewModel.cs b/src/Asv.Drones.Gui.Api/Tools/Controls/Params/ParamPageViewModel.cs
index 752a77ee..65074ac6 100644
--- a/src/Asv.Drones.Gui.Api/Tools/Controls/Params/ParamPageViewModel.cs
+++ b/src/Asv.Drones.Gui.Api/Tools/Controls/Params/ParamPageViewModel.cs
@@ -251,7 +251,7 @@ public override async Task TryClose()
return true;
}
- private void OnRefreshError(Exception ex)
+ private void OnRefreshError(Exception? ex)
{
_log.Error("Params view", "Error to read all params items", ex);
}
diff --git a/src/Asv.Drones.Gui.Api/Tools/Controls/TreePage/TreePageExplorerViewModel.cs b/src/Asv.Drones.Gui.Api/Tools/Controls/TreePage/TreePageExplorerViewModel.cs
index 4d0ffa74..021aae92 100644
--- a/src/Asv.Drones.Gui.Api/Tools/Controls/TreePage/TreePageExplorerViewModel.cs
+++ b/src/Asv.Drones.Gui.Api/Tools/Controls/TreePage/TreePageExplorerViewModel.cs
@@ -158,7 +158,7 @@ private async Task Navigate(TreePartMenuItemContainer? menu)
menu.IsSelected = true;
return true;
}
- catch (Exception e)
+ catch (Exception? e)
{
_log.Error(Title, $"Can't create page {menu.Base.Name}:{e.Message}", e);
return false;
diff --git a/src/Asv.Drones.Gui.Api/WellKnownUri.cs b/src/Asv.Drones.Gui.Api/WellKnownUri.cs
index 8bb51d87..b1854395 100644
--- a/src/Asv.Drones.Gui.Api/WellKnownUri.cs
+++ b/src/Asv.Drones.Gui.Api/WellKnownUri.cs
@@ -76,7 +76,9 @@ public static class WellKnownUri
public const string ShellPageMapPlaningActionRuler = $"{ShellPageMapPlaningAction}.ruler";
public const string ShellPageMapPlaningActionMover = $"{ShellPageMapPlaningAction}.mover";
public const string ShellPageMapPlaningMissionBrowser = $"{ShellPageMapPlaning}.browser";
+ public const string ShellPageMapPlaningMissionSavingBrowser = $"{ShellPageMapPlaning}.browser";
public static readonly Uri ShellPageMapPlaningMissionBrowserUri = new(ShellPageMapPlaningMissionBrowser);
+ public static readonly Uri ShellPageMapPlaningMissionSavingBrowserUri = new(ShellPageMapPlaningMissionSavingBrowser);
public const string ShellPageMapPlaningWidget = $"{ShellPageMapPlaning}.widget";
public const string ShellPageMapPlaningWidgetAnchorEditor = $"{ShellPageMapPlaningWidget}.editor";
public const string ShellPageMapPlaningWidgetEditor = $"{ShellPageMapPlaningWidget}.mission-editor";
diff --git a/src/Asv.Drones.Gui/Asv.Drones.Gui.csproj b/src/Asv.Drones.Gui/Asv.Drones.Gui.csproj
index bb03e1c4..7f98bd5d 100644
--- a/src/Asv.Drones.Gui/Asv.Drones.Gui.csproj
+++ b/src/Asv.Drones.Gui/Asv.Drones.Gui.csproj
@@ -26,7 +26,6 @@
-
@@ -53,14 +52,15 @@
PublicResXFileCodeGenerator
RS.Designer.cs
-
-
-
True
True
RS.resx
+
+
+
+
MainWindow.axaml
Code
@@ -93,6 +93,14 @@
ArduCopterQuickParamStandardTreePageView.axaml
Code
+
+ PlaningMissionSavingBrowserView.axaml
+ Code
+
+
+ PlaningMissionSavingBrowserView.axaml
+ Code
+
@@ -103,4 +111,22 @@
+
+
+
+ Never
+
+
+
+
+
+ ..\..\..\..\Users\Havok\.nuget\packages\asv.avalonia.toolkit\0.1.7\lib\net8.0\Asv.Avalonia.Toolkit.dll
+
+
+
+
+
+ ..\..\..\..\Users\Havok\.nuget\packages\asv.avalonia.toolkit\0.1.7\lib\net8.0\Asv.Avalonia.Toolkit.dll
+
+
diff --git a/src/Asv.Drones.Gui/RS.Designer.cs b/src/Asv.Drones.Gui/RS.Designer.cs
index 67dc91a5..a4b02864 100644
--- a/src/Asv.Drones.Gui/RS.Designer.cs
+++ b/src/Asv.Drones.Gui/RS.Designer.cs
@@ -1490,6 +1490,15 @@ public static string HeaderPlaningFileOpenMenuItem_Title {
}
}
+ ///
+ /// Looks up a localized string similar to Save As....
+ ///
+ public static string HeaderPlaningFileSaveAsMenuItem_Title {
+ get {
+ return ResourceManager.GetString("HeaderPlaningFileSaveAsMenuItem_Title", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Save.
///
@@ -3434,6 +3443,61 @@ public static string PlanningPageViewModel_MissionDownloaded {
}
}
+ ///
+ /// Looks up a localized string similar to A file with the same name already exists. After performing this action, overwriting will occur. Are you sure you want to continue?.
+ ///
+ public static string PlanningPageViewModel_MissionSavingBrowserDialog_ConfirmationText_ContentText {
+ get {
+ return ResourceManager.GetString("PlanningPageViewModel_MissionSavingBrowserDialog_ConfirmationText_ContentText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Rewrite.
+ ///
+ public static string PlanningPageViewModel_MissionSavingBrowserDialog_ConfirmationText_PrimaryButtonText {
+ get {
+ return ResourceManager.GetString("PlanningPageViewModel_MissionSavingBrowserDialog_ConfirmationText_PrimaryButtonTe" +
+ "xt", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Confirmation.
+ ///
+ public static string PlanningPageViewModel_MissionSavingBrowserDialog_ConfirmationText_Title {
+ get {
+ return ResourceManager.GetString("PlanningPageViewModel_MissionSavingBrowserDialog_ConfirmationText_Title", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Save.
+ ///
+ public static string PlanningPageViewModel_MissionSavingBrowserDialog_PrimaryButton {
+ get {
+ return ResourceManager.GetString("PlanningPageViewModel_MissionSavingBrowserDialog_PrimaryButton", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cancel.
+ ///
+ public static string PlanningPageViewModel_MissionSavingBrowserDialog_SecondaryButton {
+ get {
+ return ResourceManager.GetString("PlanningPageViewModel_MissionSavingBrowserDialog_SecondaryButton", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Chose the file name to save.
+ ///
+ public static string PlanningPageViewModel_MissionSavingBrowserDialog_Title {
+ get {
+ return ResourceManager.GetString("PlanningPageViewModel_MissionSavingBrowserDialog_Title", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Mission uploaded!.
///
@@ -3957,7 +4021,7 @@ public static string RebootAutopilotAnchorActionViewModel_LogMessage {
}
///
- /// Looks up a localized string similar to Reboot/Shutdown Autopilot.
+ /// Looks up a localized string similar to Reboot/Shutdown.
///
public static string RebootAutopilotAnchorActionViewModel_Title {
get {
diff --git a/src/Asv.Drones.Gui/RS.resx b/src/Asv.Drones.Gui/RS.resx
index 7434c1e9..605ffead 100644
--- a/src/Asv.Drones.Gui/RS.resx
+++ b/src/Asv.Drones.Gui/RS.resx
@@ -1190,8 +1190,8 @@
Choose companion reboot/shutdown mode
-
- Reboot/Shutdown Autopilot
+
+ Reboot/Shutdown
Ok
@@ -1847,4 +1847,25 @@
Accuracy
+
+ Save As...
+
+
+ Save
+
+
+ Cancel
+
+
+ Chose the file name to save
+
+
+ Confirmation
+
+
+ Rewrite
+
+
+ A file with the same name already exists. After performing this action, overwriting will occur. Are you sure you want to continue?
+
\ No newline at end of file
diff --git a/src/Asv.Drones.Gui/RS.ru.resx b/src/Asv.Drones.Gui/RS.ru.resx
index 7d9e158f..a2588ed6 100644
--- a/src/Asv.Drones.Gui/RS.ru.resx
+++ b/src/Asv.Drones.Gui/RS.ru.resx
@@ -1219,8 +1219,8 @@
Выберите режим перезапуска/отключения бортового компьютера
-
- Перезагрузить/Выключить автопилот
+
+ Перезагрузить/Выключить
Ок
@@ -1231,8 +1231,8 @@
Пользователь отправил комманду перезагрузки автопилота с параметрами {0} и {1}
-
- Выбор режима БПЛА
+
+ Режимы БПЛА
Ок
@@ -1393,8 +1393,8 @@
Настройки
-
- Немедленно приземлиться
+
+ Приземлиться
Перейти в точку
@@ -1408,8 +1408,8 @@
Пользователь отправил команду приземлиться для {0}
-
- Вернуться в точку запуска
+
+ Вернуться домой
Пользователь отправил команду вернуться в точку запуска для {0}
@@ -1811,7 +1811,7 @@
мин
- Вернуться в точку запуска
+ Вернуться домой
Начать миссию
@@ -1879,4 +1879,25 @@
Точность
+
+ Сохранить как...
+
+
+ Сохранить
+
+
+ Отменить
+
+
+ Выберите имя файла для сохранения
+
+
+ Подтверждение
+
+
+ Перезаписать
+
+
+ Файл с таким именем уже существует. После выполнения данного действия произойдёт перезапись. Вы уверены, что хотите продолжить?
+
\ No newline at end of file
diff --git a/src/Asv.Drones.Gui/Services/LogService/LogService.cs b/src/Asv.Drones.Gui/Services/LogService/LogService.cs
index b14eb9fe..a99f37da 100644
--- a/src/Asv.Drones.Gui/Services/LogService/LogService.cs
+++ b/src/Asv.Drones.Gui/Services/LogService/LogService.cs
@@ -39,9 +39,15 @@ public LogService(string logsFolder)
Layout.FromString("${longdate}|${threadid}|${level}|${logger}|${message} ${exception:format=tostring}");
#if DEBUG
builder.ForLogger().FilterMinLevel(LogLevel.Trace).WriteToDebug(layout: layout);
+ builder.ForLogger().FilterLevels(LogLevel.Trace, LogLevel.Fatal).WriteToFile(
+ fileName: _logFile,
+ lineEnding: LineEndingMode.CRLF,
+ maxArchiveDays: 30,
+ maxArchiveFiles: 30,
+ layout: layout);
#endif
builder.ForLogger().FilterMinLevel(LogLevel.Trace).WriteToColoredConsole(layout: layout);
- builder.ForLogger().FilterMinLevel(LogLevel.Trace).WriteToFile(
+ builder.ForLogger().FilterLevels(LogLevel.Trace, LogLevel.Fatal).WriteToFile(
fileName: _logFile,
lineEnding: LineEndingMode.CRLF,
maxArchiveDays: 30,
@@ -71,6 +77,7 @@ public LogService(string logsFolder)
public void SaveMessage(LogMessage logMessage)
{
_onMessage.OnNext(logMessage);
+ Logger.Log(LogLevel.FromString(logMessage.Type.ToString()), logMessage.Message );
}
public void DeleteLogFile()
diff --git a/src/Asv.Drones.Gui/Services/Mavlink/MavlinkDevicesService.cs b/src/Asv.Drones.Gui/Services/Mavlink/MavlinkDevicesService.cs
index 16220dcd..302368bf 100644
--- a/src/Asv.Drones.Gui/Services/Mavlink/MavlinkDevicesService.cs
+++ b/src/Asv.Drones.Gui/Services/Mavlink/MavlinkDevicesService.cs
@@ -33,6 +33,7 @@ public class MavlinkDeviceServiceConfig
public SdrClientDeviceConfig Sdr { get; set; } = new();
public AdsbClientDeviceConfig Adsb { get; set; } = new();
public bool WrapToV2ExtensionEnabled { get; set; } = true;
+ public RfsaClientDeviceConfig Rfsa { get; set; } = new();
}
[Export(typeof(IMavlinkDevicesService))]
@@ -61,8 +62,8 @@ public MavlinkDevicesService(IConfiguration config, IPacketSequenceCalculator se
_mavlinkRouter =
new MavlinkRouter(MavlinkV2Connection.RegisterDefaultDialects,
publishScheduler: RxApp.MainThreadScheduler).DisposeItWith(Disposable);
- _mavlinkRouter.WrapToV2ExtensionEnabled = InternalGetConfig(_ => _.WrapToV2ExtensionEnabled);
- foreach (var port in InternalGetConfig(_ => _.Ports))
+ _mavlinkRouter.WrapToV2ExtensionEnabled = InternalGetConfig(s => s.WrapToV2ExtensionEnabled);
+ foreach (var port in InternalGetConfig(s => s.Ports))
{
_mavlinkRouter.AddPort(port);
}
@@ -76,20 +77,20 @@ public MavlinkDevicesService(IConfiguration config, IPacketSequenceCalculator se
#region InitUriHost mavlink heartbeat
- var serverIdentity = InternalGetConfig(_ => new MavlinkIdentity(_.SystemId, _.ComponentId));
- var serverConfig = InternalGetConfig(_ => new ServerDeviceConfig
- { Heartbeat = new MavlinkHeartbeatServerConfig { HeartbeatRateMs = _.HeartbeatRateMs } });
+ var serverIdentity = InternalGetConfig(s => new MavlinkIdentity(s.SystemId, s.ComponentId));
+ var serverConfig = InternalGetConfig(s => new ServerDeviceConfig
+ { Heartbeat = new MavlinkHeartbeatServerConfig { HeartbeatRateMs = s.HeartbeatRateMs } });
var serverDevice =
new ServerDevice(Router, sequenceCalculator, serverIdentity, serverConfig, Scheduler.Default)
.DisposeItWith(Disposable);
- serverDevice.Heartbeat.Set(_ =>
+ serverDevice.Heartbeat.Set(p =>
{
- _.Autopilot = MavAutopilot.MavAutopilotInvalid;
- _.BaseMode = 0;
- _.CustomMode = 0;
- _.MavlinkVersion = 3;
- _.SystemStatus = MavState.MavStateActive;
- _.Type = MavType.MavTypeGcs;
+ p.Autopilot = MavAutopilot.MavAutopilotInvalid;
+ p.BaseMode = 0;
+ p.CustomMode = 0;
+ p.MavlinkVersion = 3;
+ p.SystemStatus = MavState.MavStateActive;
+ p.Type = MavType.MavTypeGcs;
});
_systemId = new RxValue(serverIdentity.SystemId).DisposeItWith(Disposable);
_systemId
@@ -97,7 +98,7 @@ public MavlinkDevicesService(IConfiguration config, IPacketSequenceCalculator se
.DistinctUntilChanged()
.Skip(1)
.Do(_ => _needReloadToApplyConfig.Value = true)
- .Subscribe(_ => InternalSaveConfig(cfg => cfg.SystemId = _))
+ .Subscribe(b => InternalSaveConfig(cfg => cfg.SystemId = b))
.DisposeItWith(Disposable);
_componentId = new RxValue(serverIdentity.ComponentId).DisposeItWith(Disposable);
_componentId
@@ -105,16 +106,16 @@ public MavlinkDevicesService(IConfiguration config, IPacketSequenceCalculator se
.DistinctUntilChanged()
.Skip(1)
.Do(_ => _needReloadToApplyConfig.Value = true)
- .Subscribe(_ => InternalSaveConfig(cfg => cfg.ComponentId = _))
+ .Subscribe(b => InternalSaveConfig(cfg => cfg.ComponentId = b))
.DisposeItWith(Disposable);
- var heartbeatRateMs = InternalGetConfig(_ => TimeSpan.FromMilliseconds(_.HeartbeatRateMs));
+ var heartbeatRateMs = InternalGetConfig(s => TimeSpan.FromMilliseconds(s.HeartbeatRateMs));
_heartBeatRate = new RxValue(heartbeatRateMs).DisposeItWith(Disposable);
_heartBeatRate
.Throttle(TimeSpan.FromSeconds(1))
.DistinctUntilChanged()
.Skip(1)
.Do(_ => _needReloadToApplyConfig.Value = true)
- .Subscribe(_ => { InternalSaveConfig(cfg => cfg.HeartbeatRateMs = (int)_.TotalMilliseconds); })
+ .Subscribe(s => { InternalSaveConfig(cfg => cfg.HeartbeatRateMs = (int)s.TotalMilliseconds); })
.DisposeItWith(Disposable);
serverDevice.Heartbeat.Start();
@@ -122,14 +123,14 @@ public MavlinkDevicesService(IConfiguration config, IPacketSequenceCalculator se
#region Mavlink devices
- var deviceTimeout = InternalGetConfig(_ => TimeSpan.FromMilliseconds(_.DeviceHeartbeatTimeoutMs));
+ var deviceTimeout = InternalGetConfig(s => TimeSpan.FromMilliseconds(s.DeviceHeartbeatTimeoutMs));
_deviceBrowser = new MavlinkDeviceBrowser(_mavlinkRouter, deviceTimeout, RxApp.MainThreadScheduler)
.DisposeItWith(Disposable);
_deviceBrowser.DeviceTimeout
.Throttle(TimeSpan.FromSeconds(1))
.DistinctUntilChanged()
.Skip(1)
- .Subscribe(_ => InternalSaveConfig(cfg => cfg.DeviceHeartbeatTimeoutMs = (int)_.TotalMilliseconds))
+ .Subscribe(s => InternalSaveConfig(cfg => cfg.DeviceHeartbeatTimeoutMs = (int)s.TotalMilliseconds))
.DisposeItWith(Disposable);
#endregion
@@ -138,29 +139,35 @@ public MavlinkDevicesService(IConfiguration config, IPacketSequenceCalculator se
Vehicles = Devices
.Transform(CreateVehicle)
- .Filter(_ => _ != null)
+ .Filter(c => c != null)
.DisposeMany()
.RefCount();
BaseStations = Devices
- .Filter(_ => _.Type == (MavType)Mavlink.V2.AsvGbs.MavType.MavTypeAsvGbs)
+ .Filter(d => d.Type == (MavType)Mavlink.V2.AsvGbs.MavType.MavTypeAsvGbs)
.Transform(CreateBaseStation)
.DisposeMany()
.RefCount();
Payloads = Devices
- .Filter(_ => _.Type == (MavType)Mavlink.V2.AsvSdr.MavType.MavTypeAsvSdrPayload)
+ .Filter(d => d.Type == (MavType)Mavlink.V2.AsvSdr.MavType.MavTypeAsvSdrPayload)
.Transform(CreateSdrDevice)
.DisposeMany()
.RefCount();
AdsbDevices = Devices
- .Filter(_ => _.Type == MavType.MavTypeAdsb)
+ .Filter(d => d.Type == MavType.MavTypeAdsb)
.Transform(CreateAdsbDevice)
.DisposeMany()
.RefCount();
+ RfsaDevices = Devices
+ .Filter(d => d.Type == (MavType)Mavlink.V2.AsvRfsa.MavType.MavTypeAsvRfsa)
+ .Transform(CreateRfsaDevice)
+ .DisposeMany()
+ .RefCount();
AllDevices = Vehicles.Transform(x => (IClientDevice)x)
.MergeChangeSets(BaseStations.Transform(x => (IClientDevice)x))
.MergeChangeSets(Payloads.Transform(x => (IClientDevice)x))
+ .MergeChangeSets(RfsaDevices.Transform(x => (IClientDevice)x))
.MergeChangeSets(AdsbDevices.Transform(x => (IClientDevice)x));
#endregion
@@ -171,9 +178,9 @@ public MavlinkDevicesService(IConfiguration config, IPacketSequenceCalculator se
.Merge(BaseStations.Transform(x => (IClientDevice)x))
.Merge(Payloads.Transform(x => (IClientDevice)x))
.Merge(AdsbDevices.Transform(x => (IClientDevice)x))
- .AutoRefreshOnObservable(_ => _.Name)
- .Filter(_ => _.Name.Value != null)
- .Transform(_ => _.Name.Value, true)
+ .AutoRefreshOnObservable(d => d.Name)
+ .Filter(d => d.Name.Value != null)
+ .Transform(d => d.Name.Value, true)
.AsObservableCache().DisposeItWith(Disposable);
_mavlinkRouter
.Filter()
@@ -184,6 +191,17 @@ public MavlinkDevicesService(IConfiguration config, IPacketSequenceCalculator se
#endregion
}
+ private IRfsaClientDevice CreateRfsaDevice(IMavlinkDevice device)
+ {
+ return new RfsaClientDevice(Router, new MavlinkClientIdentity
+ {
+ TargetSystemId = device.SystemId,
+ TargetComponentId = device.ComponentId,
+ SystemId = _systemId.Value,
+ ComponentId = _componentId.Value,
+ }, InternalGetConfig(c => c.Rfsa), _sequenceCalculator, RxApp.MainThreadScheduler);
+ }
+
private ISdrClientDevice CreateSdrDevice(IMavlinkDevice device)
{
var dev = new SdrClientDevice(Router, new MavlinkClientIdentity
@@ -192,7 +210,7 @@ private ISdrClientDevice CreateSdrDevice(IMavlinkDevice device)
TargetComponentId = device.ComponentId,
SystemId = _systemId.Value,
ComponentId = _componentId.Value,
- }, InternalGetConfig(_ => _.Sdr), _sequenceCalculator, RxApp.MainThreadScheduler);
+ }, InternalGetConfig(c => c.Sdr), _sequenceCalculator, RxApp.MainThreadScheduler);
((ParamsClientEx)dev.Params).Init(new MavParamByteWiseEncoding(), ArraySegment.Empty);
return dev;
}
@@ -205,7 +223,7 @@ private IGbsClientDevice CreateBaseStation(IMavlinkDevice device)
TargetComponentId = device.ComponentId,
SystemId = _systemId.Value,
ComponentId = _componentId.Value,
- }, _sequenceCalculator, InternalGetConfig(_ => _.Gbs));
+ }, _sequenceCalculator, InternalGetConfig(c => c.Gbs));
}
private IAdsbClientDevice CreateAdsbDevice(IMavlinkDevice device)
@@ -216,7 +234,7 @@ private IAdsbClientDevice CreateAdsbDevice(IMavlinkDevice device)
TargetComponentId = device.ComponentId,
SystemId = _systemId.Value,
ComponentId = _componentId.Value,
- }, _sequenceCalculator, InternalGetConfig(_ => _.Adsb));
+ }, _sequenceCalculator, InternalGetConfig(c => c.Adsb));
}
#region Logs
@@ -271,37 +289,44 @@ private string TryGetName(StatustextPacket pkt)
public IVehicleClient? GetVehicleByFullId(ushort id)
{
- using var a = Vehicles.BindToObservableList(out var list).Subscribe();
- return list.Items.FirstOrDefault(_ => _.Heartbeat.FullId == id);
+ using var autoDispose = Vehicles.BindToObservableList(out var list).Subscribe();
+ return list.Items.FirstOrDefault(c => c.Heartbeat.FullId == id);
}
public IObservable> BaseStations { get; }
public IGbsClientDevice? GetGbsByFullId(ushort id)
{
- BaseStations.BindToObservableList(out var list).Subscribe();
- return list.Items.FirstOrDefault(_ => _.Heartbeat.FullId == id);
+ using var autoDispose = BaseStations.BindToObservableList(out var list).Subscribe();
+ return list.Items.FirstOrDefault(d => d.Heartbeat.FullId == id);
}
public IObservable> Payloads { get; }
public ISdrClientDevice? GetPayloadsByFullId(ushort id)
{
- using var a = Payloads.BindToObservableList(out var list).Subscribe();
- return list.Items.FirstOrDefault(_ => _.Heartbeat.FullId == id);
+ using var autoDispose = Payloads.BindToObservableList(out var list).Subscribe();
+ return list.Items.FirstOrDefault(d => d.Heartbeat.FullId == id);
}
public IObservable> AdsbDevices { get; }
public IAdsbClientDevice? GetAdsbVehicleByFullId(ushort id)
{
- AdsbDevices.BindToObservableList(out var list).Subscribe();
- return list.Items.FirstOrDefault(_ => _.FullId == id);
+ using var autoDispose =AdsbDevices.BindToObservableList(out var list).Subscribe();
+ return list.Items.FirstOrDefault(d => d.FullId == id);
+ }
+
+ public IObservable> RfsaDevices { get; }
+ public IRfsaClientDevice? GetRfsaByFullId(ushort id)
+ {
+ using var autoDispose = RfsaDevices.BindToObservableList(out var list).Subscribe();
+ return list.Items.FirstOrDefault(d => d.FullId == id);
}
private IVehicleClient? CreateVehicle(IMavlinkDevice device)
{
- if (device.Autopilot == MavAutopilot.MavAutopilotArdupilotmega)
+ //if (device.Autopilot == MavAutopilot.MavAutopilotArdupilotmega)
{
switch (device.Type)
{
@@ -314,7 +339,7 @@ private string TryGetName(StatustextPacket pkt)
TargetComponentId = device.ComponentId,
SystemId = _systemId.Value,
ComponentId = _componentId.Value,
- }, InternalGetConfig(_ => _.Vehicle), _sequenceCalculator, RxApp.TaskpoolScheduler);
+ }, InternalGetConfig(c => c.Vehicle), _sequenceCalculator, RxApp.TaskpoolScheduler);
case MavType.MavTypeFixedWing:
return new ArduPlaneClient(Router, new MavlinkClientIdentity
{
@@ -322,7 +347,7 @@ private string TryGetName(StatustextPacket pkt)
TargetComponentId = device.ComponentId,
SystemId = _systemId.Value,
ComponentId = _componentId.Value,
- }, InternalGetConfig(_ => _.Vehicle), _sequenceCalculator, RxApp.TaskpoolScheduler);
+ }, InternalGetConfig(c => c.Vehicle), _sequenceCalculator, RxApp.TaskpoolScheduler);
default:
return null;
}
diff --git a/src/Asv.Drones.Gui/Services/Plugins/LocalPluginInfo.cs b/src/Asv.Drones.Gui/Services/Plugins/LocalPluginInfo.cs
index 0919867f..3559ac48 100644
--- a/src/Asv.Drones.Gui/Services/Plugins/LocalPluginInfo.cs
+++ b/src/Asv.Drones.Gui/Services/Plugins/LocalPluginInfo.cs
@@ -1,4 +1,6 @@
using System;
+using System.Linq;
+using Asv.Common;
using Asv.Drones.Gui.Api;
using NuGet.Packaging;
@@ -22,9 +24,17 @@ public LocalPluginInfo(PackageArchiveReader reader, string pluginFolder, PluginS
IsUninstalled = state.IsUninstalled;
IsLoaded = state.IsLoaded;
LoadingError = state.LoadingError;
+ var apiPackage = reader.GetPackageDependencies()
+ .SelectMany(x => x.Packages).FirstOrDefault(x => x.Id == NugetHelper.PluginApiPackageName);
+ if (apiPackage == null)
+ {
+ throw new Exception($"Plugin {Id} does not contain API package as dependency");
+ }
+ ApiVersion = apiPackage.VersionRange.MinVersion?.ToNormalizedString() ?? throw new InvalidOperationException("Api version not found in plugin dependencies");
}
public string? SourceUri { get; }
+ public SemVersion ApiVersion { get; }
public string PackageId { get; }
public string LocalFolder { get; }
public string Title { get; }
diff --git a/src/Asv.Drones.Gui/Services/Plugins/NugetHelper.cs b/src/Asv.Drones.Gui/Services/Plugins/NugetHelper.cs
index af7fc229..ae11d533 100644
--- a/src/Asv.Drones.Gui/Services/Plugins/NugetHelper.cs
+++ b/src/Asv.Drones.Gui/Services/Plugins/NugetHelper.cs
@@ -17,6 +17,7 @@ namespace Asv.Drones.Gui;
public static class NugetHelper
{
+ public const string PluginApiPackageName = "Asv.Drones.Gui.Api";
public const string NugetPluginName = "Asv.Drones.Gui.Plugin";
public const string NETCoreAppGroup = ".NETCoreApp";
diff --git a/src/Asv.Drones.Gui/Services/Plugins/PluginManager.cs b/src/Asv.Drones.Gui/Services/Plugins/PluginManager.cs
index 8a88fac6..e47745a9 100644
--- a/src/Asv.Drones.Gui/Services/Plugins/PluginManager.cs
+++ b/src/Asv.Drones.Gui/Services/Plugins/PluginManager.cs
@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Reflection;
using System.Runtime.Loader;
using System.Threading;
using System.Threading.Tasks;
@@ -61,21 +62,23 @@ public class PluginManager : ServiceWithConfigBase, IPlugin
private readonly List _repositories = new();
public const string PluginSearchTermStartWith = "Asv.Drones.Gui.Plugin.";
+
private const string PluginStateFileName = "__PLUGIN_STATE__";
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly string _sharedPluginFolder;
private readonly string _nugetFolder;
- private readonly string _nugetCache;
private readonly LoggerAdapter _nugetLogger = new(Logger);
private readonly SourceCacheContext _cache;
- private List _pluginContexts = new();
+ private readonly List _pluginContexts = new();
public PluginManager(ContainerConfiguration containerCfg, string localDirectory, IConfiguration configuration) :
base(configuration)
{
+ ApiVersion = SemVersion.Parse(typeof(WellKnownUri).Assembly.GetCustomAttribute()!.Version);
+
#region Servers
var servers = InternalGetConfig(x => x.Servers);
@@ -146,15 +149,15 @@ public PluginManager(ContainerConfiguration containerCfg, string localDirectory,
Logger.Info("Found nuget cache folder {0}", _nugetFolder);
}
- _nugetCache = Path.Combine(localDirectory, "nuget_cache");
- if (Directory.Exists(_nugetCache) == false)
+ var nugetCache = Path.Combine(localDirectory, "nuget_cache");
+ if (Directory.Exists(nugetCache) == false)
{
- Logger.Info("Create nuget folder {0}", _nugetCache);
- Directory.CreateDirectory(_nugetCache);
+ Logger.Info("Create nuget folder {0}", nugetCache);
+ Directory.CreateDirectory(nugetCache);
}
else
{
- Logger.Info("Found nuget cache folder {0}", _nugetCache);
+ Logger.Info("Found nuget cache folder {0}", nugetCache);
}
#endregion
@@ -168,7 +171,7 @@ public PluginManager(ContainerConfiguration containerCfg, string localDirectory,
MaxAge = DateTimeOffset.MaxValue*/
};
-
+ // load all plugins
foreach (var dir in Directory.EnumerateDirectories(_sharedPluginFolder, "*", SearchOption.TopDirectoryOnly))
{
if (TryGetLocalPluginInfoByFolder(dir, out var info) == false)
@@ -181,14 +184,29 @@ public PluginManager(ContainerConfiguration containerCfg, string localDirectory,
if (info == null) continue;
if (info.IsUninstalled)
{
- Logger.Info("Remove plugin {0} {1} {2}", info.PackageId, info.Version, info.LocalFolder);
+ Logger.Info("Remove deleted plugin {0} {1} {2}", info.PackageId, info.Version, info.LocalFolder);
Directory.Delete(info.LocalFolder, true);
continue;
}
+ // check API version
+ if (info.ApiVersion.CompareByPrecedence(ApiVersion) != 0)
+ {
+ Logger.Warn("Plugin {0} {1} has different API version {2} than application {3}", info.Id, info.Version,
+ info.ApiVersion, ApiVersion);
+ SetPluginStateByFolder(info.LocalFolder, x =>
+ {
+ x.IsLoaded = false;
+ x.LoadingError = $"Plugin has different API version {info.ApiVersion} than application {ApiVersion}";
+ });
+ continue;
+ }
+
+
try
{
+
Logger.Info("Load plugin {0} {1} {2}", info.PackageId, info.Version, info.LocalFolder);
_pluginContexts.Add(new PluginAssemblyLoadContext(info.LocalFolder, containerCfg));
SetPluginStateByFolder(info.LocalFolder, x =>
@@ -202,13 +220,15 @@ public PluginManager(ContainerConfiguration containerCfg, string localDirectory,
Logger.Error(e, "Error load plugin {0} {1} {2}", info.PackageId, info.Version, info.LocalFolder);
SetPluginStateByFolder(info.LocalFolder, x =>
{
- x.IsLoaded = true;
+ x.IsLoaded = false;
x.LoadingError = e.Message;
});
}
}
}
+ public SemVersion ApiVersion { get; }
+
#region Servers
public IReadOnlyList Servers
@@ -355,9 +375,23 @@ public async Task> Search(SearchQuery query, Ca
: PluginSearchTermStartWith + query.Name;
var packages =
await resource.SearchAsync(searchTerm, filter, query.Skip, query.Take, _nugetLogger, cancel);
+
foreach (var package in packages)
{
- result.Add(new PluginSearchInfo(package, repository));
+ try
+ {
+ var dependencyInfoResource = await repository.GetResourceAsync(cancel);
+ var dependencyInfo = await dependencyInfoResource.ResolvePackage(package.Identity, NugetHelper.DefaultFramework,
+ _cache, _nugetLogger, cancel);
+ if (dependencyInfo == null)
+ continue;
+ result.Add(new PluginSearchInfo(package, repository,dependencyInfo));
+ }
+ catch (Exception e)
+ {
+ Logger.Warn("Error create plugin search info from {0} {1}", package.Identity.Id, e.Message);
+ continue;
+ }
if (result.Count >= query.Take) break;
}
diff --git a/src/Asv.Drones.Gui/Services/Plugins/PluginSearchInfo.cs b/src/Asv.Drones.Gui/Services/Plugins/PluginSearchInfo.cs
index 6b0ceb80..f421bb1c 100644
--- a/src/Asv.Drones.Gui/Services/Plugins/PluginSearchInfo.cs
+++ b/src/Asv.Drones.Gui/Services/Plugins/PluginSearchInfo.cs
@@ -1,3 +1,6 @@
+using System;
+using System.Linq;
+using Asv.Common;
using Asv.Drones.Gui.Api;
using NuGet.Protocol.Core.Types;
@@ -5,7 +8,8 @@ namespace Asv.Drones.Gui;
internal class PluginSearchInfo : IPluginSearchInfo
{
- public PluginSearchInfo(IPackageSearchMetadata packageSearchMetadata, SourceRepository repository)
+ public PluginSearchInfo(IPackageSearchMetadata packageSearchMetadata, SourceRepository repository,
+ SourcePackageDependencyInfo dependencyInfo)
{
Authors = packageSearchMetadata.Authors;
Title = packageSearchMetadata.Identity.Id.Replace(PluginManager.PluginSearchTermStartWith, string.Empty);
@@ -15,9 +19,17 @@ public PluginSearchInfo(IPackageSearchMetadata packageSearchMetadata, SourceRepo
Description = packageSearchMetadata.Description;
Tags = packageSearchMetadata.Tags;
DownloadCount = packageSearchMetadata.DownloadCount;
+
+ var apiPackage = dependencyInfo.Dependencies.FirstOrDefault(x => x.Id == NugetHelper.PluginApiPackageName);
+ if (apiPackage == null)
+ {
+ throw new Exception($"Plugin {packageSearchMetadata.Identity.Id} does not contain API package as dependency");
+ }
+ ApiVersion = apiPackage.VersionRange.MinVersion?.ToNormalizedString() ?? throw new InvalidOperationException("Api version not found in plugin dependencies");
}
public IPluginServerInfo Source { get; }
+ public SemVersion ApiVersion { get; }
public string PackageId { get; }
public string? Title { get; }
public string? Authors { get; }
@@ -26,22 +38,3 @@ public PluginSearchInfo(IPackageSearchMetadata packageSearchMetadata, SourceRepo
public long? DownloadCount { get; }
public string? Tags { get; }
}
-
-public class DesginTimePluginSearchInfo : IPluginSearchInfo
-{
- public IPluginServerInfo Source { get; } = new DesingTimeSourceInfo();
- public string PackageId { get; set; }
- public string? Title { get; set; }
- public string? Authors { get; set; }
- public string LastVersion { get; set; }
- public string Description { get; set; }
- public long? DownloadCount { get; set; }
- public string? Tags { get; set; }
-}
-
-public class DesingTimeSourceInfo : IPluginServerInfo
-{
- public string SourceUri { get; set; }
- public string Name { get; set; }
- public string? Username { get; set; }
-}
\ No newline at end of file
diff --git a/src/Asv.Drones.Gui/Services/Plugins/PluginWellKnownConst.cs b/src/Asv.Drones.Gui/Services/Plugins/PluginWellKnownConst.cs
new file mode 100644
index 00000000..b4a1a71e
--- /dev/null
+++ b/src/Asv.Drones.Gui/Services/Plugins/PluginWellKnownConst.cs
@@ -0,0 +1,2 @@
+namespace Asv.Drones.Gui;
+
diff --git a/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Adsb/AdsbAnchor.cs b/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Adsb/AdsbAnchor.cs
index 9801f9d0..9a1d2283 100644
--- a/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Adsb/AdsbAnchor.cs
+++ b/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Adsb/AdsbAnchor.cs
@@ -18,7 +18,6 @@ public AdsbAnchor(IAdsbClientDevice src, IAdsbVehicle device)
OffsetX = OffsetXEnum.Center;
OffsetY = OffsetYEnum.Center;
StrokeThickness = 1;
- BaseStrokeThickness = 1;
Stroke = Brushes.SeaGreen;
IconBrush = Brushes.Teal;
IsVisible = true;
diff --git a/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/Actions/TakeOff/UavActionTakeOff.cs b/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/Actions/TakeOff/UavActionTakeOff.cs
index 3eba4733..59202aa8 100644
--- a/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/Actions/TakeOff/UavActionTakeOff.cs
+++ b/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/Actions/TakeOff/UavActionTakeOff.cs
@@ -33,7 +33,7 @@ public UavActionTakeOff(IVehicleClient vehicle, IMap map, ILogService log, IConf
Command = cmd;
}
- private void OnCommandError(Exception ex)
+ private void OnCommandError(Exception? ex)
{
_log.Error("Arm", string.Format(RS.TakeOffAnchorActionViewModel_OnCommandError, Vehicle.Name.Value),
ex); // DONE: Localize
diff --git a/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/Actions/UavActionBase.cs b/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/Actions/UavActionBase.cs
index 7a3abfda..043558f0 100644
--- a/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/Actions/UavActionBase.cs
+++ b/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/Actions/UavActionBase.cs
@@ -38,7 +38,7 @@ protected UavActionBase(IVehicleClient vehicle, IMap map, ILogService log)
protected abstract Task ExecuteImpl(CancellationToken cancel);
- protected virtual void OnExecuteError(Exception ex)
+ protected virtual void OnExecuteError(Exception? ex)
{
var sender = $"{_vehicle.Name.Value} {Title}";
_log.Error(Title, $"{sender} error", ex);
diff --git a/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/FlightUavAnchor.cs b/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/FlightUavAnchor.cs
index 989f2da9..72dc04d8 100644
--- a/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/FlightUavAnchor.cs
+++ b/src/Asv.Drones.Gui/Shell/Pages/Flight/Anchors/Uav/FlightUavAnchor.cs
@@ -30,7 +30,6 @@ public FlightUavAnchor(IVehicleClient vehicle, ILocalizationService loc,
OffsetX = OffsetXEnum.Center;
OffsetY = OffsetYEnum.Center;
StrokeThickness = 1;
- BaseStrokeThickness = 1;
Stroke = Brushes.Honeydew;
IconBrush = Brushes.Red;
IsVisible = true;
diff --git a/src/Asv.Drones.Gui/Shell/Pages/Flight/Widgets/Uav/FlightUavView.axaml b/src/Asv.Drones.Gui/Shell/Pages/Flight/Widgets/Uav/FlightUavView.axaml
index 901aef20..594ba8d8 100644
--- a/src/Asv.Drones.Gui/Shell/Pages/Flight/Widgets/Uav/FlightUavView.axaml
+++ b/src/Asv.Drones.Gui/Shell/Pages/Flight/Widgets/Uav/FlightUavView.axaml
@@ -36,6 +36,12 @@
+