Skip to content

Commit f4e9ad7

Browse files
committed
better port names, stability updates
1 parent 14aa6d3 commit f4e9ad7

6 files changed

Lines changed: 381 additions & 53 deletions

File tree

LocalRelay/Config/SerialConfig.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
namespace OpenShock.LocalRelay.Config;
1+
namespace OpenShock.LocalRelay.Config;
22

33
public sealed class SerialConfig
44
{
55
public bool AutoConnect { get; set; } = true;
66
public string? Port { get; set; } = null;
7+
public ushort? Vid { get; set; } = null;
8+
public ushort? Pid { get; set; } = null;
79
}

LocalRelay/LocalRelay.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
<AssemblyName>OpenShock.LocalRelay</AssemblyName>
99
<RootNamespace>OpenShock.LocalRelay</RootNamespace>
1010
<Company>OpenShock</Company>
11-
<AssemblyVersion>1.0.0.0</AssemblyVersion>
12-
<ApplicationTitle>LocalRelay</ApplicationTitle>
13-
<Version>1.0.0</Version>
11+
<AssemblyVersion>1.1.0.0</AssemblyVersion>
12+
13+
<Version>1.1.0</Version>
1414
<Product>LocalRelay</Product>
1515
<TargetFramework>net10.0</TargetFramework>
1616

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace OpenShock.LocalRelay.Models.Serial;
2+
3+
public sealed record SerialPortInfo(string Port, string Name, ushort? Vid, ushort? Pid);

LocalRelay/Services/FlowManager.cs

Lines changed: 116 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using LucHeart.WebsocketLibrary;
1+
using LucHeart.WebsocketLibrary;
22
using Microsoft.Extensions.Logging;
33
using OpenShock.Desktop.ModuleBase.Api;
44
using OpenShock.Desktop.ModuleBase.Config;
@@ -18,16 +18,17 @@ public sealed class FlowManager
1818
private readonly ILogger<DeviceConnection> _deviceConnectionLogger;
1919
private readonly ILogger<SerialPortClient> _serialPortClientLogger;
2020
private readonly IOpenShockService _openShockService;
21+
private readonly SerialService _serialService;
2122

2223
public Guid HubId { get; private set; } = Guid.Empty;
2324

2425
public DeviceConnection? DeviceConnection { get; private set; } = null;
2526
public SerialPortClient? SerialPortClient { get; private set; } = null;
26-
27+
2728
public IAsyncMinimalEventObservable OnConsoleBufferUpdate => _onConsoleBufferUpdate;
2829
private readonly AsyncMinimalEvent _onConsoleBufferUpdate = new();
29-
30-
30+
31+
3132
private readonly AsyncUpdatableVariable<WebsocketConnectionState> _state =
3233
new(WebsocketConnectionState.Disconnected);
3334
public IAsyncUpdatable<WebsocketConnectionState> State => _state;
@@ -37,46 +38,76 @@ public FlowManager(
3738
ILogger<FlowManager> logger,
3839
ILogger<DeviceConnection> deviceConnectionLogger,
3940
ILogger<SerialPortClient> serialPortClientLogger,
40-
IOpenShockService openShockService)
41+
IOpenShockService openShockService,
42+
SerialService serialService)
4143
{
4244
_config = config;
4345
_logger = logger;
4446
_deviceConnectionLogger = deviceConnectionLogger;
4547
_serialPortClientLogger = serialPortClientLogger;
4648
_openShockService = openShockService;
49+
_serialService = serialService;
4750
}
4851

4952
public async Task LoadConfigAndStart()
5053
{
5154
if (_config.Config.Hub.Hub != null)
5255
await SelectedDeviceChanged(_config.Config.Hub.Hub.Value);
53-
56+
5457
var serialConfig = _config.Config.Serial;
5558

5659
if (serialConfig.AutoConnect)
5760
{
61+
await AutoConnectSerialPort();
62+
}
63+
}
64+
65+
private async Task AutoConnectSerialPort()
66+
{
67+
var serialConfig = _config.Config.Serial;
68+
69+
// Try to find the device by VID/PID first
70+
if (serialConfig.Vid != null && serialConfig.Pid != null)
71+
{
72+
var match = _serialService.FindPortByVidPid(serialConfig.Vid.Value, serialConfig.Pid.Value);
73+
if (match != null)
74+
{
75+
_logger.LogInformation("Auto-connecting to {Name} on {Port} (VID:{Vid:X4} PID:{Pid:X4})",
76+
match.Name, match.Port, match.Vid, match.Pid);
77+
await ConnectSerialPort(match.Port);
78+
return;
79+
}
80+
81+
_logger.LogWarning("Could not find serial device with VID:{Vid:X4} PID:{Pid:X4}",
82+
serialConfig.Vid.Value, serialConfig.Pid.Value);
83+
}
84+
85+
// Fallback to saved port name
86+
if (!string.IsNullOrWhiteSpace(serialConfig.Port))
87+
{
88+
_logger.LogInformation("Falling back to saved port {Port}", serialConfig.Port);
5889
await ConnectSerialPort(serialConfig.Port);
5990
}
6091
}
61-
92+
6293
public async Task SelectedDeviceChanged(Guid id)
6394
{
6495
_config.Config.Hub.Hub = id;
6596
await _config.Save();
66-
97+
6798
HubId = id;
68-
99+
69100
if (HubId == Guid.Empty)
70101
{
71102
_logger.LogError("Id is empty, stopping connection");
72103
await StopHubConnection();
73104
return;
74105
}
75-
106+
76107
_logger.LogInformation("Selected device changed to {Id}", id);
77108
var deviceDetails = await _openShockService.Api.GetHub(id);
78109

79-
110+
80111
if (deviceDetails.IsT0)
81112
{
82113
var token = deviceDetails.AsT0.Value.Token;
@@ -85,14 +116,14 @@ public async Task SelectedDeviceChanged(Guid id)
85116
_logger.LogError("Token is null or empty, make sure your api token has device.auth permission");
86117
return;
87118
}
88-
119+
89120
_logger.LogDebug("Starting device connection");
90121

91122
await StartHubConnection(id, token);
92123
return;
93124
}
94125

