Skip to content

Commit af20acb

Browse files
hhvrcLucHeartCopilot
authored
Add device estop and reboot support (#224)
* Implement device estop and reboot support * Update Common/Redis/PubSub/DeviceRebootMessage.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Common/Redis/PubSub/DeviceRebootMessage.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Common/Redis/PubSub/DeviceEmergencyStopMessage.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Common/Redis/PubSub/DeviceEmergencyStopMessage.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * use correct struct for redis message * Add DeviceMissingFeature --------- Co-authored-by: Luc ♥ <luca@perugia.ovh> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: LucHeart <luc@luc.cat>
1 parent 1afa99d commit af20acb

File tree

13 files changed

+208
-1
lines changed

13 files changed

+208
-1
lines changed

Common/Hubs/UserHub.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,18 @@ public async Task CaptivePortal(Guid deviceId, bool enabled)
106106
await _redisPubService.SendDeviceCaptivePortal(deviceId, enabled);
107107
}
108108

109+
public async Task EmergencyStop(Guid deviceId)
110+
{
111+
// Require a user session basically
112+
if (_tokenPermissions is not null) return;
113+
114+
var devices = await _db.Devices.Where(x => x.OwnerId == UserId)
115+
.AnyAsync(x => x.Id == deviceId);
116+
if (!devices) return;
117+
118+
await _redisPubService.SendDeviceEmergencyStop(deviceId);
119+
}
120+
109121
public async Task OtaInstall(Guid deviceId, SemVersion version)
110122
{
111123
// Require a user session basically
@@ -118,6 +130,18 @@ public async Task OtaInstall(Guid deviceId, SemVersion version)
118130
await _redisPubService.SendDeviceOtaInstall(deviceId, version);
119131
}
120132

133+
public async Task Reboot(Guid deviceId)
134+
{
135+
// Require a user session basically
136+
if (_tokenPermissions is not null) return;
137+
138+
var devices = await _db.Devices.Where(x => x.OwnerId == UserId)
139+
.AnyAsync(x => x.Id == deviceId);
140+
if (!devices) return;
141+
142+
await _redisPubService.SendDeviceReboot(deviceId);
143+
}
144+
121145

122146
private Task<User> GetUser() => GetUser(UserId, _db);
123147

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using System.Text.Json.Serialization;
2+
namespace OpenShock.Common.Redis.PubSub;
3+
4+
public sealed class DeviceEmergencyStopMessage
5+
{
6+
public required Guid Id { get; set; }
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using System.Text.Json.Serialization;
2+
namespace OpenShock.Common.Redis.PubSub;
3+
4+
public sealed class DeviceRebootMessage
5+
{
6+
public required Guid Id { get; set; }
7+
}

Common/Services/RedisPubSub/IRedisPubService.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,25 @@ Task SendDeviceControl(Guid sender,
3636
/// <returns></returns>
3737
Task SendDeviceUpdate(Guid deviceId);
3838

39+
/// <summary>
40+
/// Trigger the emergency stop on the device if it's supported
41+
/// </summary>
42+
/// <param name="deviceId"></param>
43+
/// <returns></returns>
44+
Task SendDeviceEmergencyStop(Guid deviceId);
45+
3946
/// <summary>
4047
/// Start an OTA update on the device
4148
/// </summary>
4249
/// <param name="deviceId"></param>
4350
/// <param name="version"></param>
4451
/// <returns></returns>
4552
Task SendDeviceOtaInstall(Guid deviceId, SemVersion version);
53+
54+
/// <summary>
55+
/// Reboot the device if it's supported
56+
/// </summary>
57+
/// <param name="deviceId"></param>
58+
/// <returns></returns>
59+
Task SendDeviceReboot(Guid deviceId);
4660
}

Common/Services/RedisPubSub/RedisChannels.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ public static class RedisChannels
1313

1414
// OTA
1515
public static readonly RedisChannel DeviceOtaInstall = new("msg-device-ota-install", RedisChannel.PatternMode.Literal);
16+
17+
public static readonly RedisChannel DeviceEmergencyStop = new("msg-device-emergency-stop", RedisChannel.PatternMode.Literal);
18+
public static readonly RedisChannel DeviceReboot = new("msg-device-reboot", RedisChannel.PatternMode.Literal);
1619
}

Common/Services/RedisPubSub/RedisPubService.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,17 @@ public Task SendDeviceUpdate(Guid deviceId)
6363
return _subscriber.PublishAsync(RedisChannels.DeviceUpdate, JsonSerializer.Serialize(redisMessage));
6464
}
6565

