From 2d80f4ad899bebed09b818553ff4f4ad618874c2 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Mon, 26 Aug 2024 17:30:01 +0200 Subject: [PATCH 1/2] Align progress bar status to new OTA service capabilities --- command/ota/status.go | 4 +- internal/ota-api/dto.go | 76 ++++++++++++++++++++++++------------ internal/ota-api/dto_test.go | 4 +- 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/command/ota/status.go b/command/ota/status.go index 4bb9d067..f6077d2a 100644 --- a/command/ota/status.go +++ b/command/ota/status.go @@ -44,9 +44,11 @@ func PrintOtaStatus(otaid, otaids, device string, cred *config.Credentials, limi res, err := otapi.GetOtaStatusByOtaID(otaid, limit, order) if err == nil && res != nil { feedback.PrintResult(otaapi.OtaStatusDetail{ - FirmwareSize: res.FirmwareSize, + FirmwareSize: res.Ota.FirmwareSize, Ota: res.Ota, Details: res.States, + MaxRetries: res.Ota.MaxRetries, + RetryAttempt: res.Ota.RetryAttempt, }) } else if err != nil { return err diff --git a/internal/ota-api/dto.go b/internal/ota-api/dto.go index 04184ed8..d12f6977 100644 --- a/internal/ota-api/dto.go +++ b/internal/ota-api/dto.go @@ -31,9 +31,8 @@ const progressBarMultiplier = 2 type ( OtaStatusResponse struct { - FirmwareSize *int64 `json:"firmware_size,omitempty"` - Ota Ota `json:"ota"` - States []State `json:"states,omitempty"` + Ota Ota `json:"ota"` + States []State `json:"states,omitempty"` } OtaStatusList struct { @@ -41,12 +40,15 @@ type ( } Ota struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - DeviceID string `json:"device_id,omitempty" yaml:"device_id,omitempty"` - Status string `json:"status" yaml:"status"` - StartedAt string `json:"started_at" yaml:"started_at"` - EndedAt string `json:"ended_at,omitempty" yaml:"ended_at,omitempty"` - ErrorReason string `json:"error_reason,omitempty" yaml:"error_reason,omitempty"` + ID string `json:"id,omitempty" yaml:"id,omitempty"` + DeviceID string `json:"device_id,omitempty" yaml:"device_id,omitempty"` + Status string `json:"status" yaml:"status"` + StartedAt string `json:"started_at" yaml:"started_at"` + EndedAt string `json:"ended_at,omitempty" yaml:"ended_at,omitempty"` + ErrorReason string `json:"error_reason,omitempty" yaml:"error_reason,omitempty"` + FirmwareSize int64 `json:"firmware_size,omitempty"` + MaxRetries int64 `json:"max_retries,omitempty"` + RetryAttempt int64 `json:"retry_attempt,omitempty"` } State struct { @@ -57,7 +59,9 @@ type ( } OtaStatusDetail struct { - FirmwareSize *int64 `json:"firmware_size,omitempty"` + FirmwareSize int64 `json:"firmware_size,omitempty"` + MaxRetries int64 `json:"max_retries,omitempty"` + RetryAttempt int64 `json:"retry_attempt,omitempty"` Ota Ota `json:"ota"` Details []State `json:"details,omitempty"` } @@ -81,7 +85,7 @@ func (r OtaStatusList) String() string { } if hasErrorReason { - t.SetHeader("Device ID", "Ota ID", "Status", "Started At", "Ended At", "Error Reason") + t.SetHeader("Device ID", "Ota ID", "Status", "Started At", "Ended At", "Error Reason", "Retry Attempt") } else { t.SetHeader("Device ID", "Ota ID", "Status", "Started At", "Ended At") } @@ -91,6 +95,7 @@ func (r OtaStatusList) String() string { line := []any{r.DeviceID, r.ID, r.MapStatus(), formatHumanReadableTs(r.StartedAt), formatHumanReadableTs(r.EndedAt)} if hasErrorReason { line = append(line, r.ErrorReason) + line = append(line, strconv.FormatInt(r.RetryAttempt, 10)) } t.AddRow(line...) } @@ -114,7 +119,7 @@ func (r Ota) String() string { hasErrorReason := r.ErrorReason != "" if hasErrorReason { - t.SetHeader("Device ID", "Ota ID", "Status", "Started At", "Ended At", "Error Reason") + t.SetHeader("Device ID", "Ota ID", "Status", "Started At", "Ended At", "Error Reason", "Retry Attempt") } else { t.SetHeader("Device ID", "Ota ID", "Status", "Started At", "Ended At") } @@ -123,6 +128,7 @@ func (r Ota) String() string { line := []any{r.DeviceID, r.ID, r.MapStatus(), formatHumanReadableTs(r.StartedAt), formatHumanReadableTs(r.EndedAt)} if hasErrorReason { line = append(line, r.ErrorReason) + line = append(line, strconv.FormatInt(r.RetryAttempt, 10)) } t.AddRow(line...) @@ -138,18 +144,21 @@ func (r OtaStatusDetail) String() string { return "No OTA found" } t := table.New() - hasErrorReason := r.Ota.ErrorReason != "" - if hasErrorReason { - t.SetHeader("Device ID", "Ota ID", "Status", "Started At", "Ended At", "Error Reason") + succeeded := strings.ToLower(r.Ota.Status) == "succeeded" + hasError := r.Ota.ErrorReason != "" || !succeeded + + if hasError { + t.SetHeader("Device ID", "Ota ID", "Status", "Started At", "Ended At", "Error Reason", "Retry Attempt") } else { t.SetHeader("Device ID", "Ota ID", "Status", "Started At", "Ended At") } // Now print the table line := []any{r.Ota.DeviceID, r.Ota.ID, r.Ota.MapStatus(), formatHumanReadableTs(r.Ota.StartedAt), formatHumanReadableTs(r.Ota.EndedAt)} - if hasErrorReason { + if hasError { line = append(line, r.Ota.ErrorReason) + line = append(line, strconv.FormatInt(r.RetryAttempt, 10)) } t.AddRow(line...) @@ -160,22 +169,37 @@ func (r OtaStatusDetail) String() string { t = table.New() t.SetHeader("Time", "Status", "Detail") fwSize := int64(0) - if r.FirmwareSize != nil { - fwSize = *r.FirmwareSize + if r.FirmwareSize > 0 { + fwSize = r.FirmwareSize + } + + firstTS := formatHumanReadableTs(r.Details[0].Timestamp) + if !containsResetState(r.Details) && !hasError { + t.AddRow(firstTS, "Flash", "") } + + hasReachedFlashState := hasReachedFlashState(r.Details, succeeded) for _, s := range r.Details { - stateData := formatStateData(s.State, s.StateData, fwSize, hasReachedFlashState(r.Details)) + stateData := formatStateData(s.State, s.StateData, fwSize, hasReachedFlashState) t.AddRow(formatHumanReadableTs(s.Timestamp), upperCaseFirst(s.State), stateData) } + output += "\nDetails:\n" + t.Render() } return output } -func hasReachedFlashState(states []State) bool { +func hasReachedFlashState(states []State, succeeded bool) bool { + if succeeded { + return true + } + return containsResetState(states) +} + +func containsResetState(states []State) bool { for _, s := range states { - if s.State == "flash" || s.State == "reboot" { + if strings.ToLower(s.State) == "flash" || strings.ToLower(s.State) == "reboot" { return true } } @@ -193,15 +217,15 @@ func formatStateData(state, data string, firmware_size int64, hasReceivedFlashSt return data } if hasReceivedFlashState { - return buildSimpleProgressBar(float64(100)) + return buildSimpleProgressBar(float64(100), firmware_size) } percentage := (float64(actualDownloadedData) / float64(firmware_size)) * 100 - return buildSimpleProgressBar(percentage) + return buildSimpleProgressBar(percentage, firmware_size) } return data } -func buildSimpleProgressBar(progress float64) string { +func buildSimpleProgressBar(progress float64, fw_size int64) string { progressInt := int(progress) / 10 progressInt = progressInt * progressBarMultiplier maxProgress := 10 * progressBarMultiplier @@ -211,7 +235,9 @@ func buildSimpleProgressBar(progress float64) string { bar.WriteString(strings.Repeat(" ", maxProgress-progressInt)) bar.WriteString("] ") bar.WriteString(strconv.FormatFloat(progress, 'f', 2, 64)) - bar.WriteString("%") + bar.WriteString("% (firmware size: ") + bar.WriteString(strconv.FormatInt(fw_size, 10)) + bar.WriteString(" bytes)") return bar.String() } diff --git a/internal/ota-api/dto_test.go b/internal/ota-api/dto_test.go index bb91225f..671290e0 100644 --- a/internal/ota-api/dto_test.go +++ b/internal/ota-api/dto_test.go @@ -9,11 +9,11 @@ import ( func TestProgressBar_notCompletePct(t *testing.T) { firmwareSize := int64(25665 * 2) bar := formatStateData("fetch", "25665", firmwareSize, false) - assert.Equal(t, "[========== ] 50.00%", bar) + assert.Equal(t, "[========== ] 50.00% (firmware size: 51330 bytes)", bar) } func TestProgressBar_ifFlashState_goTo100Pct(t *testing.T) { firmwareSize := int64(25665 * 2) bar := formatStateData("fetch", "25665", firmwareSize, true) // If in flash status, go to 100% - assert.Equal(t, "[====================] 100.00%", bar) + assert.Equal(t, "[====================] 100.00% (firmware size: 51330 bytes)", bar) } From 187ff7fa42bda68e38fca7e05307ecdbad0748cb Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Tue, 27 Aug 2024 16:11:21 +0200 Subject: [PATCH 2/2] Removed double precision in progress bar --- internal/ota-api/dto.go | 2 +- internal/ota-api/dto_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/ota-api/dto.go b/internal/ota-api/dto.go index d12f6977..edb55909 100644 --- a/internal/ota-api/dto.go +++ b/internal/ota-api/dto.go @@ -234,7 +234,7 @@ func buildSimpleProgressBar(progress float64, fw_size int64) string { bar.WriteString(strings.Repeat("=", progressInt)) bar.WriteString(strings.Repeat(" ", maxProgress-progressInt)) bar.WriteString("] ") - bar.WriteString(strconv.FormatFloat(progress, 'f', 2, 64)) + bar.WriteString(strconv.FormatFloat(progress, 'f', 0, 64)) bar.WriteString("% (firmware size: ") bar.WriteString(strconv.FormatInt(fw_size, 10)) bar.WriteString(" bytes)") diff --git a/internal/ota-api/dto_test.go b/internal/ota-api/dto_test.go index 671290e0..619a7101 100644 --- a/internal/ota-api/dto_test.go +++ b/internal/ota-api/dto_test.go @@ -9,11 +9,11 @@ import ( func TestProgressBar_notCompletePct(t *testing.T) { firmwareSize := int64(25665 * 2) bar := formatStateData("fetch", "25665", firmwareSize, false) - assert.Equal(t, "[========== ] 50.00% (firmware size: 51330 bytes)", bar) + assert.Equal(t, "[========== ] 50% (firmware size: 51330 bytes)", bar) } func TestProgressBar_ifFlashState_goTo100Pct(t *testing.T) { firmwareSize := int64(25665 * 2) bar := formatStateData("fetch", "25665", firmwareSize, true) // If in flash status, go to 100% - assert.Equal(t, "[====================] 100.00% (firmware size: 51330 bytes)", bar) + assert.Equal(t, "[====================] 100% (firmware size: 51330 bytes)", bar) }