Skip to content

Commit 1ba3539

Browse files
committed
Refactor live control client a good bit
1 parent 070a834 commit 1ba3539

File tree

6 files changed

+90
-34
lines changed

6 files changed

+90
-34
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using Microsoft.Extensions.Logging;
2+
using OpenShock.SDK.CSharp;
3+
using OpenShock.SDK.CSharp.Live;
4+
using OpenShock.SDK.CSharp.Models;
5+
6+
namespace SDK.CSharp.Example.Http;
7+
8+
public sealed class LiveControlDemo : IExample
9+
{
10+
private readonly ExampleConfig _config;
11+
private readonly ILogger<OpenShockLiveControlClient> _liveControlLogger;
12+
13+
public LiveControlDemo(ExampleConfig config, ILogger<OpenShockLiveControlClient> liveControlLogger)
14+
{
15+
_config = config;
16+
_liveControlLogger = liveControlLogger;
17+
}
18+
19+
20+
public async Task Start()
21+
{
22+
var apiClient = new OpenShockApiClient(new ApiClientOptions
23+
{
24+
Token = _config.ApiToken
25+
});
26+
27+
if(!_config.Hub.HasValue)
28+
{
29+
Console.WriteLine("No hub configured, skipping live control demo.");
30+
return;
31+
}
32+
33+
var gateway = await apiClient.GetDeviceGateway(_config.Hub.Value);
34+
35+
gateway.Switch(success => {},
36+
found =>
37+
{
38+
Console.WriteLine($"Hub with ID {_config.Hub.Value} not found.");
39+
},
40+
offline =>
41+
{
42+
Console.WriteLine($"Hub with ID {_config.Hub.Value} is not online.");
43+
},
44+
unauthenticated =>
45+
{
46+
Console.WriteLine($"not authenticated");
47+
});
48+
49+
if(!gateway.IsT0) throw new Exception("Failed to get gateway for hub " + _config.Hub.Value);
50+
51+
var liveControlClient = new OpenShockLiveControlClient(gateway.AsT0.Value.Gateway, _config.Hub.Value, _config.ApiToken, _liveControlLogger);
52+
53+
await liveControlClient.InitializeAsync();
54+
55+
while (true)
56+
{
57+
Console.Write("\rHold down enter to continue to send vibrates% ");
58+
Console.ReadLine();
59+
foreach (var configShocker in _config.Shockers)
60+
{
61+
liveControlClient.IntakeFrame(configShocker, ControlType.Vibrate, 50);
62+
}
63+
}
64+
}
65+
}

SDK.CSharp.Example/SDK.CSharp.Example.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
<ItemGroup>
1717
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
18-
<PackageReference Include="Serilog" Version="4.2.0" />
18+
<PackageReference Include="Serilog" Version="4.3.0" />
1919
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
2020
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
2121
</ItemGroup>

SDK.CSharp.Live/OpenShockLiveControlClient.cs

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,12 @@ private ValueTask QueueMessage(BaseRequest<LiveRequestType> data) =>
112112

113113
public IAsyncUpdatable<WebsocketConnectionState> State => _state;
114114

115-
private async Task MessageLoop()
115+
private async Task MessageLoop(CancellationToken cancellationToken)
116116
{
117117
try
118118
{
119-
await foreach (var msg in _channel.Reader.ReadAllAsync(_linked.Token))
120-
await JsonWebSocketUtils.SendFullMessage(msg, _clientWebSocket!, _linked.Token, JsonSerializerOptions);
119+
await foreach (var msg in _channel.Reader.ReadAllAsync(cancellationToken))
120+
await JsonWebSocketUtils.SendFullMessage(msg, _clientWebSocket!, cancellationToken, JsonSerializerOptions);
121121
}
122122
catch (OperationCanceledException)
123123
{
@@ -128,11 +128,11 @@ private async Task MessageLoop()
128128
}
129129
}
130130

131-
public struct Shutdown;
131+
public readonly struct Shutdown;
132132

133-
public struct Reconnecting;
133+
public readonly struct Reconnecting;
134134

