From 2b3f392f0f4f64e1cf4365b3ed685f3fa66b594a Mon Sep 17 00:00:00 2001 From: Siyuan Date: Fri, 31 Oct 2025 16:15:42 +0000 Subject: [PATCH] cleanup: ota state --- internal/ota/app.go | 6 +- internal/ota/ota.go | 26 +++--- internal/ota/state.go | 81 +++++++++++-------- internal/ota/sys.go | 10 +-- internal/ota/utils.go | 15 +++- .../routes/devices.$id.settings.advanced.tsx | 2 +- 6 files changed, 84 insertions(+), 56 deletions(-) diff --git a/internal/ota/app.go b/internal/ota/app.go index 5554549c..301ea953 100644 --- a/internal/ota/app.go +++ b/internal/ota/app.go @@ -27,14 +27,14 @@ func (s *State) updateApp(ctx context.Context, appUpdate *componentUpdateStatus) l := s.l.With().Str("path", appUpdatePath).Logger() - if err := s.downloadFile(ctx, appUpdatePath, appUpdate.url, &appUpdate.downloadProgress); err != nil { + if err := s.downloadFile(ctx, appUpdatePath, appUpdate.url, "app"); err != nil { return s.componentUpdateError("Error downloading app update", err, &l) } downloadFinished := time.Now() appUpdate.downloadFinishedAt = downloadFinished appUpdate.downloadProgress = 1 - s.triggerStateUpdate() + s.triggerComponentUpdateState("app", appUpdate) if err := s.verifyFile( appUpdatePath, @@ -48,7 +48,7 @@ func (s *State) updateApp(ctx context.Context, appUpdate *componentUpdateStatus) appUpdate.verificationProgress = 1 appUpdate.updatedAt = verifyFinished appUpdate.updateProgress = 1 - s.triggerStateUpdate() + s.triggerComponentUpdateState("app", appUpdate) l.Info().Msg("App update downloaded") diff --git a/internal/ota/ota.go b/internal/ota/ota.go index 21b4705c..366ea922 100644 --- a/internal/ota/ota.go +++ b/internal/ota/ota.go @@ -92,6 +92,11 @@ func (s *State) triggerStateUpdate() { s.onStateUpdate(s.ToRPCState()) } +func (s *State) triggerComponentUpdateState(component string, update *componentUpdateStatus) { + s.componentUpdateStatuses[component] = *update + s.triggerStateUpdate() +} + func (s *State) doUpdate(ctx context.Context, params UpdateParams) error { scopedLogger := s.l.With(). Interface("params", params). @@ -102,32 +107,35 @@ func (s *State) doUpdate(ctx context.Context, params UpdateParams) error { return fmt.Errorf("update already in progress") } - s.updating = true - s.triggerStateUpdate() - - defer func() { - s.updating = false + if !params.CheckOnly { + s.updating = true s.triggerStateUpdate() - }() + defer func() { + s.updating = false + s.triggerStateUpdate() + }() + } appUpdate, systemUpdate, err := s.getUpdateStatus(ctx, params) if err != nil { return s.componentUpdateError("Error checking for updates", err, &scopedLogger) } + s.metadataFetchedAt = time.Now() + s.triggerStateUpdate() + if params.CheckOnly { return nil } - s.metadataFetchedAt = time.Now() - s.triggerStateUpdate() - if appUpdate.available || appUpdate.downgradeAvailable { appUpdate.pending = true + s.triggerComponentUpdateState("app", appUpdate) } if systemUpdate.available || systemUpdate.downgradeAvailable { systemUpdate.pending = true + s.triggerComponentUpdateState("system", systemUpdate) } if appUpdate.pending { diff --git a/internal/ota/state.go b/internal/ota/state.go index 6374edc9..9d9b8c01 100644 --- a/internal/ota/state.go +++ b/internal/ota/state.go @@ -67,25 +67,25 @@ type componentUpdateStatus struct { // RPCState represents the current OTA state for the RPC API type RPCState struct { - Updating bool `json:"updating"` - Error string `json:"error,omitempty"` - MetadataFetchedAt time.Time `json:"metadataFetchedAt,omitempty"` - AppUpdatePending bool `json:"appUpdatePending"` - SystemUpdatePending bool `json:"systemUpdatePending"` - AppDownloadProgress float32 `json:"appDownloadProgress,omitempty"` //TODO: implement for progress bar - AppDownloadFinishedAt time.Time `json:"appDownloadFinishedAt,omitempty"` - SystemDownloadProgress float32 `json:"systemDownloadProgress,omitempty"` //TODO: implement for progress bar - SystemDownloadFinishedAt time.Time `json:"systemDownloadFinishedAt,omitempty"` - AppVerificationProgress float32 `json:"appVerificationProgress,omitempty"` - AppVerifiedAt time.Time `json:"appVerifiedAt,omitempty"` - SystemVerificationProgress float32 `json:"systemVerificationProgress,omitempty"` - SystemVerifiedAt time.Time `json:"systemVerifiedAt,omitempty"` - AppUpdateProgress float32 `json:"appUpdateProgress,omitempty"` //TODO: implement for progress bar - AppUpdatedAt time.Time `json:"appUpdatedAt,omitempty"` - SystemUpdateProgress float32 `json:"systemUpdateProgress,omitempty"` //TODO: port rk_ota, then implement - SystemUpdatedAt time.Time `json:"systemUpdatedAt,omitempty"` - SystemTargetVersion string `json:"systemTargetVersion,omitempty"` - AppTargetVersion string `json:"appTargetVersion,omitempty"` + Updating bool `json:"updating"` + Error string `json:"error,omitempty"` + MetadataFetchedAt *time.Time `json:"metadataFetchedAt,omitempty"` + AppUpdatePending bool `json:"appUpdatePending"` + SystemUpdatePending bool `json:"systemUpdatePending"` + AppDownloadProgress *float32 `json:"appDownloadProgress,omitempty"` //TODO: implement for progress bar + AppDownloadFinishedAt *time.Time `json:"appDownloadFinishedAt,omitempty"` + SystemDownloadProgress *float32 `json:"systemDownloadProgress,omitempty"` //TODO: implement for progress bar + SystemDownloadFinishedAt *time.Time `json:"systemDownloadFinishedAt,omitempty"` + AppVerificationProgress *float32 `json:"appVerificationProgress,omitempty"` + AppVerifiedAt *time.Time `json:"appVerifiedAt,omitempty"` + SystemVerificationProgress *float32 `json:"systemVerificationProgress,omitempty"` + SystemVerifiedAt *time.Time `json:"systemVerifiedAt,omitempty"` + AppUpdateProgress *float32 `json:"appUpdateProgress,omitempty"` //TODO: implement for progress bar + AppUpdatedAt *time.Time `json:"appUpdatedAt,omitempty"` + SystemUpdateProgress *float32 `json:"systemUpdateProgress,omitempty"` //TODO: port rk_ota, then implement + SystemUpdatedAt *time.Time `json:"systemUpdatedAt,omitempty"` + SystemTargetVersion *string `json:"systemTargetVersion,omitempty"` + AppTargetVersion *string `json:"appTargetVersion,omitempty"` } // HwRebootFunc is a function that reboots the hardware @@ -221,35 +221,48 @@ func NewState(opts Options) *State { } // ToRPCState converts the State to the RPCState +// probably we need a generator for this ... func (s *State) ToRPCState() *RPCState { r := &RPCState{ Updating: s.updating, Error: s.error, - MetadataFetchedAt: s.metadataFetchedAt, + MetadataFetchedAt: &s.metadataFetchedAt, } app, ok := s.componentUpdateStatuses["app"] if ok { r.AppUpdatePending = app.pending - r.AppDownloadProgress = app.downloadProgress - r.AppDownloadFinishedAt = app.downloadFinishedAt - r.AppVerificationProgress = app.verificationProgress - r.AppVerifiedAt = app.verifiedAt - r.AppUpdateProgress = app.updateProgress - r.AppUpdatedAt = app.updatedAt - r.AppTargetVersion = app.targetVersion + r.AppDownloadProgress = &app.downloadProgress + if !app.downloadFinishedAt.IsZero() { + r.AppDownloadFinishedAt = &app.downloadFinishedAt + } + r.AppVerificationProgress = &app.verificationProgress + if !app.verifiedAt.IsZero() { + r.AppVerifiedAt = &app.verifiedAt + } + r.AppUpdateProgress = &app.updateProgress + if !app.updatedAt.IsZero() { + r.AppUpdatedAt = &app.updatedAt + } + r.AppTargetVersion = &app.targetVersion } system, ok := s.componentUpdateStatuses["system"] if ok { r.SystemUpdatePending = system.pending - r.SystemDownloadProgress = system.downloadProgress - r.SystemDownloadFinishedAt = system.downloadFinishedAt - r.SystemVerificationProgress = system.verificationProgress - r.SystemVerifiedAt = system.verifiedAt - r.SystemUpdateProgress = system.updateProgress - r.SystemUpdatedAt = system.updatedAt - r.SystemTargetVersion = system.targetVersion + r.SystemDownloadProgress = &system.downloadProgress + if !system.downloadFinishedAt.IsZero() { + r.SystemDownloadFinishedAt = &system.downloadFinishedAt + } + r.SystemVerificationProgress = &system.verificationProgress + if !system.verifiedAt.IsZero() { + r.SystemVerifiedAt = &system.verifiedAt + } + r.SystemUpdateProgress = &system.updateProgress + if !system.updatedAt.IsZero() { + r.SystemUpdatedAt = &system.updatedAt + } + r.SystemTargetVersion = &system.targetVersion } return r diff --git a/internal/ota/sys.go b/internal/ota/sys.go index 334fa1eb..575cf634 100644 --- a/internal/ota/sys.go +++ b/internal/ota/sys.go @@ -17,14 +17,14 @@ func (s *State) updateSystem(ctx context.Context, systemUpdate *componentUpdateS l := s.l.With().Str("path", systemUpdatePath).Logger() - if err := s.downloadFile(ctx, systemUpdatePath, systemUpdate.url, &systemUpdate.downloadProgress); err != nil { + if err := s.downloadFile(ctx, systemUpdatePath, systemUpdate.url, "system"); err != nil { return s.componentUpdateError("Error downloading system update", err, &l) } downloadFinished := time.Now() systemUpdate.downloadFinishedAt = downloadFinished systemUpdate.downloadProgress = 1 - s.triggerStateUpdate() + s.triggerComponentUpdateState("system", systemUpdate) if err := s.verifyFile( systemUpdatePath, @@ -38,7 +38,7 @@ func (s *State) updateSystem(ctx context.Context, systemUpdate *componentUpdateS systemUpdate.verificationProgress = 1 systemUpdate.updatedAt = verifyFinished systemUpdate.updateProgress = 1 - s.triggerStateUpdate() + s.triggerComponentUpdateState("system", systemUpdate) l.Info().Msg("System update downloaded") @@ -68,7 +68,7 @@ func (s *State) updateSystem(ctx context.Context, systemUpdate *componentUpdateS if systemUpdate.updateProgress > 0.99 { systemUpdate.updateProgress = 0.99 } - s.triggerStateUpdate() + s.triggerComponentUpdateState("system", systemUpdate) case <-ctx.Done(): return } @@ -86,7 +86,7 @@ func (s *State) updateSystem(ctx context.Context, systemUpdate *componentUpdateS rkLogger.Info().Msg("rk_ota success") systemUpdate.updateProgress = 1 systemUpdate.updatedAt = verifyFinished - s.triggerStateUpdate() + s.triggerComponentUpdateState("system", systemUpdate) return nil } diff --git a/internal/ota/utils.go b/internal/ota/utils.go index 88d99e4d..6da310ef 100644 --- a/internal/ota/utils.go +++ b/internal/ota/utils.go @@ -25,7 +25,14 @@ func syncFilesystem() error { return nil } -func (s *State) downloadFile(ctx context.Context, path string, url string, downloadProgress *float32) error { +func (s *State) downloadFile(ctx context.Context, path string, url string, component string) error { + componentUpdate, ok := s.componentUpdateStatuses[component] + if !ok { + return fmt.Errorf("component %s not found", component) + } + + downloadProgress := componentUpdate.downloadProgress + if _, err := os.Stat(path); err == nil { if err := os.Remove(path); err != nil { return fmt.Errorf("error removing existing file: %w", err) @@ -80,9 +87,9 @@ func (s *State) downloadFile(ctx context.Context, path string, url string, downl return fmt.Errorf("error writing to file: %w", ew) } progress := float32(written) / float32(totalSize) - if progress-*downloadProgress >= 0.01 { - *downloadProgress = progress - s.triggerStateUpdate() + if progress-downloadProgress >= 0.01 { + componentUpdate.downloadProgress = progress + s.triggerComponentUpdateState(component, &componentUpdate) } } if er != nil { diff --git a/ui/src/routes/devices.$id.settings.advanced.tsx b/ui/src/routes/devices.$id.settings.advanced.tsx index 84e28855..b90b874c 100644 --- a/ui/src/routes/devices.$id.settings.advanced.tsx +++ b/ui/src/routes/devices.$id.settings.advanced.tsx @@ -200,7 +200,7 @@ export default function SettingsAdvancedRoute() { // Navigate to update page navigateTo("/settings/general/update"); }); - }, [updateTarget, appVersion, systemVersion, devChannel, send, navigateTo]); + }, [appVersion, systemVersion, devChannel, send, navigateTo]); return (