Repository: asv-soft/asv-drones
Branch: main
Commit: 9f78310d7315
Files: 322
Total size: 999.4 KB
Directory structure:
gitextract_fy5d8uuq/
├── .config/
│ └── dotnet-tools.json
├── .csharpierignore
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── bug_report.md
│ └── workflows/
│ ├── api-release-dev.yml
│ ├── api-release.yml
│ └── drones-release-windows.yml
├── .gitignore
├── .husky/
│ ├── pre-commit
│ └── task-runner.json
├── LICENSE
├── README.md
├── api_build.bat
├── api_publish_github.bat
├── publish.bat
├── src/
│ ├── .aiassistant/
│ │ └── rules/
│ │ └── comments.md
│ ├── .editorconfig
│ ├── .gitignore
│ ├── .run/
│ │ └── Publish win-x64.run.xml
│ ├── Asv.Drones/
│ │ ├── App.axaml
│ │ ├── App.axaml.cs
│ │ ├── Asv.Drones.csproj
│ │ ├── Asv.Drones.csproj.DotSettings
│ │ ├── AsvDronesMixin.cs
│ │ ├── Core/
│ │ │ ├── Commands/
│ │ │ │ ├── Behaviour/
│ │ │ │ │ ├── Remove/
│ │ │ │ │ │ └── RemoveItemCommand.cs
│ │ │ │ │ └── Rename/
│ │ │ │ │ └── CommitRenameCommand.cs
│ │ │ │ ├── FileBrowser/
│ │ │ │ │ ├── FileBrowserViewModel/
│ │ │ │ │ │ └── FindFileCommand.cs
│ │ │ │ │ ├── Items/
│ │ │ │ │ │ ├── CalculateCrc32Command.cs
│ │ │ │ │ │ └── CreateDirectoryCommand.cs
│ │ │ │ │ ├── OpenFileBrowserCommand.cs
│ │ │ │ │ └── Transfer/
│ │ │ │ │ ├── BurstDownloadItemCommand.cs
│ │ │ │ │ ├── DownloadItemCommand.cs
│ │ │ │ │ ├── ITransferFtpEntries.cs
│ │ │ │ │ ├── TransferCommandBase.cs
│ │ │ │ │ └── UploadItemCommand.cs
│ │ │ │ ├── Flight/
│ │ │ │ │ ├── OpenFlight.cs
│ │ │ │ │ ├── OpenFlightMode.cs
│ │ │ │ │ └── Widgets/
│ │ │ │ │ └── UavWidget/
│ │ │ │ │ ├── AutoModeCommand.cs
│ │ │ │ │ ├── GuidedModeCommand.cs
│ │ │ │ │ ├── LandCommand.cs
│ │ │ │ │ ├── MissionProgress/
│ │ │ │ │ │ └── UpdateMissionCommand.cs
│ │ │ │ │ ├── RTLCommand.cs
│ │ │ │ │ ├── StartMissionCommand.cs
│ │ │ │ │ └── TakeOffCommand.cs
│ │ │ │ ├── MavParams/
│ │ │ │ │ ├── MavParamsPageViewModel/
│ │ │ │ │ │ ├── RemoveAllPinsCommand.cs
│ │ │ │ │ │ ├── StopUpdateParamsCommand.cs
│ │ │ │ │ │ └── UpdateParamsCommand.cs
│ │ │ │ │ └── OpenMavParamsCommand.cs
│ │ │ │ ├── Mavlink/
│ │ │ │ │ ├── MavlinkCommands.cs
│ │ │ │ │ ├── MavlinkCommandsMixin.cs
│ │ │ │ │ ├── MavlinkParamReadCommand.cs
│ │ │ │ │ ├── MavlinkParamsWriteCommand.cs
│ │ │ │ │ └── NullMavlinkCommands.cs
│ │ │ │ ├── PacketViewer/
│ │ │ │ │ ├── ClearAllPacketsCommand.cs
│ │ │ │ │ ├── ExportPacketsToCsvCommand.cs
│ │ │ │ │ └── OpenPacketViewer.cs
│ │ │ │ └── Setup/
│ │ │ │ ├── FrameType/
│ │ │ │ │ └── ChangeFrameTypeCommand.cs
│ │ │ │ └── OpenSetupCommand.cs
│ │ │ ├── Controls/
│ │ │ │ └── DeviceTelemetry/
│ │ │ │ ├── AngleUavIndicator/
│ │ │ │ │ ├── Items/
│ │ │ │ │ │ ├── Pitch/
│ │ │ │ │ │ │ ├── PitchItem.cs
│ │ │ │ │ │ │ └── PitchItem.properties.cs
│ │ │ │ │ │ └── Roll/
│ │ │ │ │ │ ├── RollItem.cs
│ │ │ │ │ │ └── RollItem.properties.cs
│ │ │ │ │ ├── UavAngleIndicator.cs
│ │ │ │ │ ├── UavAngleIndicator.properties.cs
│ │ │ │ │ └── UavAngleIndicatorStyles.axaml
│ │ │ │ ├── CompassUavIndicator/
│ │ │ │ │ ├── CompassScaleItem.cs
│ │ │ │ │ ├── CompassUavIndicator.cs
│ │ │ │ │ ├── CompassUavIndicator.properties.cs
│ │ │ │ │ └── CompassUavIndicatorStyles.axaml
│ │ │ │ ├── DeviceTelemetryDesignPreview.cs
│ │ │ │ ├── OldAttitudeIndicator/
│ │ │ │ │ ├── AttitudeIndicator.cs
│ │ │ │ │ ├── AttitudeIndicator.properties.cs
│ │ │ │ │ ├── AttitudeIndicatorStyles.axaml
│ │ │ │ │ └── Items/
│ │ │ │ │ ├── Heading/
│ │ │ │ │ │ └── HeadingScaleItem.cs
│ │ │ │ │ ├── Pitch/
│ │ │ │ │ │ ├── PitchItem.cs
│ │ │ │ │ │ └── PitchItem.properties.cs
│ │ │ │ │ ├── Roll/
│ │ │ │ │ │ ├── RollItem.cs
│ │ │ │ │ │ └── RollItem.properties.cs
│ │ │ │ │ └── Scale/
│ │ │ │ │ ├── ScaleItem.cs
│ │ │ │ │ └── ScaleItem.properties.cs
│ │ │ │ ├── RouteUavIndicator/
│ │ │ │ │ ├── RouteUavIndicator.cs
│ │ │ │ │ └── RouteUavIndicatorStyles.axaml
│ │ │ │ └── Rtt/
│ │ │ │ ├── AltitudeUavIndicator/
│ │ │ │ │ ├── AltitudeUavIndicator.axaml
│ │ │ │ │ ├── AltitudeUavIndicator.axaml.cs
│ │ │ │ │ └── AltitudeUavIndicatorViewModel.cs
│ │ │ │ ├── AngleUavRttIndicator/
│ │ │ │ │ ├── AngleUavRttIndicator.axaml
│ │ │ │ │ ├── AngleUavRttIndicator.axaml.cs
│ │ │ │ │ └── AngleUavRttIndicatorViewModel.cs
│ │ │ │ ├── BatteryUavIndicator/
│ │ │ │ │ ├── BatteryUavIndicator.axaml
│ │ │ │ │ ├── BatteryUavIndicator.axaml.cs
│ │ │ │ │ └── BatteryUavIndicatorViewModel.cs
│ │ │ │ ├── HeadingUavIndicator/
│ │ │ │ │ ├── HeadingUavIndicator.axaml
│ │ │ │ │ ├── HeadingUavIndicator.axaml.cs
│ │ │ │ │ └── HeadingUavIndicatorViewModel.cs
│ │ │ │ ├── HomeAzimuthUavIndicator/
│ │ │ │ │ ├── HomeAzimuthUavIndicator.axaml
│ │ │ │ │ ├── HomeAzimuthUavIndicator.axaml.cs
│ │ │ │ │ └── HomeAzimuthUavIndicatorViewModel.cs
│ │ │ │ └── VelocityUavIndicator/
│ │ │ │ ├── VelocityUavIndicator.axaml
│ │ │ │ ├── VelocityUavIndicator.axaml.cs
│ │ │ │ └── VelocityUavIndicatorViewModel.cs
│ │ │ ├── Converters/
│ │ │ │ └── Crc32StatusToColorConverter.cs
│ │ │ └── Services/
│ │ │ ├── ClientDeviceWidgetFactory/
│ │ │ │ └── ClientDeviceWidgetFactory.cs
│ │ │ ├── Devices/
│ │ │ │ └── Gnss/
│ │ │ │ └── GnssDeviceManagerExtentsion.cs
│ │ │ └── Files/
│ │ │ ├── BusyFlag.cs
│ │ │ ├── Local/
│ │ │ │ └── LocalFilesService.cs
│ │ │ ├── PathHelper.cs
│ │ │ ├── ProgressWithLock.cs
│ │ │ └── Remote/
│ │ │ ├── FtpClientService.cs
│ │ │ └── RemoteEntriesSync.cs
│ │ ├── RS.Designer.cs
│ │ ├── RS.resx
│ │ ├── RS.ru.resx
│ │ └── Shell/
│ │ └── Pages/
│ │ ├── Device/
│ │ │ ├── FileBrowser/
│ │ │ │ ├── Contracts/
│ │ │ │ │ ├── FileBrowserBackend.cs
│ │ │ │ │ ├── IBrowserItemsOperations.cs
│ │ │ │ │ ├── LocalBrowserItemsOperations.cs
│ │ │ │ │ └── RemoteBrowserItemsOperations.cs
│ │ │ │ ├── Dialogs/
│ │ │ │ │ ├── BurstDownloadDialogView.axaml
│ │ │ │ │ ├── BurstDownloadDialogView.axaml.cs
│ │ │ │ │ └── BurstDownloadDialogViewModel.cs
│ │ │ │ ├── FileBrowserView.axaml
│ │ │ │ ├── FileBrowserView.axaml.cs
│ │ │ │ ├── FileBrowserViewModel.cs
│ │ │ │ ├── HomePageFileBrowserDeviceItemAction.cs
│ │ │ │ ├── IO/
│ │ │ │ │ ├── FileSize.cs
│ │ │ │ │ ├── FtpBrowserNamingPolicy.cs
│ │ │ │ │ ├── FtpBrowserPath.cs
│ │ │ │ │ └── Types/
│ │ │ │ │ ├── Crc32Status.cs
│ │ │ │ │ └── FtpBrowserSourceType.cs
│ │ │ │ └── Tree/
│ │ │ │ ├── BrowserNode.cs
│ │ │ │ ├── BrowserTree.cs
│ │ │ │ └── Items/
│ │ │ │ ├── BrowserItemComparer.cs
│ │ │ │ ├── BrowserItemViewModel.cs
│ │ │ │ ├── DirectoryItemViewModel.cs
│ │ │ │ ├── FileItemViewModel.cs
│ │ │ │ └── IBrowserItemViewModel.cs
│ │ │ ├── MavParams/
│ │ │ │ ├── Dialog/
│ │ │ │ │ ├── TryCloseWithApprovalDialogView.axaml
│ │ │ │ │ ├── TryCloseWithApprovalDialogView.axaml.cs
│ │ │ │ │ └── TryCloseWithApprovalDialogViewModel.cs
│ │ │ │ ├── HomePageParamsDeviceItemAction.cs
│ │ │ │ ├── MavParamsPageView.axaml
│ │ │ │ ├── MavParamsPageView.axaml.cs
│ │ │ │ ├── MavParamsPageViewModel.cs
│ │ │ │ └── ParamItem/
│ │ │ │ ├── ParamItemView.axaml
│ │ │ │ ├── ParamItemView.axaml.cs
│ │ │ │ ├── ParamItemViewModel.cs
│ │ │ │ └── RoutedEvents/
│ │ │ │ └── ParamItemChangedEvent.cs
│ │ │ └── Setup/
│ │ │ ├── DefaultSetupExtension.cs
│ │ │ ├── HomePageSetupDeviceItemAction.cs
│ │ │ ├── SetupMixin.cs
│ │ │ ├── SetupPageView.axaml
│ │ │ ├── SetupPageView.axaml.cs
│ │ │ ├── SetupPageViewModel.cs
│ │ │ └── Subpage/
│ │ │ ├── FrameType/
│ │ │ │ ├── DroneFrameItem/
│ │ │ │ │ ├── DroneFrameItemView.axaml
│ │ │ │ │ ├── DroneFrameItemView.axaml.cs
│ │ │ │ │ ├── DroneFrameItemViewModel.cs
│ │ │ │ │ ├── NullDroneFrame.cs
│ │ │ │ │ └── RoutedEvents/
│ │ │ │ │ └── CurrentDroneFrameChangeEvent.cs
│ │ │ │ ├── FrameTypeSetupPageExtension.cs
│ │ │ │ ├── SetupFrameTypeView.axaml
│ │ │ │ ├── SetupFrameTypeView.axaml.cs
│ │ │ │ └── SetupFrameTypeViewModel.cs
│ │ │ ├── Motors/
│ │ │ │ ├── MotorItem/
│ │ │ │ │ ├── MotorItemView.axaml
│ │ │ │ │ ├── MotorItemView.axaml.cs
│ │ │ │ │ └── MotorItemViewModel.cs
│ │ │ │ ├── MotorsSetupPageExtension.cs
│ │ │ │ ├── SetupMotorsView.axaml
│ │ │ │ ├── SetupMotorsView.axaml.cs
│ │ │ │ └── SetupMotorsViewModel.cs
│ │ │ └── SetupSubpage.cs
│ │ ├── Flight/
│ │ │ ├── Anchors/
│ │ │ │ ├── FlightUavAnchorsExtension.cs
│ │ │ │ ├── MissionAnchor.cs
│ │ │ │ └── UavAnchor.cs
│ │ │ ├── FlightPageView.axaml
│ │ │ ├── FlightPageView.axaml.cs
│ │ │ ├── FlightPageViewModel.cs
│ │ │ ├── HomePageFlightExtension.cs
│ │ │ └── Widgets/
│ │ │ ├── FlightWidgetsExtension.cs
│ │ │ └── UavWidget/
│ │ │ ├── Dialogs/
│ │ │ │ ├── SetAltitudeDialogView.axaml
│ │ │ │ ├── SetAltitudeDialogView.axaml.cs
│ │ │ │ └── SetAltitudeDialogViewModel.cs
│ │ │ ├── MissionProgress/
│ │ │ │ ├── MissionProgressView.axaml
│ │ │ │ ├── MissionProgressView.axaml.cs
│ │ │ │ └── MissionProgressViewModel.cs
│ │ │ ├── UavWidgetView.axaml
│ │ │ ├── UavWidgetView.axaml.cs
│ │ │ └── UavWidgetViewModel.cs
│ │ ├── FlightMode/
│ │ │ ├── Anchors/
│ │ │ │ └── FlightModeAnchorsExtension.cs
│ │ │ ├── FlightModePageView.axaml
│ │ │ ├── FlightModePageView.axaml.cs
│ │ │ ├── FlightModePageViewModel.cs
│ │ │ ├── HomePageFlightModeExtension.cs
│ │ │ └── Widgets/
│ │ │ ├── Device/
│ │ │ │ ├── FlightModeClientDeviceWidgetExtension.cs
│ │ │ │ └── Mavlink/
│ │ │ │ └── Drone/
│ │ │ │ ├── DroneFlightWidgetViewModel.cs
│ │ │ │ ├── DroneWidgetCreationHandler.cs
│ │ │ │ ├── Plane/
│ │ │ │ │ ├── PlaneWidgetCreationHandler.cs
│ │ │ │ │ ├── PlaneWidgetViewModel.cs
│ │ │ │ │ └── Sections/
│ │ │ │ │ ├── PlaneSectionExtension.cs
│ │ │ │ │ ├── PlaneSectionView.axaml
│ │ │ │ │ ├── PlaneSectionView.axaml.cs
│ │ │ │ │ └── PlaneSectionViewModel.cs
│ │ │ │ └── Sections/
│ │ │ │ ├── AttitudeIndicator/
│ │ │ │ │ ├── AttitudeIndicatorSectionView.axaml
│ │ │ │ │ ├── AttitudeIndicatorSectionView.axaml.cs
│ │ │ │ │ ├── AttitudeIndicatorSectionViewModel.cs
│ │ │ │ │ └── DroneFlightWidgetExtensionAttitudeIndicatorSection.cs
│ │ │ │ ├── FlightControl/
│ │ │ │ │ ├── DroneFlightWidgetFlightControlSectionExtension.cs
│ │ │ │ │ ├── FlightControlSectionView.axaml
│ │ │ │ │ ├── FlightControlSectionView.axaml.cs
│ │ │ │ │ └── FlightControlSectionViewModel.cs
│ │ │ │ └── Telemetry/
│ │ │ │ ├── DroneFlightWidgetTelemetrySectionExtension.cs
│ │ │ │ ├── TelemetrySectionView.axaml
│ │ │ │ ├── TelemetrySectionView.axaml.cs
│ │ │ │ └── TelemetrySectionViewModel.cs
│ │ │ └── Test/
│ │ │ ├── PluginFlightItemView.axaml
│ │ │ ├── PluginFlightItemView.axaml.cs
│ │ │ ├── PluginFlightItemViewModel.cs
│ │ │ └── PluginFlightItemWidgetExtension.cs
│ │ └── PacketViewer/
│ │ ├── Dialogs/
│ │ │ ├── SavePacketMessagesDialogView.axaml
│ │ │ ├── SavePacketMessagesDialogView.axaml.cs
│ │ │ └── SavePacketMessagesDialogViewModel.cs
│ │ ├── Filters/
│ │ │ ├── Comparers/
│ │ │ │ ├── PacketFilterComparerBase.cs
│ │ │ │ ├── SourcePacketFilterComparer.cs
│ │ │ │ └── TypePacketFilterComparer.cs
│ │ │ ├── PacketFilterViewModelBase.cs
│ │ │ ├── SourcePacketFilterViewModel.cs
│ │ │ └── TypePacketFilterViewModel.cs
│ │ ├── HomePacketViewerExtension.cs
│ │ ├── PacketConverter/
│ │ │ └── DefaultMavlinkPacketConverter.cs
│ │ ├── PacketMessage/
│ │ │ ├── PacketMessageView.axaml
│ │ │ ├── PacketMessageView.axaml.cs
│ │ │ └── PacketMessageViewModel.cs
│ │ ├── PacketViewerView.axaml
│ │ ├── PacketViewerView.axaml.cs
│ │ └── PacketViewerViewModel.cs
│ ├── Asv.Drones.Android/
│ │ ├── Asv.Drones.Android.csproj
│ │ ├── MainActivity.cs
│ │ ├── Properties/
│ │ │ └── AndroidManifest.xml
│ │ └── Resources/
│ │ ├── AboutResources.txt
│ │ ├── drawable/
│ │ │ └── splash_screen.xml
│ │ ├── drawable-night-v31/
│ │ │ └── avalonia_anim.xml
│ │ ├── drawable-v31/
│ │ │ └── avalonia_anim.xml
│ │ ├── values/
│ │ │ ├── colors.xml
│ │ │ └── styles.xml
│ │ ├── values-night/
│ │ │ └── colors.xml
│ │ └── values-v31/
│ │ └── styles.xml
│ ├── Asv.Drones.Api/
│ │ ├── Asv.Drones.Api.csproj
│ │ ├── Asv.Drones.Api.csproj.DotSettings
│ │ ├── Core/
│ │ │ ├── Commands/
│ │ │ │ ├── Behaviour/
│ │ │ │ │ ├── Remove/
│ │ │ │ │ │ ├── IRemoveItemCommand.cs
│ │ │ │ │ │ └── ISupportRemove.cs
│ │ │ │ │ └── Rename/
│ │ │ │ │ ├── ICommitRenameCommand.cs
│ │ │ │ │ └── ISupportRename.cs
│ │ │ │ ├── Commands.cs
│ │ │ │ ├── Flight/
│ │ │ │ │ └── IFlightModeCommands.cs
│ │ │ │ ├── Mavlink/
│ │ │ │ │ └── IMavlinkCommands.cs
│ │ │ │ └── MavlinkMicroserviceCommand.cs
│ │ │ ├── Controls/
│ │ │ │ ├── FlightWidget/
│ │ │ │ │ ├── FlightWidgetView.axaml
│ │ │ │ │ ├── FlightWidgetView.axaml.cs
│ │ │ │ │ ├── FlightWidgetViewModel.cs
│ │ │ │ │ ├── FlightWidgetsComparer.cs
│ │ │ │ │ ├── IFlightWidget.cs
│ │ │ │ │ └── Section/
│ │ │ │ │ ├── FlightWidgetSectionsComparer.cs
│ │ │ │ │ └── IFlightWidgetSection.cs
│ │ │ │ └── MavParam/
│ │ │ │ ├── Button/
│ │ │ │ │ ├── MavParamButtonView.axaml
│ │ │ │ │ ├── MavParamButtonView.axaml.cs
│ │ │ │ │ └── MavParamButtonViewModel.cs
│ │ │ │ ├── ComboBox/
│ │ │ │ │ ├── MavParamComboBoxView.axaml
│ │ │ │ │ ├── MavParamComboBoxView.axaml.cs
│ │ │ │ │ └── MavParamComboboxViewModel.cs
│ │ │ │ ├── Geopoint/
│ │ │ │ │ ├── MavParamAltitudeTextBoxView.cs
│ │ │ │ │ ├── MavParamAltitudeTextBoxViewModel.cs
│ │ │ │ │ ├── MavParamLatLonTextBoxView.cs
│ │ │ │ │ └── MavParamLatLonTextBoxViewModel.cs
│ │ │ │ ├── MavParamFactory.cs
│ │ │ │ ├── MavParamInfo.cs
│ │ │ │ ├── MavParamViewModel.cs
│ │ │ │ ├── String/
│ │ │ │ │ ├── MavParamAsciiCharView.cs
│ │ │ │ │ └── MavParamAsciiCharViewModel.cs
│ │ │ │ └── TextBox/
│ │ │ │ ├── MavParamTextBoxView.axaml
│ │ │ │ ├── MavParamTextBoxView.axaml.cs
│ │ │ │ └── MavParamTextBoxViewModel.cs
│ │ │ └── Services/
│ │ │ ├── ClientDeviceWidgetFactory/
│ │ │ │ ├── IClientDeviceWidgetCreationHandler.cs
│ │ │ │ └── IClientDeviceWidgetFactory.cs
│ │ │ ├── Converters/
│ │ │ │ └── IPacketConverter.cs
│ │ │ └── Devices/
│ │ │ └── Mavlink/
│ │ │ └── IMavlinkHost.cs
│ │ ├── RS.Designer.cs
│ │ ├── RS.resx
│ │ ├── RS.ru.resx
│ │ ├── Shell/
│ │ │ └── Pages/
│ │ │ ├── FileBrowser/
│ │ │ │ └── IFileBrowserViewModel.cs
│ │ │ ├── Flight/
│ │ │ │ ├── FlightMode.cs
│ │ │ │ ├── IFlightMode.cs
│ │ │ │ └── Widgets/
│ │ │ │ └── UavWidget/
│ │ │ │ └── IUavFlightWidget.cs
│ │ │ ├── FlightMode/
│ │ │ │ ├── IFlightModePage.cs
│ │ │ │ └── Widgets/
│ │ │ │ └── Device/
│ │ │ │ ├── DeviceFlightWidgetViewModelBase.cs
│ │ │ │ ├── IDeviceFlightWidget.cs
│ │ │ │ └── Mavlink/
│ │ │ │ ├── Drone/
│ │ │ │ │ ├── DroneFlightWidgetViewModelBase.cs
│ │ │ │ │ └── IDroneFlightWidget.cs
│ │ │ │ ├── IMavlinkDeviceFlightWidget.cs
│ │ │ │ └── MavlinkDeviceFlightWidgetViewModelBase.cs
│ │ │ ├── MavParams/
│ │ │ │ └── IMavParamsPageViewModel.cs
│ │ │ └── Setup/
│ │ │ ├── ISetupPage.cs
│ │ │ └── Subpage/
│ │ │ └── ISetupSubpage.cs
│ │ └── Tools/
│ │ └── Mavlink/
│ │ ├── DeviceIconMixin.cs
│ │ └── MavlinkHost.cs
│ ├── Asv.Drones.Desktop/
│ │ ├── Asv.Drones.Desktop.csproj
│ │ ├── Program.cs
│ │ ├── app.manifest
│ │ ├── appsettings.Development.json
│ │ ├── appsettings.Production.json
│ │ └── appsettings.json
│ ├── Asv.Drones.iOS/
│ │ ├── AppDelegate.cs
│ │ ├── Asv.Drones.iOS.csproj
│ │ ├── Entitlements.plist
│ │ ├── Info.plist
│ │ ├── Main.cs
│ │ └── Resources/
│ │ └── LaunchScreen.xib
│ ├── Asv.Drones.slnx
│ ├── CodeStyle.ruleset
│ ├── Directory.Build.props
│ └── global.json
├── win-64-install.nsi
├── win-arm-install.iss
├── win-arm64-install.iss
├── win-x64-install.iss
└── win-x86-install.iss
================================================
FILE CONTENTS
================================================
================================================
FILE: .config/dotnet-tools.json
================================================
{
"version": 1,
"isRoot": true,
"tools": {
"husky": {
"version": "0.8.0",
"commands": [
"husky"
],
"rollForward": false
},
"run-script": {
"version": "0.6.0",
"commands": [
"r"
],
"rollForward": false
},
"csharpier": {
"version": "1.2.1",
"commands": [
"csharpier"
],
"rollForward": false
}
}
}
================================================
FILE: .csharpierignore
================================================
*
!**/*.cs
!**/*.axaml
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
1. Description:
A brief description of the error or issue you have encountered.
2. Steps to Reproduce:
[Step 1]
[Step 2]
[Step 3]
...
3. Expected Result:
Describe what you expected to see after performing the steps mentioned above.
4. Actual Result:
Describe what actually happened after performing the steps mentioned above.
5. Screenshots:
If applicable, add screenshots to help explain your problem.
6. Environment:
Desktop:
OS: [e.g., Windows 10]
Version: [e.g., 99.0]
7. Additional Context:
Include relevant console logs.[Your console logs here]❌Errors [], ⚠️Info [], 📜Log []
Please ensure you have provided all necessary steps and information to reproduce the bug.
================================================
FILE: .github/workflows/api-release-dev.yml
================================================
name: Release Api Github only
on:
push:
tags:
- "api-v[0-9]+.[0-9]+.[0-9]+-dev.[0-9]+"
- "api-v[0-9]+.[0-9]+.[0-9]+-dev"
env:
GITHUB_PACKAGES_URL: 'https://nuget.pkg.github.com/asv-soft/index.json'
PROJECT_NAME: 'Asv.Drones'
PROPS_VERSION_VAR_NAME: 'ApiVersion'
jobs:
build:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/api-v')
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x.x'
- name: Add NuGet source
run: dotnet nuget add source ${{ env.GITHUB_PACKAGES_URL }} \--username '${{secrets.USER_NAME}}' \--password '${{secrets.GIHUB_NUGET_AUTH_TOKEN}}' \--store-password-in-clear-text
- name: Install dependencies
run: |
dotnet restore ./src/${{env.PROJECT_NAME}}/${{env.PROJECT_NAME}}.csproj
dotnet restore ./src/${{env.PROJECT_NAME}}.Api/${{env.PROJECT_NAME}}.Api.csproj
- name: Build
run: |
dotnet build ./src/${{env.PROJECT_NAME}}/${{env.PROJECT_NAME}}.csproj --configuration Release --no-restore
dotnet build ./src/${{env.PROJECT_NAME}}.Api/${{env.PROJECT_NAME}}.Api.csproj --configuration Release --no-restore
- name: Set version variable
env:
TAG: ${{ github.ref_name }}
run: echo "VERSION=${TAG#api-v}" >> $GITHUB_ENV
- name: Read version from Directory.Build.props
id: read-version
run: |
version=$(grep -oP '<${{env.PROPS_VERSION_VAR_NAME}}>\K[^<]+' ./src/Directory.Build.props)
echo "PropsVersion=${version}" >> $GITHUB_ENV
- name: Compare tag with NuGet package version
run: |
if [ "${{ env.PropsVersion }}" != "${{ env.VERSION }}" ]; then
echo "Error: Tag does not match NuGet package version"
exit 1
fi
- name: Pack package
run: |
dotnet pack ./src/${{env.PROJECT_NAME}}/${{env.PROJECT_NAME}}.csproj -c Release /p:ProductVersion=${VERSION} --no-build -o .
dotnet pack ./src/${{env.PROJECT_NAME}}.Api/${{env.PROJECT_NAME}}.Api.csproj -c Release /p:ProductVersion=${VERSION} --no-build -o .
- name: List output files
run: ls -la
- name: Push package to GitHub
run: |
dotnet nuget push ${{env.PROJECT_NAME}}.${{ env.VERSION }}.nupkg --api-key ${{ secrets.GIHUB_NUGET_AUTH_TOKEN }} --skip-duplicate --source ${{ env.GITHUB_PACKAGES_URL }}
dotnet nuget push ${{env.PROJECT_NAME}}.Api.${{ env.VERSION }}.nupkg --api-key ${{ secrets.GIHUB_NUGET_AUTH_TOKEN }} --skip-duplicate --source ${{ env.GITHUB_PACKAGES_URL }}
================================================
FILE: .github/workflows/api-release.yml
================================================
name: Release Api NuGet
on:
push:
tags:
- "api-v[0-9]+.[0-9]+.[0-9]+"
- "api-v[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+"
- "api-v[0-9]+.[0-9]+.[0-9]+-rc"
env:
NUGET_SOURCE_URL: 'https://api.nuget.org/v3/index.json'
GITHUB_PACKAGES_URL: 'https://nuget.pkg.github.com/asv-soft/index.json'
PROJECT_NAME: 'Asv.Drones'
PROPS_VERSION_VAR_NAME: 'ApiVersion'
jobs:
build:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/api-v')
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x.x'
- name: Add NuGet source
run: dotnet nuget add source ${{ env.GITHUB_PACKAGES_URL }} \--username '${{secrets.USER_NAME}}' \--password '${{secrets.GIHUB_NUGET_AUTH_TOKEN}}' \--store-password-in-clear-text
- name: Install dependencies
run: |
dotnet restore ./src/${{env.PROJECT_NAME}}/${{env.PROJECT_NAME}}.csproj
dotnet restore ./src/${{env.PROJECT_NAME}}.Api/${{env.PROJECT_NAME}}.Api.csproj
- name: Build
run: |
dotnet build ./src/${{env.PROJECT_NAME}}/${{env.PROJECT_NAME}}.csproj --configuration Release --no-restore
dotnet build ./src/${{env.PROJECT_NAME}}.Api/${{env.PROJECT_NAME}}.Api.csproj --configuration Release --no-restore
- name: Set version variable
env:
TAG: ${{ github.ref_name }}
run: echo "VERSION=${TAG#api-v}" >> $GITHUB_ENV
- name: Read version from Directory.Build.props
id: read-version
run: |
version=$(grep -oP '<${{env.PROPS_VERSION_VAR_NAME}}>\K[^<]+' ./src/Directory.Build.props)
echo "PropsVersion=${version}" >> $GITHUB_ENV
- name: Compare tag with NuGet package version
run: |
if [ "${{ env.PropsVersion }}" != "${{ env.VERSION }}" ]; then
echo "Error: Tag does not match NuGet package version"
exit 1
fi
- name: Pack package
run: |
dotnet pack ./src/${{env.PROJECT_NAME}}/${{env.PROJECT_NAME}}.csproj -c Release /p:ProductVersion=${VERSION} --no-build -o .
dotnet pack ./src/${{env.PROJECT_NAME}}.Api/${{env.PROJECT_NAME}}.Api.csproj -c Release /p:ProductVersion=${VERSION} --no-build -o .
- name: List output files
run: ls -la
- name: Push package to GitHub
run: |
dotnet nuget push ${{env.PROJECT_NAME}}.${{ env.VERSION }}.nupkg --api-key ${{ secrets.GIHUB_NUGET_AUTH_TOKEN }} --skip-duplicate --source ${{ env.GITHUB_PACKAGES_URL }}
dotnet nuget push ${{env.PROJECT_NAME}}.Api.${{ env.VERSION }}.nupkg --api-key ${{ secrets.GIHUB_NUGET_AUTH_TOKEN }} --skip-duplicate --source ${{ env.GITHUB_PACKAGES_URL }}
- name: Push package to Nuget
run: |
dotnet nuget push ${{env.PROJECT_NAME}}.${{ env.VERSION }}.nupkg --api-key ${{ secrets.NUGET_AUTH_TOKEN }} --skip-duplicate --source ${{ env.NUGET_SOURCE_URL }}
dotnet nuget push ${{env.PROJECT_NAME}}.Api.${{ env.VERSION }}.nupkg --api-key ${{ secrets.NUGET_AUTH_TOKEN }} --skip-duplicate --source ${{ env.NUGET_SOURCE_URL }}
================================================
FILE: .github/workflows/drones-release-windows.yml
================================================
name: Build and Release Drones For Windows
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-rc'
env:
NUGET_SOURCE_URL: 'https://api.nuget.org/v3/index.json'
GITHUB_PACKAGES_URL: 'https://nuget.pkg.github.com/asv-soft/index.json'
PROJECT_NAME: 'Asv.Drones'
PROPS_VERSION_VAR_NAME: 'ApiVersion'
jobs:
build-and-release:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x.x'
- name: Add NuGet source
run: dotnet nuget add source ${{ env.GITHUB_PACKAGES_URL }} --username '${{secrets.USER_NAME}}' --password '${{secrets.GIHUB_NUGET_AUTH_TOKEN}}' --store-password-in-clear-text
- name: Install dependencies
run: |
dotnet restore ./src/${{ env.PROJECT_NAME }}/${{ env.PROJECT_NAME }}.csproj
dotnet restore ./src/${{ env.PROJECT_NAME }}.Desktop/${{ env.PROJECT_NAME }}.Desktop.csproj
- name: Build
run: |
dotnet build ./src/${{ env.PROJECT_NAME }}/${{ env.PROJECT_NAME }}.csproj --no-restore
dotnet build ./src/${{ env.PROJECT_NAME }}.Desktop/${{ env.PROJECT_NAME }}.Desktop.csproj --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/${{ env.PROJECT_NAME }}.Desktop/${{ env.PROJECT_NAME }}.Desktop.csproj -c Release -o ./publish/app
- name: Sign app files
uses: dlemstra/code-sign-action@v1
with:
certificate: '${{ secrets.WINDOWS_SIGNING_CERTIFICATE }}'
password: '${{ secrets.WINDOWS_SIGNING_PASSWORD }}'
folder: './publish/app'
recursive: true
description: 'Sign The 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
uses: joncloud/makensis-action@v4
with:
script-file: win-64-install.nsi
- name: Sign the installer
uses: dlemstra/code-sign-action@v1
with:
certificate: '${{ secrets.WINDOWS_SIGNING_CERTIFICATE }}'
password: '${{ secrets.WINDOWS_SIGNING_PASSWORD }}'
files: |
AsvDronesInstaller.exe
description: 'Sign The Installer'
- 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, 'rc') }}
- 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: ./AsvDronesInstaller.exe
asset_name: asv-drones-${{ github.ref_name }}-setup-windows-64.exe
asset_content_type: application/vnd.microsoft.portable-executable
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
## husky task runner examples -------------------
## Note : for local installation use 'dotnet' prefix. e.g. 'dotnet husky'
## run all tasks
#husky run
### run all tasks with group: 'group-name'
#husky run --group group-name
## run task with name: 'task-name'
#husky run --name task-name
## pass hook arguments to task
#husky run --args "$1" "$2"
## or put your custom commands -------------------
#echo 'Husky.Net is awesome!'
dotnet husky run --group check
dotnet husky run --group check
================================================
FILE: .husky/task-runner.json
================================================
{
"$schema": "https://alirezanet.github.io/Husky.Net/schema.json",
"tasks": [
{
"name": "Run csharpier",
"command": "dotnet",
"args": [ "csharpier", "format", "." ],
"group": "prettier"
},
{
"name": "Run csharpier check",
"command": "dotnet",
"args": [ "csharpier", "check", "." ],
"group": "check"
}
]
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 Asv Soft LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================

[//]: [](https://github.com/asv-soft/asv-drones/actions/workflows/ReleaseDeployAction.yml)
## 1. Introduction
Asv.Drones: Empowering Innovation in Unmanned Aerial Systems
Welcome to Asv.Drones, an advanced and modular open-source application designed to revolutionize the field of Unmanned Aerial Systems (UAS).
Committed to fostering innovation and collaboration, Asv.Drones is not just a drone application; it's a community-driven platform that opens the doors to limitless possibilities.
Key Features:
1. **Modularity:**
Asv.Drones embraces a modular architecture, allowing users to tailor the application to their specific needs. Each module serves a distinct purpose, contributing to the overall functionality and versatility of the platform.
2. **Open Source Philosophy:**
Transparency and collaboration lie at the heart of Asv.Drones. The entire application, along with its constituent modules, is open source. This means that not only can users benefit from the software, but they can also actively contribute to its enhancement and evolution.
3. **Modules Overview:**
- **[Asv.Drones.Gbs](https://github.com/asv-soft/asv-drones-gbs) (Ground Base Station Service):**
This module provides a robust ground base station service, ensuring seamless communication between the drone and the operator on the ground. Open source nature encourages customization for specific ground station requirements.
- **Obsolete [Asv.Drones.Sdr](https://github.com/asv-soft/asv-drones-sdr) (SDR Payload Example Project):**
Explore the possibilities of Software-Defined Radio (SDR) payloads with this open-source example project. Asv.Drones.Sdr serves as a foundation for integrating cutting-edge SDR technologies into unmanned aerial systems.
- **[Asv.Gnss](https://github.com/asv-soft/asv-gnss) (GNSS Library):**
The Asv.Gnss module is a comprehensive GNSS library that parses RTCMv2, RTCMv3 and NMEA protocols. It goes a step further by providing control over receivers through SBF, ComNav and UBX protocols, all tailored for .NET environments.
- **[Asv.Mavlink](https://github.com/asv-soft/asv-mavlink) (MAVLink Library for .NET 9.0):**
For seamless communication and control, Asv.MAVLink is a dedicated library compatible with .NET 9.0. It ensures that your drone's communication adheres to the MAVLink protocol standards.
- **[Asv.Common](https://github.com/asv-soft/asv-common):**
Asv.Common serves as the backbone, offering common types and extensions for all Asv-based libraries. It streamlines development, ensuring consistency and efficiency across different modules.
- **[Asv.Avalonia](https://github.com/asv-soft/asv-avalonia):**
Asv.Avalonia is a custom framework built on top of Avalonia. It defines the fundamental rules for cross-platform applications and allows for their rapid assembly.
Out of the box, the framework provides an event system for ViewModels, a powerful undo/redo mechanism for various user actions, a theme with diverse styles, a cross-platform dialog system and more.
Asv.Avalonia features various additional modules that extend its functionality.
Here's a schematic representation of the whole project:
4. **Community Collaboration:**
Asv.Drones thrives on community collaboration. Developers, enthusiasts and innovators are encouraged to contribute, share insights and collectively shape the future of unmanned aerial systems.
Embark on a journey of exploration, experimentation and innovation with Asv.Drones. Whether you're a developer, researcher or drone enthusiast, this open-source platform invites you to redefine the possibilities of unmanned aerial systems.
## 2. Different sets of components
Asv.Drones can work with different combinations of its components
### Example Of Usage With GBS
**Ground Base Station Integration:** Asv.Drones offers seamless integration with ground base stations through our proprietary implementation called [Asv.Drones.Gbs](https://github.com/asv-soft/asv-drones-gbs).
Built to operate via the MAVLink protocol, Asv.Drones.Gbs allows users to remotely manage and monitor drone operations from a centralized platform.
Moreover, any other ground base station software compatible with MAVLink can seamlessly interface with our application, ensuring flexibility and interoperability across different systems (development of additional UI controls may be required).
With Asv.Drones.Gbs, users can plan missions, monitor telemetry data and adjust flight parameters with ease.
To connect to a gbs, create a new connection (usually tcp) in the connection settings.
## 3. Getting Started
### Setting Up the Development Environment
To ensure a smooth development experience, follow the steps below to set up your development environment:
### 3.1 Prerequisites:
- **Operating System:** This project is compatible with Windows, macOS and Linux. Ensure that your development machine runs one of these supported operating systems.
- **IDE (Integrated Development Environment):** We recommend using [Visual Studio](https://visualstudio.microsoft.com/) or [JetBrains Rider](https://www.jetbrains.com/rider/) as your IDE for C# development.
- Make sure to install the necessary extensions and plugins for a better development experience.
### 3.2 .NET Installation:
- This project is built using [.NET 9.0](https://dotnet.microsoft.com/download/dotnet/9.0), the latest version of the .NET platform.
We recommend installing .NET 9.0 by following the instructions provided on the official [.NET website](https://dotnet.microsoft.com/download/dotnet/9.0).
```bash
# Check your current .NET version
dotnet --version
```
### 3.3 Version Control:
- If you haven't already, install a version control system such as [Git](https://git-scm.com/) to track changes and collaborate with other developers.
### 3.4 Clone the Repository:
- Clone the project repository to your local machine using the following command:
```bash
git clone https://github.com/asv-soft/asv-drones.git
```
### 3.5 Restore Dependencies:
- Navigate to the platform project directory and restore the required dependencies.
There are three possible platform directories to build and debug our app: __Asv.Drones.Desktop__, __Asv.Drones.Android__, __Asv.Drones.iOS__.
Currently, we support only the desktop platform.
For example, we will use __Asv.Drones.Desktop__ platform, so you have to execute the following command:
```bash
cd asv-drones/src/Asv.Drones.Desktop
dotnet workload restore
dotnet workload repair
```
### 3.6 Build and Run:
- After restoring, you have to build the project to ensure that everything is set up correctly, and if it's not - try to restore workloads again:
```bash
dotnet build
```
- Run the project:
```bash
dotnet run
```
Congratulations! Your development environment is now set up, and you are ready to start contributing to the project.
If you encounter any issues during the setup process, refer to the project's documentation or reach out to the development team for assistance.
### Building for Android
Coming soon...
## 4. Code Structure
The organization of the codebase plays a crucial role in maintaining a clean, scalable and easily understandable project.
This section outlines the structure of our codebase, highlighting key directories and their purposes.
### 4.1 Solution Organization
Our solution is organized the following way:
- **`src/`:** This directory contains the source code of the application.
The code is further organized into projects, each residing in its own subdirectory. The goal is to promote modularity and maintainability.
```
src/
├── Asv.Drones.Android/
├── Asv.Drones.Desktop/
├── Asv.Drones.iOS/
├── Asv.Drones/
│ ├── Commands/
│ ├── Controls/
│ ├── Shell/
│ └── ...
└── Asv.Drones.Api/
├── Commands/
├── Controls/
├── Shell/
└── ...
### 4.2 Naming Conventions
Consistent naming conventions are essential for code readability.
Throughout the codebase, we follow the guidelines outlined [in our documentation](https://docs.asv.me/use-cases/for-developers)
These conventions contribute to a unified and coherent codebase.
By adhering to this organized structure and naming conventions, we aim to create a codebase that is easy to navigate, scalable and conducive to collaboration among developers.
## 5. Coding Style
Maintaining a consistent coding style across the project enhances readability, reduces errors and facilitates collaboration.
The following guidelines outline our preferred coding style for C#:
**Note:** We have auto formatters in our project to make your life easier. Read more about them in the husky section.
### 5.1 C# Coding Style
#### 5.1.1 Formatting
- **Indentation:** Use tabs for indentation. Each level of indentation should consist of one tab.
- **Brace Placement:** Place opening braces on the same line as the statement they belong to, and closing braces on a new line.
```c#
// Good
if (condition)
{
// Code here
}
// Bad
if (condition) {
// Code here
}
```
#### 5.1.2 Naming Conventions
- **Pascal Case:** Use Pascal case for class names, method names and property names.
```c#
public class MyClass
{
public void MyMethod()
{
// Code here
}
public int MyProperty { get; set; }
}
```
#### 5.1.3 Language Features
- **Expression-bodied Members:** Utilize expression-bodied members for concise one-liners.
```c#
// Good
public int CalculateSquare(int x) => x * x;
// Bad
public int CalculateSquare(int x)
{
return x * x;
}
```
- **Null Conditional Operator:** Use the null conditional operator (`?.`) for safe property or method access.
```c#
// Good
int? length = text?.Length;
// Bad
int length = (text != null) ? text.Length : 0;
// or
int length = text!.Length;
```
#### 5.1.4 Special Cases
Usually you place public members after private members, but we have some exceptions:
- **Page constants:** We place page ids and viewmodel ids at the top of the class.
```c#
public const string PageId = "files.browser";
public const MaterialIconKind PageIcon = MaterialIconKind.FolderEye;
```
- **Export information:** We place export information at the end of the class.
```c#
```
#### 5.1.5 Husky
We use husky to make sure that our code is formatted correctly before committing. You may use it for your own code.
1. Go to the src folder
2. run:
```bash
dotnet tool restore
```
3. run (use husky-unix instead of husky if you are on linux or macOS):
```bash
dotnet r husky
```
4. run the following command to format the code:
```bash
dotnet husky run
```
### 5.2 Documentation
#### 5.2.1 Comments
- **XML Documentation:** Include XML comments for classes, methods, and properties to provide comprehensive documentation.
```c#
///
/// Represents a sample class.
///
public class SampleClass
{
///
/// Calculates the sum of two numbers.
///
/// The first number.
/// The second number.
/// The sum of the two numbers.
public int Add(int a, int b)
{
// Code here
}
}
```
#### 5.2.2 Code Comments
- Use comments sparingly and focus on explaining complex or non-intuitive code sections.
By adhering to these coding style guidelines, we aim to create code that is straightforward to read, understand, and maintain.
## 6. Version Control
Version control is a fundamental aspect of our development process, providing a systematic way to track changes, collaborate with team members and manage the evolution of our codebase.
We use Git as our version control system.
### 6.1 Branching Strategy
#### 6.1.1 Feature Branches
For each new feature or bug fix, create a dedicated feature branch.
The branch name should be descriptive of the feature or issue it addresses.
```bash
# Example: Creating a new feature branch
git checkout -b feature/my-new-feature
```
#### 6.1.2 Hotfix Branches
In case of critical issues in the production environment, create a hotfix branch.
This allows for a quick resolution without affecting the main development branch.
```bash
# Example: Creating a hotfix branch
git checkout -b hotfix/1.0.1
```
### 6.2 Commit Messages
Write clear and concise commit messages that convey the purpose of the change. Follow these guidelines:
- Start with a verb in the imperative mood (e.g., "Add," "Fix," "Update").
- Keep messages short but descriptive.
- Use present tense.
- Use feat, fix or chore prefixes to indicate the type of change.
Example:
```bash
# Good
git commit -m "fix: add user authentication feature"
# Bad
git commit -m "Updated stuff"
```
### 6.3 Pull Requests
Before merging changes into the main branch, create a pull request (PR).
This allows for code review and ensures that changes adhere to coding standards.
- Assign reviewers to the PR.
- Include a clear description of the changes.
- Ensure that automated tests pass before merging.
### 6.4 Merging Strategy
Adopt a merging strategy based on the nature of the changes:
- **Feature Branches:** Merge feature branches into the main branch after code review and approval.
- **Release Branches:** Merge release branches into the main branch and tag the commit for the release.
```bash
# Example: Merging a feature branch
git checkout main
git merge --no-ff feature/my-new-feature
```
### 6.5 Repository Hosting
Our Git repository is hosted on [GitHub](https://github.com/asv-soft/asv-drones).
Ensure that you have the necessary permissions and follow the best practices for repository management.
By following these version control practices, we aim to maintain a well-organized and collaborative development process.
## 7. Build and Deployment
The build and deployment processes are crucial parts of our development workflow.
This section outlines the steps for building the project and deploying it using GitHub Actions.
### 7.1 Build Process
To compile the project, use the following command:
```bash
dotnet build
```
This command compiles the code and produces executable binaries.
### 7.2 Deployment using GitHub Releases
Our application is deployed using [GitHub Actions](https://docs.github.com/en/actions).
The latest release can be found [here](https://github.com/asv-soft/asv-drones/releases).
## 8. Contributing
We welcome contributions from the community to help enhance and improve our project.
Before contributing, please take a moment to review this guide.
### 8.1 Code Reviews
All code changes undergo a review process to ensure quality and consistency. Here are the steps to follow:
1. **Fork the Repository:** Start by forking the repository to your own GitHub account.
2. **Create a Feature Branch:** Create a new branch for your feature or bug fix.
```bash
git checkout -b feature/my-feature
```
3. **Commit Changes:** Make your changes, commit them with clear and concise messages, and push the branch to your forked repository.
```bash
git commit -m "feat: add new feature"
git push origin feature/my-feature
```
4. **Squash your commit:** Squash your commits into a single commit before submitting a pull request.
```bash
git rebase -i main
# squash your commits into a single commit by leaving the first line as "pick" and changing the rest to "squash"
git push --force
```
5**Open a Pull Request (PR):** Submit a pull request to the main repository, detailing the changes made and any relevant information. Ensure your PR adheres to the established coding standards.
6**Code Review:** Participate in the code review process by responding to feedback and making necessary adjustments.
Addressing comments promptly helps streamline the review process.
7**Merge:** Once the code review is complete and the changes are approved, your pull request will be merged into the main branch.
### 8.2 Submitting Changes
Before submitting changes, ensure the following:
- **Coding Standards:** Adhere to the coding standards and guidelines outlined in this document.
- **Tests:** If applicable, include tests for your changes and ensure that existing tests pass.
- **Documentation:** Update relevant documentation, including code comments and external documentation, to reflect your changes.
### 8.3 Communication
For larger changes or feature additions, it's beneficial to discuss the proposed changes beforehand. Engage with the community through:
- **Opening an Issue:** Discuss your proposed changes by opening an issue.
This provides an opportunity for community input before investing significant time in development.
- **Joining Discussions:** Participate in existing discussions related to the project. Your insights and feedback are valuable.
### 8.4 Contributor License Agreement (CLA)
By contributing to this project, you agree that your contributions will be licensed under the project's license.
If a Contributor License Agreement (CLA) is required, it will be provided in the repository.
We appreciate your contributions, and together we can make this project even better!
## 9. Code Documentation
Clear and comprehensive code documentation is essential for ensuring that developers can easily understand, use and contribute to the project.
Follow these guidelines for documenting your code:
### 9.1 Inline Comments
Use inline comments to explain specific sections of your code, especially for complex logic or non-intuitive implementations. Follow these principles:
- **Clarity:** Write comments that enhance code comprehension. If a piece of code is not self-explanatory, provide comments explaining the reasoning or intention.
- **Conciseness:** Keep comments concise and to the point. Avoid unnecessary comments that do not add value.
- **Update Comments:** Regularly review and update comments to reflect any changes in the code. Outdated comments can be misleading.
Example:
```c#
// Calculate the sum of two numbers
int CalculateSum(int a, int b)
{
return a + b;
}
```
### 9.2 XML Documentation
For classes, methods, properties and other significant code elements, use XML documentation comments to provide comprehensive information. Follow these guidelines:
- **Summary:** Provide a summary that succinctly describes the purpose of the class or member.
- **Parameters:** Document each parameter, specifying its purpose and any constraints.
- **Returns:** If applicable, document the return value and its significance.
- **Examples:** Include examples that demonstrate how to use the class or member.
Example:
```c#
///
/// Represents a utility class for mathematical operations.
///
public class MathUtility
{
///
/// Calculates the sum of two numbers.
///
/// The first number.
/// The second number.
/// The sum of the two numbers.
public int CalculateSum(int a, int b)
{
return a + b;
}
}
```
### 9.3 Consistency
Ensure consistency in your documentation style across the codebase.
Consistent documentation makes it easier for developers to navigate and understand the project.
By following these documentation guidelines, we aim to create a codebase that is not only functional but also accessible and easily maintainable for all contributors.
## 10. Security
Ensuring the security of our software is paramount to maintaining the integrity and confidentiality of user data.
Developers should adhere to best practices and follow guidelines outlined in this section.
### 10.1 Code Security Practices
#### 10.1.1 Input Validation
Always validate and sanitize user input to prevent injection attacks and ensure the integrity of your application.
```c#
// Example for C#
public ActionResult ProcessUserInput(string userInput)
{
if (string.IsNullOrWhiteSpace(userInput))
{
// Handle invalid input
}
// Process input
}
```
#### 10.1.2 Authentication and Authorization
Implement secure authentication and authorization mechanisms to control access to sensitive functionalities and data.
Leverage industry-standard protocols like OAuth when applicable.
#### 10.1.3 Secure Communication
Ensure that communication between components, APIs and external services is encrypted using secure protocols (e.g., HTTPS).
### 10.2 Dependency Security
#### 10.2.1 Dependency Scanning
Regularly scan and update dependencies to identify and address security vulnerabilities.
Leverage tools and services that provide automated dependency analysis.
#### 10.2.2 Minimal Dependencies
Keep dependencies to a minimum and only include libraries and packages that are actively maintained and have a good security track record.
### 10.3 Data Protection
#### 10.3.1 Encryption
Sensitive data, both at rest and in transit, should be encrypted. Use strong encryption algorithms and ensure proper key management.
#### 10.3.2 Data Backups
Implement regular data backup procedures to prevent data loss in the event of security incidents or system failures.
### 10.4 Secure Coding Standards
Adhere to secure coding standards to mitigate common vulnerabilities.
Follow principles such as the [OWASP Top Ten](https://owasp.org/www-project-top-ten/) to address security concerns in your codebase.
### 10.5 Reporting Security Issues
If you discover a security vulnerability or have concerns about the security of the project, please report it immediately to our team at [our telegram channel](https://t.me/asvsoft).
Do not disclose security-related issues publicly until they have been addressed.
### 9.6 Security Training
Encourage ongoing security training for all team members to stay informed about the latest security threats and best practices.
Knowledgeable developers are key to maintaining a secure codebase.
By incorporating security practices into our development process, we aim to create a robust and secure software environment for our users.
## 11. License
This project is licensed under the terms of the MIT License.
A copy of the MIT License is provided in the [LICENSE](https://github.com/asv-soft/asv-drones?tab=MIT-1-ov-file#) file.
### MIT License
```
MIT License
Copyright (c) 2023 Asv Soft LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
### Using the MIT License
The MIT License is a permissive open-source license that allows for the free use, modification and distribution of the software.
It is important to review and understand the terms of the license before using, contributing to or distributing this software.
By contributing to this project, you agree that your contributions will be licensed under the MIT License.
For more details about the MIT License, please visit [opensource.org/licenses/MIT](https://opensource.org/licenses/MIT).
## 12. Contact
If you have questions, suggestions, or need assistance with the project, we encourage you to reach out through the following channels:
### 12.1 Telegram Channel
Visit our Telegram channel: [ASVSoft on Telegram](https://t.me/asvsoft)
Feel free to join our Telegram community to engage in discussions, seek help, or share your insights.
### 12.2 GitHub Issues
For bug reports, feature requests or any project-related discussions, please use our GitHub Issues:
[Project Issues on GitHub](https://github.com/asv-soft/asv-drones/issues)
Our GitHub repository is the central hub for project-related discussions and issue tracking.
Please check existing issues before creating new ones to avoid duplication.
### 12.3 Security Concerns
If you discover a security vulnerability or have concerns about the security of the project, please report it immediately to our telegram channel: [ASVSoft on Telegram](https://t.me/asvsoft).
Do not disclose security-related issues publicly until they have been addressed.
### 12.4 General Inquiries
For general inquiries or if you prefer email communication, you can reach us at [me@asv.me](mailto:me@asv.me).
We value your feedback and contributions, and we look forward to hearing from you!
================================================
FILE: api_build.bat
================================================
@echo off
setlocal enabledelayedexpansion
rem ====== projects ======
set project=Asv.Drones.Gui.Api
set "file=.\src\Directory.Build.props"
:: ApiVersion
for /f "tokens=2 delims=> " %%a in ('findstr /i /c:"" "%file%"') do (
set "line=%%a"
for /f "tokens=1 delims=<" %%b in ("!line!") do (
set "ApiVersion=%%b"
)
)
::
if defined ApiVersion (
echo ApiVersion: %ApiVersion%
dotnet restore ./src/%project%/%project%.csproj
dotnet build /p:SolutionDir=../;ProductVersion=%ApiVersion% ./src/%project%/%project%.csproj -c Release
dotnet pack ./src/%project%/%project%.csproj -c Release
) else (
echo ApiVersion not found
)
endlocal
pause
================================================
FILE: api_publish_github.bat
================================================
@echo off
setlocal enabledelayedexpansion
rem ====== projects ======
set project=Asv.Drones.Gui.Api
set "file=.\src\Directory.Build.props"
:: ApiVersion
for /f "tokens=2 delims=> " %%a in ('findstr /i /c:"" "%file%"') do (
set "line=%%a"
for /f "tokens=1 delims=<" %%b in ("!line!") do (
set "ApiVersion=%%b"
)
)
::
if defined ApiVersion (
echo ApiVersion: %ApiVersion%
cd src\%project%\bin\Release\
rem dotnet nuget push %project%.%ApiVersion%.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json
dotnet nuget push %project%.%ApiVersion%.nupkg --skip-duplicate --source https://nuget.pkg.github.com/asv-soft/index.json
) else (
echo ApiVersion not found
)
endlocal
pause
================================================
FILE: publish.bat
================================================
cd publish
for /d %%i in (".\*") do (
rmdir /s /q "%%i"
)
cd ../src/Asv.Drones.Gui.Desktop
dotnet publish -c Release -r win-arm --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ~/../../../publish/win-arm/app
dotnet publish -c Release -r win-arm64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ~/../../../publish/win-arm64/app
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ~/../../../publish/win-x64/app
dotnet publish -c Release -r win-x86 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ~/../../../publish/win-x86/app
dotnet publish -c Release -r linux-arm --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ~/../../../publish/linux-arm/app
dotnet publish -c Release -r linux-arm64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ~/../../../publish/linux-arm64/app
dotnet publish -c Release -r linux-musl-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ~/../../../publish/linux-musl-x64/app
dotnet publish -c Release -r linux-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ~/../../../publish/linux-x64/app
dotnet publish -c Release -r osx-arm64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ~/../../../publish/osx-arm64/app
dotnet publish -c Release -r osx-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ~/../../../publish/osx-x64/app
cd ../../publish
del /S *.pdb
cd win-arm/app
move Asv.Drones.Gui.Desktop.exe asv-drones-win-arm.exe
cd ../../win-arm64/app
move Asv.Drones.Gui.Desktop.exe asv-drones-win-arm64.exe
cd ../../win-x64/app
move Asv.Drones.Gui.Desktop.exe asv-drones-win-x64.exe
cd ../../win-x86/app
move Asv.Drones.Gui.Desktop.exe asv-drones-win-x86.exe
cd ../../linux-arm/app
move Asv.Drones.Gui.Desktop asv-drones-linux-arm
cd ../../linux-arm64/app
move Asv.Drones.Gui.Desktop asv-drones-linux-arm64
cd ../../linux-musl-x64/app
move Asv.Drones.Gui.Desktop asv-drones-linux-musl-x64
cd ../../linux-x64/app
move Asv.Drones.Gui.Desktop asv-drones-linux-x64
cd ../../osx-arm64/app
move Asv.Drones.Gui.Desktop asv-drones-osx-arm64
cd ../../osx-x64/app
move Asv.Drones.Gui.Desktop asv-drones-osx-x64
cd ../../..
setlocal enabledelayedexpansion
set "xmlFile=src\Directory.Build.props"
for /f "tokens=2 delims=<> " %%a in ('findstr /i "" "%xmlFile%"') do (
set "productVersion=%%a"
)
set "issFile=win-arm-install.iss"
set "tempFile=%temp%\temp.iss"
for /f "tokens=*" %%a in ('type "%issFile%"') do (
set "line=%%a"
echo !line! | findstr /C:"#define MyAppVersion" > nul
if !errorlevel! == 0 (
echo #define MyAppVersion "%productVersion%" >> "%tempFile%"
) else (
echo !line! >> "%tempFile%"
)
)
move /y "%tempFile%" "%issFile%" > nul
set "issFile=win-arm64-install.iss"
set "tempFile=%temp%\temp.iss"
for /f "tokens=*" %%a in ('type "%issFile%"') do (
set "line=%%a"
echo !line! | findstr /C:"#define MyAppVersion" > nul
if !errorlevel! == 0 (
echo #define MyAppVersion "%productVersion%" >> "%tempFile%"
) else (
echo !line! >> "%tempFile%"
)
)
move /y "%tempFile%" "%issFile%" > nul
set "issFile=win-x64-install.iss"
set "tempFile=%temp%\temp.iss"
for /f "tokens=*" %%a in ('type "%issFile%"') do (
set "line=%%a"
echo !line! | findstr /C:"#define MyAppVersion" > nul
if !errorlevel! == 0 (
echo #define MyAppVersion "%productVersion%" >> "%tempFile%"
) else (
echo !line! >> "%tempFile%"
)
)
move /y "%tempFile%" "%issFile%" > nul
set "issFile=win-x86-install.iss"
set "tempFile=%temp%\temp.iss"
for /f "tokens=*" %%a in ('type "%issFile%"') do (
set "line=%%a"
echo !line! | findstr /C:"#define MyAppVersion" > nul
if !errorlevel! == 0 (
echo #define MyAppVersion "%productVersion%" >> "%tempFile%"
) else (
echo !line! >> "%tempFile%"
)
)
move /y "%tempFile%" "%issFile%" > nul
endlocal
cd publish
iscc ../win-arm-install.iss
iscc ../win-arm64-install.iss
iscc ../win-x86-install.iss
iscc ../win-x64-install.iss
wsl sed -i 's/\r//' linux_packages.sh
wsl sed -i 's/\r//' osx_packages.sh
wsl ../linux_packages.sh
wsl ../osx_packages.sh
================================================
FILE: src/.aiassistant/rules/comments.md
================================================
---
apply: always
---
## Comment and Documentation Language
- Write all code comments in English.
- Write all XML documentation, Markdown documentation, README content, and other developer-facing documentation in English.
- Do not use Russian or mixed-language comments or documentation.
- Keep terminology consistent across code, comments, and documentation.
- Use clear English names for types, members, variables, files, modules, and public APIs.
## Comment Quality
- Prefer self-explanatory code over excessive comments.
- Add comments only when they explain intent, constraints, assumptions, tradeoffs, or non-obvious behavior.
- Do not add comments that only restate what the code already makes obvious.
- Keep comments concise, accurate, and aligned with the current implementation.
- Update or remove comments when the code changes so documentation never becomes misleading.
## Architecture and Design
- Keep the architecture clean, modular, and easy to maintain.
- Follow SOLID principles in design and implementation.
- Give each class, service, and module a single, well-defined responsibility.
- Prefer composition over inheritance unless inheritance is clearly justified.
- Minimize coupling and keep related behavior cohesive.
- Separate domain logic from UI, infrastructure, persistence, and framework-specific concerns.
- Depend on abstractions at system boundaries when this improves testability, extensibility, or clarity.
- Keep public APIs explicit, stable, and easy to understand.
- Eliminate duplicated logic through extraction or refactoring instead of copying behavior.
- Avoid god objects, hidden side effects, and unclear ownership of responsibilities.
---
apply: always
---
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
## 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
---
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
================================================
FILE: src/.editorconfig
================================================
[*.cs]
dotnet_diagnostic.RCS1037.severity = none
dotnet_diagnostic.RCS1251.severity = none
[*.axaml]
csharpier_formatter = xml
indent_size = 4
================================================
FILE: src/.gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# JetBrains Rider
.idea/
*.sln.iml
##
## Visual Studio Code
##
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.qodo
================================================
FILE: src/.run/Publish win-x64.run.xml
================================================
================================================
FILE: src/Asv.Drones/App.axaml
================================================
================================================
FILE: src/Asv.Drones/App.axaml.cs
================================================
using Asv.Avalonia;
using Avalonia.Markup.Xaml;
namespace Asv.Drones;
public partial class App : AsvApplication
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
}
================================================
FILE: src/Asv.Drones/Asv.Drones.csproj
================================================
$(TargetFramework)
$(ProductVersion)
$(ProductVersion)
https://github.com/asv-soft
https://github.com/asv-soft
https://github.com/asv-soft
enable
preview
true
true
../CodeStyle.ruleset
CS0169,
CS0618,
CS1502,
CS1503,
CS8524,
CS8600,
CS8601,
CS8602,
CS8603,
CS8604,
CS8625,
CS8629,
CS8762,
CA1510,
CA1851
true
all
runtime; build; native; contentfiles; analyzers; buildtransitive
all
runtime; build; native; contentfiles; analyzers; buildtransitive
PublicResXFileCodeGenerator
RS.Designer.cs
True
True
RS.resx
BurstDownloadDialogView.axaml
Code
================================================
FILE: src/Asv.Drones/Asv.Drones.csproj.DotSettings
================================================
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
================================================
FILE: src/Asv.Drones/AsvDronesMixin.cs
================================================
using System;
using Asv.Avalonia;
using Asv.Avalonia.IO;
using Asv.Drones.Api;
using Asv.Drones.Plane;
using Asv.Mavlink;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Asv.Drones;
public static class AsvDronesMixin
{
extension(IHostApplicationBuilder builder)
{
public IHostApplicationBuilder UseDronesApp(Action? configure = null)
{
configure ??= b => b.UseDefault();
configure(new Builder(builder));
return builder;
}
}
public class Builder(IHostApplicationBuilder builder)
{
public IHostApplicationBuilder Parent => builder;
public Builder UseDefault()
{
builder.Services.AddSingleton();
builder.Services.AddSingleton();
return UseMavlinkHost()
.UseMavParams()
.UseOptionalPacketViewer()
.UseFlightMode()
.UseExtendableFlightMode()
.UseCommands()
.UseFileBrowser()
.UseSetupPage();
}
public Builder UseControls()
{
builder.ViewLocator.RegisterViewFor();
builder.ViewLocator.RegisterViewFor<
VelocityUavIndicatorViewModel,
VelocityUavIndicator
>();
builder.ViewLocator.RegisterViewFor<
BatteryUavIndicatorViewModel,
BatteryUavIndicator
>();
return this;
}
public Builder UseCommands()
{
builder.Services.AddSingleton();
builder
.RegisterMavlinkCommands()
.Commands.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register()
.Register();
return this;
}
public Builder UseMavlinkHost()
{
builder.Services.AddSingleton();
builder.Services.AddHostedService(svc => svc.GetRequiredService());
builder.Services.AddSingleton(svc =>
svc.GetRequiredService()
);
builder.Services.AddSingleton(svc =>
svc.GetRequiredService()
);
return this;
}
public Builder UseMavParams()
{
builder.ViewLocator.RegisterViewFor();
builder.ViewLocator.RegisterViewFor();
builder.ViewLocator.RegisterViewFor();
builder.ViewLocator.RegisterViewFor<
MavParamAltitudeTextBoxViewModel,
MavParamAltitudeTextBoxView
>();
builder.ViewLocator.RegisterViewFor<
MavParamLatLonTextBoxViewModel,
MavParamLatLonTextBoxView
>();
builder.ViewLocator.RegisterViewFor<
MavParamAsciiCharViewModel,
MavParamAsciiCharView
>();
builder.ViewLocator.RegisterViewFor();
builder.Shell.Pages.Register(
MavParamsPageViewModel.PageId
);
builder.Shell.Pages.Home.UseItemExtension();
builder.ViewLocator.RegisterViewFor();
builder.ViewLocator.RegisterViewFor<
TryCloseWithApprovalDialogViewModel,
TryCloseWithApprovalDialogView
>();
return this;
}
public Builder UseFileBrowser()
{
builder.Shell.Pages.Register(
FileBrowserViewModel.PageId
);
builder.Shell.Pages.Home.UseItemExtension();
builder.ViewLocator.RegisterViewFor<
BurstDownloadDialogViewModel,
BurstDownloadDialogView
>();
return this;
}
public Builder UseFlightMode()
{
builder.Shell.Pages.Register(
FlightPageViewModel.PageId
);
builder.Shell.Pages.Home.UseExtension();
builder.Extensions.Register();
builder.Extensions.Register();
builder.ViewLocator.RegisterViewFor();
builder.ViewLocator.RegisterViewFor();
builder.ViewLocator.RegisterViewFor<
SetAltitudeDialogViewModel,
SetAltitudeDialogView
>();
return this;
}
public Builder UseExtendableFlightMode()
{
// FlightMode
builder.Shell.Pages.Register(
FlightModePageViewModel.PageId
);
builder.Shell.Pages.Home.UseExtension();
// Anchors
builder.Extensions.Register();
// Factory for client device widgets
builder.Services.AddSingleton();
// Create widgets for client devices
builder.Extensions.Register();
// Widget for all drones
builder.Services.AddSingleton<
IClientDeviceWidgetCreationHandler,
DroneWidgetCreationHandler
>();
builder.Services.AddTransient();
builder.ViewLocator.RegisterViewFor();
// Sections for the drone Widget
builder.Services.AddKeyedTransient(
TelemetrySectionViewModel.SectionId
);
builder.Extensions.Register<
IDroneFlightWidget,
DroneFlightWidgetTelemetrySectionExtension
>();
builder.ViewLocator.RegisterViewFor();
builder.Services.AddKeyedTransient(
AttitudeIndicatorSectionViewModel.SectionId
);
builder.Extensions.Register<
IDroneFlightWidget,
DroneFlightWidgetExtensionAttitudeIndicatorSection
>();
builder.ViewLocator.RegisterViewFor<
AttitudeIndicatorSectionViewModel,
AttitudeIndicatorSectionView
>();
builder.Services.AddKeyedTransient(
FlightControlSectionViewModel.SectionId
);
builder.Extensions.Register<
IDroneFlightWidget,
DroneFlightWidgetFlightControlSectionExtension
>();
builder.ViewLocator.RegisterViewFor<
FlightControlSectionViewModel,
FlightControlSectionView
>();
// Test plugin widget
builder.ViewLocator.RegisterViewFor();
builder.Extensions.Register();
// Test plane widget
builder.Services.AddSingleton<
IClientDeviceWidgetCreationHandler,
PlaneWidgetCreationHandler
>();
builder.Services.AddTransient();
builder.ViewLocator.RegisterViewFor();
// Test plane section
builder.Services.AddKeyedTransient(
PlaneSectionViewModel.SectionId
);
builder.Extensions.Register();
builder.ViewLocator.RegisterViewFor();
return this;
}
public Builder UseOptionalPacketViewer()
{
builder.Shell.Pages.Register(
PacketViewerViewModel.PageId
);
builder.Shell.Pages.Home.UseExtension();
builder.ViewLocator.RegisterViewFor();
builder.Services.AddSingleton();
builder.ViewLocator.RegisterViewFor<
SavePacketMessagesDialogViewModel,
SavePacketMessagesDialogView
>();
return this;
}
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Behaviour/Remove/RemoveItemCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.Drones.Api;
using Material.Icons;
namespace Asv.Drones;
public class RemoveItemCommand : ContextCommand, IRemoveItemCommand
{
public const string Id = IRemoveItemCommand.CommandId;
private static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.RemoveItemCommand_CommandInfo_Name,
Description = RS.RemoveItemCommand_CommandInfo_Description,
Icon = MaterialIconKind.Delete,
DefaultHotKey = "Shift + Delete",
};
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
ISupportRemove context,
CommandArg newValue,
CancellationToken cancel
)
{
// TODO: make removing items command undoable
await context.RemoveAsync(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Behaviour/Rename/CommitRenameCommand.cs
================================================
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.Drones.Api;
using Material.Icons;
namespace Asv.Drones;
///
/// Executes with:
/// - arg["old"] as Old Value.
/// - arg["new"] as New Value.
///
public class CommitRenameCommand : ContextCommand, ICommitRenameCommand
{
public const string Id = ICommitRenameCommand.CommandId;
public const string OldValue = "old";
public const string NewValue = "new";
private static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.RenameItemCommand_CommandInfo_Name,
Description = RS.RenameItemCommand_CommandInfo_Description,
Icon = MaterialIconKind.Check,
DefaultHotKey = "Enter",
};
public override ICommandInfo Info => StaticInfo;
public override async ValueTask InternalExecute(
ISupportRename context,
DictArg arg,
CancellationToken cancel
)
{
arg.TryGetValue(OldValue, out var oldValue);
arg.TryGetValue(NewValue, out var newValue);
if (oldValue is not StringArg || newValue is not StringArg)
{
return null;
}
var oldString = oldValue.AsString();
var newString = newValue.AsString();
if (string.IsNullOrWhiteSpace(oldString) || string.IsNullOrWhiteSpace(newString))
{
return null;
}
try
{
await context.RenameAsync(oldString, newString, cancel);
}
catch
{
return null;
}
return CommandArg.CreateDictionary(
new Dictionary
{
{ NewValue, CommandArg.CreateString(oldString) },
{ OldValue, CommandArg.CreateString(newString) },
}
);
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/FileBrowser/FileBrowserViewModel/FindFileCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Material.Icons;
namespace Asv.Drones;
public class FindFileCommand : ContextCommand
{
public const string Id = $"{BaseId}.find_file";
private static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.FindFileOnLocalCommand_CommandInfo_Name,
Description = RS.FindFileOnLocalCommand_CommandInfo_Description,
Icon = MaterialIconKind.Magnify,
DefaultHotKey = null,
};
public override ICommandInfo Info => StaticInfo;
protected override ValueTask InternalExecute(
FileBrowserViewModel context,
CommandArg arg,
CancellationToken cancel
)
{
context.FindFileOnLocal();
return ValueTask.FromResult(null);
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/FileBrowser/Items/CalculateCrc32Command.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Material.Icons;
namespace Asv.Drones;
public class CalculateCrc32Command : ContextCommand
{
public const string Id = $"{BaseId}.crc32";
private static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.CalculateCrc32Command_CommandInfo_Name,
Description = RS.CalculateCrc32Command_CommandInfo_Description,
Icon = MaterialIconKind.KeyOutline,
DefaultHotKey = "Ctrl + Q",
};
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
IBrowserItemViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
await context.CalculateCrc32Async(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/FileBrowser/Items/CreateDirectoryCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Material.Icons;
namespace Asv.Drones;
public class CreateDirectoryCommand : ContextCommand
{
public const string Id = $"{BaseId}.create_directory";
private static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.CreateDirectoryCommand_CommandInfo_Name,
Description = RS.CreateDirectoryCommand_CommandInfo_Description,
Icon = MaterialIconKind.FolderAdd,
DefaultHotKey = "Ctrl + N",
};
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
IBrowserItemViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
await context.CreateDirectoryAsync(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/FileBrowser/OpenFileBrowserCommand.cs
================================================
using Asv.Avalonia;
namespace Asv.Drones;
public class OpenFileBrowserCommand(INavigationService nav)
: OpenPageCommandBase(FileBrowserViewModel.PageId, nav)
{
#region Static
public const string Id = $"{BaseId}.open.{FileBrowserViewModel.PageId}";
public static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.OpenFileBrowserCommand_CommandInfo_Name,
Description = RS.OpenFileBrowserCommand_CommandInfo_Description,
Icon = FileBrowserViewModel.PageIcon,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
}
================================================
FILE: src/Asv.Drones/Core/Commands/FileBrowser/Transfer/BurstDownloadItemCommand.cs
================================================
using System;
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.Mavlink;
using Material.Icons;
namespace Asv.Drones;
///
/// Executes with:
/// - arg["src"] as string sourcePath.
/// - arg["dst"] as string destinationPath.
/// - arg["prt"] as int partSize.
/// - arg["typ"] as string entryType.
///
public class BurstDownloadItemCommand : TransferCommandBase
{
public const string Id = $"{BaseIdTransferCmd}.burst_download";
private static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.BurstDownloadItemCommand_CommandInfo_Name,
Description = RS.BurstDownloadItemCommand_CommandInfo_Description,
Icon = MaterialIconKind.TransferLeft,
DefaultHotKey = null,
};
public override ICommandInfo Info => StaticInfo;
public override async ValueTask InternalExecute(
ITransferFtpEntries context,
DictArg newValue,
CancellationToken cancel
)
{
if (!TryReadRequiredString(newValue, SourcePath, out var src))
{
return null;
}
if (!TryReadRequiredString(newValue, DestinationPath, out var dst))
{
return null;
}
if (!TryReadRequiredByte(newValue, PartSize, out var part))
{
return null;
}
if (!TryReadRequiredEntryType(newValue, EntryType, out var type))
{
return null;
}
await context.BurstDownloadItem(src, dst, part, type, cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/FileBrowser/Transfer/DownloadItemCommand.cs
================================================
using System;
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.Mavlink;
using Material.Icons;
namespace Asv.Drones;
///
/// Executes with:
/// - arg["src"] as string sourcePath.
/// - arg["dst"] as string destinationPath.
/// - arg["prt"] as int partSize.
/// - arg["typ"] as string entryType.
///
public class DownloadItemCommand : TransferCommandBase
{
public const string Id = $"{BaseIdTransferCmd}.download";
private static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.DownloadItemCommand_CommandInfo_Name,
Description = RS.DownloadItemCommand_CommandInfo_Description,
Icon = MaterialIconKind.TransferLeft,
DefaultHotKey = null,
};
public override ICommandInfo Info => StaticInfo;
public override async ValueTask InternalExecute(
ITransferFtpEntries context,
DictArg newValue,
CancellationToken cancel
)
{
if (!TryReadRequiredString(newValue, SourcePath, out var src))
{
return null;
}
if (!TryReadRequiredString(newValue, DestinationPath, out var dst))
{
return null;
}
if (!TryReadRequiredByte(newValue, PartSize, out var part))
{
return null;
}
if (!TryReadRequiredEntryType(newValue, EntryType, out var type))
{
return null;
}
await context.DownloadItem(src, dst, part, type, cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/FileBrowser/Transfer/ITransferFtpEntries.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.Mavlink;
namespace Asv.Drones;
public interface ITransferFtpEntries : IRoutable
{
ValueTask UploadItem(
string source,
string destination,
FtpEntryType type,
CancellationToken ct
);
ValueTask DownloadItem(
string source,
string destination,
byte partSize,
FtpEntryType type,
CancellationToken ct
);
ValueTask BurstDownloadItem(
string source,
string destination,
byte partSize,
FtpEntryType type,
CancellationToken ct
);
}
================================================
FILE: src/Asv.Drones/Core/Commands/FileBrowser/Transfer/TransferCommandBase.cs
================================================
using System;
using Asv.Avalonia;
using Asv.Mavlink;
namespace Asv.Drones;
public abstract class TransferCommandBase : ContextCommand
{
protected const string BaseIdTransferCmd = $"{BaseId}.transfer";
public const string SourcePath = "src";
public const string DestinationPath = "dst";
public const string PartSize = "prt";
public const string EntryType = "typ";
protected static bool TryReadRequiredString(DictArg args, string key, out string value)
{
value = string.Empty;
if (!args.TryGetValue(key, out var v))
{
return false;
}
value = v.AsString();
return value.Length > 0;
}
protected static bool TryReadRequiredByte(DictArg args, string key, out byte value)
{
value = 0;
if (!args.TryGetValue(key, out var v))
{
return false;
}
var i = v.AsInt();
if (i is < byte.MinValue or > byte.MaxValue)
{
return false;
}
value = (byte)i;
return true;
}
protected static bool TryReadRequiredEntryType(
DictArg args,
string key,
out FtpEntryType entryType
)
{
entryType = default;
return args.TryGetValue(key, out var v)
&& Enum.TryParse(v.AsString(), ignoreCase: true, out entryType);
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/FileBrowser/Transfer/UploadItemCommand.cs
================================================
using System;
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.Mavlink;
using Material.Icons;
namespace Asv.Drones;
///
/// Executes with:
/// - arg["src"] as string sourcePath.
/// - arg["dst"] as string destinationPath.
/// - arg["typ"] as string entryType.
///
public class UploadItemCommand : TransferCommandBase
{
public const string Id = $"{BaseIdTransferCmd}.upload";
private static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.UploadItemCommand_CommandInfo_Name,
Description = RS.UploadItemCommand_CommandInfo_Description,
Icon = MaterialIconKind.TransferRight,
DefaultHotKey = null,
};
public override ICommandInfo Info => StaticInfo;
public override async ValueTask InternalExecute(
ITransferFtpEntries context,
DictArg newValue,
CancellationToken cancel
)
{
if (!TryReadRequiredString(newValue, SourcePath, out var src))
{
return null;
}
if (!TryReadRequiredString(newValue, DestinationPath, out var dst))
{
return null;
}
if (!TryReadRequiredEntryType(newValue, EntryType, out var type))
{
return null;
}
await context.UploadItem(src, dst, type, cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Flight/OpenFlight.cs
================================================
using Asv.Avalonia;
namespace Asv.Drones;
public class OpenFlightCommand(INavigationService nav)
: OpenPageCommandBase(FlightModePageViewModel.PageId, nav)
{
#region Static
public const string Id = $"{BaseId}.open.{FlightModePageViewModel.PageId}";
public static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = "Open Flight Mode (BETA)",
Description = "Command opens Flight Mode (BETA)",
Icon = FlightModePageViewModel.PageIcon,
DefaultHotKey = null, // TODO: add after BETA
};
#endregion
public override ICommandInfo Info => StaticInfo;
}
================================================
FILE: src/Asv.Drones/Core/Commands/Flight/OpenFlightMode.cs
================================================
using Asv.Avalonia;
using Asv.Drones.Api;
namespace Asv.Drones;
public class OpenFlightModeCommand(INavigationService nav)
: OpenPageCommandBase(FlightMode.PageId, nav)
{
#region Static
public const string Id = $"{BaseId}.open.{FlightMode.PageId}";
public static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.OpenFlightModeCommand_CommandInfo_Name,
Description = RS.OpenFlightModeCommand_CommandInfo_Description,
Icon = FlightMode.PageIcon,
DefaultHotKey = "Ctrl+F2",
};
#endregion
public override ICommandInfo Info => StaticInfo;
}
================================================
FILE: src/Asv.Drones/Core/Commands/Flight/Widgets/UavWidget/AutoModeCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.IO;
using Asv.Mavlink;
using Material.Icons;
namespace Asv.Drones;
public class AutoModeCommand : ContextCommand // TODO: make basic class for commands that change the uav mode
{
#region Static
public const string Id = $"{BaseId}.change.mode.auto";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.UavAction_AutoMode_Name,
Description = RS.UavAction_AutoMode_Description,
Icon = MaterialIconKind.Automatic,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
UavWidgetViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
var control = context.Device.GetMicroservice();
if (control == null)
{
return null;
}
await control.SetAutoMode(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Flight/Widgets/UavWidget/GuidedModeCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.IO;
using Asv.Mavlink;
using Material.Icons;
namespace Asv.Drones;
public class GuidedModeCommand : ContextCommand
{
#region Static
public const string Id = $"{BaseId}.change.mode.guided";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.UavAction_GuidedMode,
Description = RS.UavAction_GuidedMode_Description,
Icon = MaterialIconKind.Controller,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
UavWidgetViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
var control = context.Device.GetMicroservice();
if (control == null)
{
return null;
}
await control.SetGuidedMode(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Flight/Widgets/UavWidget/LandCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.IO;
using Asv.Mavlink;
using Material.Icons;
namespace Asv.Drones;
public class LandCommand : ContextCommand
{
#region Static
public const string Id = $"{BaseId}.uav.land";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.UavAction_Land,
Description = RS.UavAction_Land_Description,
Icon = MaterialIconKind.AeroplaneLanding,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
UavWidgetViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
var control = context.Device.GetMicroservice();
if (control == null)
{
return null;
}
await control.EnsureGuidedMode(cancel: cancel);
await control.DoLand(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Flight/Widgets/UavWidget/MissionProgress/UpdateMissionCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Material.Icons;
namespace Asv.Drones;
public class UpdateMissionCommand : ContextCommand
{
#region StaticInfo
public const string Id = $"{BaseId}.mission-items.update";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.UavAction_Land,
Description = RS.UavAction_Land_Description,
Icon = MaterialIconKind.Reload,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
MissionProgressViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
await context.InitiateMissionPoints(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Flight/Widgets/UavWidget/RTLCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.IO;
using Asv.Mavlink;
using Material.Icons;
namespace Asv.Drones;
public class RTLCommand : ContextCommand
{
#region Static
public const string Id = $"{BaseId}.uav.rtl";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.UavAction_Rtl_Name,
Description = RS.UavAction_Rtl_Description,
Icon = MaterialIconKind.Home,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
UavWidgetViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
var control = context.Device.GetMicroservice();
if (control is null)
{
return null;
}
await control.EnsureGuidedMode(cancel: cancel);
await control.DoRtl(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Flight/Widgets/UavWidget/StartMissionCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.IO;
using Asv.Mavlink;
using Material.Icons;
using R3;
namespace Asv.Drones;
public class StartMissionCommand : ContextCommand
{
#region Static
public const string Id = $"{BaseId}.uav.start";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.UavAction_StartMission,
Description = RS.UavAction_StartMission_Description,
Icon = MaterialIconKind.MapMarkerPath,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
UavWidgetViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
var control = context.Device.GetMicroservice();
var mission = context.Device.GetMicroservice();
if (control is null || mission is null)
{
return null;
}
context.MissionProgress.UpdateMission.Execute(Unit.Default);
await mission.SetCurrent(0, cancel);
await control.SetAutoMode(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Flight/Widgets/UavWidget/TakeOffCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.IO;
using Asv.Mavlink;
using Material.Icons;
namespace Asv.Drones;
public class TakeOffCommand : ContextCommand
{
#region Static
public const string Id = $"{BaseId}.uav.takeOff";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.UavAction_TakeOff,
Description = RS.UavAction_TakeOff_Description,
Icon = MaterialIconKind.AeroplaneTakeoff,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
public override async ValueTask InternalExecute(
UavWidgetViewModel context,
DoubleArg arg,
CancellationToken cancel
)
{
var device = context.Device;
var controlClient = device.GetMicroservice();
if (controlClient == null)
{
return null;
}
await controlClient.SetGuidedMode(cancel);
await controlClient.TakeOff(arg.Value, cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/MavParams/MavParamsPageViewModel/RemoveAllPinsCommand.cs
================================================
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Material.Icons;
namespace Asv.Drones;
public class RemoveAllPinsCommand : ContextCommand
{
public const string Id = $"{BaseId}.params.remove-all-pins";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.UnpinAllParamsCommand_CommandInfo_Name,
Description = RS.UnpinAllParamsCommand_CommandInfo_Description,
Icon = MaterialIconKind.PinOff,
DefaultHotKey = null, // TODO: make a key bind when new key listener system appears
};
public override ICommandInfo Info => StaticInfo;
public override ValueTask InternalExecute(
MavParamsPageViewModel context,
DictArg arg,
CancellationToken cancel
)
{
if (context.AllParams is null)
{
return ValueTask.FromResult(null);
}
if (arg.Count == 0)
{
var oldValue = new DictArg();
foreach (var item in context.AllParams.Where(item => item.IsPinned.ViewValue.Value))
{
oldValue.Add(
new KeyValuePair(item.Id.ToString(), new BoolArg(true))
);
item.IsPinned.ModelValue.Value = false;
}
var notSelected = context
.ViewedParams.Where(it => it.Id != context.SelectedItem.Value?.Id)
.ToArray();
foreach (var item in notSelected)
{
context.ViewedParams.Remove(item);
}
return ValueTask.FromResult(oldValue);
}
foreach (var item in context.AllParams.Where(item => arg.ContainsKey(item.Id.ToString())))
{
item.IsPinned.ModelValue.Value = !item.IsPinned.ModelValue.Value;
}
return ValueTask.FromResult(arg);
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/MavParams/MavParamsPageViewModel/StopUpdateParamsCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Material.Icons;
namespace Asv.Drones;
public class StopUpdateParamsCommand : ContextCommand
{
public const string Id = $"{BaseId}.params.stop-update";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.StopUpdateParamsCommand_CommandInfo_Name,
Description = RS.StopUpdateParamsCommand_CommandInfo_Description,
Icon = MaterialIconKind.CancelCircle,
DefaultHotKey = null, // TODO: make a key bind when new key listener system appears
};
public override ICommandInfo Info => StaticInfo;
protected override ValueTask InternalExecute(
MavParamsPageViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
context.StopUpdateParamsImpl();
return default;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/MavParams/MavParamsPageViewModel/UpdateParamsCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Material.Icons;
namespace Asv.Drones;
public class UpdateParamsCommand : ContextCommand
{
public const string Id = $"{BaseId}.params.update";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.UpdateParamsCommand_CommandInfo_Name,
Description = RS.UpdateParamsCommand_CommandInfo_Description,
Icon = MaterialIconKind.Refresh,
DefaultHotKey = null, // TODO: make a key bind when new key listener system appears
};
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
MavParamsPageViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
await context.UpdateParamsImpl(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/MavParams/OpenMavParamsCommand.cs
================================================
using Asv.Avalonia;
namespace Asv.Drones;
public class OpenMavParamsCommand(INavigationService nav)
: OpenPageCommandBase(MavParamsPageViewModel.PageId, nav)
{
#region Static
public const string Id = $"{BaseId}.open.{MavParamsPageViewModel.PageId}";
public static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.OpenMavParamsCommand_CommandInfo_Name,
Description = RS.OpenMavParamsCommand_CommandInfo_Description,
Icon = MavParamsPageViewModel.PageIcon,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
}
================================================
FILE: src/Asv.Drones/Core/Commands/Mavlink/MavlinkCommands.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.Drones.Api;
using Asv.Mavlink;
namespace Asv.Drones;
public class MavlinkCommands : IMavlinkCommands
{
public ICommandInfo WriteParamInfo => MavlinkParamsWriteCommand.StaticInfo;
public ValueTask WriteParam(
IRoutable context,
string name,
MavParamValue value,
CancellationToken cancel = default
) => MavlinkParamsWriteCommand.Execute(context, name, value, cancel);
public ICommandInfo ReadParamInfo => MavlinkParamReadCommand.StaticInfo;
public ValueTask ReadParam(
IRoutable context,
string name,
CancellationToken cancel = default
) => MavlinkParamReadCommand.Execute(context, name, cancel);
}
================================================
FILE: src/Asv.Drones/Core/Commands/Mavlink/MavlinkCommandsMixin.cs
================================================
using Asv.Drones.Api;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Asv.Drones;
public static class MavlinkCommandsMixin
{
public static IHostApplicationBuilder RegisterMavlinkCommands(
this IHostApplicationBuilder builder
)
{
builder.Services.AddSingleton();
return builder;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Mavlink/MavlinkParamReadCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.Drones.Api;
using Asv.Mavlink;
using Material.Icons;
namespace Asv.Drones;
public class MavlinkParamReadCommand : MavlinkMicroserviceCommand
{
public const string Id = $"{BaseId}.mavlink.param.read";
public static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.ReadParamCommand_CommandInfo_Name,
Description = RS.ReadParamCommand_CommandInfo_Description,
Icon = MaterialIconKind.Set,
DefaultHotKey = null,
};
public static ValueTask Execute(
IRoutable context,
string name,
CancellationToken cancel = default
)
{
return context.ExecuteCommand(Id, CommandArg.CreateString(name), cancel: cancel);
}
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
IParamsClientEx microservice,
StringArg arg,
CancellationToken cancel
)
{
await microservice.ReadOnce(arg.Value, cancel);
return null; // this is command without undo, so we return null
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Mavlink/MavlinkParamsWriteCommand.cs
================================================
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.Drones.Api;
using Asv.Mavlink;
using Material.Icons;
namespace Asv.Drones;
public class MavlinkParamsWriteCommand : MavlinkMicroserviceCommand
{
public const string Id = $"{BaseId}.mavlink.param.write";
public static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.WritePatamCommand_CommandInfo_Name,
Description = RS.WriteParamCommand_CommandInfo_Description,
Icon = MaterialIconKind.Set,
DefaultHotKey = null,
};
public static ValueTask Execute(
IRoutable context,
string name,
MavParamValue value,
CancellationToken cancel = default
)
{
return context.ExecuteCommand(
Id,
CommandArg.ChangeAction(name, CommandArg.CreateString(value.PrintValue())),
cancel
);
}
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
IParamsClientEx microservice,
ActionArg arg,
CancellationToken cancel
)
{
if (string.IsNullOrWhiteSpace(arg.SubjectId))
{
throw new ArgumentException(
$@"{nameof(arg.SubjectId)} cannot be null or empty.",
nameof(arg.SubjectId)
);
}
MavParamValue prevValue;
if (!microservice.Items.TryGetValue(arg.SubjectId, out var param))
{
prevValue = await microservice.ReadOnce(arg.SubjectId, cancel);
}
else
{
prevValue = param.Value.Value;
}
var stringValue = arg.Value?.AsString();
if (string.IsNullOrWhiteSpace(stringValue))
{
throw new ArgumentException(
$@"{nameof(arg.Value)} must be of type {CommandArg.Id.String}.",
nameof(arg.Value)
);
}
var result = MavParamValue.TryParseValue(stringValue, prevValue.Type, out var value);
if (!result.IsSuccess)
{
Debug.Assert(result.ValidationException != null, "result.ValidationException != null");
throw new ArgumentException(
$"Cannot parse value '{stringValue}' to type {prevValue.Type}: {result.ValidationException.Message}",
nameof(arg.Value),
result.ValidationException
);
}
Debug.Assert(value != null, nameof(value) + " != null");
await microservice.WriteOnce(arg.SubjectId, value.Value, cancel);
return CommandArg.ChangeAction(
arg.SubjectId,
CommandArg.CreateString(prevValue.PrintValue())
);
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Mavlink/NullMavlinkCommands.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Asv.Drones.Api;
using Asv.Mavlink;
namespace Asv.Drones;
public sealed class NullMavlinkCommands : IMavlinkCommands
{
public static NullMavlinkCommands Instance { get; } = new();
private NullMavlinkCommands() { }
public ICommandInfo WriteParamInfo => MavlinkParamsWriteCommand.StaticInfo;
public ValueTask WriteParam(
IRoutable context,
string name,
MavParamValue value,
CancellationToken cancel = default
)
{
return ValueTask.CompletedTask;
}
public ICommandInfo ReadParamInfo => MavlinkParamReadCommand.StaticInfo;
public ValueTask ReadParam(IRoutable context, string name, CancellationToken cancel = default)
{
return ValueTask.CompletedTask;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/PacketViewer/ClearAllPacketsCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Material.Icons;
namespace Asv.Drones;
public sealed class ClearAllPacketsCommand : ContextCommand
{
public const string Id = $"{BaseId}.packet-viewer.clear-all";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.ClearAllPacketsCommand_CommandInfo_Name,
Description = RS.ClearAllPacketsCommand_CommandInfo_Description,
Icon = MaterialIconKind.Bin,
DefaultHotKey = null, // TODO: make a key bind later
};
public override ICommandInfo Info => StaticInfo;
protected override ValueTask InternalExecute(
PacketViewerViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
context.ClearAllImpl();
return ValueTask.FromResult(null);
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/PacketViewer/ExportPacketsToCsvCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Material.Icons;
namespace Asv.Drones;
public class ExportPacketsToCsvCommand : ContextCommand
{
public const string Id = $"{BaseId}.packet-viewer.export-to-csv";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.ExportPacketsToCsvCommand_CommandInfo_Name,
Description = RS.ExportPacketsToCsvCommand_CommandInfo_Description,
Icon = MaterialIconKind.ContentSave,
DefaultHotKey = null, // TODO: make a key bind later
};
public override ICommandInfo Info => StaticInfo;
protected override async ValueTask InternalExecute(
PacketViewerViewModel context,
CommandArg newValue,
CancellationToken cancel
)
{
await context.ExportToCsvImpl(cancel);
return null;
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/PacketViewer/OpenPacketViewer.cs
================================================
using Asv.Avalonia;
namespace Asv.Drones;
public class OpenPacketViewerCommand(INavigationService nav)
: OpenPageCommandBase(PacketViewerViewModel.PageId, nav)
{
#region Static
public const string Id = $"{BaseId}.open.{PacketViewerViewModel.PageId}";
public static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.OpenPacketViewerCommand_CommandInfo_Name,
Description = RS.OpenPacketViewerCommand_CommandInfo_Description,
Icon = PacketViewerViewModel.PageIcon,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
}
================================================
FILE: src/Asv.Drones/Core/Commands/Setup/FrameType/ChangeFrameTypeCommand.cs
================================================
using System.Threading;
using System.Threading.Tasks;
using Asv.Avalonia;
using Material.Icons;
namespace Asv.Drones;
public class ChangeFrameTypeCommand : ContextCommand
{
public const string Id = $"{BaseId}.setup.frame-type.change";
internal static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.ChangeFrameTypeCommand_CommandInfo_Name,
Description = RS.ChangeFrameTypeCommand_CommandInfo_Description,
Icon = MaterialIconKind.KeyChange,
DefaultHotKey = null,
};
public override ICommandInfo Info => StaticInfo;
public override async ValueTask InternalExecute(
SetupFrameTypeViewModel context,
StringArg newValue,
CancellationToken cancel
)
{
var currentFrameId = context.CurrentFrame?.Value?.Id;
if (currentFrameId is null)
{
return null;
}
await context.ChangeFrameType(newValue.Value, cancel);
return new StringArg(currentFrameId);
}
}
================================================
FILE: src/Asv.Drones/Core/Commands/Setup/OpenSetupCommand.cs
================================================
using Asv.Avalonia;
namespace Asv.Drones;
public class OpenSetupCommand(INavigationService nav)
: OpenPageCommandBase(SetupPageViewModel.PageId, nav)
{
#region Static
public const string Id = $"{BaseId}.open.{SetupPageViewModel.PageId}";
public static readonly ICommandInfo StaticInfo = new CommandInfo
{
Id = Id,
Name = RS.OpenSetupCommand_CommandInfo_Name,
Description = RS.OpenSetupCommand_CommandInfo_Description,
Icon = SetupPageViewModel.PageIcon,
DefaultHotKey = null,
};
#endregion
public override ICommandInfo Info => StaticInfo;
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/AngleUavIndicator/Items/Pitch/PitchItem.cs
================================================
using System;
using Avalonia;
namespace Asv.Drones;
public partial class PitchItem : AvaloniaObject
{
private readonly int _pitch;
public PitchItem(
int pitch,
double scale,
bool titleIsVisible = true,
double controlHeight = 284
)
{
_pitch = pitch;
Value = ((controlHeight / 2) - pitch) * scale;
if (titleIsVisible)
{
Title = pitch.ToString();
StartLine = new Point(0 * scale, 0 * scale);
StopLine = new Point(20 * scale, 0 * scale);
}
else
{
Title = string.Empty;
StartLine = new Point(4 * scale, 0 * scale);
StopLine = new Point(16 * scale, 0 * scale);
}
IsVisible = Math.Abs(pitch) <= 20;
}
public void UpdateVisibility(double pitch)
{
IsVisible = pitch >= _pitch - 20 && pitch <= _pitch + 20;
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/AngleUavIndicator/Items/Pitch/PitchItem.properties.cs
================================================
using Avalonia;
namespace Asv.Drones;
public partial class PitchItem
{
public static readonly DirectProperty TitleProperty =
AvaloniaProperty.RegisterDirect(
nameof(Title),
_ => _.Title,
(_, value) => _.Title = value
);
public string Title
{
get;
set => SetAndRaise(TitleProperty, ref field, value);
}
public static readonly DirectProperty ValueProperty =
AvaloniaProperty.RegisterDirect(
nameof(Value),
_ => _.Value,
(_, value) => _.Value = value
);
public double Value
{
get;
set => SetAndRaise(ValueProperty, ref field, value);
}
public static readonly DirectProperty IsVisibleProperty =
AvaloniaProperty.RegisterDirect(
nameof(IsVisible),
_ => _.IsVisible,
(_, value) => _.IsVisible = value
);
public bool IsVisible
{
get;
set => SetAndRaise(IsVisibleProperty, ref field, value);
}
public static readonly DirectProperty StartLineProperty =
AvaloniaProperty.RegisterDirect(
nameof(StartLine),
_ => _.StartLine,
(_, value) => _.StartLine = value
);
public Point StartLine
{
get;
set => SetAndRaise(StartLineProperty, ref field, value);
}
public static readonly DirectProperty StopLineProperty =
AvaloniaProperty.RegisterDirect(
nameof(StopLine),
_ => _.StopLine,
(_, value) => _.StopLine = value
);
public Point StopLine
{
get;
set => SetAndRaise(StopLineProperty, ref field, value);
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/AngleUavIndicator/Items/Roll/RollItem.cs
================================================
using System;
using Avalonia;
namespace Asv.Drones;
public partial class RollItem : AvaloniaObject
{
public RollItem(int angle)
{
Value = angle;
Title =
Math.Abs(angle) > 180 ? (360 - Math.Abs(angle)).ToString() : Math.Abs(angle).ToString();
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/AngleUavIndicator/Items/Roll/RollItem.properties.cs
================================================
using Avalonia;
namespace Asv.Drones;
public partial class RollItem
{
public static readonly DirectProperty TitleProperty =
AvaloniaProperty.RegisterDirect(
nameof(Title),
_ => _.Title,
(_, value) => _.Title = value
);
public string Title
{
get;
set => SetAndRaise(TitleProperty, ref field, value);
}
public static readonly DirectProperty ValueProperty =
AvaloniaProperty.RegisterDirect(
nameof(Value),
_ => _.Value,
(_, value) => _.Value = value
);
public double Value
{
get;
set => SetAndRaise(ValueProperty, ref field, value);
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/AngleUavIndicator/UavAngleIndicator.cs
================================================
using System;
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls.Primitives;
namespace Asv.Drones;
public partial class UavAngleIndicator : TemplatedControl
{
public UavAngleIndicator()
{
Scale = Math.Min(InternalWidth, InternalHeight) / 100;
RollItems = new AvaloniaList(
new OldAttitudeIndicator.RollItem(0),
new OldAttitudeIndicator.RollItem(10),
new OldAttitudeIndicator.RollItem(20),
new OldAttitudeIndicator.RollItem(30),
new OldAttitudeIndicator.RollItem(45),
new OldAttitudeIndicator.RollItem(60),
new OldAttitudeIndicator.RollItem(300),
new OldAttitudeIndicator.RollItem(315),
new OldAttitudeIndicator.RollItem(330),
new OldAttitudeIndicator.RollItem(340),
new OldAttitudeIndicator.RollItem(350)
);
PitchItems = new AvaloniaList(
new OldAttitudeIndicator.PitchItem(135, Scale, false),
new OldAttitudeIndicator.PitchItem(130, Scale),
new OldAttitudeIndicator.PitchItem(125, Scale, false),
new OldAttitudeIndicator.PitchItem(120, Scale),
new OldAttitudeIndicator.PitchItem(115, Scale, false),
new OldAttitudeIndicator.PitchItem(110, Scale),
new OldAttitudeIndicator.PitchItem(105, Scale, false),
new OldAttitudeIndicator.PitchItem(100, Scale),
new OldAttitudeIndicator.PitchItem(95, Scale, false),
new OldAttitudeIndicator.PitchItem(90, Scale),
new OldAttitudeIndicator.PitchItem(85, Scale, false),
new OldAttitudeIndicator.PitchItem(80, Scale),
new OldAttitudeIndicator.PitchItem(75, Scale, false),
new OldAttitudeIndicator.PitchItem(70, Scale),
new OldAttitudeIndicator.PitchItem(65, Scale, false),
new OldAttitudeIndicator.PitchItem(60, Scale),
new OldAttitudeIndicator.PitchItem(55, Scale, false),
new OldAttitudeIndicator.PitchItem(50, Scale),
new OldAttitudeIndicator.PitchItem(45, Scale, false),
new OldAttitudeIndicator.PitchItem(40, Scale),
new OldAttitudeIndicator.PitchItem(35, Scale, false),
new OldAttitudeIndicator.PitchItem(30, Scale),
new OldAttitudeIndicator.PitchItem(25, Scale, false),
new OldAttitudeIndicator.PitchItem(20, Scale),
new OldAttitudeIndicator.PitchItem(15, Scale, false),
new OldAttitudeIndicator.PitchItem(10, Scale),
new OldAttitudeIndicator.PitchItem(5, Scale, false),
new OldAttitudeIndicator.PitchItem(0, Scale),
new OldAttitudeIndicator.PitchItem(-5, Scale, false),
new OldAttitudeIndicator.PitchItem(-10, Scale),
new OldAttitudeIndicator.PitchItem(-15, Scale, false),
new OldAttitudeIndicator.PitchItem(-20, Scale),
new OldAttitudeIndicator.PitchItem(-25, Scale, false),
new OldAttitudeIndicator.PitchItem(-30, Scale),
new OldAttitudeIndicator.PitchItem(-35, Scale, false),
new OldAttitudeIndicator.PitchItem(-40, Scale),
new OldAttitudeIndicator.PitchItem(-45, Scale, false),
new OldAttitudeIndicator.PitchItem(-50, Scale),
new OldAttitudeIndicator.PitchItem(-55, Scale, false),
new OldAttitudeIndicator.PitchItem(-60, Scale),
new OldAttitudeIndicator.PitchItem(-65, Scale, false),
new OldAttitudeIndicator.PitchItem(-70, Scale),
new OldAttitudeIndicator.PitchItem(-75, Scale, false),
new OldAttitudeIndicator.PitchItem(-80, Scale),
new OldAttitudeIndicator.PitchItem(-85, Scale, false),
new OldAttitudeIndicator.PitchItem(-90, Scale),
new OldAttitudeIndicator.PitchItem(-95, Scale, false),
new OldAttitudeIndicator.PitchItem(-100, Scale),
new OldAttitudeIndicator.PitchItem(-105, Scale, false),
new OldAttitudeIndicator.PitchItem(-110, Scale),
new OldAttitudeIndicator.PitchItem(-115, Scale, false),
new OldAttitudeIndicator.PitchItem(-120, Scale),
new OldAttitudeIndicator.PitchItem(-125, Scale, false),
new OldAttitudeIndicator.PitchItem(-130, Scale),
new OldAttitudeIndicator.PitchItem(-135, Scale, false)
);
}
public double Scale { get; }
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == RollAngleProperty)
{
UpdateRollAngle(change.Sender);
}
else if (change.Property == PitchAngleProperty)
{
UpdateAngle(change.Sender);
}
}
private static void UpdateAngle(AvaloniaObject source)
{
if (source is not UavAngleIndicator indicator)
{
return;
}
var pitch = indicator.PitchAngle;
UpdateRollAngle(source);
foreach (var item in indicator.PitchItems)
{
item.UpdateVisibility(pitch);
}
}
private static void UpdateRollAngle(AvaloniaObject source)
{
if (source is not UavAngleIndicator indicator)
{
return;
}
var roll = indicator.RollAngle;
var pitch = indicator.PitchAngle;
indicator.PitchTranslateX =
-pitch * indicator.Scale * Math.Cos((roll - 90.0) * Math.PI / 180.0);
indicator.PitchTranslateY =
pitch * indicator.Scale * Math.Sin((90 - roll) * Math.PI / 180.0);
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/AngleUavIndicator/UavAngleIndicator.properties.cs
================================================
using System.Collections.Generic;
using Avalonia;
namespace Asv.Drones;
public partial class UavAngleIndicator
{
public static readonly StyledProperty RollAngleProperty = AvaloniaProperty.Register<
UavAngleIndicator,
double
>(nameof(RollAngle));
public double RollAngle
{
get => GetValue(RollAngleProperty);
set => SetValue(RollAngleProperty, value);
}
public static readonly StyledProperty PitchAngleProperty = AvaloniaProperty.Register<
UavAngleIndicator,
double
>(nameof(PitchAngle));
public double PitchAngle
{
get => GetValue(PitchAngleProperty);
set => SetValue(PitchAngleProperty, value);
}
#region Internal direct property
public static readonly DirectProperty InternalWidthProperty =
AvaloniaProperty.RegisterDirect(
nameof(InternalWidth),
_ => _.InternalWidth,
(_, value) => _.InternalWidth = value
);
public double InternalWidth
{
get;
set => SetAndRaise(InternalWidthProperty, ref field, value);
} = 1000;
public static readonly DirectProperty InternalHeightProperty =
AvaloniaProperty.RegisterDirect(
nameof(InternalHeight),
_ => _.InternalHeight,
(_, value) => _.InternalHeight = value
);
public double InternalHeight
{
get;
set => SetAndRaise(InternalHeightProperty, ref field, value);
} = 1000;
public static readonly DirectProperty PitchTranslateXProperty =
AvaloniaProperty.RegisterDirect(
nameof(PitchTranslateX),
_ => _.PitchTranslateX,
(_, value) => _.PitchTranslateX = value
);
private double PitchTranslateX
{
get;
set => SetAndRaise(PitchTranslateXProperty, ref field, value);
}
public static readonly DirectProperty PitchTranslateYProperty =
AvaloniaProperty.RegisterDirect(
nameof(PitchTranslateY),
_ => _.PitchTranslateY,
(_, value) => _.PitchTranslateY = value
);
public double PitchTranslateY
{
get;
set => SetAndRaise(PitchTranslateYProperty, ref field, value);
}
public static readonly DirectProperty<
UavAngleIndicator,
IEnumerable
> RollItemsProperty = AvaloniaProperty.RegisterDirect<
UavAngleIndicator,
IEnumerable
>(nameof(RollItems), _ => _.RollItems, (_, value) => _.RollItems = value);
public IEnumerable RollItems
{
get;
set => SetAndRaise(RollItemsProperty, ref field, value);
}
public static readonly DirectProperty<
UavAngleIndicator,
IEnumerable
> PitchItemsProperty = AvaloniaProperty.RegisterDirect<
UavAngleIndicator,
IEnumerable
>(nameof(PitchItems), _ => _.PitchItems, (_, value) => _.PitchItems = value);
public IEnumerable PitchItems
{
get;
set => SetAndRaise(PitchItemsProperty, ref field, value);
}
#endregion
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/AngleUavIndicator/UavAngleIndicatorStyles.axaml
================================================
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/CompassUavIndicator/CompassScaleItem.cs
================================================
using System;
using Avalonia;
namespace Asv.Drones;
public class CompassScaleItem : AvaloniaObject
{
private const double Center = 150.0;
private const double OuterRadius = 128.0;
private const double LabelRadius = 88.0;
private const double LabelWidth = 44.0;
private const double LabelHeight = 24.0;
public CompassScaleItem(double angle, string? title, bool isMajor)
{
Angle = angle;
Title = title;
HasTitle = !string.IsNullOrWhiteSpace(title);
TickLength = isMajor ? 20 : 10;
TickWidth = isMajor ? 3 : 2;
Update(0);
}
public double Angle { get; }
public string? Title { get; }
public bool HasTitle { get; }
public double TickLength { get; }
public double TickWidth { get; }
public static readonly DirectProperty TickStartProperty =
AvaloniaProperty.RegisterDirect(
nameof(TickStart),
item => item.TickStart,
(item, value) => item.TickStart = value
);
public Point TickStart
{
get;
private set => SetAndRaise(TickStartProperty, ref field, value);
}
public static readonly DirectProperty TickEndProperty =
AvaloniaProperty.RegisterDirect(
nameof(TickEnd),
item => item.TickEnd,
(item, value) => item.TickEnd = value
);
public Point TickEnd
{
get;
private set => SetAndRaise(TickEndProperty, ref field, value);
}
public static readonly DirectProperty LabelLeftProperty =
AvaloniaProperty.RegisterDirect(
nameof(LabelLeft),
item => item.LabelLeft,
(item, value) => item.LabelLeft = value
);
public double LabelLeft
{
get;
private set => SetAndRaise(LabelLeftProperty, ref field, value);
}
public static readonly DirectProperty LabelTopProperty =
AvaloniaProperty.RegisterDirect(
nameof(LabelTop),
item => item.LabelTop,
(item, value) => item.LabelTop = value
);
public double LabelTop
{
get;
private set => SetAndRaise(LabelTopProperty, ref field, value);
}
public void Update(double heading)
{
var visualAngle = NormalizeSignedAngle(Angle - heading);
var radians = visualAngle * Math.PI / 180.0;
var sin = Math.Sin(radians);
var cos = Math.Cos(radians);
TickStart = new Point(
Center + ((OuterRadius - TickLength) * sin),
Center - ((OuterRadius - TickLength) * cos)
);
TickEnd = new Point(Center + (OuterRadius * sin), Center - (OuterRadius * cos));
LabelLeft = Center + (LabelRadius * sin) - (LabelWidth / 2.0);
LabelTop = Center - (LabelRadius * cos) - (LabelHeight / 2.0);
}
private static double NormalizeSignedAngle(double value)
{
var angle = value % 360.0;
if (angle <= -180.0)
{
angle += 360.0;
}
else if (angle > 180.0)
{
angle -= 360.0;
}
return angle;
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/CompassUavIndicator/CompassUavIndicator.cs
================================================
using System.Linq;
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls.Primitives;
namespace Asv.Drones;
public partial class CompassUavIndicator : TemplatedControl
{
public CompassUavIndicator()
{
CompassItems = new AvaloniaList(
Enumerable
.Range(0, 24)
.Select(index =>
{
var angle = index * 15.0;
var title = angle switch
{
0 => RS.HeadingScaleItem_Direction_N,
90 => RS.HeadingScaleItem_Direction_E,
180 => RS.HeadingScaleItem_Direction_S,
270 => RS.HeadingScaleItem_Direction_W,
_ when angle % 30 == 0 => angle.ToString("F0"),
_ => null,
};
return new CompassScaleItem(angle, title, angle % 30 == 0);
})
);
UpdateCompass();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == HeadingProperty || change.Property == HomeAzimuthProperty)
{
UpdateCompass();
}
}
private void UpdateCompass()
{
var heading = NormalizeAngle(Heading);
HeadingText = $"{heading:F0}°";
foreach (var item in CompassItems)
{
item.Update(heading);
}
HomeMarkerRotation = double.IsNaN(HomeAzimuth)
? 0.0
: NormalizeSignedAngle(HomeAzimuth - heading);
}
private static double NormalizeAngle(double value)
{
if (double.IsNaN(value) || double.IsInfinity(value))
{
return 0.0;
}
var angle = value % 360.0;
return angle < 0.0 ? angle + 360.0 : angle;
}
private static double NormalizeSignedAngle(double value)
{
var angle = value % 360.0;
if (angle <= -180.0)
{
angle += 360.0;
}
else if (angle > 180.0)
{
angle -= 360.0;
}
return angle;
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/CompassUavIndicator/CompassUavIndicator.properties.cs
================================================
using System.Collections.Generic;
using Avalonia;
namespace Asv.Drones;
public partial class CompassUavIndicator
{
public static readonly StyledProperty HeadingProperty = AvaloniaProperty.Register<
CompassUavIndicator,
double
>(nameof(Heading));
public double Heading
{
get => GetValue(HeadingProperty);
set => SetValue(HeadingProperty, value);
}
public static readonly StyledProperty HomeAzimuthProperty = AvaloniaProperty.Register<
CompassUavIndicator,
double
>(nameof(HomeAzimuth), double.NaN);
public double HomeAzimuth
{
get => GetValue(HomeAzimuthProperty);
set => SetValue(HomeAzimuthProperty, value);
}
public static readonly DirectProperty<
CompassUavIndicator,
IEnumerable
> CompassItemsProperty = AvaloniaProperty.RegisterDirect<
CompassUavIndicator,
IEnumerable
>(nameof(CompassItems), indicator => indicator.CompassItems);
public IEnumerable CompassItems { get; }
public static readonly DirectProperty HeadingTextProperty =
AvaloniaProperty.RegisterDirect(
nameof(HeadingText),
indicator => indicator.HeadingText,
(indicator, value) => indicator.HeadingText = value
);
public string HeadingText
{
get;
private set => SetAndRaise(HeadingTextProperty, ref field, value);
} = "0°";
public static readonly DirectProperty HomeMarkerRotationProperty =
AvaloniaProperty.RegisterDirect(
nameof(HomeMarkerRotation),
indicator => indicator.HomeMarkerRotation,
(indicator, value) => indicator.HomeMarkerRotation = value
);
public double HomeMarkerRotation
{
get;
private set => SetAndRaise(HomeMarkerRotationProperty, ref field, value);
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/CompassUavIndicator/CompassUavIndicatorStyles.axaml
================================================
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/DeviceTelemetryDesignPreview.cs
================================================
using Asv.Avalonia;
namespace Asv.Drones;
internal static class DeviceTelemetryDesignPreview
{
public const AsvColorKind DefaultStatusColor = AsvColorKind.Info5;
private static bool _isConfigured;
public static IUnitService UnitService
{
get
{
ConfigureUnits();
return NullUnitService.Instance;
}
}
public static IUnit Unit(string id) => UnitService.Units[id];
private static void ConfigureUnits()
{
if (_isConfigured)
{
return;
}
var unitService = NullUnitService.Instance;
unitService.Extend(
new VelocityUnit(
DesignTime.Configuration,
[new VelocityMetersPerSecondUnitItem(), new VelocityMilesPerHourUnitItem()]
)
);
unitService.Extend(
new ProgressUnit(
DesignTime.Configuration,
[new ProgressPercentUnitItem(), new ProgressInPartsUnitItem()]
)
);
unitService.Extend(
new CapacityUnit(DesignTime.Configuration, [new CapacityMilliAmperePerHourUnitItem()])
);
unitService.Extend(
new AmperageUnit(
DesignTime.Configuration,
[new AmperageAmpereUnitItem(), new AmperageMilliAmpereUnitItem()]
)
);
unitService.Extend(
new VoltageUnit(
DesignTime.Configuration,
[new VoltageVoltUnitItem(), new VoltageMilliVoltUnitItem()]
)
);
_isConfigured = true;
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/OldAttitudeIndicator/AttitudeIndicator.cs
================================================
using System;
using System.Linq;
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using R3;
namespace Asv.Drones.OldAttitudeIndicator;
public partial class AttitudeIndicator : TemplatedControl
{
private const int VelocityItemCount = 6;
private const int VelocityValueRange = 5;
private const double VelocityControlLengthPrc = 0.4;
private const int AltitudeItemCount = 6;
private const int AltitudeValueRange = 5;
private const double AltitudeControlLengthPrc = 0.4;
private const int HeadingItemCount = 10;
private const double HeadingControlLengthPrc = 1.0;
private const int HeadingValueRange = 15;
private static double _headingPositionStep;
private static double _headingCenterPosition;
public double Scale { get; }
public AttitudeIndicator()
{
if (Design.IsDesignMode)
{
var status = new[] { "Armed", "Disarmed" };
Observable
.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), TimeProvider.System)
.Subscribe(_ =>
{
StatusText = status[1 % 2];
});
StatusText = status[1];
}
var smallerSide = Math.Min((double)InternalWidth, InternalHeight);
Scale = smallerSide / 100;
RollItems = new AvaloniaList(
new OldAttitudeIndicator.RollItem(0),
new OldAttitudeIndicator.RollItem(10),
new OldAttitudeIndicator.RollItem(20),
new OldAttitudeIndicator.RollItem(30),
new OldAttitudeIndicator.RollItem(45),
new OldAttitudeIndicator.RollItem(60),
new OldAttitudeIndicator.RollItem(300),
new OldAttitudeIndicator.RollItem(315),
new OldAttitudeIndicator.RollItem(330),
new OldAttitudeIndicator.RollItem(340),
new OldAttitudeIndicator.RollItem(350)
);
PitchItems = new AvaloniaList(
new OldAttitudeIndicator.PitchItem(135, Scale, false),
new OldAttitudeIndicator.PitchItem(130, Scale),
new OldAttitudeIndicator.PitchItem(125, Scale, false),
new OldAttitudeIndicator.PitchItem(120, Scale),
new OldAttitudeIndicator.PitchItem(115, Scale, false),
new OldAttitudeIndicator.PitchItem(110, Scale),
new OldAttitudeIndicator.PitchItem(105, Scale, false),
new OldAttitudeIndicator.PitchItem(100, Scale),
new OldAttitudeIndicator.PitchItem(95, Scale, false),
new OldAttitudeIndicator.PitchItem(90, Scale),
new OldAttitudeIndicator.PitchItem(85, Scale, false),
new OldAttitudeIndicator.PitchItem(80, Scale),
new OldAttitudeIndicator.PitchItem(75, Scale, false),
new OldAttitudeIndicator.PitchItem(70, Scale),
new OldAttitudeIndicator.PitchItem(65, Scale, false),
new OldAttitudeIndicator.PitchItem(60, Scale),
new OldAttitudeIndicator.PitchItem(55, Scale, false),
new OldAttitudeIndicator.PitchItem(50, Scale),
new OldAttitudeIndicator.PitchItem(45, Scale, false),
new OldAttitudeIndicator.PitchItem(40, Scale),
new OldAttitudeIndicator.PitchItem(35, Scale, false),
new OldAttitudeIndicator.PitchItem(30, Scale),
new OldAttitudeIndicator.PitchItem(25, Scale, false),
new OldAttitudeIndicator.PitchItem(20, Scale),
new OldAttitudeIndicator.PitchItem(15, Scale, false),
new OldAttitudeIndicator.PitchItem(10, Scale),
new OldAttitudeIndicator.PitchItem(5, Scale, false),
new OldAttitudeIndicator.PitchItem(0, Scale),
new OldAttitudeIndicator.PitchItem(-5, Scale, false),
new OldAttitudeIndicator.PitchItem(-10, Scale),
new OldAttitudeIndicator.PitchItem(-15, Scale, false),
new OldAttitudeIndicator.PitchItem(-20, Scale),
new OldAttitudeIndicator.PitchItem(-25, Scale, false),
new OldAttitudeIndicator.PitchItem(-30, Scale),
new OldAttitudeIndicator.PitchItem(-35, Scale, false),
new OldAttitudeIndicator.PitchItem(-40, Scale),
new OldAttitudeIndicator.PitchItem(-45, Scale, false),
new OldAttitudeIndicator.PitchItem(-50, Scale),
new OldAttitudeIndicator.PitchItem(-55, Scale, false),
new OldAttitudeIndicator.PitchItem(-60, Scale),
new OldAttitudeIndicator.PitchItem(-65, Scale, false),
new OldAttitudeIndicator.PitchItem(-70, Scale),
new OldAttitudeIndicator.PitchItem(-75, Scale, false),
new OldAttitudeIndicator.PitchItem(-80, Scale),
new OldAttitudeIndicator.PitchItem(-85, Scale, false),
new OldAttitudeIndicator.PitchItem(-90, Scale),
new OldAttitudeIndicator.PitchItem(-95, Scale, false),
new OldAttitudeIndicator.PitchItem(-100, Scale),
new OldAttitudeIndicator.PitchItem(-105, Scale, false),
new OldAttitudeIndicator.PitchItem(-110, Scale),
new OldAttitudeIndicator.PitchItem(-115, Scale, false),
new OldAttitudeIndicator.PitchItem(-120, Scale),
new OldAttitudeIndicator.PitchItem(-125, Scale, false),
new OldAttitudeIndicator.PitchItem(-130, Scale),
new OldAttitudeIndicator.PitchItem(-135, Scale, false)
);
var velocityControlLength = smallerSide * VelocityControlLengthPrc;
var velocityItemLength = velocityControlLength / (VelocityItemCount - 1);
VelocityItems = new AvaloniaList(
Enumerable
.Range(0, VelocityItemCount)
.Select(_ => new OldAttitudeIndicator.ScaleItem(
0,
VelocityValueRange,
_,
VelocityItemCount,
velocityControlLength + velocityItemLength,
velocityControlLength,
showNegative: false
))
);
var altitudeControlLength = smallerSide * AltitudeControlLengthPrc;
var altitudeItemLength = altitudeControlLength / (AltitudeItemCount - 1);
AltitudeItems = new AvaloniaList(
Enumerable
.Range(0, AltitudeItemCount)
.Select(_ => new OldAttitudeIndicator.ScaleItem(
0,
AltitudeValueRange,
_,
AltitudeItemCount,
altitudeControlLength + altitudeItemLength,
altitudeControlLength
))
);
var headingControlLength = smallerSide * HeadingControlLengthPrc;
var headingItemLength = headingControlLength / (HeadingItemCount - 1);
HeadingItems = new AvaloniaList(
Enumerable
.Range(0, HeadingItemCount)
.Select(_ => new HeadingScaleItem(
0,
HeadingValueRange,
_,
HeadingItemCount,
headingControlLength + headingItemLength,
headingControlLength
))
);
var headingItemStep =
(headingControlLength + headingItemLength)
/ (HeadingItemCount % 2 != 0 ? HeadingItemCount - 1 : HeadingItemCount);
_headingPositionStep = -1 * headingItemStep / HeadingValueRange;
_headingCenterPosition = headingControlLength / 2;
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == AttitudeIndicator.VibrationXProperty)
{
UpdateColorX(change.Sender);
}
else if (change.Property == AttitudeIndicator.VibrationYProperty)
{
UpdateColorY(change.Sender);
}
else if (change.Property == AttitudeIndicator.VibrationZProperty)
{
UpdateColorZ(change.Sender);
}
else if (change.Property == AttitudeIndicator.VelocityProperty)
{
UpdateVelocityItems(change.Sender);
}
else if (change.Property == AttitudeIndicator.RollAngleProperty)
{
UpdateRollAngle(change.Sender);
}
else if (change.Property == AttitudeIndicator.PitchAngleProperty)
{
UpdateAngle(change.Sender);
}
else if (change.Property == AttitudeIndicator.AltitudeProperty)
{
UpdateAltitudeItems(change.Sender);
}
else if (change.Property == AttitudeIndicator.HeadingProperty)
{
UpdateHeadingItems(change.Sender);
}
else if (change.Property == AttitudeIndicator.HomeAzimuthProperty)
{
UpdateHomeAzimuthPosition(change.Sender);
}
}
private static void UpdateColorX(AvaloniaObject source)
{
if (source is not AttitudeIndicator indicator)
{
return;
}
if (indicator.VibrationX < 30)
{
indicator.BrushVibrationX = Colors.Red;
}
else if (indicator.VibrationX is > 30 and < 60)
{
indicator.BrushVibrationX = Colors.Yellow;
}
else if (indicator.VibrationX > 60)
{
indicator.BrushVibrationX = Colors.GreenYellow;
}
}
private static void UpdateColorY(AvaloniaObject source)
{
if (source is not AttitudeIndicator indicator)
{
return;
}
if (indicator.VibrationY < 30)
{
indicator.BrushVibrationY = Colors.Red;
}
else if (indicator.VibrationY > 30 & indicator.VibrationY < 60)
{
indicator.BrushVibrationY = Colors.Yellow;
}
else if (indicator.VibrationY > 60)
{
indicator.BrushVibrationY = Colors.GreenYellow;
}
}
private static void UpdateColorZ(AvaloniaObject source)
{
if (source is not AttitudeIndicator indicator)
{
return;
}
if (indicator.VibrationZ < 30)
{
indicator.BrushVibrationZ = Colors.Red;
}
else if (indicator.VibrationZ > 30 & indicator.VibrationZ < 60)
{
indicator.BrushVibrationZ = Colors.Yellow;
}
else if (indicator.VibrationZ > 60)
{
indicator.BrushVibrationZ = Colors.GreenYellow;
}
}
private static void UpdateAngle(AvaloniaObject source)
{
if (source is not AttitudeIndicator indicator)
{
return;
}
var pitch = indicator.PitchAngle;
UpdateRollAngle(source);
foreach (var item in indicator.PitchItems)
{
item.UpdateVisibility(pitch);
}
}
private static void UpdateRollAngle(AvaloniaObject source)
{
if (source is not AttitudeIndicator indicator)
{
return;
}
var roll = indicator.RollAngle;
var pitch = indicator.PitchAngle;
indicator.PitchTranslateX =
-pitch * indicator.Scale * Math.Cos((roll - 90.0) * Math.PI / 180.0);
indicator.PitchTranslateY =
pitch * indicator.Scale * Math.Sin((90 - roll) * Math.PI / 180.0);
}
private static void UpdateVelocityItems(AvaloniaObject source)
{
if (source is not AttitudeIndicator indicator)
{
return;
}
foreach (var item in indicator.VelocityItems)
{
item.UpdateValue(indicator.Velocity);
}
}
private static void UpdateAltitudeItems(AvaloniaObject source)
{
if (source is not AttitudeIndicator indicator)
{
return;
}
foreach (var item in indicator.AltitudeItems)
{
item.UpdateValue(indicator.Altitude);
}
}
private static void UpdateHeadingItems(AvaloniaObject source)
{
if (source is not AttitudeIndicator indicator)
{
return;
}
foreach (var item in indicator.HeadingItems)
{
item.UpdateValue(indicator.Heading);
}
indicator.HomeAzimuthPosition = GetHomeAzimuthPosition(
indicator.HomeAzimuth,
indicator.Heading
);
}
private static void UpdateHomeAzimuthPosition(AvaloniaObject source)
{
if (source is not AttitudeIndicator indicator)
{
return;
}
foreach (var item in indicator.HeadingItems)
{
item.UpdateValue(indicator.Heading);
}
indicator.HomeAzimuthPosition = GetHomeAzimuthPosition(
indicator.HomeAzimuth,
indicator.Heading
);
}
private static double GetHomeAzimuthPosition(double? value, double headingValue)
{
if (value == null)
{
return -100;
}
var distance = (headingValue - value.Value) % 360;
if (distance < -180)
{
distance += 360;
}
else if (distance > 179)
{
distance -= 360;
}
return _headingCenterPosition + (distance * _headingPositionStep);
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/OldAttitudeIndicator/AttitudeIndicator.properties.cs
================================================
using System;
using System.Collections.Generic;
using Avalonia;
using Avalonia.Media;
namespace Asv.Drones.OldAttitudeIndicator;
public partial class AttitudeIndicator
{
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
Color
> BrushVibrationXProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
Color
>(nameof(BrushVibrationX), o => o.BrushVibrationX, (o, v) => o.BrushVibrationX = v);
public Color BrushVibrationX
{
get;
set => SetAndRaise(BrushVibrationXProperty, ref field, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
Color
> BrushVibrationYProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
Color
>(nameof(BrushVibrationY), o => o.BrushVibrationY, (o, v) => o.BrushVibrationY = v);
public Color BrushVibrationY
{
get;
set => SetAndRaise(BrushVibrationYProperty, ref field, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
Color
> BrushVibrationZProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
Color
>(nameof(BrushVibrationZ), o => o.BrushVibrationZ, (o, v) => o.BrushVibrationZ = v);
public Color BrushVibrationZ
{
get;
set => SetAndRaise(BrushVibrationZProperty, ref field, value);
}
public static readonly StyledProperty VibrationXProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
float
>(nameof(VibrationX), defaultValue: -1);
public float VibrationX
{
get => GetValue(VibrationXProperty);
set => SetValue(VibrationXProperty, value);
}
public static readonly StyledProperty VibrationYProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
float
>(nameof(VibrationY), defaultValue: -1);
public float VibrationY
{
get => GetValue(VibrationYProperty);
set => SetValue(VibrationYProperty, value);
}
public static readonly StyledProperty VibrationZProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
float
>(nameof(VibrationZ), defaultValue: -1);
public float VibrationZ
{
get => GetValue(VibrationZProperty);
set => SetValue(VibrationZProperty, value);
}
public static readonly StyledProperty Clipping0Property = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
uint
>(nameof(Clipping0));
public uint Clipping0
{
get => GetValue(Clipping0Property);
set => SetValue(Clipping0Property, value);
}
public static readonly StyledProperty Clipping1Property = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
uint
>(nameof(Clipping1));
public uint Clipping1
{
get => GetValue(Clipping1Property);
set => SetValue(Clipping1Property, value);
}
public static readonly StyledProperty Clipping2Property = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
uint
>(nameof(Clipping2));
public uint Clipping2
{
get => GetValue(Clipping2Property);
set => SetValue(Clipping2Property, value);
}
public static readonly StyledProperty RollAngleProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
double
>(nameof(RollAngle));
public double RollAngle
{
get => GetValue(RollAngleProperty);
set => SetValue(RollAngleProperty, value);
}
public static readonly StyledProperty PitchAngleProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
double
>(nameof(PitchAngle));
public double PitchAngle
{
get => GetValue(PitchAngleProperty);
set => SetValue(PitchAngleProperty, value);
}
public static readonly StyledProperty VelocityProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
double
>(nameof(Velocity));
public double Velocity
{
get => GetValue(VelocityProperty);
set => SetValue(VelocityProperty, value);
}
public static readonly StyledProperty AltitudeProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
double
>(nameof(Altitude));
public double Altitude
{
get => GetValue(AltitudeProperty);
set => SetValue(AltitudeProperty, value);
}
public static readonly StyledProperty HeadingProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
double
>(nameof(Heading));
public double Heading
{
get => GetValue(HeadingProperty);
set => SetValue(HeadingProperty, value);
}
public static readonly StyledProperty HomeAzimuthProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
double
>(nameof(HomeAzimuth));
public double HomeAzimuth
{
get => GetValue(HomeAzimuthProperty);
set => SetValue(HomeAzimuthProperty, value);
}
public static readonly StyledProperty IsArmedProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
bool
>(nameof(IsArmed));
public bool IsArmed
{
get => GetValue(IsArmedProperty);
set => SetValue(IsArmedProperty, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
string
> StatusTextProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
string
>(nameof(StatusText), _ => _.StatusText, (_, value) => _.StatusText = value);
public string StatusText
{
get;
set => SetAndRaise(StatusTextProperty, ref field, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
string
> RightStatusTextProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
string
>(nameof(RightStatusText), _ => _.RightStatusText, (_, value) => _.RightStatusText = value);
public string RightStatusText
{
get;
set => SetAndRaise(RightStatusTextProperty, ref field, value);
}
public static readonly StyledProperty ArmedTimeProperty = AvaloniaProperty.Register<
OldAttitudeIndicator.AttitudeIndicator,
TimeSpan
>(nameof(ArmedTime));
public TimeSpan ArmedTime
{
get => GetValue(ArmedTimeProperty);
set => SetValue(ArmedTimeProperty, value);
}
#region Internal direct property
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
double
> InternalWidthProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
double
>(nameof(InternalWidth), _ => _.InternalWidth, (_, value) => _.InternalWidth = value);
public double InternalWidth
{
get;
set => SetAndRaise(InternalWidthProperty, ref field, value);
} = 1000;
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
double
> InternalHeightProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
double
>(nameof(InternalHeight), _ => _.InternalHeight, (_, value) => _.InternalHeight = value);
public double InternalHeight
{
get;
set => SetAndRaise(InternalHeightProperty, ref field, value);
} = 1000;
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
double
> PitchTranslateXProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
double
>(nameof(PitchTranslateX), _ => _.PitchTranslateX, (_, value) => _.PitchTranslateX = value);
private double PitchTranslateX
{
get;
set => SetAndRaise(PitchTranslateXProperty, ref field, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
double
> PitchTranslateYProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
double
>(nameof(PitchTranslateY), _ => _.PitchTranslateY, (_, value) => _.PitchTranslateY = value);
public double PitchTranslateY
{
get;
set => SetAndRaise(PitchTranslateYProperty, ref field, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
IEnumerable
> RollItemsProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
IEnumerable
>(nameof(RollItems), _ => _.RollItems, (_, value) => _.RollItems = value);
public IEnumerable RollItems
{
get;
set => SetAndRaise(RollItemsProperty, ref field, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
IEnumerable
> PitchItemsProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
IEnumerable
>(nameof(PitchItems), _ => _.PitchItems, (_, value) => _.PitchItems = value);
public IEnumerable PitchItems
{
get;
set => SetAndRaise(PitchItemsProperty, ref field, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
IEnumerable
> VelocityItemsProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
IEnumerable
>(nameof(VelocityItems), _ => _.VelocityItems, (_, value) => _.VelocityItems = value);
public IEnumerable VelocityItems
{
get;
set => SetAndRaise(VelocityItemsProperty, ref field, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
IEnumerable
> AltitudeItemsProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
IEnumerable
>(nameof(AltitudeItems), _ => _.AltitudeItems, (_, value) => _.AltitudeItems = value);
public IEnumerable AltitudeItems
{
get;
set => SetAndRaise(AltitudeItemsProperty, ref field, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
IEnumerable
> HeadingItemsProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
IEnumerable
>(nameof(HeadingItems), _ => _.HeadingItems, (_, value) => _.HeadingItems = value);
public IEnumerable HeadingItems
{
get;
set => SetAndRaise(HeadingItemsProperty, ref field, value);
}
public static readonly DirectProperty<
OldAttitudeIndicator.AttitudeIndicator,
double
> HomeAzimuthPositionProperty = AvaloniaProperty.RegisterDirect<
OldAttitudeIndicator.AttitudeIndicator,
double
>(
nameof(HomeAzimuthPosition),
_ => _.HomeAzimuthPosition,
(_, value) => _.HomeAzimuthPosition = value
);
public double HomeAzimuthPosition
{
get;
set => SetAndRaise(HomeAzimuthPositionProperty, ref field, value);
} = -100;
#endregion
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/OldAttitudeIndicator/AttitudeIndicatorStyles.axaml
================================================
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/OldAttitudeIndicator/Items/Heading/HeadingScaleItem.cs
================================================
using System;
namespace Asv.Drones.OldAttitudeIndicator;
public class HeadingScaleItem : ScaleItem
{
public HeadingScaleItem(
double value,
double valueRange,
int index,
int itemCount,
double fullLength,
double length
)
: base(value, valueRange, index, itemCount, fullLength, length, true) { }
protected override string GetTitle(double value)
{
var v = value < 0 ? ((int)Math.Round(value) % 360) + 360 : (int)Math.Round(value) % 360;
return v switch
{
0 => RS.HeadingScaleItem_Direction_N,
45 => RS.HeadingScaleItem_Direction_NE,
90 => RS.HeadingScaleItem_Direction_E,
135 => RS.HeadingScaleItem_Direction_SE,
180 => RS.HeadingScaleItem_Direction_S,
225 => RS.HeadingScaleItem_Direction_SW,
270 => RS.HeadingScaleItem_Direction_W,
315 => RS.HeadingScaleItem_Direction_NW,
360 => RS.HeadingScaleItem_Direction_N,
_ => v.ToString("F0"),
};
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/OldAttitudeIndicator/Items/Pitch/PitchItem.cs
================================================
using System;
using Avalonia;
namespace Asv.Drones.OldAttitudeIndicator;
public partial class PitchItem : AvaloniaObject
{
private readonly int _pitch;
public PitchItem(
int pitch,
double scale,
bool titleIsVisible = true,
double controlHeight = 284
)
{
_pitch = pitch;
Value = ((controlHeight / 2) - pitch) * scale;
if (titleIsVisible)
{
Title = pitch.ToString();
StartLine = new Point(0 * scale, 0 * scale);
StopLine = new Point(20 * scale, 0 * scale);
}
else
{
Title = string.Empty;
StartLine = new Point(4 * scale, 0 * scale);
StopLine = new Point(16 * scale, 0 * scale);
}
IsVisible = Math.Abs(pitch) <= 20;
}
public void UpdateVisibility(double pitch)
{
IsVisible = pitch >= _pitch - 20 && pitch <= _pitch + 20;
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/OldAttitudeIndicator/Items/Pitch/PitchItem.properties.cs
================================================
using Avalonia;
namespace Asv.Drones.OldAttitudeIndicator;
public partial class PitchItem
{
public static readonly DirectProperty TitleProperty =
AvaloniaProperty.RegisterDirect(
nameof(Title),
_ => _.Title,
(_, value) => _.Title = value
);
public string Title
{
get;
set => SetAndRaise(TitleProperty, ref field, value);
}
public static readonly DirectProperty ValueProperty =
AvaloniaProperty.RegisterDirect(
nameof(Value),
_ => _.Value,
(_, value) => _.Value = value
);
public double Value
{
get;
set => SetAndRaise(ValueProperty, ref field, value);
}
public static readonly DirectProperty IsVisibleProperty =
AvaloniaProperty.RegisterDirect(
nameof(IsVisible),
_ => _.IsVisible,
(_, value) => _.IsVisible = value
);
public bool IsVisible
{
get;
set => SetAndRaise(IsVisibleProperty, ref field, value);
}
public static readonly DirectProperty StartLineProperty =
AvaloniaProperty.RegisterDirect(
nameof(StartLine),
_ => _.StartLine,
(_, value) => _.StartLine = value
);
public Point StartLine
{
get;
set => SetAndRaise(StartLineProperty, ref field, value);
}
public static readonly DirectProperty StopLineProperty =
AvaloniaProperty.RegisterDirect(
nameof(StopLine),
_ => _.StopLine,
(_, value) => _.StopLine = value
);
public Point StopLine
{
get;
set => SetAndRaise(StopLineProperty, ref field, value);
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/OldAttitudeIndicator/Items/Roll/RollItem.cs
================================================
using System;
using Avalonia;
namespace Asv.Drones.OldAttitudeIndicator;
public partial class RollItem : AvaloniaObject
{
public RollItem(int angle)
{
Value = angle;
Title =
Math.Abs(angle) > 180 ? (360 - Math.Abs(angle)).ToString() : Math.Abs(angle).ToString();
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/OldAttitudeIndicator/Items/Roll/RollItem.properties.cs
================================================
using Avalonia;
namespace Asv.Drones.OldAttitudeIndicator;
public partial class RollItem
{
public static readonly DirectProperty TitleProperty =
AvaloniaProperty.RegisterDirect(
nameof(Title),
_ => _.Title,
(_, value) => _.Title = value
);
public string Title
{
get;
set => SetAndRaise(TitleProperty, ref field, value);
}
public static readonly DirectProperty ValueProperty =
AvaloniaProperty.RegisterDirect(
nameof(Value),
_ => _.Value,
(_, value) => _.Value = value
);
public double Value
{
get;
set => SetAndRaise(ValueProperty, ref field, value);
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/OldAttitudeIndicator/Items/Scale/ScaleItem.cs
================================================
using System;
using Avalonia;
namespace Asv.Drones.OldAttitudeIndicator;
public partial class ScaleItem : AvaloniaObject
{
public ScaleItem(
double value,
double valueRange,
int index,
int itemCount,
double fullLength,
double length,
bool isInverse = false,
bool showNegative = true,
string? fixedTitle = null
)
{
_valueRange = valueRange;
_showNegative = showNegative;
_isFixedTitle = fixedTitle != null;
var step = fullLength / (itemCount % 2 != 0 ? itemCount - 1 : itemCount);
_positionStep = step / valueRange;
if (!isInverse)
{
_startPosition = ((length - fullLength) / 2.0) + (step * index);
}
else
{
_startPosition = ((length - fullLength) / 2.0) + (step * (itemCount - index));
_positionStep *= -1;
}
var centerIndex = itemCount % 2 == 0 ? itemCount / 2 : (itemCount / 2) + 1;
var indexOffset = index - centerIndex;
_valueOffset = -1 * valueRange * indexOffset;
if (_isFixedTitle)
{
Title = fixedTitle;
}
UpdateValue(value);
}
public void UpdateValue(double value)
{
Value = GetValue(value);
Position = GetPosition(value);
if (!_isFixedTitle)
{
Title = GetTitle(Value);
}
IsVisible = _showNegative || Value >= 0;
}
protected virtual string? GetTitle(double value)
{
return Math.Round(value).ToString("F0");
}
private double GetValue(double value)
{
return Math.Round(value) - (Math.Round(value) % _valueRange) + _valueOffset;
}
private double GetPosition(double value)
{
return _startPosition + (_positionStep * (Math.Round(value) % _valueRange));
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/OldAttitudeIndicator/Items/Scale/ScaleItem.properties.cs
================================================
using Avalonia;
namespace Asv.Drones.OldAttitudeIndicator;
public partial class ScaleItem
{
private readonly double _valueRange;
private readonly bool _showNegative;
private readonly double _startPosition;
private readonly double _positionStep;
private readonly double _valueOffset;
private readonly bool _isFixedTitle;
public static readonly DirectProperty IsVisibleProperty =
AvaloniaProperty.RegisterDirect(
nameof(IsVisible),
_ => _.IsVisible,
(_, value) => _.IsVisible = value
);
public bool IsVisible
{
get;
set => SetAndRaise(IsVisibleProperty, ref field, value);
}
public static readonly DirectProperty TitleProperty =
AvaloniaProperty.RegisterDirect(
nameof(Title),
_ => _.Title,
(_, value) => _.Title = value
);
public string? Title
{
get;
set => SetAndRaise(TitleProperty, ref field, value);
}
public static readonly DirectProperty ValueProperty =
AvaloniaProperty.RegisterDirect(
nameof(Value),
_ => _.Value,
(_, value) => _.Value = value
);
public double Value
{
get;
set => SetAndRaise(ValueProperty, ref field, value);
}
public static readonly DirectProperty PositionProperty =
AvaloniaProperty.RegisterDirect(
nameof(Position),
_ => _.Position,
(_, value) => _.Position = value
);
public double Position
{
get;
set => SetAndRaise(PositionProperty, ref field, value);
}
}
================================================
FILE: src/Asv.Drones/Core/Controls/DeviceTelemetry/RouteUavIndicator/RouteUavIndicator.cs
================================================
using System;
using Asv.Avalonia;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
namespace Asv.Drones;
[PseudoClasses(ProgressDisabledPseudoclass, ProgressCompletedPseudoclass)]
public class RouteUavIndicator : IndicatorBase
{
private double _internalBorderWidth;
public static readonly DirectProperty InternalBorderWidthProperty =
AvaloniaProperty.RegisterDirect(
nameof(InternalBorderWidth),
o => o.InternalBorderWidth,
(o, v) => o.InternalBorderWidth = v
);
public double InternalBorderWidth
{
get => _internalBorderWidth;
private set => SetAndRaise(InternalBorderWidthProperty, ref _internalBorderWidth, value);
}
private double _internalBorderLeft;
public static readonly DirectProperty InternalBorderLeftProperty =
AvaloniaProperty.RegisterDirect(
nameof(InternalBorderLeft),
o => o.InternalBorderLeft,
(o, v) => o.InternalBorderLeft = v
);
public double InternalBorderLeft
{
get => _internalBorderLeft;
set => SetAndRaise(InternalBorderLeftProperty, ref _internalBorderLeft, value);
}
public static readonly StyledProperty ProgressProperty = AvaloniaProperty.Register<
RouteUavIndicator,
double
>(nameof(Progress));
public double Progress
{
get => GetValue(ProgressProperty);
set => SetValue(ProgressProperty, value);
}
private double _internalIndicatorLeft;
public static readonly DirectProperty InternalIndicatorLeftProperty =
AvaloniaProperty.RegisterDirect(
nameof(InternalIndicatorLeft),
o => o.InternalIndicatorLeft,
(o, v) => o.InternalIndicatorLeft = v
);
public double InternalIndicatorLeft
{
get => _internalIndicatorLeft;
set => SetAndRaise(InternalIndicatorLeftProperty, ref _internalIndicatorLeft, value);
}
private string _internalProgressText;
public static readonly DirectProperty InternalProgressTextProperty =
AvaloniaProperty.RegisterDirect