< Summary

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

Feature is only available for sponsors

Upgrade to PRO version

Metrics

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{
 14  public Guid RobotId { get; set; }
 15  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
 035public class OnlineRobotsService(
 036    IBus bus,
 037    IEmailService emailService,
 038    IEventService eventService,
 039    IMemoryCache memoryCache,
 040    IRobotService robotService
 041  ) : IOnlineRobotsService
 42{
 043  private readonly IBus _bus = bus ?? throw new ArgumentNullException(nameof(bus));
 044  private readonly IEmailService _emailService = emailService ?? throw new ArgumentNullException(nameof(emailService));
 045  private readonly IEventService _eventService = eventService ?? throw new ArgumentNullException(nameof(eventService));
 046  private readonly IMemoryCache _memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
 047  private readonly IRobotService _robotService = robotService ?? throw new ArgumentNullException(nameof(robotService));
 048  private static string GetOnlineRobotsKey(int realmId) => $"OnlineRobotsService_OnlineRobots_{realmId}";
 049  private static string GetRobotCommandsKey(Guid robotId) => $"OnlineRobotsService_RobotCommands_{robotId}";
 50
 51  private static RobotStatus ConvertRobotStatus(RobotClientsRobotStatus robotStatus)
 052  {
 053    return robotStatus switch
 054    {
 055      RobotClientsRobotStatus.Idle => RobotStatus.Idle,
 056      RobotClientsRobotStatus.Running => RobotStatus.Running,
 057      RobotClientsRobotStatus.Stuck => RobotStatus.Stuck,
 058      RobotClientsRobotStatus.Aborting => RobotStatus.Aborting,
 059      RobotClientsRobotStatus.Paused => RobotStatus.Paused,
 060      RobotClientsRobotStatus.Critical => RobotStatus.Critical,
 061      RobotClientsRobotStatus.Charging => RobotStatus.Charging,
 062      RobotClientsRobotStatus.Offline => RobotStatus.Offline,
 063      _ => RobotStatus.Offline,
 064    };
 065  }
 66
 67  static private bool GenerateUnresolvableCriticalStatus(RobotClientsRobotCriticalStatus criticalStatus)
 068  {
 069    if (criticalStatus.HardwareEmergencyStop ||
 070        criticalStatus.BatteryLow.Count > 0 ||
 071        criticalStatus.MotorDamaged.Count > 0)
 072    {
 073      return true;
 74    }
 075    return false;
 076  }
 77
 78  public async Task AddRobotAsync(Guid robotId)
 079  {
 080    var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0;
 081    var OnlineRobotsIds = _memoryCache.Get<HashSet<Guid>>(GetOnlineRobotsKey(realmId)) ?? [];
 082    OnlineRobotsIds.Add(robotId);
 83    // Register the robot
 084    _memoryCache.Set(GetOnlineRobotsKey(realmId), OnlineRobotsIds);
 085    _memoryCache.Set(GetRobotCommandsKey(robotId), new RobotClientsRobotCommands());
 086  }
 87
 88  public async Task RemoveRobotAsync(Guid robotId)
 089  {
 090    var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0;
 91    // Unregister the robot
 092    var OnlineRobotsIds = _memoryCache.Get<HashSet<Guid>>(GetOnlineRobotsKey(realmId));
 093    if (OnlineRobotsIds != null && OnlineRobotsIds.Contains(robotId))
 094    {
 095      OnlineRobotsIds.Remove(robotId);
 096      _memoryCache.Set(GetOnlineRobotsKey(realmId), OnlineRobotsIds);
 097    }
 098    _memoryCache.Remove(GetRobotCommandsKey(robotId));
 099  }
 100
 101  public async Task UpdateRobotDataAsync(Guid robotId, RobotClientsExchange data)
 0102  {
 0103    if (_memoryCache.TryGetValue<bool>($"OnlineRobotsService_RobotData_Pause_{robotId}", out var _))
 0104    {
 105      // Blocking too much data to rabbitmq
 0106      return;
 107    }
 0108    _memoryCache.Set($"OnlineRobotsService_RobotData_Pause_{robotId}", true, TimeSpan.FromSeconds(1));
 109
 0110    var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0;
 0111    var robotStatus = ConvertRobotStatus(data.RobotStatus);
 0112    await _bus.Publish(new RobotDataContract {
 0113      RobotId = robotId,
 0114      RealmId = realmId,
 0115      RobotStatus = robotStatus,
 0116      CriticalStatus = new RobotCriticalStatus {
 0117        HardwareEmergencyStop = data.CriticalStatus.HardwareEmergencyStop,
 0118        SoftwareEmergencyStop = data.CriticalStatus.SoftwareEmergencyStop,
 0119        BatteryLow = [.. data.CriticalStatus.BatteryLow],
 0120        MotorDamaged = [.. data.CriticalStatus.MotorDamaged]
 0121      },
 0122      Batteries = [.. data.Batteries],
 0123      Position = new RobotDof {
 0124        X = data.Position.X,
 0125        Y = data.Position.Y,
 0126        Rotation = data.Position.Rotation
 0127      },
 0128      NavProgress = new AutoTaskNavProgress {
 0129        Eta = data.NavProgress.Eta,
 0130        Recoveries = data.NavProgress.Recoveries,
 0131        DistanceRemaining = data.NavProgress.DistanceRemaining,
 0132        WaypointsRemaining = data.NavProgress.WaypointsRemaining
 0133      }
 0134    });
 135
 0136    if(_memoryCache.TryGetValue<RobotClientsRobotCommands>(GetRobotCommandsKey(robotId), out var robotCommands))
 0137    {
 0138      if (robotCommands != null)
 0139      {
 0140        await _bus.Publish(new RobotCommandsContract {
 0141          RobotId = robotId,
 0142          RealmId = realmId,
 0143          Commands = new RobotCommands {
 0144            AbortTask = robotCommands.AbortTask,
 0145            PauseTaskAssigement = robotCommands.PauseTaskAssigement,
 0146            SoftwareEmergencyStop = robotCommands.SoftwareEmergencyStop,
 0147            RenewCertificate = robotCommands.RenewCertificate
 0148          }
 0149        });
 0150      }
 0151    }
 152
 0153    if (robotStatus == RobotStatus.Stuck)
 0154    {
 0155      if (!_memoryCache.TryGetValue<bool>($"OnlineRobotsService_RobotStuck_{robotId}", out var _))
 0156      {
 157        // First stuck in 5 minutes, sending email
 0158        await _emailService.SendRobotStuckEmailAsync(robotId, data.Position.X, data.Position.Y);
 0159      }
 0160      _memoryCache.Set($"OnlineRobotsService_RobotStuck_{robotId}", true, TimeSpan.FromMinutes(5));
 0161    }
 0162  }
 163
 164  private async Task SetRobotCommandsAsync(Guid robotId, RobotClientsRobotCommands commands)
 0165  {
 0166    _memoryCache.Set(GetRobotCommandsKey(robotId), commands);
 0167    var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0;
 0168    await _bus.Publish(new RobotCommandsContract {
 0169      RobotId = robotId,
 0170      RealmId = realmId,
 0171      Commands = new RobotCommands {
 0172        AbortTask = commands.AbortTask,
 0173        PauseTaskAssigement = commands.PauseTaskAssigement,
 0174        SoftwareEmergencyStop = commands.SoftwareEmergencyStop,
 0175        RenewCertificate = commands.RenewCertificate
 0176      }
 0177    });
 0178  }
 179
 180  public RobotClientsRobotCommands? GetRobotCommands(Guid robotId)
 0181  {
 0182    return _memoryCache.Get<RobotClientsRobotCommands>(GetRobotCommandsKey(robotId));
 0183  }
 184
 185  public async Task<bool> IsRobotOnlineAsync(Guid robotId)
 0186  {
 0187    var realmId = await _robotService.GetRobotRealmIdAsync(robotId) ?? 0;
 0188    var onlineRobotsIds = _memoryCache.Get<HashSet<Guid>>(GetOnlineRobotsKey((int)realmId));
 0189    return onlineRobotsIds != null && onlineRobotsIds.Contains(robotId);
 0190  }
 191
 192  public async Task<bool> SetAbortTaskAsync(Guid robotId, bool enable)
 0193  {
 0194    var robotCommands = GetRobotCommands(robotId);
 0195    if (robotCommands != null)
 0196    {
 0197      robotCommands.AbortTask = enable;
 0198      await SetRobotCommandsAsync(robotId, robotCommands);
 0199      _eventService.RobotCommandsHasUpdated(robotId);
 0200      return true;
 201    }
 202    else
 0203    {
 0204      return false;
 205    }
 0206  }
 207
 208  public async Task<bool> SetSoftwareEmergencyStopAsync(Guid robotId, bool enable)
 0209  {
 0210    var robotCommands = GetRobotCommands(robotId);
 0211    if (robotCommands != null)
 0212    {
 0213      robotCommands.SoftwareEmergencyStop = enable;
 0214      await SetRobotCommandsAsync(robotId, robotCommands);
 0215      _eventService.RobotCommandsHasUpdated(robotId);
 0216      return true;
 217    }
 218    else
 0219    {
 0220      return false;
 221    }
 0222  }
 223
 224  public async Task<bool> SetPauseTaskAssigementAsync(Guid robotId, bool enable)
 0225  {
 0226    var robotCommands = GetRobotCommands(robotId);
 0227    if (robotCommands != null)
 0228    {
 0229      robotCommands.PauseTaskAssigement = enable;
 0230      await SetRobotCommandsAsync(robotId, robotCommands);
 0231      _eventService.RobotCommandsHasUpdated(robotId);
 0232      return true;
 233    }
 234    else
 0235    {
 0236      return false;
 237    }
 0238  }
 239
 240  public bool GetPauseAutoTaskAssignment(Guid robotId)
 0241  {
 0242    var robotCommands = GetRobotCommands(robotId);
 0243    if (robotCommands != null)
 0244    {
 0245      return robotCommands.PauseTaskAssigement;
 246    }
 247    else
 0248    {
 0249      return false;
 250    }
 0251  }
 252
 253  public void SetAutoTaskNextApi(Guid robotId, AutoTask autoTask)
 0254  {
 0255    _memoryCache.Set($"OnlineRobotsService_RobotHasNextTask_{robotId}", autoTask);
 0256    _eventService.RobotHasNextTaskTriggered(robotId);
 0257  }
 258
 259  public AutoTask? GetAutoTaskNextApi(Guid robotId)
 0260  {
 0261    if (_memoryCache.TryGetValue($"OnlineRobotsService_RobotHasNextTask_{robotId}", out AutoTask? autoTask))
 0262    {
 0263      _memoryCache.Remove($"OnlineRobotsService_RobotHasNextTask_{robotId}");
 0264      return autoTask;
 265    }
 266    else
 0267    {
 0268      return null;
 269    }
 0270  }
 271}