Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/arduino-flasher-cli/flash/flash.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func runFlashCommand(ctx context.Context, args []string, forceYes bool, preserve
}
}

err = updater.Flash(ctx, imagePath, args[0], forceYes, preserveUser, tempDir)
err = updater.Flash(ctx, imagePath, args[0], forceYes, preserveUser, tempDir, nil)
if err != nil {
feedback.Fatal(i18n.Tr("error flashing the board: %v", err), feedback.ErrBadArgument)
}
Expand Down
34 changes: 34 additions & 0 deletions internal/helper/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package helper

import (
"bytes"
)

// CallbackWriter is a custom writer that processes each line calling the callback.
type CallbackWriter struct {
callback func(line string)
buffer []byte
}

// NewCallbackWriter creates a new CallbackWriter.
func NewCallbackWriter(process func(line string)) *CallbackWriter {
return &CallbackWriter{
callback: process,
buffer: make([]byte, 0, 1024),
}
}

// Write implements the io.Writer interface.
func (p *CallbackWriter) Write(data []byte) (int, error) {
p.buffer = append(p.buffer, data...)
for {
idx := bytes.IndexByte(p.buffer, '\n')
if idx == -1 {
break
}
line := p.buffer[:idx] // Do not include \n
p.buffer = p.buffer[idx+1:]
p.callback(string(line))
}
return len(data), nil
}
56 changes: 51 additions & 5 deletions internal/updater/flasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/arduino/arduino-flasher-cli/cmd/feedback"
"github.com/arduino/arduino-flasher-cli/cmd/i18n"
"github.com/arduino/arduino-flasher-cli/internal/helper"
"github.com/arduino/arduino-flasher-cli/internal/updater/artifacts"
)

Expand All @@ -36,7 +37,7 @@ const DownloadDiskSpace = uint64(12)
const ExtractDiskSpace = uint64(10)
const yesPrompt = "yes"

func Flash(ctx context.Context, imagePath *paths.Path, version string, forceYes bool, preserveUser bool, tempDir string) error {
func Flash(ctx context.Context, imagePath *paths.Path, version string, forceYes bool, preserveUser bool, tempDir string, callback FlahsCallback) error {
if !imagePath.Exist() {
temp, err := SetTempDir("download-", tempDir)
if err != nil {
Expand Down Expand Up @@ -90,10 +91,26 @@ func Flash(ctx context.Context, imagePath *paths.Path, version string, forceYes
imagePath = tempContent[0]
}

return FlashBoard(ctx, imagePath.String(), version, preserveUser)
return FlashBoard(ctx, imagePath.String(), version, preserveUser, nil)
}

func FlashBoard(ctx context.Context, downloadedImagePath string, version string, preserveUser bool) error {
type EventType int

const (
EventLog EventType = iota
EventProgress
)

type FlashEvent struct {
Type EventType
Log string
Progress int
Total int
}

type FlahsCallback func(FlashEvent)

func FlashBoard(ctx context.Context, downloadedImagePath string, version string, preserveUser bool, callback FlahsCallback) error {
var flashDir *paths.Path
for _, entry := range []string{"flash", "flash_UnoQ"} {
if p := paths.New(downloadedImagePath, entry); p.Exist() {
Expand Down Expand Up @@ -161,15 +178,44 @@ func FlashBoard(ctx context.Context, downloadedImagePath string, version string,

}

totalPartitions, err := getTotalPartition(flashDir.Join(rawProgram))
if err != nil {
return err
}

feedback.Print(i18n.Tr("Flashing with qdl"))
cmd, err := paths.NewProcess(nil, qdlPath.String(), "--allow-missing", "--storage", "emmc", "prog_firehose_ddr.elf", rawProgram, "patch0.xml")
if err != nil {
return err
}
// Setting the directory is needed because rawprogram0.xml contains relative file paths
cmd.SetDir(flashDir.String())
cmd.RedirectStderrTo(stdout)
cmd.RedirectStdoutTo(stdout)

w := stdout
if callback != nil {
progress := 0
w = helper.NewCallbackWriter(func(line string) {
parsedLine := parseQdlLogLine(line)

switch parsedLine.Op {
case Flashed:
progress++
callback(FlashEvent{
Type: EventProgress,
Log: line,
Progress: progress,
Total: totalPartitions,
})
default:
callback(FlashEvent{
Type: EventLog,
Log: line,
})
}
})
}
cmd.RedirectStderrTo(w)
cmd.RedirectStdoutTo(w)
if err := cmd.RunWithinContext(ctx); err != nil {
return err
}
Expand Down
61 changes: 61 additions & 0 deletions internal/updater/parseqdl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package updater

import (
"bufio"
"strings"

"github.com/arduino/go-paths-helper"
)

type Op int

const (
Waiting Op = iota
Flashed
Unknown
)

type QDLLogLine struct {
Op Op
Log string
}

func parseQdlLogLine(line string) QDLLogLine {
lower := strings.ToLower(line)

switch {
case strings.HasPrefix(lower, "waiting for"):
return QDLLogLine{
Op: Waiting,
Log: line,
}
case strings.HasPrefix(lower, "flashed"):
return QDLLogLine{
Op: Flashed,
Log: line,
}
default:
return QDLLogLine{
Op: Unknown,
Log: line,
}
}
}

func getTotalPartition(path *paths.Path) (int, error) {
f, err := path.Open()
if err != nil {
return 0, err
}

r := bufio.NewScanner(f)
var total int
for r.Scan() {
c := strings.Count(r.Text(), "<program")
total += c
}
if err := r.Err(); err != nil {
return 0, err
}
return total, nil
}
72 changes: 72 additions & 0 deletions internal/updater/parseqdl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package updater

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestParseQdlLogLine(t *testing.T) {
tests := []struct {
line string
want QDLLogLine
wantErr bool
}{
{
line: "Waiting for EDL device...",
want: QDLLogLine{
Op: Waiting,
Log: "Waiting for EDL device...",
},
},
{
line: "waiting for programmer...",
want: QDLLogLine{
Op: Waiting,
Log: "waiting for programmer...",
},
},
{
line: `flashed "xbl_a" successfully`,
want: QDLLogLine{
Op: Flashed,
Log: `flashed "xbl_a" successfully`,
},
},
{
line: `flashed "rootfs" successfully at 65058kB/s`,
want: QDLLogLine{
Op: Flashed,
Log: `flashed "rootfs" successfully at 65058kB/s`,
},
},
{

line: "13 patches applied",
want: QDLLogLine{
Op: Unknown,
Log: "13 patches applied",
},
},
{
line: "partition 0 is now bootable",
want: QDLLogLine{
Op: Unknown,
Log: "partition 0 is now bootable",
},
},
}

for _, tt := range tests {
t.Run(tt.line, func(t *testing.T) {
result, err := parseQdlLogLine(tt.line)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tt.want, result)
}

})
}
}
45 changes: 28 additions & 17 deletions rpc/cc/arduino/flasher/v1/common.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading