| | 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 | | } |