66+
/// <inheritdoc />
67+
public Task SendDeviceEmergencyStop(Guid deviceId)
68+
{
69+
var redisMessage = new DeviceEmergencyStopMessage
70+
{
71+
Id = deviceId
72+
};
73+
74+
return _subscriber.PublishAsync(RedisChannels.DeviceEmergencyStop, JsonSerializer.Serialize(redisMessage));
75+
}
76+
6677
/// <inheritdoc />
6778
public Task SendDeviceOtaInstall(Guid deviceId, SemVersion version)
6879
{
@@ -74,4 +85,15 @@ public Task SendDeviceOtaInstall(Guid deviceId, SemVersion version)
7485

7586
return _subscriber.PublishAsync(RedisChannels.DeviceOtaInstall, JsonSerializer.Serialize(redisMessage));
7687
}
88+
89+
/// <inheritdoc />
90+
public Task SendDeviceReboot(Guid deviceId)
91+
{
92+
var redisMessage = new DeviceRebootMessage
93+
{
94+
Id = deviceId
95+
};
96+
97+
return _subscriber.PublishAsync(RedisChannels.DeviceReboot, JsonSerializer.Serialize(redisMessage));
98+
}
7799
}

LiveControlGateway/Controllers/HubControllerBase.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,15 @@ protected override async Task UnregisterConnection()
172172
/// <inheritdoc />
173173
public abstract ValueTask CaptivePortal(bool enable);
174174

175+
/// <inheritdoc />
176+
public abstract ValueTask<bool> EmergencyStop();
177+
175178
/// <inheritdoc />
176179
public abstract ValueTask OtaInstall(SemVersion version);
177180

181+
/// <inheritdoc />
182+
public abstract ValueTask<bool> Reboot();
183+
178184
/// <inheritdoc />
179185
public async Task DisconnectOld()
180186
{

LiveControlGateway/Controllers/HubV1Controller.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ public override ValueTask Control(List<OpenShock.Serialization.Gateway.ShockerCo
181181
})
182182
});
183183

184+
/// <inheritdoc />
185+
public override ValueTask<bool> EmergencyStop()
186+
{
187+
return ValueTask.FromResult(false);
188+
}
189+
184190
/// <inheritdoc />
185191
public override ValueTask CaptivePortal(bool enable)
186192
=> QueueMessage(new GatewayToHubMessage
@@ -191,6 +197,12 @@ public override ValueTask CaptivePortal(bool enable)
191197
})
192198
});
193199

200+
/// <inheritdoc />
201+
public override ValueTask<bool> Reboot()
202+
{
203+
return ValueTask.FromResult(false);
204+
}
205+
194206

195207
/// <inheritdoc />
196208
public override ValueTask OtaInstall(SemVersion version)

LiveControlGateway/Controllers/HubV2Controller.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,19 @@ public override ValueTask CaptivePortal(bool enable)
217217
})
218218
});
219219

220+
/// <inheritdoc />
221+
public override async ValueTask<bool> EmergencyStop()
222+
{
223+
await QueueMessage(new GatewayToHubMessage
224+
{
225+
Payload = new GatewayToHubMessagePayload(new Trigger
226+
{
227+
Type = TriggerType.EmergencyStop
228+
})
229+
});
230+
return true;
231+
}
232+
220233
/// <inheritdoc />
221234
public override ValueTask OtaInstall(SemVersion version)
222235
=> QueueMessage(new GatewayToHubMessage
@@ -227,6 +240,19 @@ public override ValueTask OtaInstall(SemVersion version)
227240
})
228241
});
229242

243+
/// <inheritdoc />
244+
public override async ValueTask<bool> Reboot()
245+
{
246+
await QueueMessage(new GatewayToHubMessage
247+
{
248+
Payload = new GatewayToHubMessagePayload(new Trigger
249+
{
250+
Type = TriggerType.Restart
251+
})
252+
});
253+
return true;
254+
}
255+
230256

231257
/// <inheritdoc />
232258
protected override async ValueTask DisposeControllerAsync()

LiveControlGateway/Controllers/IHubController.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,24 @@ public interface IHubController : IAsyncDisposable
2727
/// <returns></returns>
2828
public ValueTask CaptivePortal(bool enable);
2929

30+
/// <summary>
31+
/// Trigger EStop for device (cannot be undone remotely)
32+
/// </summary>
33+
/// <returns></returns>
34+
public ValueTask<bool> EmergencyStop();
35+
3036
/// <summary>
3137
/// Start an OTA install
3238
/// </summary>
3339
/// <param name="version"></param>
3440
/// <returns></returns>
3541
public ValueTask OtaInstall(SemVersion version);
42+
43+
/// <summary>
44+
/// Reboot the device
45+
/// </summary>
46+
/// <returns></returns>
47+
public ValueTask<bool> Reboot();
3648

3749
/// <summary>
3850
/// Disconnect the old connection in favor of the new one

0 commit comments

Comments
 (0)