1- using LucHeart . WebsocketLibrary ;
1+ using LucHeart . WebsocketLibrary ;
22using Microsoft . Extensions . Logging ;
33using OpenShock . Desktop . ModuleBase . Api ;
44using 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