using System.Diagnostics; using System.IO; using Azaion.Common.DTO; using Azaion.Common.Events; using MediatR; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NetMQ; using NetMQ.Sockets; namespace Azaion.Common.Services; public interface IGpsMatcherClient : IDisposable { Task StartMatching(StartMatchingEvent startEvent); void Stop(); } public class StartMatchingEvent { public string RouteDir { get; set; } = null!; public string SatelliteImagesDir { get; set; } = null!; public int ImagesCount { get; set; } public double Latitude { get; set; } public double Longitude { get; set; } public int Altitude { get; set; } = 400; public double CameraSensorWidth { get; set; } = 23.5; public double CameraFocalLength { get; set; } = 24; public override string ToString() => $"{RouteDir},{SatelliteImagesDir},{ImagesCount},{Latitude},{Longitude},{Altitude},{CameraSensorWidth},{CameraFocalLength}"; } 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, ILogger logger) { _mediator = mediator; _logger = logger; try { using var process = new Process(); process.StartInfo = new ProcessStartInfo { FileName = SecurityConstants.ExternalGpsDeniedPath, WorkingDirectory = SecurityConstants.EXTERNAL_GPS_DENIED_FOLDER, CreateNoWindow = true }; process.Start(); } catch (Exception e) { _logger.LogError(e, e.ToString()); throw; } _requestAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqPort}"; _requestSocket.Connect(_requestAddress); _subscriberAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqReceiverPort}"; _subscriberSocket.Connect(_subscriberAddress); _subscriberSocket.Subscribe(""); _subscriberSocket.ReceiveReady += async (sender, e) => await ProcessClientCommand(sender, e); _poller.Add(_subscriberSocket); _poller.RunAsync(); } private async Task ProcessClientCommand(object? sender, NetMQSocketEventArgs e) { while (e.Socket.TryReceiveFrameString(TimeSpan.FromMilliseconds(100), out var str)) { try { if (string.IsNullOrEmpty(str)) continue; 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); } } } public async Task StartMatching(StartMatchingEvent e) { _requestSocket.SendFrame(e.ToString()); _requestSocket.TryReceiveFrameString(TimeSpan.FromMilliseconds(300), out var response); if (response != "OK") { _logger.LogError(response); await _mediator.Publish(new SetStatusTextEvent(response ?? "", true)); } } public void Stop() => _requestSocket.SendFrame("STOP"); public void Dispose() { _poller.Stop(); _poller.Dispose(); _requestSocket.SendFrame("EXIT"); _requestSocket.Disconnect(_requestAddress); _requestSocket.Dispose(); _subscriberSocket.Disconnect(_subscriberAddress); _subscriberSocket.Dispose(); } }