95-
126+
96127
deviceDetails.Switch(success => {}, found =>
97128
{
98129
_logger.LogError("Hub not found");
@@ -101,7 +132,7 @@ public async Task SelectedDeviceChanged(Guid id)
101132
{
102133
_logger.LogError("Unauthorized, make sure your logged in");
103134
});
104-
135+
105136
throw new Exception("Unhandled OneOf type");
106137
}
107138

@@ -117,7 +148,7 @@ private async Task<bool> StopHubConnection()
117148
private async Task StartHubConnection(Guid id, string authToken)
118149
{
119150
await StopHubConnection();
120-
151+
121152
DeviceConnection =
122153
new DeviceConnection(_openShockService.Auth.BackendBaseUri, authToken, _deviceConnectionLogger);
123154
DeviceConnection.OnControlMessage += OnControlMessage;
@@ -144,22 +175,87 @@ private async Task OnControlMessage(ShockerCommandList shockerCommandList)
144175
}));
145176
await Task.WhenAll(transmitTasks);
146177
}
147-
178+
148179
private IAsyncDisposable? _onConsoleBufferUpdateDisposable = null;
180+
private IAsyncDisposable? _onCloseDisposable = null;
181+
182+
public async Task ConnectSerialPort(SerialPortInfo portInfo)
183+
{
184+
// Save VID/PID and port to config
185+
var serialConfig = _config.Config.Serial;
186+
serialConfig.Port = portInfo.Port;
187+
serialConfig.Vid = portInfo.Vid;
188+
serialConfig.Pid = portInfo.Pid;
189+
await _config.Save();
190+
191+
await ConnectSerialPort(portInfo.Port);
192+
}
149193

150194
public async Task ConnectSerialPort(string? portName)
151195
{
152196
if (SerialPortClient != null)
153197
{
154-
if(_onConsoleBufferUpdateDisposable != null) await _onConsoleBufferUpdateDisposable.DisposeAsync();
198+
if (_onCloseDisposable != null) await _onCloseDisposable.DisposeAsync();
199+
if (_onConsoleBufferUpdateDisposable != null) await _onConsoleBufferUpdateDisposable.DisposeAsync();
155200
await SerialPortClient.DisposeAsync();
156201
SerialPortClient = null;
157202
}
158-
159-
if(string.IsNullOrWhiteSpace(portName)) return;
160-
203+
204+
if (string.IsNullOrWhiteSpace(portName)) return;
205+
161206
SerialPortClient = new SerialPortClient(_serialPortClientLogger, portName);
162207
_onConsoleBufferUpdateDisposable = await SerialPortClient.OnConsoleBufferUpdate.SubscribeAsync(_onConsoleBufferUpdate.InvokeAsyncParallel);
208+
_onCloseDisposable = await SerialPortClient.OnClose.SubscribeAsync(OnSerialPortClosed);
163209
await SerialPortClient.Open();
164210
}
211+
212+
private async Task OnSerialPortClosed()
213+
{
214+
_logger.LogWarning("Serial port closed, attempting to reconnect...");
215+
216+
var serialConfig = _config.Config.Serial;
217+
if (!serialConfig.AutoConnect) return;
218+
219+
// Retry reconnection with backoff
220+
for (var attempt = 0; attempt < 10; attempt++)
221+
{
222+
await Task.Delay(TimeSpan.FromSeconds(Math.Min(2 * (attempt + 1), 10)));
223+
224+
// Try VID/PID match first
225+
if (serialConfig.Vid != null && serialConfig.Pid != null)
226+
{
227+
var match = _serialService.FindPortByVidPid(serialConfig.Vid.Value, serialConfig.Pid.Value);
228+
if (match != null)
229+
{
230+
_logger.LogInformation("Reconnecting to {Name} on {Port}", match.Name, match.Port);
231+
try
232+
{
233+
await ConnectSerialPort(match.Port);
234+
return;
235+
}
236+
catch (Exception e)
237+
{
238+
_logger.LogError(e, "Failed to reconnect on attempt {Attempt}", attempt + 1);
239+
}
240+
241+
continue;
242+
}
243+
}
244+
245+
_logger.LogDebug("Device not found, retrying... (attempt {Attempt}/10)", attempt + 1);
246+
}
247+
248+
_logger.LogError("Failed to reconnect to serial device after 10 attempts");
249+
}
250+
251+
public async Task DisconnectSerialPort()
252+
{
253+
var serialConfig = _config.Config.Serial;
254+
serialConfig.Port = null;
255+
serialConfig.Vid = null;
256+
serialConfig.Pid = null;
257+
await _config.Save();
258+
259+
await ConnectSerialPort((string?)null);
260+
}
165261
}

0 commit comments

Comments
 (0)