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

Methods/Properties

get_RobotId()
get_Commands()