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

Methods/Properties

get_RobotId()
get_Commands()