Skip to content

Commit 3fdc669

Browse files
committed
wip
1 parent 6278ad8 commit 3fdc669

File tree

6 files changed

+390
-226
lines changed

6 files changed

+390
-226
lines changed

cmd/arduino-flasher-cli/flash/flash.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func runFlashCommand(ctx context.Context, args []string, forceYes bool, preserve
113113
}
114114
}
115115

116-
err = updater.Flash(ctx, imagePath, args[0], forceYes, preserveUser, tempDir)
116+
err = updater.Flash(ctx, imagePath, args[0], forceYes, preserveUser, tempDir, nil)
117117
if err != nil {
118118
feedback.Fatal(i18n.Tr("error flashing the board: %v", err), feedback.ErrBadArgument)
119119
}

internal/helper/helper.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package helper
2+
3+
import (
4+
"bytes"
5+
)
6+
7+
// CallbackWriter is a custom writer that processes each line calling the callback.
8+
type CallbackWriter struct {
9+
callback func(line string)
10+
buffer []byte
11+
}
12+
13+
// NewCallbackWriter creates a new CallbackWriter.
14+
func NewCallbackWriter(process func(line string)) *CallbackWriter {
15+
return &CallbackWriter{
16+
callback: process,
17+
buffer: make([]byte, 0, 1024),
18+
}
19+
}
20+
21+
// Write implements the io.Writer interface.
22+
func (p *CallbackWriter) Write(data []byte) (int, error) {
23+
p.buffer = append(p.buffer, data...)
24+
for {
25+
idx := bytes.IndexByte(p.buffer, '\n')
26+
if idx == -1 {
27+
break
28+
}
29+
line := p.buffer[:idx] // Do not include \n
30+
p.buffer = p.buffer[idx+1:]
31+
p.callback(string(line))
32+
}
33+
return len(data), nil
34+
}

internal/updater/flasher.go

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
package updater
1717

