fix bugs with UI for gps denied

This commit is contained in:
Alex Bezdieniezhnykh
2025-05-30 02:09:15 +03:00
parent d87ddb5f6a
commit b345137f16
10 changed files with 143 additions and 85 deletions
+8 -11
View File
@@ -52,7 +52,6 @@ public partial class Annotator
private readonly TimeSpan _thresholdBefore = TimeSpan.FromMilliseconds(50); private readonly TimeSpan _thresholdBefore = TimeSpan.FromMilliseconds(50);
private readonly TimeSpan _thresholdAfter = TimeSpan.FromMilliseconds(150); private readonly TimeSpan _thresholdAfter = TimeSpan.FromMilliseconds(150);
private readonly IGpsMatcherService _gpsMatcherService;
private static readonly Guid SaveConfigTaskId = Guid.NewGuid(); private static readonly Guid SaveConfigTaskId = Guid.NewGuid();
public ObservableCollection<MediaFileInfo> AllMediaFiles { get; set; } = new(); public ObservableCollection<MediaFileInfo> AllMediaFiles { get; set; } = new();
@@ -88,7 +87,6 @@ public partial class Annotator
_dbFactory = dbFactory; _dbFactory = dbFactory;
_inferenceService = inferenceService; _inferenceService = inferenceService;
_inferenceClient = inferenceClient; _inferenceClient = inferenceClient;
_gpsMatcherService = gpsMatcherService;
Loaded += OnLoaded; Loaded += OnLoaded;
Closed += OnFormClosed; Closed += OnFormClosed;
@@ -101,7 +99,7 @@ public partial class Annotator
{ {
_appConfig.DirectoriesConfig.VideosDirectory = TbFolder.Text; _appConfig.DirectoriesConfig.VideosDirectory = TbFolder.Text;
await ReloadFiles(); await ReloadFiles();
await SaveUserSettings(); SaveUserSettings();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -132,7 +130,7 @@ public partial class Annotator
_inferenceClient.Send(RemoteCommand.Create(CommandType.AIAvailabilityCheck)); _inferenceClient.Send(RemoteCommand.Create(CommandType.AIAvailabilityCheck));
Editor.GetTimeFunc = () => TimeSpan.FromMilliseconds(_mediaPlayer.Time); Editor.GetTimeFunc = () => TimeSpan.FromMilliseconds(_mediaPlayer.Time);
MapMatcherComponent.Init(_appConfig, _gpsMatcherService); MapMatcherComponent.Init(_appConfig, gpsMatcherService);
} }
private void OnLoaded(object sender, RoutedEventArgs e) private void OnLoaded(object sender, RoutedEventArgs e)
@@ -217,9 +215,9 @@ public partial class Annotator
Volume.ValueChanged += (_, newValue) => Volume.ValueChanged += (_, newValue) =>
_mediator.Publish(new VolumeChangedEvent((int)newValue)); _mediator.Publish(new VolumeChangedEvent((int)newValue));
SizeChanged += async (_, _) => await SaveUserSettings(); SizeChanged += (_, _) => SaveUserSettings();
LocationChanged += async (_, _) => await SaveUserSettings(); LocationChanged += (_, _) => SaveUserSettings();
StateChanged += async (_, _) => await SaveUserSettings(); StateChanged += (_, _) => SaveUserSettings();
DgAnnotations.MouseDoubleClick += (sender, args) => DgAnnotations.MouseDoubleClick += (sender, args) =>
{ {
@@ -269,10 +267,10 @@ public partial class Annotator
ShowAnnotations(res.Annotation, showImage: true); ShowAnnotations(res.Annotation, showImage: true);
} }
private Task SaveUserSettings() private void SaveUserSettings()
{ {
if (_suspendLayout) if (_suspendLayout)
return Task.CompletedTask; return;
_appConfig.UIConfig.LeftPanelWidth = MainGrid.ColumnDefinitions.FirstOrDefault()!.Width.Value; _appConfig.UIConfig.LeftPanelWidth = MainGrid.ColumnDefinitions.FirstOrDefault()!.Width.Value;
_appConfig.UIConfig.RightPanelWidth = MainGrid.ColumnDefinitions.LastOrDefault()!.Width.Value; _appConfig.UIConfig.RightPanelWidth = MainGrid.ColumnDefinitions.LastOrDefault()!.Width.Value;
@@ -282,7 +280,6 @@ public partial class Annotator
_configUpdater.Save(_appConfig); _configUpdater.Save(_appConfig);
return Task.CompletedTask; return Task.CompletedTask;
}, SaveConfigTaskId, TimeSpan.FromSeconds(5)); }, SaveConfigTaskId, TimeSpan.FromSeconds(5));
return Task.CompletedTask;
} }
private void ShowTimeAnnotations(TimeSpan time) private void ShowTimeAnnotations(TimeSpan time)
@@ -508,7 +505,7 @@ public partial class Annotator
_helpWindow.Activate(); _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) private void LvFilesContextOpening(object sender, ContextMenuEventArgs e)
{ {
+39 -1
View File
@@ -1,6 +1,9 @@
using System.IO; using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using Azaion.Annotator.Controls;
using Azaion.Annotator.DTO; using Azaion.Annotator.DTO;
using Azaion.Common; using Azaion.Common;
using Azaion.Common.Database; using Azaion.Common.Database;
@@ -11,6 +14,8 @@ using Azaion.Common.Extensions;
using Azaion.Common.Services; using Azaion.Common.Services;
using Azaion.CommonSecurity.DTO; using Azaion.CommonSecurity.DTO;
using Azaion.CommonSecurity.Services; using Azaion.CommonSecurity.Services;
using GMap.NET;
using GMap.NET.WindowsPresentation;
using LibVLCSharp.Shared; using LibVLCSharp.Shared;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -38,7 +43,9 @@ public class AnnotatorEventHandler(
INotificationHandler<AnnotatorControlEvent>, INotificationHandler<AnnotatorControlEvent>,
INotificationHandler<VolumeChangedEvent>, INotificationHandler<VolumeChangedEvent>,
INotificationHandler<AnnotationsDeletedEvent>, INotificationHandler<AnnotationsDeletedEvent>,
INotificationHandler<AnnotationAddedEvent> INotificationHandler<AnnotationAddedEvent>,
INotificationHandler<SetStatusTextEvent>,
INotificationHandler<GPSMatcherResultEvent>
{ {
private const int STEP = 20; private const int STEP = 20;
private const int LARGE_STEP = 5000; private const int LARGE_STEP = 5000;
@@ -363,4 +370,35 @@ public class AnnotatorEventHandler(
}); });
return Task.CompletedTask; 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;
}
} }
@@ -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<GPSMatcherResultEvent>
{
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;
}
}
@@ -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;
}
@@ -0,0 +1,14 @@
using MediatR;
namespace Azaion.Common.Services;
public class GPSMatcherEventHandler(IGpsMatcherService gpsMatcherService) :
INotificationHandler<GPSMatcherResultEvent>,
INotificationHandler<GPSMatcherFinishedEvent>
{
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);
}
+8 -11
View File
@@ -1,9 +1,7 @@
using System.IO; using System.IO;
using Azaion.CommonSecurity; using Azaion.CommonSecurity;
using Azaion.CommonSecurity.DTO; using Azaion.CommonSecurity.DTO;
using MediatR;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace Azaion.Common.Services; namespace Azaion.Common.Services;
@@ -11,12 +9,11 @@ public interface IGpsMatcherService
{ {
Task RunGpsMatching(string userRouteDir, double initialLatitude, double initialLongitude, CancellationToken detectToken = default); Task RunGpsMatching(string userRouteDir, double initialLatitude, double initialLongitude, CancellationToken detectToken = default);
void StopGpsMatching(); void StopGpsMatching();
Task SetGpsResult(GPSMatcherResultEvent result, CancellationToken detectToken = default);
Task FinishGPS(GPSMatcherFinishedEvent notification, CancellationToken cancellationToken);
} }
public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDownloader satelliteTileDownloader, IOptions<DirectoriesConfig> dirConfig) public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDownloader satelliteTileDownloader, IOptions<DirectoriesConfig> dirConfig) : IGpsMatcherService
: IGpsMatcherService,
INotificationHandler<GPSMatcherResultEvent>,
INotificationHandler<GPSMatcherFinishedEvent>
{ {
private readonly DirectoriesConfig _dirConfig = dirConfig.Value; private readonly DirectoriesConfig _dirConfig = dirConfig.Value;
private const int ZOOM_LEVEL = 18; private const int ZOOM_LEVEL = 18;
@@ -63,7 +60,7 @@ public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDow
{ {
var filename = Path.GetFileName(fullName); var filename = Path.GetFileName(fullName);
File.Copy(Path.Combine(_userRouteDir, filename), Path.Combine(_routeDir, filename)); 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); .ToDictionary(x => x.Filename, x => x.Index);
@@ -83,16 +80,16 @@ public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDow
gpsMatcherClient.Stop(); 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); _currentRouteImages.Remove(result.Image);
_currentLat = result.Latitude; _currentLat = result.Latitude;
_currentLon = result.Longitude; _currentLon = result.Longitude;
_currentIndex = _currentRouteImages[result.Image]; await Task.CompletedTask;
return 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) if (_currentRouteImages.Count == 0 && _currentIndex < _allRouteImages.Count)
await StartMatchingRound(_currentIndex); await StartMatchingRound(_currentIndex);
+44 -29
View File
@@ -1,7 +1,9 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using Azaion.CommonSecurity; using Azaion.CommonSecurity;
using Azaion.CommonSecurity.DTO; using Azaion.CommonSecurity.DTO;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using NetMQ; using NetMQ;
using NetMQ.Sockets; using NetMQ.Sockets;
@@ -32,15 +34,17 @@ public class StartMatchingEvent
public class GpsMatcherClient : IGpsMatcherClient public class GpsMatcherClient : IGpsMatcherClient
{ {
private readonly IMediator _mediator; private readonly IMediator _mediator;
private readonly ILogger<GpsMatcherClient> _logger;
private readonly string _requestAddress; private readonly string _requestAddress;
private readonly RequestSocket _requestSocket = new(); private readonly RequestSocket _requestSocket = new();
private readonly string _subscriberAddress; private readonly string _subscriberAddress;
private readonly SubscriberSocket _subscriberSocket = new(); private readonly SubscriberSocket _subscriberSocket = new();
private readonly NetMQPoller _poller = new();
public GpsMatcherClient(IMediator mediator, IOptions<GpsDeniedClientConfig> gpsConfig, ILogger<GpsMatcherClient> logger)
public GpsMatcherClient(IMediator mediator, IOptions<GpsDeniedClientConfig> gpsConfig)
{ {
_mediator = mediator; _mediator = mediator;
_logger = logger;
try try
{ {
using var process = new Process(); using var process = new Process();
@@ -67,41 +71,52 @@ public class GpsMatcherClient : IGpsMatcherClient
_requestAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqPort}"; _requestAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqPort}";
_requestSocket.Connect(_requestAddress); _requestSocket.Connect(_requestAddress);
_subscriberAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqSubscriberPort}"; _subscriberAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqReceiverPort}";
_subscriberSocket.Connect(_subscriberAddress); _subscriberSocket.Connect(_subscriberAddress);
_subscriberSocket.Subscribe(""); _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)) try
continue;
switch (str)
{ {
case "FINISHED": if (string.IsNullOrEmpty(str))
await _mediator.Publish(new GPSMatcherFinishedEvent()); continue;
break;
case "OK":
await _mediator.Publish(new GPSMatcherJobAcceptedEvent());
break;
default:
var parts = str.Split(',');
if (parts.Length != 5)
throw new Exception("Matching Result Failed");
await _mediator.Publish(new GPSMatcherResultEvent switch (str)
{ {
Index = int.Parse(parts[0]), case "FINISHED":
Image = parts[1], await _mediator.Publish(new GPSMatcherFinishedEvent());
Latitude = double.Parse(parts[2]), break;
Longitude = double.Parse(parts[3]), case "OK":
MatchType = parts[4] await _mediator.Publish(new GPSMatcherJobAcceptedEvent());
}); break;
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);
} }
} }
} }
+10 -3
View File
@@ -5,9 +5,11 @@ using System.Net.Http;
using System.Net.Http.Json; using System.Net.Http.Json;
using Azaion.Common.DTO; using Azaion.Common.DTO;
using Azaion.Common.DTO.Config; using Azaion.Common.DTO.Config;
using Azaion.Common.Events;
using Azaion.Common.Extensions; using Azaion.Common.Extensions;
using Azaion.CommonSecurity; using Azaion.CommonSecurity;
using Azaion.CommonSecurity.DTO; using Azaion.CommonSecurity.DTO;
using MediatR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
@@ -26,7 +28,8 @@ public class SatelliteDownloader(
ILogger<SatelliteDownloader> logger, ILogger<SatelliteDownloader> logger,
IOptions<MapConfig> mapConfig, IOptions<MapConfig> mapConfig,
IOptions<DirectoriesConfig> directoriesConfig, IOptions<DirectoriesConfig> directoriesConfig,
IHttpClientFactory httpClientFactory) IHttpClientFactory httpClientFactory,
IMediator mediator)
: ISatelliteDownloader : ISatelliteDownloader
{ {
private const int INPUT_TILE_SIZE = 256; 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) 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 //empty Satellite directory
if (Directory.Exists(_satDirectory)) if (Directory.Exists(_satDirectory))
Directory.Delete(_satDirectory, true); Directory.Delete(_satDirectory, true);
Directory.CreateDirectory(_satDirectory); Directory.CreateDirectory(_satDirectory);
var downloadTilesResult = await DownloadTiles(centerLat, centerLon, radiusM, zoomLevel, token); var downloadTilesResult = await DownloadTiles(centerLat, centerLon, radiusM, zoomLevel, token);
await mediator.Publish(new SetStatusTextEvent("Завершено! Склеюється в 1 зображення..."), token);
var image = ComposeTiles(downloadTilesResult.Tiles, token); var image = ComposeTiles(downloadTilesResult.Tiles, token);
if (image != null) if (image == null)
await SplitToTiles(image, downloadTilesResult, token); return;
await mediator.Publish(new SetStatusTextEvent("Розбиття на малі зображення для опрацювання..."), token);
await SplitToTiles(image, downloadTilesResult, token);
} }
private async Task SplitToTiles(Image<Rgba32> image, DownloadTilesResult bounds, CancellationToken token = default) private async Task SplitToTiles(Image<Rgba32> image, DownloadTilesResult bounds, CancellationToken token = default)
@@ -15,5 +15,5 @@ public class InferenceClientConfig : ExternalClientConfig
public class GpsDeniedClientConfig : ExternalClientConfig public class GpsDeniedClientConfig : ExternalClientConfig
{ {
public int ZeroMqSubscriberPort { get; set; } public int ZeroMqReceiverPort { get; set; }
} }
+9 -1
View File
@@ -1,6 +1,7 @@
using Azaion.Common.DTO.Config; using Azaion.Common.DTO.Config;
using Azaion.Common.Services; using Azaion.Common.Services;
using Azaion.CommonSecurity.DTO; using Azaion.CommonSecurity.DTO;
using MediatR;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@@ -11,6 +12,13 @@ namespace Azaion.Annotator.Test;
public class GetTilesTestClass public class GetTilesTestClass
{ {
[Fact]
public void TestPath()
{
var filename = "./input/images-scaled\\AD000010.tif";
filename = Path.GetFileName(filename);
}
[Fact] [Fact]
public async Task GetTilesTest() public async Task GetTilesTest()
{ {
@@ -26,7 +34,7 @@ public class GetTilesTestClass
}), new OptionsWrapper<DirectoriesConfig>(new DirectoriesConfig }), new OptionsWrapper<DirectoriesConfig>(new DirectoriesConfig
{ {
GpsSatDirectory = "satelliteMaps" GpsSatDirectory = "satelliteMaps"
}), httpClientFactory); }), httpClientFactory, Mock.Of<IMediator>());
await satelliteDownloader.GetTiles(48.2748909, 37.3834877, 600, 18); await satelliteDownloader.GetTiles(48.2748909, 37.3834877, 600, 18);
} }