|  |  | 1 |  | using LGDXRobotCloud.API.Repositories; | 
|  |  | 2 |  | using LGDXRobotCloud.API.Services.Administration; | 
|  |  | 3 |  | using LGDXRobotCloud.API.Services.Common; | 
|  |  | 4 |  | using LGDXRobotCloud.Data.Entities; | 
|  |  | 5 |  | using LGDXRobotCloud.Data.Models.Business.Administration; | 
|  |  | 6 |  | using LGDXRobotCloud.Data.Models.Redis; | 
|  |  | 7 |  | using LGDXRobotCloud.Protos; | 
|  |  | 8 |  | using LGDXRobotCloud.Utilities.Enums; | 
|  |  | 9 |  | using Microsoft.Extensions.Caching.Distributed; | 
|  |  | 10 |  | using Microsoft.Extensions.Caching.Memory; | 
|  |  | 11 |  |  | 
|  |  | 12 |  | namespace LGDXRobotCloud.API.Services.Navigation; | 
|  |  | 13 |  |  | 
|  |  | 14 |  | public interface IOnlineRobotsService | 
|  |  | 15 |  | { | 
|  |  | 16 |  |   Task AddRobotAsync(Guid robotId); | 
|  |  | 17 |  |   Task RemoveRobotAsync(Guid robotId); | 
|  |  | 18 |  |   Task UpdateRobotDataAsync(Guid robotId, RobotClientsData data); | 
|  |  | 19 |  |  | 
|  |  | 20 |  |   Task<bool> SetAbortTaskAsync(Guid robotId); | 
|  |  | 21 |  |   Task<bool> SetSoftwareEmergencyStopAsync(Guid robotId, bool enable); | 
|  |  | 22 |  |   Task<bool> SetPauseTaskAssignmentAsync(Guid robotId, bool enable); | 
|  |  | 23 |  |   Task<bool> GetPauseAutoTaskAssignmentAsync(Guid robotId); | 
|  |  | 24 |  | } | 
|  |  | 25 |  |  | 
|  | 0 | 26 |  | public class OnlineRobotsService( | 
|  | 0 | 27 |  |     IActivityLogService activityLogService, | 
|  | 0 | 28 |  |     IEmailService emailService, | 
|  | 0 | 29 |  |     IMemoryCache memoryCache, | 
|  | 0 | 30 |  |     IRobotDataRepository robotDataRepository, | 
|  | 0 | 31 |  |     IRobotService robotService | 
|  | 0 | 32 |  |   ) : IOnlineRobotsService | 
|  |  | 33 |  | { | 
|  | 0 | 34 |  |   private readonly IActivityLogService _activityLogService = activityLogService ?? throw new ArgumentNullException(nameo | 
|  | 0 | 35 |  |   private readonly IEmailService _emailService = emailService ?? throw new ArgumentNullException(nameof(emailService)); | 
|  | 0 | 36 |  |   private readonly IMemoryCache _memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache)); | 
|  | 0 | 37 |  |   private readonly IRobotDataRepository _robotDataRepository = robotDataRepository ?? throw new ArgumentNullException(na | 
|  | 0 | 38 |  |   private readonly IRobotService _robotService = robotService ?? throw new ArgumentNullException(nameof(robotService)); | 
|  |  | 39 |  |  | 
|  |  | 40 |  |   public async Task AddRobotAsync(Guid robotId) | 
|  | 0 | 41 |  |   { | 
|  | 0 | 42 |  |     var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0; | 
|  | 0 | 43 |  |     await _robotDataRepository.StartExchangeAsync(realmId, robotId); | 
|  | 0 | 44 |  |   } | 
|  |  | 45 |  |  | 
|  |  | 46 |  |   public async Task RemoveRobotAsync(Guid robotId) | 
|  | 0 | 47 |  |   { | 
|  | 0 | 48 |  |     var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0; | 
|  | 0 | 49 |  |     await _robotDataRepository.StopExchangeAsync(realmId, robotId); | 
|  | 0 | 50 |  |   } | 
|  |  | 51 |  |  | 
|  |  | 52 |  |   public async Task UpdateRobotDataAsync(Guid robotId, RobotClientsData data) | 
|  | 0 | 53 |  |   { | 
|  | 0 | 54 |  |     if (_memoryCache.TryGetValue<bool>($"OnlineRobotsService_RobotData_Pause_{robotId}", out var _)) | 
|  | 0 | 55 |  |     { | 
|  |  | 56 |  |       // Blocking too much data to rabbitmq | 
|  | 0 | 57 |  |       return; | 
|  |  | 58 |  |     } | 
|  | 0 | 59 |  |     _memoryCache.Set($"OnlineRobotsService_RobotData_Pause_{robotId}", true, TimeSpan.FromMilliseconds(100)); | 
|  |  | 60 |  |  | 
|  | 0 | 61 |  |     var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0; | 
|  | 0 | 62 |  |     await _robotDataRepository.SetRobotDataAsync(realmId, robotId, new RobotData | 
|  | 0 | 63 |  |     { | 
|  | 0 | 64 |  |       RobotStatus = (RobotStatus)data.RobotStatus, | 
|  | 0 | 65 |  |       CriticalStatus = new RobotCriticalStatus | 
|  | 0 | 66 |  |       { | 
|  | 0 | 67 |  |         SoftwareEmergencyStop = data.CriticalStatus.SoftwareEmergencyStop, | 
|  | 0 | 68 |  |         HardwareEmergencyStop = data.CriticalStatus.HardwareEmergencyStop, | 
|  | 0 | 69 |  |         BatteryLow = [.. data.CriticalStatus.BatteryLow], | 
|  | 0 | 70 |  |         MotorDamaged = [.. data.CriticalStatus.MotorDamaged] | 
|  | 0 | 71 |  |       }, | 
|  | 0 | 72 |  |       Batteries = [.. data.Batteries], | 
|  | 0 | 73 |  |       Position = new RobotDof | 
|  | 0 | 74 |  |       { | 
|  | 0 | 75 |  |         X = data.Position.X, | 
|  | 0 | 76 |  |         Y = data.Position.Y, | 
|  | 0 | 77 |  |         Rotation = data.Position.Rotation | 
|  | 0 | 78 |  |       }, | 
|  | 0 | 79 |  |       NavProgress = new AutoTaskNavProgress | 
|  | 0 | 80 |  |       { | 
|  | 0 | 81 |  |         Eta = data.NavProgress.Eta, | 
|  | 0 | 82 |  |         Recoveries = data.NavProgress.Recoveries, | 
|  | 0 | 83 |  |         DistanceRemaining = data.NavProgress.DistanceRemaining, | 
|  | 0 | 84 |  |         WaypointsRemaining = data.NavProgress.WaypointsRemaining, | 
|  | 0 | 85 |  |         Plan = [.. data.NavProgress.Plan.Select(x => new Robot2Dof { X = x.X, Y = x.Y })] | 
|  | 0 | 86 |  |       }, | 
|  | 0 | 87 |  |       PauseTaskAssignment = data.PauseTaskAssignment | 
|  | 0 | 88 |  |     }); | 
|  |  | 89 |  |  | 
|  | 0 | 90 |  |     var robotStatus = (RobotStatus)data.RobotStatus; | 
|  | 0 | 91 |  |     if (robotStatus == RobotStatus.Stuck) | 
|  | 0 | 92 |  |     { | 
|  | 0 | 93 |  |       if (!_memoryCache.TryGetValue<bool>($"OnlineRobotsService_RobotStuck_{robotId}", out var _)) | 
|  | 0 | 94 |  |       { | 
|  |  | 95 |  |         // First stuck in 5 minutes, sending email | 
|  | 0 | 96 |  |         await _emailService.SendRobotStuckEmailAsync(robotId, data.Position.X, data.Position.Y); | 
|  | 0 | 97 |  |       } | 
|  | 0 | 98 |  |       _memoryCache.Set($"OnlineRobotsService_RobotStuck_{robotId}", true, TimeSpan.FromMinutes(5)); | 
|  | 0 | 99 |  |     } | 
|  | 0 | 100 |  |   } | 
|  |  | 101 |  |  | 
|  |  | 102 |  |   public async Task<bool> SetAbortTaskAsync(Guid robotId) | 
|  | 0 | 103 |  |   { | 
|  | 0 | 104 |  |     var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0; | 
|  | 0 | 105 |  |     if (await _robotDataRepository.AddRobotCommandAsync(realmId, robotId, new RobotClientsRobotCommands { AbortTask = tr | 
|  | 0 | 106 |  |     { | 
|  | 0 | 107 |  |       return true; | 
|  |  | 108 |  |     } | 
|  |  | 109 |  |     else | 
|  | 0 | 110 |  |     { | 
|  | 0 | 111 |  |       return false; | 
|  |  | 112 |  |     } | 
|  | 0 | 113 |  |   } | 
|  |  | 114 |  |  | 
|  |  | 115 |  |   public async Task<bool> SetSoftwareEmergencyStopAsync(Guid robotId, bool enable) | 
|  | 0 | 116 |  |   { | 
|  | 0 | 117 |  |     var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0; | 
|  | 0 | 118 |  |     bool result = false; | 
|  |  | 119 |  |  | 
|  | 0 | 120 |  |     if (enable) | 
|  | 0 | 121 |  |     { | 
|  | 0 | 122 |  |       result = await _robotDataRepository.AddRobotCommandAsync(realmId, robotId, new RobotClientsRobotCommands { Softwar | 
|  | 0 | 123 |  |     } | 
|  |  | 124 |  |     else | 
|  | 0 | 125 |  |     { | 
|  | 0 | 126 |  |       result = await _robotDataRepository.AddRobotCommandAsync(realmId, robotId, new RobotClientsRobotCommands { Softwar | 
|  | 0 | 127 |  |     } | 
|  |  | 128 |  |  | 
|  | 0 | 129 |  |     if (result) | 
|  | 0 | 130 |  |     { | 
|  | 0 | 131 |  |       await _activityLogService.CreateActivityLogAsync(new ActivityLogCreateBusinessModel | 
|  | 0 | 132 |  |       { | 
|  | 0 | 133 |  |         EntityName = nameof(Robot), | 
|  | 0 | 134 |  |         EntityId = robotId.ToString(), | 
|  | 0 | 135 |  |         Action = enable ? ActivityAction.RobotSoftwareEmergencyStopEnabled : ActivityAction.RobotSoftwareEmergencyStopDi | 
|  | 0 | 136 |  |       }); | 
|  | 0 | 137 |  |     } | 
|  | 0 | 138 |  |     return result; | 
|  | 0 | 139 |  |   } | 
|  |  | 140 |  |  | 
|  |  | 141 |  |   public async Task<bool> SetPauseTaskAssignmentAsync(Guid robotId, bool enable) | 
|  | 0 | 142 |  |   { | 
|  | 0 | 143 |  |     var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0; | 
|  | 0 | 144 |  |     bool result = false; | 
|  |  | 145 |  |  | 
|  | 0 | 146 |  |     if (enable) | 
|  | 0 | 147 |  |     { | 
|  | 0 | 148 |  |       result = await _robotDataRepository.AddRobotCommandAsync(realmId, robotId, new RobotClientsRobotCommands { PauseTa | 
|  | 0 | 149 |  |     } | 
|  |  | 150 |  |     else | 
|  | 0 | 151 |  |     { | 
|  | 0 | 152 |  |       result = await _robotDataRepository.AddRobotCommandAsync(realmId, robotId, new RobotClientsRobotCommands { PauseTa | 
|  | 0 | 153 |  |     } | 
|  |  | 154 |  |  | 
|  | 0 | 155 |  |     if (result) | 
|  | 0 | 156 |  |     { | 
|  | 0 | 157 |  |       await _activityLogService.CreateActivityLogAsync(new ActivityLogCreateBusinessModel | 
|  | 0 | 158 |  |       { | 
|  | 0 | 159 |  |         EntityName = nameof(Robot), | 
|  | 0 | 160 |  |         EntityId = robotId.ToString(), | 
|  | 0 | 161 |  |         Action = enable ? ActivityAction.RobotPauseTaskAssignmentEnabled : ActivityAction.RobotPauseTaskAssignmentDisabl | 
|  | 0 | 162 |  |       }); | 
|  | 0 | 163 |  |     } | 
|  | 0 | 164 |  |     return result; | 
|  | 0 | 165 |  |   } | 
|  |  | 166 |  |  | 
|  |  | 167 |  |   public async Task<bool> GetPauseAutoTaskAssignmentAsync(Guid robotId) | 
|  | 0 | 168 |  |   { | 
|  | 0 | 169 |  |     var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0; | 
|  | 0 | 170 |  |     var robotData = await _robotDataRepository.GetRobotDataAsync(realmId, robotId); | 
|  | 0 | 171 |  |     if (robotData != null) | 
|  | 0 | 172 |  |     { | 
|  | 0 | 173 |  |       return robotData.PauseTaskAssignment; | 
|  |  | 174 |  |     } | 
|  | 0 | 175 |  |     return false; | 
|  | 0 | 176 |  |   } | 
|  |  | 177 |  | } |