1- using System . Text . Json ;
1+ using MessagePack ;
22using Microsoft . AspNetCore . SignalR ;
33using Microsoft . EntityFrameworkCore ;
44using OpenShock . Common . Hubs ;
99using OpenShock . Common . Services . RedisPubSub ;
1010using OpenShock . Common . Utils ;
1111using Redis . OM . Contracts ;
12- using Redis . OM . Searching ;
1312using StackExchange . Redis ;
1413
1514namespace OpenShock . API . Realtime ;
@@ -23,6 +22,7 @@ public sealed class RedisSubscriberService : IHostedService, IAsyncDisposable
2322 private readonly IDbContextFactory < OpenShockContext > _dbContextFactory ;
2423 private readonly IRedisConnectionProvider _redisConnectionProvider ;
2524 private readonly ISubscriber _subscriber ;
25+ private readonly ILogger < RedisSubscriberService > _logger ;
2626
2727 /// <summary>
2828 /// DI Constructor
@@ -31,46 +31,87 @@ public sealed class RedisSubscriberService : IHostedService, IAsyncDisposable
3131 /// <param name="hubContext"></param>
3232 /// <param name="dbContextFactory"></param>
3333 /// <param name="redisConnectionProvider"></param>
34+ /// <param name="logger"></param>
3435 public RedisSubscriberService (
3536 IConnectionMultiplexer connectionMultiplexer ,
3637 IHubContext < UserHub , IUserHub > hubContext ,
3738 IDbContextFactory < OpenShockContext > dbContextFactory ,
38- IRedisConnectionProvider redisConnectionProvider )
39+ IRedisConnectionProvider redisConnectionProvider ,
40+ ILogger < RedisSubscriberService > logger
41+ )
3942 {
4043 _hubContext = hubContext ;
4144 _dbContextFactory = dbContextFactory ;
4245 _redisConnectionProvider = redisConnectionProvider ;
4346 _subscriber = connectionMultiplexer . GetSubscriber ( ) ;
47+ _logger = logger ;
4448 }
4549
4650 /// <inheritdoc />
4751 public async Task StartAsync ( CancellationToken cancellationToken )
4852 {
49- await _subscriber . SubscribeAsync ( RedisChannels . KeyEventExpired , ( _ , message ) => { OsTask . Run ( ( ) => HandleKeyExpired ( message ) ) ; } ) ;
50- await _subscriber . SubscribeAsync ( RedisChannels . DeviceOnlineStatus , ( _ , message ) => { OsTask . Run ( ( ) => HandleDeviceOnlineStatus ( message ) ) ; } ) ;
53+ await _subscriber . SubscribeAsync ( RedisChannels . KeyEventExpired , HandleKeyExpired ) ;
54+ await _subscriber . SubscribeAsync ( RedisChannels . DeviceStatus , HandleDeviceStatus ) ;
5155 }
5256
53- private async Task HandleDeviceOnlineStatus ( RedisValue message )
57+ private void HandleKeyExpired ( RedisChannel _ , RedisValue message )
5458 {
5559 if ( ! message . HasValue ) return ;
56- var data = JsonSerializer . Deserialize < DeviceUpdatedMessage > ( message . ToString ( ) ) ;
57- if ( data is null ) return ;
60+ if ( message . ToString ( ) . Split ( ':' , 2 ) is not [ { } guid , { } name ] ) return ;
61+
62+ if ( ! Guid . TryParse ( guid , out var id ) ) return ;
5863
59- await LogicDeviceOnlineStatus ( data . Id ) ;
64+ if ( typeof ( DeviceOnline ) . FullName == name )
65+ {
66+ OsTask . Run ( ( ) => LogicDeviceOnlineStatus ( id ) ) ;
67+ }
6068 }
61-
62- private async Task HandleKeyExpired ( RedisValue message )
69+
70+ private void HandleDeviceStatus ( RedisChannel _ , RedisValue value )
6371 {
64- if ( ! message . HasValue ) return ;
65- var msg = message . ToString ( ) . Split ( ':' ) ;
66- if ( msg . Length < 2 ) return ;
72+ if ( ! value . HasValue ) return ;
73+
74+ DeviceStatus message ;
75+ try
76+ {
77+ message = MessagePackSerializer . Deserialize < DeviceStatus > ( value ) ;
78+ }
79+ catch ( Exception e )
80+ {
81+ _logger . LogError ( e , "Failed to deserialize redis message" ) ;
82+ return ;
83+ }
84+
85+ OsTask . Run ( ( ) => HandleDeviceStatusMessage ( message ) ) ;
86+ }
6787
88+ private async Task HandleDeviceStatusMessage ( DeviceStatus message )
89+ {
90+ switch ( message . Payload )
91+ {
92+ case DeviceBoolStatePayload boolState :
93+ await HandleDeviceBoolState ( message . DeviceId , boolState ) ;
94+ break ;
95+ default :
96+ _logger . LogError ( "Got DeviceStatus with unknown payload type: {PayloadType}" , message . Payload . GetType ( ) . Name ) ;
97+ break ;
98+ }
6899
69- if ( ! Guid . TryParse ( msg [ 1 ] , out var id ) ) return ;
100+ }
70101
71- if ( typeof ( DeviceOnline ) . FullName == msg [ 0 ] )
102+ private async Task HandleDeviceBoolState ( Guid deviceId , DeviceBoolStatePayload state )
103+ {
104+ switch ( state . Type )
72105 {
73- await LogicDeviceOnlineStatus ( id ) ;
106+ case DeviceBoolStateType . Online :
107+ await LogicDeviceOnlineStatus ( deviceId ) ; // TODO: Handle device offline messages too
108+ break ;
109+ case DeviceBoolStateType . EStopped :
110+ _logger . LogInformation ( "EStopped state not implemented yet for DeviceId {DeviceId}" , deviceId ) ;
111+ break ;
112+ default :
113+ _logger . LogError ( "Unknown DeviceBoolStateType: {StateType}" , state . Type ) ;
114+ break ;
74115 }
75116 }
76117
@@ -111,15 +152,23 @@ await _hubContext.Clients.Users(userIds).DeviceStatus([
111152 }
112153
113154 /// <inheritdoc />
114- public Task StopAsync ( CancellationToken cancellationToken )
155+ public async Task StopAsync ( CancellationToken cancellationToken )
115156 {
116- return Task . CompletedTask ;
157+ await _subscriber . UnsubscribeAllAsync ( ) ;
117158 }
118159
119160 /// <inheritdoc />
120161 public async ValueTask DisposeAsync ( )
121162 {
122- await _subscriber . UnsubscribeAllAsync ( ) ;
163+ try
164+ {
165+ await _subscriber . UnsubscribeAllAsync ( ) ;
166+ }
167+ catch ( Exception ex )
168+ {
169+ _logger . LogError ( ex , "Error during Redis unsubscribe in DisposeAsync" ) ;
170+ }
171+
123172 GC . SuppressFinalize ( this ) ;
124173 }
125174
0 commit comments