< Summary

Information
Class: LGDXRobotCloud.API.Services.Navigation.RobotCommandsEventArgs
Assembly: LGDXRobotCloud.API
File(s): /builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Navigation/OnlineRobotsService.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 2
Coverable lines: 2
Total lines: 318
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_RobotId()100%210%
get_Commands()100%210%

File(s)

/builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Navigation/OnlineRobotsService.cs

#LineLine coverage
 1using LGDXRobotCloud.API.Services.Administration;
 2using LGDXRobotCloud.API.Services.Common;
 3using LGDXRobotCloud.Data.Contracts;
 4using LGDXRobotCloud.Data.Entities;
 5using LGDXRobotCloud.Data.Models.Business.Administration;
 6using LGDXRobotCloud.Protos;
 7using LGDXRobotCloud.Utilities.Enums;
 8using MassTransit;
 9using Microsoft.Extensions.Caching.Distributed;
 10using Microsoft.Extensions.Caching.Memory;
 11
 12namespace LGDXRobotCloud.API.Services.Navigation;
 13
 14public class RobotCommandsEventArgs : EventArgs
 15{
 016  public Guid RobotId { get; set; }
 017  public required RobotClientsRobotCommands Commands { get; set; }
 18}
 19
 20public interface IOnlineRobotsService
 21{
 22  Task AddRobotAsync(Guid robotId);
 23  Task RemoveRobotAsync(Guid robotId);
 24  Task UpdateRobotDataAsync(Guid robotId, RobotClientsExchange data, bool realtime = false);
 25  RobotClientsRobotCommands? GetRobotCommands(Guid robotId);
 26  RobotDataContract? GetRobotData(Guid robotId);
 27
 28  Task<bool> IsRobotOnlineAsync(Guid robotId);
 29  Task<bool> SetAbortTaskAsync(Guid robotId, bool enable);
 30  Task<bool> SetSoftwareEmergencyStopAsync(Guid robotId, bool enable);
 31  Task<bool> SetPauseTaskAssigementAsync(Guid robotId, bool enable);
 32  bool GetPauseAutoTaskAssignment(Guid robotId);
 33
 34  void SetAutoTaskNextApi(Guid robotId, AutoTask task);
 35  AutoTask? GetAutoTaskNextApi(Guid robotId);
 36}
 37
 38public class OnlineRobotsService(
 39    IActivityLogService activityLogService,
 40    IBus bus,
 41    IEmailService emailService,
 42    IEventService eventService,
 43    IMemoryCache memoryCache,
 44    IRobotService robotService
 45  ) : IOnlineRobotsService
 46{
 47  private readonly IActivityLogService _activityLogService = activityLogService ?? throw new ArgumentNullException(nameo
 48  private readonly IBus _bus = bus ?? throw new ArgumentNullException(nameof(bus));
 49  private readonly IEmailService _emailService = emailService ?? throw new ArgumentNullException(nameof(emailService));
 50  private readonly IEventService _eventService = eventService ?? throw new ArgumentNullException(nameof(eventService));
 51  private readonly IMemoryCache _memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
 52  private readonly IRobotService _robotService = robotService ?? throw new ArgumentNullException(nameof(robotService));
 53  private static string GetOnlineRobotsKey(int realmId) => $"OnlineRobotsService_OnlineRobots_{realmId}";
 54  private static string GetRobotCommandsKey(Guid robotId) => $"OnlineRobotsService_RobotCommands_{robotId}";
 55  private const int robotAliveMinutes = 5;
 56
 57  private static RobotStatus ConvertRobotStatus(RobotClientsRobotStatus robotStatus)
 58  {
 59    return robotStatus switch
 60    {
 61      RobotClientsRobotStatus.Idle => RobotStatus.Idle,
 62      RobotClientsRobotStatus.Running => RobotStatus.Running,
 63      RobotClientsRobotStatus.Stuck => RobotStatus.Stuck,
 64      RobotClientsRobotStatus.Aborting => RobotStatus.Aborting,
 65      RobotClientsRobotStatus.Paused => RobotStatus.Paused,
 66      RobotClientsRobotStatus.Critical => RobotStatus.Critical,
 67      RobotClientsRobotStatus.Charging => RobotStatus.Charging,
 68      RobotClientsRobotStatus.Offline => RobotStatus.Offline,
 69      _ => RobotStatus.Offline,
 70    };
 71  }
 72
 73  static private bool GenerateUnresolvableCriticalStatus(RobotClientsRobotCriticalStatus criticalStatus)
 74  {
 75    if (criticalStatus.HardwareEmergencyStop ||
 76        criticalStatus.BatteryLow.Count > 0 ||
 77        criticalStatus.MotorDamaged.Count > 0)
 78    {
 79      return true;
 80    }
 81    return false;
 82  }
 83
 84  public async Task AddRobotAsync(Guid robotId)
 85  {
 86    var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0;
 87    var OnlineRobotsIds = _memoryCache.Get<HashSet<Guid>>(GetOnlineRobotsKey(realmId)) ?? [];
 88    OnlineRobotsIds.Add(robotId);
 89    // Register the robot
 90    _memoryCache.Set(GetOnlineRobotsKey(realmId), OnlineRobotsIds);
 91    _memoryCache.Set(GetRobotCommandsKey(robotId), new RobotClientsRobotCommands());
 92    _memoryCache.Set($"OnlineRobotsService_RobotData_ConnectionAlive_{robotId}", true, TimeSpan.FromMinutes(robotAliveMi
 93  }
 94
 95  public async Task RemoveRobotAsync(Guid robotId)
 96  {
 97    var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0;
 98    // Unregister the robot
 99    _memoryCache.TryGetValue(GetOnlineRobotsKey(realmId), out HashSet<Guid>? OnlineRobotsIds);
 100    if (OnlineRobotsIds != null && OnlineRobotsIds.Contains(robotId))
 101    {
 102      OnlineRobotsIds.Remove(robotId);
 103      _memoryCache.Set(GetOnlineRobotsKey(realmId), OnlineRobotsIds);
 104    }
 105    _memoryCache.Remove(GetRobotCommandsKey(robotId));
 106
 107    // Publish the robot is offline
 108    await _bus.Publish(new RobotDataContract
 109    {
 110      RobotId = robotId,
 111      RealmId = realmId,
 112      RobotStatus = RobotStatus.Offline,
 113      CurrentTime = DateTime.MinValue
 114    });
 115  }
 116
 117  public async Task UpdateRobotDataAsync(Guid robotId, RobotClientsExchange data, bool realtime = false)
 118  {
 119    if (_memoryCache.TryGetValue<bool>($"OnlineRobotsService_RobotData_Pause_{robotId}", out var _))
 120    {
 121      // Blocking too much data to rabbitmq
 122      return;
 123    }
 124    _memoryCache.Set($"OnlineRobotsService_RobotData_Pause_{robotId}", true, TimeSpan.FromSeconds(1));
 125    _memoryCache.Set($"OnlineRobotsService_RobotData_ConnectionAlive_{robotId}", true, TimeSpan.FromMinutes(robotAliveMi
 126
 127    var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0;
 128    var robotStatus = ConvertRobotStatus(data.RobotStatus);
 129    var robotData = new RobotDataContract
 130    {
 131      RobotId = robotId,
 132      RealmId = realmId,
 133      RobotStatus = robotStatus,
 134      CriticalStatus = new RobotCriticalStatus
 135      {
 136        HardwareEmergencyStop = data.CriticalStatus.HardwareEmergencyStop,
 137        SoftwareEmergencyStop = data.CriticalStatus.SoftwareEmergencyStop,
 138        BatteryLow = [.. data.CriticalStatus.BatteryLow],
 139        MotorDamaged = [.. data.CriticalStatus.MotorDamaged]
 140      },
 141      Batteries = [.. data.Batteries],
 142      Position = new RobotDof
 143      {
 144        X = data.Position.X,
 145        Y = data.Position.Y,
 146        Rotation = data.Position.Rotation
 147      },
 148      NavProgress = new AutoTaskNavProgress
 149      {
 150        Eta = data.NavProgress.Eta,
 151        Recoveries = data.NavProgress.Recoveries,
 152        DistanceRemaining = data.NavProgress.DistanceRemaining,
 153        WaypointsRemaining = data.NavProgress.WaypointsRemaining
 154      },
 155      CurrentTime = realtime ? DateTime.MaxValue : DateTime.UtcNow
 156    };
 157    await _bus.Publish(robotData);
 158    _memoryCache.Set($"OnlineRobotsService_RobotData_{robotId}", robotData, TimeSpan.FromMinutes(robotAliveMinutes));
 159
 160    if (_memoryCache.TryGetValue<RobotClientsRobotCommands>(GetRobotCommandsKey(robotId), out var robotCommands))
 161    {
 162      if (robotCommands != null)
 163      {
 164        await _bus.Publish(new RobotCommandsContract
 165        {
 166          RobotId = robotId,
 167          RealmId = realmId,
 168          Commands = new RobotCommands
 169          {
 170            AbortTask = robotCommands.AbortTask,
 171            PauseTaskAssigement = robotCommands.PauseTaskAssigement,
 172            SoftwareEmergencyStop = robotCommands.SoftwareEmergencyStop,
 173            RenewCertificate = robotCommands.RenewCertificate
 174          }
 175        });
 176      }
 177    }
 178
 179    if (robotStatus == RobotStatus.Stuck)
 180    {
 181      if (!_memoryCache.TryGetValue<bool>($"OnlineRobotsService_RobotStuck_{robotId}", out var _))
 182      {
 183        // First stuck in 5 minutes, sending email
 184        await _emailService.SendRobotStuckEmailAsync(robotId, data.Position.X, data.Position.Y);
 185      }
 186      _memoryCache.Set($"OnlineRobotsService_RobotStuck_{robotId}", true, TimeSpan.FromMinutes(5));
 187    }
 188  }
 189
 190  private async Task SetRobotCommandsAsync(Guid robotId, RobotClientsRobotCommands commands)
 191  {
 192    _memoryCache.Set(GetRobotCommandsKey(robotId), commands);
 193    var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0;
 194    await _bus.Publish(new RobotCommandsContract {
 195      RobotId = robotId,
 196      RealmId = realmId,
 197      Commands = new RobotCommands {
 198        AbortTask = commands.AbortTask,
 199        PauseTaskAssigement = commands.PauseTaskAssigement,
 200        SoftwareEmergencyStop = commands.SoftwareEmergencyStop,
 201        RenewCertificate = commands.RenewCertificate
 202      }
 203    });
 204  }
 205
 206  public RobotClientsRobotCommands? GetRobotCommands(Guid robotId)
 207  {
 208    _memoryCache.TryGetValue(GetRobotCommandsKey(robotId), out RobotClientsRobotCommands? robotCommands);
 209    return robotCommands;
 210  }
 211
 212  public async Task<bool> IsRobotOnlineAsync(Guid robotId)
 213  {
 214    var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0;
 215    var onlineRobotsIds = _memoryCache.Get<HashSet<Guid>>(GetOnlineRobotsKey((int)realmId));
 216    return onlineRobotsIds != null && onlineRobotsIds.Contains(robotId);
 217  }
 218
 219  public RobotDataContract? GetRobotData(Guid robotId)
 220  {
 221    _memoryCache.TryGetValue($"OnlineRobotsService_RobotData_{robotId}", out RobotDataContract? robotData);
 222    return robotData;
 223  }
 224
 225  public async Task<bool> SetAbortTaskAsync(Guid robotId, bool enable)
 226  {
 227    var robotCommands = GetRobotCommands(robotId);
 228    if (robotCommands != null)
 229    {
 230      robotCommands.AbortTask = enable;
 231      await SetRobotCommandsAsync(robotId, robotCommands);
 232      _eventService.RobotCommandsHasUpdated(robotId);
 233      return true;
 234    }
 235    else
 236    {
 237      return false;
 238    }
 239  }
 240
 241  public async Task<bool> SetSoftwareEmergencyStopAsync(Guid robotId, bool enable)
 242  {
 243    var robotCommands = GetRobotCommands(robotId);
 244    if (robotCommands != null)
 245    {
 246      robotCommands.SoftwareEmergencyStop = enable;
 247      await SetRobotCommandsAsync(robotId, robotCommands);
 248      _eventService.RobotCommandsHasUpdated(robotId);
 249
 250      await _activityLogService.CreateActivityLogAsync(new ActivityLogCreateBusinessModel
 251      {
 252        EntityName = nameof(Robot),
 253        EntityId = robotId.ToString(),
 254        Action = enable ? ActivityAction.RobotSoftwareEmergencyStopEnabled : ActivityAction.RobotSoftwareEmergencyStopDi
 255      });
 256      return true;
 257    }
 258    else
 259    {
 260      return false;
 261    }
 262  }
 263
 264  public async Task<bool> SetPauseTaskAssigementAsync(Guid robotId, bool enable)
 265  {
 266    var robotCommands = GetRobotCommands(robotId);
 267    if (robotCommands != null)
 268    {
 269      robotCommands.PauseTaskAssigement = enable;
 270      await SetRobotCommandsAsync(robotId, robotCommands);
 271      _eventService.RobotCommandsHasUpdated(robotId);
 272
 273      await _activityLogService.CreateActivityLogAsync(new ActivityLogCreateBusinessModel
 274      {
 275        EntityName = nameof(Robot),
 276        EntityId = robotId.ToString(),
 277        Action = enable ? ActivityAction.RobotPauseTaskAssignmentEnabled : ActivityAction.RobotPauseTaskAssignmentDisabl
 278      });
 279      return true;
 280    }
 281    else
 282    {
 283      return false;
 284    }
 285  }
 286
 287  public bool GetPauseAutoTaskAssignment(Guid robotId)
 288  {
 289    var robotCommands = GetRobotCommands(robotId);
 290    if (robotCommands != null)
 291    {
 292      return robotCommands.PauseTaskAssigement;
 293    }
 294    else
 295    {
 296      return false;
 297    }
 298  }
 299
 300  public void SetAutoTaskNextApi(Guid robotId, AutoTask autoTask)
 301  {
 302    _memoryCache.Set($"OnlineRobotsService_RobotHasNextTask_{robotId}", autoTask);
 303    _eventService.RobotHasNextTaskTriggered(robotId);
 304  }
 305
 306  public AutoTask? GetAutoTaskNextApi(Guid robotId)
 307  {
 308    if (_memoryCache.TryGetValue($"OnlineRobotsService_RobotHasNextTask_{robotId}", out AutoTask? autoTask))
 309    {
 310      _memoryCache.Remove($"OnlineRobotsService_RobotHasNextTask_{robotId}");
 311      return autoTask;
 312    }
 313    else
 314    {
 315      return null;
 316    }
 317  }
 318}

Methods/Properties

get_RobotId()
get_Commands()