@@ -19,12 +19,14 @@ package main
19
19
20
20
import (
21
21
"encoding/json"
22
+ "slices"
22
23
"strconv"
23
24
"strings"
24
25
"sync"
26
+ "time"
25
27
28
+ discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2"
26
29
"github.com/sirupsen/logrus"
27
- "go.bug.st/serial/enumerator"
28
30
)
29
31
30
32
type writeRequest struct {
@@ -51,9 +53,8 @@ type serialhub struct {
51
53
52
54
// SerialPortList is the serial port list
53
55
type SerialPortList struct {
54
- Ports []SpPortItem
55
- portsLock sync.Mutex
56
- enumerationLock sync.Mutex
56
+ Ports []* SpPortItem
57
+ portsLock sync.Mutex
57
58
}
58
59
59
60
// SpPortItem is the serial port item
@@ -146,60 +147,118 @@ func (sp *SerialPortList) List() {
146
147
}
147
148
}
148
149
149
- func (sp * SerialPortList ) Update () {
150
- if ! sp .enumerationLock .TryLock () {
151
- // already enumerating...
152
- return
150
+ // Run is the main loop for port discovery and management
151
+ func (sp * SerialPortList ) Run () {
152
+ for retries := 0 ; retries < 10 ; retries ++ {
153
+ sp .runSerialDiscovery ()
154
+
155
+ logrus .Errorf ("Serial discovery stopped working, restarting it in 10 seconds..." )
156
+ time .Sleep (10 * time .Second )
153
157
}
154
- defer sp .enumerationLock .Unlock ()
158
+ logrus .Errorf ("Failed restarting serial discovery. Giving up..." )
159
+ }
155
160
156
- livePorts , _ := enumerator .GetDetailedPortsList ()
157
- // TODO: report error?
161
+ func (sp * SerialPortList ) runSerialDiscovery () {
162
+ // First ensure that all the discoveries are available
163
+ if err := Tools .Download ("builtin" , "serial-discovery" , "latest" , "keep" ); err != nil {
164
+ logrus .Errorf ("Error downloading serial-discovery: %s" , err )
165
+ panic (err )
166
+ }
167
+ sd , err := Tools .GetLocation ("serial-discovery" )
168
+ if err != nil {
169
+ logrus .Errorf ("Error downloading serial-discovery: %s" , err )
170
+ panic (err )
171
+ }
172
+ d := discovery .NewClient ("serial" , sd + "/serial-discovery" )
173
+ dLogger := logrus .WithField ("discovery" , "serial" )
174
+ if * verbose {
175
+ d .SetLogger (dLogger )
176
+ }
177
+ d .SetUserAgent ("arduino-create-agent/" + version )
178
+ if err := d .Run (); err != nil {
179
+ logrus .Errorf ("Error running serial-discovery: %s" , err )
180
+ panic (err )
181
+ }
182
+ defer d .Quit ()
158
183
159
- var ports []SpPortItem
160
- for _ , livePort := range livePorts {
161
- if ! livePort .IsUSB {
162
- continue
163
- }
164
- vid , pid := "0x" + livePort .VID , "0x" + livePort .PID
165
- if vid == "0x0000" || pid == "0x0000" {
166
- continue
167
- }
168
- if portsFilter != nil && ! portsFilter .MatchString (livePort .Name ) {
169
- logrus .Debugf ("ignoring port not matching filter. port: %v\n " , livePort )
170
- continue
171
- }
184
+ events , err := d .StartSync (10 )
185
+ if err != nil {
186
+ logrus .Errorf ("Error downloading serial-discovery: %s" , err )
187
+ panic (err )
188
+ }
172
189
173
- port := SpPortItem {
174
- Name : livePort .Name ,
175
- SerialNumber : livePort .SerialNumber ,
176
- VendorID : vid ,
177
- ProductID : pid ,
178
- Ver : version ,
179
- IsOpen : false ,
180
- IsPrimary : false ,
181
- Baud : 0 ,
182
- BufferAlgorithm : "" ,
190
+ logrus .Infof ("Serial discovery started, watching for events" )
191
+ for ev := range events {
192
+ logrus .WithField ("event" , ev ).Debugf ("Serial discovery event" )
193
+ switch ev .Type {
194
+ case "add" :
195
+ sp .add (ev .Port )
196
+ case "remove" :
197
+ sp .remove (ev .Port )
183
198
}
199
+ }
184
200
185
- // we have a full clean list of ports now. iterate thru them
186
- // to append the open/close state, baud rates, etc to make
187
- // a super clean nice list to send back to browser
201
+ sp . reset ()
202
+ logrus . Errorf ( "Serial discovery stopped." )
203
+ }
188
204
189
- // figure out if port is open
190
- if myport , isFound := sh .FindPortByName (port .Name ); isFound {
191
- // and update data with the open port parameters
192
- port .IsOpen = true
193
- port .Baud = myport .portConf .Baud
194
- port .BufferAlgorithm = myport .BufferType
195
- }
205
+ func (sp * SerialPortList ) reset () {
206
+ sp .portsLock .Lock ()
207
+ defer sp .portsLock .Unlock ()
208
+ sp .Ports = []* SpPortItem {}
209
+ }
210
+
211
+ func (sp * SerialPortList ) add (addedPort * discovery.Port ) {
212
+ if addedPort .Protocol != "serial" {
213
+ return
214
+ }
215
+ props := addedPort .Properties
216
+ if ! props .ContainsKey ("vid" ) {
217
+ return
218
+ }
219
+ vid , pid := props .Get ("vid" ), props .Get ("pid" )
220
+ if vid == "0x0000" || pid == "0x0000" {
221
+ return
222
+ }
223
+ if portsFilter != nil && ! portsFilter .MatchString (addedPort .Address ) {
224
+ logrus .Debugf ("ignoring port not matching filter. port: %v\n " , addedPort .Address )
225
+ return
226
+ }
196
227
197
- ports = append (ports , port )
228
+ sp .portsLock .Lock ()
229
+ defer sp .portsLock .Unlock ()
230
+
231
+ // If the port is already in the list, just update the metadata...
232
+ for _ , oldPort := range sp .Ports {
233
+ if oldPort .Name == addedPort .Address {
234
+ oldPort .SerialNumber = props .Get ("serialNumber" )
235
+ oldPort .VendorID = vid
236
+ oldPort .ProductID = pid
237
+ return
238
+ }
198
239
}
240
+ // ...otherwise, add it to the list
241
+ sp .Ports = append (sp .Ports , & SpPortItem {
242
+ Name : addedPort .Address ,
243
+ SerialNumber : props .Get ("serialNumber" ),
244
+ VendorID : vid ,
245
+ ProductID : pid ,
246
+ Ver : version ,
247
+ IsOpen : false ,
248
+ IsPrimary : false ,
249
+ Baud : 0 ,
250
+ BufferAlgorithm : "" ,
251
+ })
252
+ }
253
+
254
+ func (sp * SerialPortList ) remove (removedPort * discovery.Port ) {
255
+ sp .portsLock .Lock ()
256
+ defer sp .portsLock .Unlock ()
199
257
200
- serialPorts .portsLock .Lock ()
201
- serialPorts .Ports = ports
202
- serialPorts .portsLock .Unlock ()
258
+ // Remove the port from the list
259
+ sp .Ports = slices .DeleteFunc (sp .Ports , func (oldPort * SpPortItem ) bool {
260
+ return oldPort .Name == removedPort .Address
261
+ })
203
262
}
204
263
205
264
func spErr (err string ) {
0 commit comments