From b345137f16fdac82d5b259c3f4d25767200d7335 Mon Sep 17 00:00:00 2001 From: Alex Bezdieniezhnykh Date: Fri, 30 May 2025 02:09:15 +0300 Subject: [PATCH] fix bugs with UI for gps denied --- Azaion.Annotator/Annotator.xaml.cs | 19 ++--- Azaion.Annotator/AnnotatorEventHandler.cs | 40 +++++++++- .../Controls/MapMatcherEventHandler.cs | 28 ------- Azaion.Common/Events/SetStatusTextEvent.cs | 10 +++ .../Services/GPSMatcherEventHandler.cs | 14 ++++ Azaion.Common/Services/GPSMatcherService.cs | 19 ++--- Azaion.Common/Services/GpsMatcherClient.cs | 73 +++++++++++-------- Azaion.Common/Services/SatelliteDownloader.cs | 13 +++- .../DTO/ExternalClientsConfig.cs | 2 +- Azaion.Test/GetTilesTest.cs | 10 ++- 10 files changed, 143 insertions(+), 85 deletions(-) delete mode 100644 Azaion.Annotator/Controls/MapMatcherEventHandler.cs create mode 100644 Azaion.Common/Events/SetStatusTextEvent.cs create mode 100644 Azaion.Common/Services/GPSMatcherEventHandler.cs diff --git a/Azaion.Annotator/Annotator.xaml.cs b/Azaion.Annotator/Annotator.xaml.cs index 76b901f..7d86bed 100644 --- a/Azaion.Annotator/Annotator.xaml.cs +++ b/Azaion.Annotator/Annotator.xaml.cs @@ -52,7 +52,6 @@ public partial class Annotator private readonly TimeSpan _thresholdBefore = TimeSpan.FromMilliseconds(50); private readonly TimeSpan _thresholdAfter = TimeSpan.FromMilliseconds(150); - private readonly IGpsMatcherService _gpsMatcherService; private static readonly Guid SaveConfigTaskId = Guid.NewGuid(); public ObservableCollection AllMediaFiles { get; set; } = new(); @@ -88,7 +87,6 @@ public partial class Annotator _dbFactory = dbFactory; _inferenceService = inferenceService; _inferenceClient = inferenceClient; - _gpsMatcherService = gpsMatcherService; Loaded += OnLoaded; Closed += OnFormClosed; @@ -101,7 +99,7 @@ public partial class Annotator { _appConfig.DirectoriesConfig.VideosDirectory = TbFolder.Text; await ReloadFiles(); - await SaveUserSettings(); + SaveUserSettings(); } catch (Exception e) { @@ -132,7 +130,7 @@ public partial class Annotator _inferenceClient.Send(RemoteCommand.Create(CommandType.AIAvailabilityCheck)); Editor.GetTimeFunc = () => TimeSpan.FromMilliseconds(_mediaPlayer.Time); - MapMatcherComponent.Init(_appConfig, _gpsMatcherService); + MapMatcherComponent.Init(_appConfig, gpsMatcherService); } private void OnLoaded(object sender, RoutedEventArgs e) @@ -217,9 +215,9 @@ public partial class Annotator Volume.ValueChanged += (_, newValue) => _mediator.Publish(new VolumeChangedEvent((int)newValue)); - SizeChanged += async (_, _) => await SaveUserSettings(); - LocationChanged += async (_, _) => await SaveUserSettings(); - StateChanged += async (_, _) => await SaveUserSettings(); + SizeChanged += (_, _) => SaveUserSettings(); + LocationChanged += (_, _) => SaveUserSettings(); + StateChanged += (_, _) => SaveUserSettings(); DgAnnotations.MouseDoubleClick += (sender, args) => { @@ -269,10 +267,10 @@ public partial class Annotator ShowAnnotations(res.Annotation, showImage: true); } - private Task SaveUserSettings() + private void SaveUserSettings() { if (_suspendLayout) - return Task.CompletedTask; + return; _appConfig.UIConfig.LeftPanelWidth = MainGrid.ColumnDefinitions.FirstOrDefault()!.Width.Value; _appConfig.UIConfig.RightPanelWidth = MainGrid.ColumnDefinitions.LastOrDefault()!.Width.Value; @@ -282,7 +280,6 @@ public partial class Annotator _configUpdater.Save(_appConfig); return Task.CompletedTask; }, SaveConfigTaskId, TimeSpan.FromSeconds(5)); - return Task.CompletedTask; } private void ShowTimeAnnotations(TimeSpan time) @@ -508,7 +505,7 @@ public partial class Annotator _helpWindow.Activate(); } - private void Thumb_OnDragCompleted(object sender, DragCompletedEventArgs e) => _ = SaveUserSettings(); + private void Thumb_OnDragCompleted(object sender, DragCompletedEventArgs e) => SaveUserSettings(); private void LvFilesContextOpening(object sender, ContextMenuEventArgs e) { diff --git a/Azaion.Annotator/AnnotatorEventHandler.cs b/Azaion.Annotator/AnnotatorEventHandler.cs index e9e18cd..9879ada 100644 --- a/Azaion.Annotator/AnnotatorEventHandler.cs +++ b/Azaion.Annotator/AnnotatorEventHandler.cs @@ -1,6 +1,9 @@ using System.IO; using System.Windows; using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Threading; +using Azaion.Annotator.Controls; using Azaion.Annotator.DTO; using Azaion.Common; using Azaion.Common.Database; @@ -11,6 +14,8 @@ using Azaion.Common.Extensions; using Azaion.Common.Services; using Azaion.CommonSecurity.DTO; using Azaion.CommonSecurity.Services; +using GMap.NET; +using GMap.NET.WindowsPresentation; using LibVLCSharp.Shared; using MediatR; using Microsoft.Extensions.Logging; @@ -38,7 +43,9 @@ public class AnnotatorEventHandler( INotificationHandler, INotificationHandler, INotificationHandler, - INotificationHandler + INotificationHandler, + INotificationHandler, + INotificationHandler { private const int STEP = 20; private const int LARGE_STEP = 5000; @@ -363,4 +370,35 @@ public class AnnotatorEventHandler( }); return Task.CompletedTask; } + + public Task Handle(SetStatusTextEvent e, CancellationToken cancellationToken) + { + mainWindow.Dispatcher.Invoke(() => + { + mainWindow.StatusHelp.Text = e.Text; + if (e.Color.HasValue) + mainWindow.StatusHelp.Foreground = new SolidColorBrush(e.Color.Value); + }); + return Task.CompletedTask; + } + + public Task Handle(GPSMatcherResultEvent e, CancellationToken cancellationToken) + { + mainWindow.Dispatcher.Invoke(() => + { + var mapMatcher = mainWindow.MapMatcherComponent; + var marker = new GMapMarker(new PointLatLng(e.Latitude, e.Longitude)); + var ann = mapMatcher.Annotations[e.Index]; + marker.Shape = new CircleVisual(marker, Brushes.Blue) + { + Text = e.Image + }; + mapMatcher.SatelliteMap.Markers.Add(marker); + ann.Lat = e.Latitude; + ann.Lon = e.Longitude; + mapMatcher.SatelliteMap.Position = new PointLatLng(e.Latitude, e.Longitude); + mapMatcher.SatelliteMap.ZoomAndCenterMarkers(null); + }); + return Task.CompletedTask; + } } diff --git a/Azaion.Annotator/Controls/MapMatcherEventHandler.cs b/Azaion.Annotator/Controls/MapMatcherEventHandler.cs deleted file mode 100644 index 99abd20..0000000 --- a/Azaion.Annotator/Controls/MapMatcherEventHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Azaion.Common.Services; -using GMap.NET; -using GMap.NET.WindowsPresentation; -using MediatR; - -namespace Azaion.Annotator.Controls; - -public class MapMatcherEventHandler(MapMatcher mapMatcher) : INotificationHandler -{ - public Task Handle(GPSMatcherResultEvent result, CancellationToken cancellationToken) - { - mapMatcher.Dispatcher.Invoke(() => - { - var marker = new GMapMarker(new PointLatLng(result.Latitude, result.Longitude)); - var ann = mapMatcher.Annotations[result.Index]; - marker.Shape = new CircleVisual(marker, System.Windows.Media.Brushes.Blue) - { - Text = result.Image - }; - mapMatcher.SatelliteMap.Markers.Add(marker); - ann.Lat = result.Latitude; - ann.Lon = result.Longitude; - mapMatcher.SatelliteMap.Position = new PointLatLng(result.Latitude, result.Longitude); - mapMatcher.SatelliteMap.ZoomAndCenterMarkers(null); - }); - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/Azaion.Common/Events/SetStatusTextEvent.cs b/Azaion.Common/Events/SetStatusTextEvent.cs new file mode 100644 index 0000000..8481c81 --- /dev/null +++ b/Azaion.Common/Events/SetStatusTextEvent.cs @@ -0,0 +1,10 @@ +using System.Windows.Media; +using MediatR; + +namespace Azaion.Common.Events; + +public class SetStatusTextEvent(string text, Color? color = null) : INotification +{ + public string Text { get; set; } = text; + public Color? Color { get; set; } = color; +} \ No newline at end of file diff --git a/Azaion.Common/Services/GPSMatcherEventHandler.cs b/Azaion.Common/Services/GPSMatcherEventHandler.cs new file mode 100644 index 0000000..4790616 --- /dev/null +++ b/Azaion.Common/Services/GPSMatcherEventHandler.cs @@ -0,0 +1,14 @@ +using MediatR; + +namespace Azaion.Common.Services; + +public class GPSMatcherEventHandler(IGpsMatcherService gpsMatcherService) : + INotificationHandler, + INotificationHandler +{ + public async Task Handle(GPSMatcherResultEvent result, CancellationToken cancellationToken) => + await gpsMatcherService.SetGpsResult(result, cancellationToken); + + public async Task Handle(GPSMatcherFinishedEvent notification, CancellationToken cancellationToken) => + await gpsMatcherService.FinishGPS(notification, cancellationToken); +} \ No newline at end of file diff --git a/Azaion.Common/Services/GPSMatcherService.cs b/Azaion.Common/Services/GPSMatcherService.cs index 4847b20..51b91d2 100644 --- a/Azaion.Common/Services/GPSMatcherService.cs +++ b/Azaion.Common/Services/GPSMatcherService.cs @@ -1,9 +1,7 @@ using System.IO; using Azaion.CommonSecurity; using Azaion.CommonSecurity.DTO; -using MediatR; using Microsoft.Extensions.Options; -using Microsoft.IdentityModel.Tokens; namespace Azaion.Common.Services; @@ -11,12 +9,11 @@ public interface IGpsMatcherService { Task RunGpsMatching(string userRouteDir, double initialLatitude, double initialLongitude, CancellationToken detectToken = default); void StopGpsMatching(); + Task SetGpsResult(GPSMatcherResultEvent result, CancellationToken detectToken = default); + Task FinishGPS(GPSMatcherFinishedEvent notification, CancellationToken cancellationToken); } -public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDownloader satelliteTileDownloader, IOptions dirConfig) - : IGpsMatcherService, - INotificationHandler, - INotificationHandler +public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDownloader satelliteTileDownloader, IOptions dirConfig) : IGpsMatcherService { private readonly DirectoriesConfig _dirConfig = dirConfig.Value; private const int ZOOM_LEVEL = 18; @@ -63,7 +60,7 @@ public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDow { var filename = Path.GetFileName(fullName); File.Copy(Path.Combine(_userRouteDir, filename), Path.Combine(_routeDir, filename)); - return new { Filename = filename, Index = startIndex + index }; + return new { Filename = Path.GetFileNameWithoutExtension(fullName), Index = startIndex + index }; }) .ToDictionary(x => x.Filename, x => x.Index); @@ -83,16 +80,16 @@ public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDow gpsMatcherClient.Stop(); } - public Task Handle(GPSMatcherResultEvent result, CancellationToken cancellationToken) + public async Task SetGpsResult(GPSMatcherResultEvent result, CancellationToken detectToken = default) { + _currentIndex = _currentRouteImages[result.Image]; _currentRouteImages.Remove(result.Image); _currentLat = result.Latitude; _currentLon = result.Longitude; - _currentIndex = _currentRouteImages[result.Image]; - return Task.CompletedTask; + await Task.CompletedTask; } - public async Task Handle(GPSMatcherFinishedEvent notification, CancellationToken cancellationToken) + public async Task FinishGPS(GPSMatcherFinishedEvent notification, CancellationToken cancellationToken) { if (_currentRouteImages.Count == 0 && _currentIndex < _allRouteImages.Count) await StartMatchingRound(_currentIndex); diff --git a/Azaion.Common/Services/GpsMatcherClient.cs b/Azaion.Common/Services/GpsMatcherClient.cs index bfcb86a..ab0a4d9 100644 --- a/Azaion.Common/Services/GpsMatcherClient.cs +++ b/Azaion.Common/Services/GpsMatcherClient.cs @@ -1,7 +1,9 @@ using System.Diagnostics; +using System.IO; using Azaion.CommonSecurity; using Azaion.CommonSecurity.DTO; using MediatR; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NetMQ; using NetMQ.Sockets; @@ -32,15 +34,17 @@ public class StartMatchingEvent public class GpsMatcherClient : IGpsMatcherClient { private readonly IMediator _mediator; + private readonly ILogger _logger; private readonly string _requestAddress; private readonly RequestSocket _requestSocket = new(); private readonly string _subscriberAddress; private readonly SubscriberSocket _subscriberSocket = new(); + private readonly NetMQPoller _poller = new(); - - public GpsMatcherClient(IMediator mediator, IOptions gpsConfig) + public GpsMatcherClient(IMediator mediator, IOptions gpsConfig, ILogger logger) { _mediator = mediator; + _logger = logger; try { using var process = new Process(); @@ -67,41 +71,52 @@ public class GpsMatcherClient : IGpsMatcherClient _requestAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqPort}"; _requestSocket.Connect(_requestAddress); - _subscriberAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqSubscriberPort}"; + _subscriberAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqReceiverPort}"; _subscriberSocket.Connect(_subscriberAddress); _subscriberSocket.Subscribe(""); - _subscriberSocket.ReceiveReady += async (_, e) => await ProcessClientCommand(e.Socket); + _subscriberSocket.ReceiveReady += async (sender, e) => await ProcessClientCommand(sender, e); + + _poller.Add(_subscriberSocket); + _poller.RunAsync(); } - private async Task ProcessClientCommand(NetMQSocket socket) + private async Task ProcessClientCommand(object? sender, NetMQSocketEventArgs e) { - while (socket.TryReceiveFrameString(TimeSpan.Zero, out var str)) + while (e.Socket.TryReceiveFrameString(TimeSpan.FromMilliseconds(100), out var str)) { - if (string.IsNullOrEmpty(str)) - continue; - - switch (str) + try { - case "FINISHED": - await _mediator.Publish(new GPSMatcherFinishedEvent()); - break; - case "OK": - await _mediator.Publish(new GPSMatcherJobAcceptedEvent()); - break; - default: - var parts = str.Split(','); - if (parts.Length != 5) - throw new Exception("Matching Result Failed"); + if (string.IsNullOrEmpty(str)) + continue; - await _mediator.Publish(new GPSMatcherResultEvent - { - Index = int.Parse(parts[0]), - Image = parts[1], - Latitude = double.Parse(parts[2]), - Longitude = double.Parse(parts[3]), - MatchType = parts[4] - }); - break; + switch (str) + { + case "FINISHED": + await _mediator.Publish(new GPSMatcherFinishedEvent()); + break; + case "OK": + await _mediator.Publish(new GPSMatcherJobAcceptedEvent()); + break; + default: + var parts = str.Split(','); + if (parts.Length != 5) + throw new Exception("Matching Result Failed"); + + var filename = Path.GetFileNameWithoutExtension(parts[1]); + await _mediator.Publish(new GPSMatcherResultEvent + { + Index = int.Parse(parts[0]), + Image = filename, + Latitude = double.Parse(parts[2]), + Longitude = double.Parse(parts[3]), + MatchType = parts[4] + }); + break; + } + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); } } } diff --git a/Azaion.Common/Services/SatelliteDownloader.cs b/Azaion.Common/Services/SatelliteDownloader.cs index ffa9120..ad527ef 100644 --- a/Azaion.Common/Services/SatelliteDownloader.cs +++ b/Azaion.Common/Services/SatelliteDownloader.cs @@ -5,9 +5,11 @@ using System.Net.Http; using System.Net.Http.Json; using Azaion.Common.DTO; using Azaion.Common.DTO.Config; +using Azaion.Common.Events; using Azaion.Common.Extensions; using Azaion.CommonSecurity; using Azaion.CommonSecurity.DTO; +using MediatR; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; @@ -26,7 +28,8 @@ public class SatelliteDownloader( ILogger logger, IOptions mapConfig, IOptions directoriesConfig, - IHttpClientFactory httpClientFactory) + IHttpClientFactory httpClientFactory, + IMediator mediator) : ISatelliteDownloader { private const int INPUT_TILE_SIZE = 256; @@ -45,15 +48,19 @@ public class SatelliteDownloader( public async Task GetTiles(double centerLat, double centerLon, double radiusM, int zoomLevel, CancellationToken token = default) { + await mediator.Publish(new SetStatusTextEvent($"Завантажується супутникові зображення по координатах: центр: lat: {centerLat:F3} lon: {centerLon:F3} квадрат {radiusM}м * {radiusM}м, zoom: {zoomLevel}..."), token); //empty Satellite directory if (Directory.Exists(_satDirectory)) Directory.Delete(_satDirectory, true); Directory.CreateDirectory(_satDirectory); var downloadTilesResult = await DownloadTiles(centerLat, centerLon, radiusM, zoomLevel, token); + await mediator.Publish(new SetStatusTextEvent("Завершено! Склеюється в 1 зображення..."), token); var image = ComposeTiles(downloadTilesResult.Tiles, token); - if (image != null) - await SplitToTiles(image, downloadTilesResult, token); + if (image == null) + return; + await mediator.Publish(new SetStatusTextEvent("Розбиття на малі зображення для опрацювання..."), token); + await SplitToTiles(image, downloadTilesResult, token); } private async Task SplitToTiles(Image image, DownloadTilesResult bounds, CancellationToken token = default) diff --git a/Azaion.CommonSecurity/DTO/ExternalClientsConfig.cs b/Azaion.CommonSecurity/DTO/ExternalClientsConfig.cs index 25b1276..9d545ba 100644 --- a/Azaion.CommonSecurity/DTO/ExternalClientsConfig.cs +++ b/Azaion.CommonSecurity/DTO/ExternalClientsConfig.cs @@ -15,5 +15,5 @@ public class InferenceClientConfig : ExternalClientConfig public class GpsDeniedClientConfig : ExternalClientConfig { - public int ZeroMqSubscriberPort { get; set; } + public int ZeroMqReceiverPort { get; set; } } \ No newline at end of file diff --git a/Azaion.Test/GetTilesTest.cs b/Azaion.Test/GetTilesTest.cs index 64e011f..09a4bd3 100644 --- a/Azaion.Test/GetTilesTest.cs +++ b/Azaion.Test/GetTilesTest.cs @@ -1,6 +1,7 @@ using Azaion.Common.DTO.Config; using Azaion.Common.Services; using Azaion.CommonSecurity.DTO; +using MediatR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -11,6 +12,13 @@ namespace Azaion.Annotator.Test; public class GetTilesTestClass { + [Fact] + public void TestPath() + { + var filename = "./input/images-scaled\\AD000010.tif"; + filename = Path.GetFileName(filename); + } + [Fact] public async Task GetTilesTest() { @@ -26,7 +34,7 @@ public class GetTilesTestClass }), new OptionsWrapper(new DirectoriesConfig { GpsSatDirectory = "satelliteMaps" - }), httpClientFactory); + }), httpClientFactory, Mock.Of()); await satelliteDownloader.GetTiles(48.2748909, 37.3834877, 600, 18); }