@@ -21,6 +21,8 @@ import (
21
21
"io"
22
22
"path/filepath"
23
23
"strings"
24
+ "sync"
25
+ "time"
24
26
25
27
"github.com/arduino/arduino-cli/arduino"
26
28
"github.com/arduino/arduino-cli/arduino/cores"
@@ -29,8 +31,10 @@ import (
29
31
"github.com/arduino/arduino-cli/arduino/serialutils"
30
32
"github.com/arduino/arduino-cli/arduino/sketch"
31
33
"github.com/arduino/arduino-cli/commands"
34
+ "github.com/arduino/arduino-cli/commands/board"
32
35
"github.com/arduino/arduino-cli/executils"
33
36
"github.com/arduino/arduino-cli/i18n"
37
+ f "github.com/arduino/arduino-cli/internal/algorithms"
34
38
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
35
39
paths "github.com/arduino/go-paths-helper"
36
40
properties "github.com/arduino/go-properties-orderedmap"
@@ -134,15 +138,23 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er
134
138
return nil , & arduino.CantOpenSketchError {Cause : err }
135
139
}
136
140
137
- pme , release := commands .GetPackageManagerExplorer (req )
141
+ pme , pmeRelease := commands .GetPackageManagerExplorer (req )
138
142
if pme == nil {
139
143
return nil , & arduino.InvalidInstanceError {}
140
144
}
141
- defer release ()
145
+ defer pmeRelease ()
146
+
147
+ watch , watchRelease , err := board .Watch (& rpc.BoardListWatchRequest {Instance : req .GetInstance ()})
148
+ if err != nil {
149
+ logrus .WithError (err ).Error ("Error watching board ports" )
150
+ } else {
151
+ defer watchRelease ()
152
+ }
142
153
143
154
updatedPort , err := runProgramAction (
144
155
pme ,
145
156
sk ,
157
+ watch ,
146
158
req .GetImportFile (),
147
159
req .GetImportDir (),
148
160
req .GetFqbn (),
@@ -189,21 +201,43 @@ func UsingProgrammer(ctx context.Context, req *rpc.UploadUsingProgrammerRequest,
189
201
190
202
func runProgramAction (pme * packagemanager.Explorer ,
191
203
sk * sketch.Sketch ,
204
+ watch <- chan * rpc.BoardListWatchResponse ,
192
205
importFile , importDir , fqbnIn string , port * rpc.Port ,
193
206
programmerID string ,
194
207
verbose , verify , burnBootloader bool ,
195
208
outStream , errStream io.Writer ,
196
209
dryRun bool , userFields map [string ]string ) (* rpc.Port , error ) {
197
210
198
- if burnBootloader && programmerID == "" {
199
- return nil , & arduino.MissingProgrammerError {}
200
- }
201
211
if port == nil || (port .Address == "" && port .Protocol == "" ) {
202
212
// For no-port uploads use "default" protocol
203
213
port = & rpc.Port {Protocol : "default" }
204
214
}
205
215
logrus .WithField ("port" , port ).Tracef ("Upload port" )
206
216
217
+ // Default newPort
218
+ uploadCompleted := func () * rpc.Port { return nil }
219
+ if watch != nil {
220
+ // Run port detector
221
+ uploadCompletedCtx , cancel := context .WithCancel (context .Background ())
222
+ var newUploadPort * rpc.Port
223
+ var wg sync.WaitGroup
224
+ wg .Add (1 )
225
+ go func () {
226
+ newUploadPort = detectUploadPort (port , watch , uploadCompletedCtx )
227
+ wg .Done ()
228
+ }()
229
+ uploadCompleted = func () * rpc.Port {
230
+ cancel ()
231
+ wg .Wait ()
232
+ return newUploadPort
233
+ }
234
+ defer uploadCompleted () // defer in case of exit on error (ensures goroutine completion)
235
+ }
236
+
237
+ if burnBootloader && programmerID == "" {
238
+ return nil , & arduino.MissingProgrammerError {}
239
+ }
240
+
207
241
fqbn , err := cores .ParseFQBN (fqbnIn )
208
242
if err != nil {
209
243
return nil , & arduino.InvalidFQBNError {Cause : err }
@@ -485,13 +519,73 @@ func runProgramAction(pme *packagemanager.Explorer,
485
519
}
486
520
487
521
logrus .Tracef ("Upload successful" )
488
- return nil , nil // TODO: return new port
522
+ return uploadCompleted () , nil
489
523
}
490
524
491
- func detectNewUploadPort (oldPort * rpc.Port ) * rpc.Port {
492
- logrus .Tracef ("Detecting new board port" )
493
- // TODO
494
- return nil
525
+ func detectUploadPort (uploadPort * rpc.Port , watch <- chan * rpc.BoardListWatchResponse , uploadCtx context.Context ) * rpc.Port {
526
+ log := logrus .WithField ("task" , "port_detection" )
527
+ log .Tracef ("Detecting new board port after upload" )
528
+
529
+ defer func () {
530
+ // On exit, discard all events until the watcher is closed
531
+ go f .DiscardCh (watch )
532
+ }()
533
+
534
+ // Ignore all events during the upload
535
+ for {
536
+ select {
537
+ case ev , ok := <- watch :
538
+ if ! ok {
539
+ log .Error ("Upload port detection failed, watcher closed" )
540
+ return nil
541
+ }
542
+ log .WithField ("event" , ev ).Trace ("Ignored watcher event before upload" )
543
+ continue
544
+ case <- uploadCtx .Done ():
545
+ // Upload completed, move to the next phase
546
+ }
547
+ break
548
+ }
549
+
550
+ // Pick the first port that is detected after the upload
551
+ desiredHwID := uploadPort .HardwareId
552
+ timeout := time .After (5 * time .Second )
553
+ var candidate * rpc.Port
554
+ for {
555
+ select {
556
+ case ev , ok := <- watch :
557
+ if ! ok {
558
+ log .Error ("Upload port detection failed, watcher closed" )
559
+ return candidate
560
+ }
561
+ if ev .EventType == "remove" && candidate != nil {
562
+ if candidate .Equals (ev .Port .GetPort ()) {
563
+ log .WithField ("event" , ev ).Trace ("Candidate port is no more available" )
564
+ candidate = nil
565
+ continue
566
+ }
567
+ }
568
+ if ev .EventType != "add" {
569
+ log .WithField ("event" , ev ).Trace ("Ignored non-add event" )
570
+ continue
571
+ }
572
+ candidate = ev .Port .GetPort ()
573
+ log .WithField ("event" , ev ).Trace ("New upload port candidate" )
574
+
575
+ // If the current candidate port does not have the desired HW-ID do
576
+ // not return it immediately.
577
+ if desiredHwID != "" && candidate .GetHardwareId () != desiredHwID {
578
+ log .Trace ("New candidate port did not match desired HW ID, keep watching..." )
579
+ continue
580
+ }
581
+
582
+ log .Trace ("Found new upload port!" )
583
+ return candidate
584
+ case <- timeout :
585
+ log .Trace ("Timeout waiting for candidate port" )
586
+ return candidate
587
+ }
588
+ }
495
589
}
496
590
497
591
func runTool (recipeID string , props * properties.Map , outStream , errStream io.Writer , verbose bool , dryRun bool , toolEnv []string ) error {
0 commit comments