|  |  | 1 |  | using LGDXRobotCloud.Data.Models.Redis; | 
|  |  | 2 |  | using LGDXRobotCloud.Protos; | 
|  |  | 3 |  | using LGDXRobotCloud.Utilities.Helpers; | 
|  |  | 4 |  | using NRedisStack; | 
|  |  | 5 |  | using NRedisStack.RedisStackCommands; | 
|  |  | 6 |  | using NRedisStack.Search; | 
|  |  | 7 |  | using NRedisStack.Search.Literals.Enums; | 
|  |  | 8 |  | using StackExchange.Redis; | 
|  |  | 9 |  | using static StackExchange.Redis.RedisChannel; | 
|  |  | 10 |  |  | 
|  |  | 11 |  | namespace LGDXRobotCloud.API.Repositories; | 
|  |  | 12 |  |  | 
|  |  | 13 |  | public interface IRobotDataRepository | 
|  |  | 14 |  | { | 
|  |  | 15 |  |   // Exchange | 
|  |  | 16 |  |   Task StartExchangeAsync(int realmId, Guid robotId); | 
|  |  | 17 |  |   Task StopExchangeAsync(int realmId, Guid robotId); | 
|  |  | 18 |  |  | 
|  |  | 19 |  |   Task<RobotData?> GetRobotDataAsync(int realmId, Guid robotId); | 
|  |  | 20 |  |   Task SetRobotDataAsync(int realmId, Guid robotId, RobotData data); | 
|  |  | 21 |  |  | 
|  |  | 22 |  |   Task<bool> AddRobotCommandAsync(int realmId, Guid robotId, RobotClientsRobotCommands cmd); | 
|  |  | 23 |  | } | 
|  |  | 24 |  |  | 
|  | 0 | 25 |  | public partial class RobotDataRepository( | 
|  | 0 | 26 |  |     IConnectionMultiplexer redisConnection, | 
|  | 0 | 27 |  |     ILogger<RobotDataRepository> logger | 
|  | 0 | 28 |  |   ) : IRobotDataRepository | 
|  |  | 29 |  | { | 
|  | 0 | 30 |  |   private readonly IConnectionMultiplexer _redisConnection = redisConnection ?? throw new ArgumentNullException(nameof(r | 
|  |  | 31 |  |  | 
|  |  | 32 |  |   [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "Redis RobotDataRepository Exception: {Msg}")] | 
|  |  | 33 |  |   public partial void LogException(string msg); | 
|  |  | 34 |  |  | 
|  |  | 35 |  |   private async Task<bool> IndexExistsAsync(string indexName) | 
|  | 0 | 36 |  |   { | 
|  | 0 | 37 |  |     var db = _redisConnection.GetDatabase(); | 
|  |  | 38 |  |     try | 
|  | 0 | 39 |  |     { | 
|  | 0 | 40 |  |       var index = await db.FT().InfoAsync(indexName); | 
|  | 0 | 41 |  |     } | 
|  | 0 | 42 |  |     catch (Exception ex) | 
|  | 0 | 43 |  |     { | 
|  | 0 | 44 |  |       if (!ex.Message.Contains("Unknown index name", StringComparison.CurrentCultureIgnoreCase)) | 
|  | 0 | 45 |  |       { | 
|  | 0 | 46 |  |         LogException(ex.Message); | 
|  | 0 | 47 |  |       } | 
|  | 0 | 48 |  |       return false; | 
|  |  | 49 |  |     } | 
|  | 0 | 50 |  |     return true; | 
|  | 0 | 51 |  |   } | 
|  |  | 52 |  |  | 
|  |  | 53 |  |   public async Task StartExchangeAsync(int realmId, Guid robotId) | 
|  | 0 | 54 |  |   { | 
|  | 0 | 55 |  |     var db = _redisConnection.GetDatabase(); | 
|  |  | 56 |  |  | 
|  |  | 57 |  |     // Create index | 
|  | 0 | 58 |  |     if (!await IndexExistsAsync(RedisHelper.GetRobotDataIndex(realmId))) | 
|  | 0 | 59 |  |     { | 
|  |  | 60 |  |       try | 
|  | 0 | 61 |  |       { | 
|  | 0 | 62 |  |         await db.FT().CreateAsync(RedisHelper.GetRobotDataIndex(realmId), | 
|  | 0 | 63 |  |           new FTCreateParams() | 
|  | 0 | 64 |  |             .On(IndexDataType.JSON) | 
|  | 0 | 65 |  |             .Prefix(RedisHelper.GetRobotDataPrefix(realmId)), | 
|  | 0 | 66 |  |           new Schema() | 
|  | 0 | 67 |  |           .AddNumericField(new FieldName($"$.{nameof(RobotData.RobotStatus)}", $"{nameof(RobotData.RobotStatus)}")) | 
|  | 0 | 68 |  |           .AddTagField(new FieldName($"$.{nameof(RobotData.PauseTaskAssignment)}", $"{nameof(RobotData.PauseTaskAssignme | 
|  | 0 | 69 |  |       } | 
|  | 0 | 70 |  |       catch (Exception ex) | 
|  | 0 | 71 |  |       { | 
|  | 0 | 72 |  |         if (!ex.Message.Contains("Index already exists", StringComparison.CurrentCultureIgnoreCase)) | 
|  | 0 | 73 |  |         { | 
|  | 0 | 74 |  |           LogException(ex.Message); | 
|  | 0 | 75 |  |         } | 
|  | 0 | 76 |  |       } | 
|  | 0 | 77 |  |     } | 
|  |  | 78 |  |     try | 
|  | 0 | 79 |  |     { | 
|  | 0 | 80 |  |       await db.JSON().SetAsync(RedisHelper.GetRobotData(realmId, robotId), "$", new RobotData()); | 
|  | 0 | 81 |  |       await db.KeyExpireAsync(RedisHelper.GetRobotData(realmId, robotId), TimeSpan.FromMinutes(5)); | 
|  | 0 | 82 |  |     } | 
|  | 0 | 83 |  |     catch (Exception ex) | 
|  | 0 | 84 |  |     { | 
|  | 0 | 85 |  |       LogException(ex.Message); | 
|  | 0 | 86 |  |     } | 
|  | 0 | 87 |  |   } | 
|  |  | 88 |  |  | 
|  |  | 89 |  |   public async Task StopExchangeAsync(int realmId, Guid robotId) | 
|  | 0 | 90 |  |   { | 
|  | 0 | 91 |  |     var db = _redisConnection.GetDatabase(); | 
|  |  | 92 |  |     try | 
|  | 0 | 93 |  |     { | 
|  | 0 | 94 |  |       await db.KeyDeleteAsync(RedisHelper.GetRobotData(realmId, robotId)); | 
|  | 0 | 95 |  |     } | 
|  | 0 | 96 |  |     catch (Exception ex) | 
|  | 0 | 97 |  |     { | 
|  | 0 | 98 |  |       LogException(ex.Message); | 
|  | 0 | 99 |  |     } | 
|  | 0 | 100 |  |   } | 
|  |  | 101 |  |  | 
|  |  | 102 |  |   public async Task<RobotData?> GetRobotDataAsync(int realmId, Guid robotId) | 
|  | 0 | 103 |  |   { | 
|  | 0 | 104 |  |     var db = _redisConnection.GetDatabase(); | 
|  | 0 | 105 |  |     RobotData? result = null; | 
|  |  | 106 |  |     try | 
|  | 0 | 107 |  |     { | 
|  | 0 | 108 |  |       result = await db.JSON().GetAsync<RobotData>(RedisHelper.GetRobotData(realmId, robotId)); | 
|  | 0 | 109 |  |     } | 
|  | 0 | 110 |  |     catch (Exception ex) | 
|  | 0 | 111 |  |     { | 
|  | 0 | 112 |  |       LogException(ex.Message); | 
|  | 0 | 113 |  |     } | 
|  | 0 | 114 |  |     return result; | 
|  | 0 | 115 |  |   } | 
|  |  | 116 |  |  | 
|  |  | 117 |  |   public async Task SetRobotDataAsync(int realmId, Guid robotId, RobotData data) | 
|  | 0 | 118 |  |   { | 
|  | 0 | 119 |  |     var db = _redisConnection.GetDatabase(); | 
|  | 0 | 120 |  |     var pipeline = new Pipeline(db); | 
|  | 0 | 121 |  |     List<Task> tasks = []; | 
|  |  | 122 |  |     try | 
|  | 0 | 123 |  |     { | 
|  | 0 | 124 |  |       tasks.Add(pipeline.Json.SetAsync(RedisHelper.GetRobotData(realmId, robotId), "$", data)); | 
|  | 0 | 125 |  |       tasks.Add(db.KeyExpireAsync(RedisHelper.GetRobotData(realmId, robotId), TimeSpan.FromMinutes(5))); | 
|  | 0 | 126 |  |       pipeline.Execute(); | 
|  | 0 | 127 |  |       await Task.WhenAll(tasks); | 
|  | 0 | 128 |  |     } | 
|  | 0 | 129 |  |     catch (Exception ex) | 
|  | 0 | 130 |  |     { | 
|  | 0 | 131 |  |       LogException(ex.Message); | 
|  | 0 | 132 |  |     } | 
|  | 0 | 133 |  |   } | 
|  |  | 134 |  |  | 
|  |  | 135 |  |   public async Task<bool> AddRobotCommandAsync(int realmId, Guid robotId, RobotClientsRobotCommands cmd) | 
|  | 0 | 136 |  |   { | 
|  | 0 | 137 |  |     var db = _redisConnection.GetDatabase(); | 
|  |  | 138 |  |     try | 
|  | 0 | 139 |  |     { | 
|  | 0 | 140 |  |       if (!await db.KeyExistsAsync(RedisHelper.GetRobotData(realmId, robotId))) | 
|  | 0 | 141 |  |       { | 
|  | 0 | 142 |  |         return false; | 
|  |  | 143 |  |       } | 
|  | 0 | 144 |  |       var subscriber = _redisConnection.GetSubscriber(); | 
|  | 0 | 145 |  |       var data = new RobotClientsResponse { Commands = cmd }; | 
|  | 0 | 146 |  |       var base64 = SerialiserHelper.ToBase64(data); | 
|  | 0 | 147 |  |       await subscriber.PublishAsync(new RedisChannel(RedisHelper.GetRobotExchangeQueue(robotId), PatternMode.Literal), b | 
|  | 0 | 148 |  |       return true; | 
|  |  | 149 |  |     } | 
|  | 0 | 150 |  |     catch (Exception ex) | 
|  | 0 | 151 |  |     { | 
|  | 0 | 152 |  |       LogException(ex.Message); | 
|  | 0 | 153 |  |     } | 
|  | 0 | 154 |  |     return false; | 
|  | 0 | 155 |  |   } | 
|  |  | 156 |  | } |