135-
private async Task<OneOf<Success, NotFound, Shutdown, Reconnecting>> ConnectAsync()
135+
private async Task<OneOf<Success, Shutdown, Reconnecting>> ConnectAsync()
136136
{
137137
if (_dispose.IsCancellationRequested)
138138
{
@@ -141,13 +141,21 @@ private async Task<OneOf<Success, NotFound, Shutdown, Reconnecting>> ConnectAsyn
141141
}
142142

143143
_state.Value = WebsocketConnectionState.Connecting;
144+
144145
#if NETSTANDARD2_1
145146
_currentConnectionClose?.Cancel();
146147
#else
147148
if (_currentConnectionClose != null) await _currentConnectionClose.CancelAsync();
148149
#endif
150+
149151
_currentConnectionClose = new CancellationTokenSource();
152+
153+
if (_linked != _dispose)
154+
{
155+
_linked.Dispose();
156+
}
150157
_linked = CancellationTokenSource.CreateLinkedTokenSource(_dispose.Token, _currentConnectionClose.Token);
158+
var cancellationToken = _linked.Token;
151159

152160
_clientWebSocket?.Abort();
153161
_clientWebSocket?.Dispose();
@@ -160,32 +168,18 @@ private async Task<OneOf<Success, NotFound, Shutdown, Reconnecting>> ConnectAsyn
160168
_logger.LogInformation("Connecting to websocket....");
161169
try
162170
{
163-
await _clientWebSocket.ConnectAsync(new Uri($"wss://{Gateway}/1/ws/live/{DeviceId}"), _linked.Token);
171+
await _clientWebSocket.ConnectAsync(new Uri($"wss://{Gateway}/1/ws/live/{DeviceId}"), cancellationToken);
164172

165173
_logger.LogInformation("Connected to websocket");
166174
_state.Value = WebsocketConnectionState.Connected;
167175

168176
#pragma warning disable CS4014
169-
Run(ReceiveLoop, _linked.Token);
170-
Run(MessageLoop, _linked.Token);
177+
Run(ReceiveLoop(cancellationToken), cancellationToken);
178+
Run(MessageLoop(cancellationToken), cancellationToken);
171179
#pragma warning restore CS4014
172180

173181
return new Success();
174182
}
175-
catch (WebSocketException e)
176-
{
177-
if (e.Message.Contains("404"))
178-
{
179-
_logger.LogError("Device not found, shutting down");
180-
_dispose.Dispose();
181-
#pragma warning disable CS4014
182-
Run(async () => await _onDispose.InvokeAsyncParallel());
183-
#pragma warning restore CS4014
184-
return new NotFound();
185-
}
186-
187-
_logger.LogError(e, "Error while connecting, retrying in 3 seconds");
188-
}
189183
catch (Exception e)
190184
{
191185
_logger.LogError(e, "Error while connecting, retrying in 3 seconds");
@@ -229,9 +223,9 @@ private string GetUserAgent()
229223
}
230224

231225

232-
private async Task ReceiveLoop()
226+
private async Task ReceiveLoop(CancellationToken cancellationToken)
233227
{
234-
while (!_linked.Token.IsCancellationRequested)
228+
while (!cancellationToken.IsCancellationRequested)
235229
{
236230
try
237231
{
@@ -244,7 +238,7 @@ private async Task ReceiveLoop()
244238
var message =
245239
await JsonWebSocketUtils
246240
.ReceiveFullMessageAsyncNonAlloc<LiveControlModels.BaseResponse<LiveResponseType>>(
247-
_clientWebSocket, _linked.Token, JsonSerializerOptions);
241+
_clientWebSocket, cancellationToken, JsonSerializerOptions);
248242

249243
if (message.IsT2)
250244
{
@@ -257,7 +251,7 @@ await JsonWebSocketUtils
257251
try
258252
{
259253
await _clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Normal close",
260-
_linked.Token);
254+
cancellationToken);
261255
}
262256
catch (OperationCanceledException e)
263257
{

SDK.CSharp.Tests/SDK.CSharp.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<PrivateAssets>all</PrivateAssets>
1313
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1414
</PackageReference>
15-
<PackageReference Include="TUnit" Version="0.19.148" />
15+
<PackageReference Include="TUnit" Version="0.23.0" />
1616
</ItemGroup>
1717

1818
<ItemGroup>

SDK.CSharp/IOpenShockApiClient.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ public Task<OneOf<Success<ImmutableArray<ResponseDeviceWithShockers>>, Unauthent
2222
/// <param name="deviceId"></param>
2323
/// <param name="cancellationToken"></param>
2424
/// <returns></returns>
25-
public Task<OneOf<Success<LcgResponse>, NotFound, DeviceOffline, DeviceNotConnectedToGateway, UnauthenticatedError>>
26-
GetDeviceGateway(Guid deviceId, CancellationToken cancellationToken = default);
25+
public Task<OneOf<Success<LcgResponse>, NotFound, DeviceOffline, UnauthenticatedError>> GetDeviceGateway(Guid deviceId, CancellationToken cancellationToken = default);
2726

2827
/// <summary>
2928
/// Gets the root for the API, this has some useful information and can be used to check if the API is reachable
@@ -66,5 +65,4 @@ public Task<OneOf<Success<bool>, NotFound>> PauseShocker(Guid shockerId, bool pa
6665
}
6766

6867
public struct DeviceOffline;
69-
public struct DeviceNotConnectedToGateway;
7068

SDK.CSharp/OpenShockApiClient.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ await ownShockersResponse.Content
7373

7474
/// <inheritdoc />
7575
public async
76-
Task<OneOf<Success<LcgResponse>, NotFound, DeviceOffline, DeviceNotConnectedToGateway, UnauthenticatedError>>
76+
Task<OneOf<Success<LcgResponse>, NotFound, DeviceOffline, UnauthenticatedError>>
7777
GetDeviceGateway(Guid deviceId, CancellationToken cancellationToken = default)
7878
{
7979
using var gatewayResponse =
@@ -98,7 +98,6 @@ await gatewayResponse.Content.ReadAsJsonAsync<ProblemDetails>(cancellationToken,
9898
{
9999
"Device.NotFound" => new NotFound(),
100100
"Device.NotOnline" => new DeviceOffline(),
101-
"Device.NotConnectedToGateway" => new DeviceNotConnectedToGateway(),
102101
_ => throw new OpenShockApiError($"Unknown problem type [{problem.Type}]", gatewayResponse.StatusCode)
103102
};
104103
}

0 commit comments

Comments
 (0)