1818
import (
19+
"bufio"
1920
"context"
2021
"encoding/hex"
2122
"fmt"
23+
"log/slog"
24+
"regexp"
2225
"runtime"
2326
"strconv"
2427
"strings"
@@ -28,6 +31,7 @@ import (
2831

2932
"github.com/arduino/arduino-flasher-cli/cmd/feedback"
3033
"github.com/arduino/arduino-flasher-cli/cmd/i18n"
34+
"github.com/arduino/arduino-flasher-cli/internal/helper"
3135
"github.com/arduino/arduino-flasher-cli/internal/updater/artifacts"
3236
)
3337

@@ -36,7 +40,7 @@ const DownloadDiskSpace = uint64(12)
3640
const ExtractDiskSpace = uint64(10)
3741
const yesPrompt = "yes"
3842

39-
func Flash(ctx context.Context, imagePath *paths.Path, version string, forceYes bool, preserveUser bool, tempDir string) error {
43+
func Flash(ctx context.Context, imagePath *paths.Path, version string, forceYes bool, preserveUser bool, tempDir string, callback FlahsCallback) error {
4044
if !imagePath.Exist() {
4145
temp, err := SetTempDir("download-", tempDir)
4246
if err != nil {
@@ -90,10 +94,26 @@ func Flash(ctx context.Context, imagePath *paths.Path, version string, forceYes
9094
imagePath = tempContent[0]
9195
}
9296

93-
return FlashBoard(ctx, imagePath.String(), version, preserveUser)
97+
return FlashBoard(ctx, imagePath.String(), version, preserveUser, nil)
9498
}
9599

96-
func FlashBoard(ctx context.Context, downloadedImagePath string, version string, preserveUser bool) error {
100+
type TypeEvent int
101+
102+
const (
103+
EventWaiting TypeEvent = 3
104+
EventFlashed TypeEvent = 4
105+
)
106+
107+
type FlashEvent struct {
108+
Type TypeEvent
109+
Progress int
110+
MaxProgress int
111+
Log string
112+
}
113+
114+
type FlahsCallback func(FlashEvent)
115+
116+
func FlashBoard(ctx context.Context, downloadedImagePath string, version string, preserveUser bool, callback FlahsCallback) error {
97117
var flashDir *paths.Path
98118
for _, entry := range []string{"flash", "flash_UnoQ"} {
99119
if p := paths.New(downloadedImagePath, entry); p.Exist() {
@@ -161,15 +181,48 @@ func FlashBoard(ctx context.Context, downloadedImagePath string, version string,
161181

162182
}
163183

184+
totalPartitions, err := getTotalPartition(flashDir.Join(rawProgram))
185+
if err != nil {
186+
return err
187+
}
188+
164189
feedback.Print(i18n.Tr("Flashing with qdl"))
165190
cmd, err := paths.NewProcess(nil, qdlPath.String(), "--allow-missing", "--storage", "emmc", "prog_firehose_ddr.elf", rawProgram, "patch0.xml")
166191
if err != nil {
167192
return err
168193
}
169194
// Setting the directory is needed because rawprogram0.xml contains relative file paths
170195
cmd.SetDir(flashDir.String())
171-
cmd.RedirectStderrTo(stdout)
172-
cmd.RedirectStdoutTo(stdout)
196+
197+
w := stdout
198+
if callback != nil {
199+
progress := 0
200+
w = helper.NewCallbackWriter(func(line string) {
201+
parsedLine, err := parseQdlLogLine(line)
202+
if err != nil {
203+
slog.Warn("could not parse qdl log line", "error", err, "line", line)
204+
return
205+
}
206+
207+
switch parsedLine.Op {
208+
case Waiting:
209+
callback(FlashEvent{
210+
Type: EventWaiting,
211+
Log: line,
212+
})
213+
case Flasherd:
214+
progress++
215+
callback(FlashEvent{
216+
Type: EventFlashed,
217+
Log: line,
218+
Progress: progress,
219+
MaxProgress: totalPartitions,
220+
})
221+
}
222+
})
223+
}
224+
cmd.RedirectStderrTo(w)
225+
cmd.RedirectStdoutTo(w)
173226
if err := cmd.RunWithinContext(ctx); err != nil {
174227
return err
175228
}
@@ -226,3 +279,59 @@ func checkBoardGPTTable(ctx context.Context, qdlPath, flashDir *paths.Path) erro
226279

227280
return nil
228281
}
282+
283+
type Op int
284+
285+
const (
286+
Waiting Op = 1
287+
Flasherd Op = 2
288+
)
289+
290+
var qdlProgressRegex = regexp.MustCompile(`(\w)\s+(:?(".*?")\s+(\w+)(?:\s+at\s+(\d+kB/s))?)?`)
291+
292+
type QDLLogLine struct {
293+
Op Op
294+
Log string
295+
}
296+
297+
func parseQdlLogLine(line string) (QDLLogLine, error) {
298+
matches := qdlProgressRegex.FindStringSubmatch(line)
299+
if matches == nil {
300+
return QDLLogLine{}, fmt.Errorf("line %q does not match progress format", line)
301+
}
302+
slog.Debug("parsed qdl log line", "full", matches[0], "matches", matches)
303+
304+
if strings.HasPrefix(matches[1], "Waiting for") || strings.HasPrefix(matches[1], "waiting for") {
305+
return QDLLogLine{
306+
Op: Waiting,
307+
Log: line,
308+
}, nil
309+
}
310+
311+
if strings.HasPrefix(matches[1], "Flashed") {
312+
return QDLLogLine{
313+
Op: Flasherd,
314+
Log: line,
315+
}, nil
316+
}
317+
318+
return QDLLogLine{}, fmt.Errorf("line %q does not match known operations", line)
319+
}
320+
321+
func getTotalPartition(path *paths.Path) (int, error) {
322+
f, err := path.Open()
323+
if err != nil {
324+
return 0, err
325+
}
326+
327+
r := bufio.NewScanner(f)
328+
var total int
329+
for r.Scan() {
330+
c := strings.Count(r.Text(), "<program")
331+
total += c
332+
}
333+
if err := r.Err(); err != nil {
334+
return 0, err
335+
}
336+
return total, nil
337+
}

rpc/cc/arduino/flasher/v1/common.pb.go

Lines changed: 28 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)