From 9bab631d9073153b1b48bdcc3a12ab5ad177fff9 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 3 Oct 2023 12:13:02 +0200 Subject: [PATCH 1/7] :bug: (LekaUpdater): Enforce reset of sha256 in RobotManager --- .../Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift | 1 + Apps/LekaUpdater/Sources/Model/RobotManager.swift | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift index b1b29f4300..bd5dce81b1 100644 --- a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift +++ b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift @@ -318,6 +318,7 @@ private class StateVerifyingFile: GKState, StateEventProcessor { override func willExit(to nextState: GKState) { cancellables.removeAll() + globalRobotManager.sha256 = nil } func process(event: UpdateEvent) { diff --git a/Apps/LekaUpdater/Sources/Model/RobotManager.swift b/Apps/LekaUpdater/Sources/Model/RobotManager.swift index c7f9640f4d..82247952fc 100644 --- a/Apps/LekaUpdater/Sources/Model/RobotManager.swift +++ b/Apps/LekaUpdater/Sources/Model/RobotManager.swift @@ -27,6 +27,7 @@ public class RobotManager: ObservableObject { self.battery = battery self.isCharging = isCharging self.osVersion = osVersion + self.sha256 = nil } @@ -37,6 +38,7 @@ public class RobotManager: ObservableObject { self.battery = robotDiscovery.battery self.isCharging = robotDiscovery.isCharging self.osVersion = robotDiscovery.osVersion + self.sha256 = nil } From 3bfc8727ee4e07fd40b5238f0b2a12bf43a34598 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 3 Oct 2023 12:14:42 +0200 Subject: [PATCH 2/7] :zap: (LekaUpdater): Add delay between subscribing and requesting sha256 --- .../Libs/UpdateProcess/Version/UpdateProcessV130.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift index bd5dce81b1..085c275a97 100644 --- a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift +++ b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift @@ -339,8 +339,8 @@ private class StateVerifyingFile: GKState, StateEventProcessor { } private func startFileVerification() { - subscribeActualSHA256Updates() - readRequestSHA256() + DispatchQueue.main.asyncAfter(deadline: .now(), execute: subscribeActualSHA256Updates) + DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: readRequestSHA256) } private func subscribeActualSHA256Updates() { From 54790bdd1b33383096956990ae0e606c635c2be9 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 3 Oct 2023 12:21:46 +0200 Subject: [PATCH 3/7] :fire: (LekaUpdater): Remove StateError FailedToVerifyFile --- .../Libs/UpdateProcess/UpdateProcessController.swift | 3 --- .../Libs/UpdateProcess/Version/UpdateProcessV130.swift | 7 ------- .../Sources/ViewModel/UpdateStatusViewModel.swift | 6 ------ 3 files changed, 16 deletions(-) diff --git a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/UpdateProcessController.swift b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/UpdateProcessController.swift index 4173e4760e..bbc3d49049 100644 --- a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/UpdateProcessController.swift +++ b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/UpdateProcessController.swift @@ -23,9 +23,6 @@ enum UpdateProcessError: Error { case failedToLoadFile case robotNotUpToDate case robotUnexpectedDisconnection - - // LekaOS 1.3.0+ - case failedToVerifyFile } // MARK: - Controller diff --git a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift index 085c275a97..c89ac8a758 100644 --- a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift +++ b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift @@ -302,7 +302,6 @@ private class StateVerifyingFile: GKState, StateEventProcessor { override func isValidNextState(_ stateClass: AnyClass) -> Bool { stateClass is StateClearingFile.Type || stateClass is StateApplyingUpdate.Type - || stateClass is StateErrorFailedToVerifyFile.Type || stateClass is StateErrorRobotUnexpectedDisconnection.Type } @@ -328,8 +327,6 @@ private class StateVerifyingFile: GKState, StateEventProcessor { self.stateMachine?.enter(StateApplyingUpdate.self) } else if nextStateIsClearingFile { self.stateMachine?.enter(StateClearingFile.self) - } else { - self.stateMachine?.enter(StateErrorFailedToVerifyFile.self) } case .robotDisconnected: self.stateMachine?.enter(StateErrorRobotUnexpectedDisconnection.self) @@ -513,7 +510,6 @@ private class StateFinal: GKState {} private protocol StateError {} private class StateErrorFailedToLoadFile: GKState, StateError {} -private class StateErrorFailedToVerifyFile: GKState, StateError {} private class StateErrorRobotNotUpToDate: GKState, StateError {} private class StateErrorRobotUnexpectedDisconnection: GKState, StateError {} @@ -550,7 +546,6 @@ class UpdateProcessV130: UpdateProcessProtocol { StateFinal(), StateErrorFailedToLoadFile(), - StateErrorFailedToVerifyFile(), StateErrorRobotNotUpToDate(), StateErrorRobotUnexpectedDisconnection(), ]) @@ -617,8 +612,6 @@ class UpdateProcessV130: UpdateProcessProtocol { switch state { case is StateErrorFailedToLoadFile: currentStage.send(completion: .failure(.failedToLoadFile)) - case is StateErrorFailedToVerifyFile: - currentStage.send(completion: .failure(.failedToVerifyFile)) case is StateErrorRobotNotUpToDate: currentStage.send(completion: .failure(.robotNotUpToDate)) case is StateErrorRobotUnexpectedDisconnection: diff --git a/Apps/LekaUpdater/Sources/ViewModel/UpdateStatusViewModel.swift b/Apps/LekaUpdater/Sources/ViewModel/UpdateStatusViewModel.swift index 6d9e3e4473..7719552402 100644 --- a/Apps/LekaUpdater/Sources/ViewModel/UpdateStatusViewModel.swift +++ b/Apps/LekaUpdater/Sources/ViewModel/UpdateStatusViewModel.swift @@ -88,12 +88,6 @@ class UpdateStatusViewModel: ObservableObject { Redémarrer le robot à l'aide de la carte \"Arrêt d'urgence\", reconnectez le robot et relancez le processus """ - case .failedToVerifyFile: - self.errorDescription = """ - Echec de la réception de la mise à jour - (Code erreur #0005) - """ - self.errorInstruction = "Reconnectez le robot et relancez le processus" default: self.errorDescription = """ Une erreur inconnue s'est produite From 3f124c3903fdf3662592bd361bd978631e711c65 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 3 Oct 2023 12:24:38 +0200 Subject: [PATCH 4/7] :fire: (LekaUpdater): After verifying, proceed to clear file if not correct, otherwise apply update --- .../Libs/UpdateProcess/Version/UpdateProcessV130.swift | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift index c89ac8a758..e95302b16a 100644 --- a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift +++ b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift @@ -298,20 +298,12 @@ private class StateVerifyingFile: GKState, StateEventProcessor { private var isFileValid = false private var lastValue = "0000000000000000000000000000000000000000000000000000000000000000" - private var nextStateIsClearingFile = false - override func isValidNextState(_ stateClass: AnyClass) -> Bool { stateClass is StateClearingFile.Type || stateClass is StateApplyingUpdate.Type || stateClass is StateErrorRobotUnexpectedDisconnection.Type } override func didEnter(from previousState: GKState?) { - if previousState is StateSettingDestinationPath { - nextStateIsClearingFile = true - } else { - nextStateIsClearingFile = false - } - DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: startFileVerification) } @@ -325,7 +317,7 @@ private class StateVerifyingFile: GKState, StateEventProcessor { case .fileVerificationReceived: if isFileValid { self.stateMachine?.enter(StateApplyingUpdate.self) - } else if nextStateIsClearingFile { + } else { self.stateMachine?.enter(StateClearingFile.self) } case .robotDisconnected: From 564ac5e2a11b3c82107fb8b07cd2cde917600875 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 3 Oct 2023 12:25:40 +0200 Subject: [PATCH 5/7] :recycle: (LekaUpdater): Rename lastValue by defaultValue in StateVerifyingFile process --- .../Libs/UpdateProcess/Version/UpdateProcessV130.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift index e95302b16a..63c570d937 100644 --- a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift +++ b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift @@ -296,7 +296,7 @@ private class StateVerifyingFile: GKState, StateEventProcessor { private var cancellables: Set = [] private var isFileValid = false - private var lastValue = "0000000000000000000000000000000000000000000000000000000000000000" + private let defaultValue = "0000000000000000000000000000000000000000000000000000000000000000" override func isValidNextState(_ stateClass: AnyClass) -> Bool { stateClass is StateClearingFile.Type || stateClass is StateApplyingUpdate.Type @@ -338,10 +338,9 @@ private class StateVerifyingFile: GKState, StateEventProcessor { .sink { value in guard let value = value else { return } - if value == self.lastValue { + if value == self.defaultValue { return } - self.lastValue = value self.isFileValid = value == globalFirmwareManager.sha256 self.process(event: .fileVerificationReceived) From 1bd70157cd05a0883286145fb02d8bc0e12fef48 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 3 Oct 2023 12:28:04 +0200 Subject: [PATCH 6/7] :building_construction: (LekaUpdater): Jump directly to applying update after sending file --- .../Libs/UpdateProcess/Version/UpdateProcessV130.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift index 63c570d937..3a9c523223 100644 --- a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift +++ b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift @@ -244,7 +244,7 @@ private class StateSendingFile: GKState, StateEventProcessor { } override func isValidNextState(_ stateClass: AnyClass) -> Bool { - stateClass is StateVerifyingFile.Type || stateClass is StateErrorRobotUnexpectedDisconnection.Type + stateClass is StateApplyingUpdate.Type || stateClass is StateErrorRobotUnexpectedDisconnection.Type } override func didEnter(from previousState: GKState?) { @@ -258,7 +258,7 @@ private class StateSendingFile: GKState, StateEventProcessor { func process(event: UpdateEvent) { switch event { case .fileSent: - self.stateMachine?.enter(StateVerifyingFile.self) + self.stateMachine?.enter(StateApplyingUpdate.self) case .robotDisconnected: self.stateMachine?.enter(StateErrorRobotUnexpectedDisconnection.self) default: From 6ab2602359e31cb6c954a1b1c432cf41e4a4a4bd Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 3 Oct 2023 12:29:00 +0200 Subject: [PATCH 7/7] :art: (LekaUpdater): Move StateVerifyingFile in relevant step Between StateSettingDestinationPath and StateClearingFile --- .../Version/UpdateProcessV130.swift | 154 +++++++++--------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift index 3a9c523223..48f54fde64 100644 --- a/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift +++ b/Apps/LekaUpdater/Sources/Libs/UpdateProcess/Version/UpdateProcessV130.swift @@ -155,6 +155,83 @@ private class StateSettingDestinationPath: GKState, StateEventProcessor { } } +private class StateVerifyingFile: GKState, StateEventProcessor { + + private var cancellables: Set = [] + + private var isFileValid = false + private let defaultValue = "0000000000000000000000000000000000000000000000000000000000000000" + + override func isValidNextState(_ stateClass: AnyClass) -> Bool { + stateClass is StateClearingFile.Type || stateClass is StateApplyingUpdate.Type + || stateClass is StateErrorRobotUnexpectedDisconnection.Type + } + + override func didEnter(from previousState: GKState?) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: startFileVerification) + } + + override func willExit(to nextState: GKState) { + cancellables.removeAll() + globalRobotManager.sha256 = nil + } + + func process(event: UpdateEvent) { + switch event { + case .fileVerificationReceived: + if isFileValid { + self.stateMachine?.enter(StateApplyingUpdate.self) + } else { + self.stateMachine?.enter(StateClearingFile.self) + } + case .robotDisconnected: + self.stateMachine?.enter(StateErrorRobotUnexpectedDisconnection.self) + default: + return + } + } + + private func startFileVerification() { + DispatchQueue.main.asyncAfter(deadline: .now(), execute: subscribeActualSHA256Updates) + DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: readRequestSHA256) + } + + private func subscribeActualSHA256Updates() { + globalRobotManager.$sha256 + .receive(on: DispatchQueue.main) + .sink { value in + guard let value = value else { return } + + if value == self.defaultValue { + return + } + + self.isFileValid = value == globalFirmwareManager.sha256 + self.process(event: .fileVerificationReceived) + } + .store(in: &cancellables) + } + + private func readRequestSHA256() { + globalRobotManager.robotPeripheral?.peripheral + .readValue( + forCharacteristic: BLESpecs.FileExchange.Characteristics.fileSHA256, + inService: BLESpecs.FileExchange.service + ) + .receive(on: DispatchQueue.main) + .sink( + receiveCompletion: { _ in + // nothing to do + }, + receiveValue: { data in + // nothing to do + } + ) + .store(in: &cancellables) + } + +} + private class StateClearingFile: GKState, StateEventProcessor { override func isValidNextState(_ stateClass: AnyClass) -> Bool { @@ -291,83 +368,6 @@ private class StateSendingFile: GKState, StateEventProcessor { } } -private class StateVerifyingFile: GKState, StateEventProcessor { - - private var cancellables: Set = [] - - private var isFileValid = false - private let defaultValue = "0000000000000000000000000000000000000000000000000000000000000000" - - override func isValidNextState(_ stateClass: AnyClass) -> Bool { - stateClass is StateClearingFile.Type || stateClass is StateApplyingUpdate.Type - || stateClass is StateErrorRobotUnexpectedDisconnection.Type - } - - override func didEnter(from previousState: GKState?) { - DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: startFileVerification) - } - - override func willExit(to nextState: GKState) { - cancellables.removeAll() - globalRobotManager.sha256 = nil - } - - func process(event: UpdateEvent) { - switch event { - case .fileVerificationReceived: - if isFileValid { - self.stateMachine?.enter(StateApplyingUpdate.self) - } else { - self.stateMachine?.enter(StateClearingFile.self) - } - case .robotDisconnected: - self.stateMachine?.enter(StateErrorRobotUnexpectedDisconnection.self) - default: - return - } - } - - private func startFileVerification() { - DispatchQueue.main.asyncAfter(deadline: .now(), execute: subscribeActualSHA256Updates) - DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: readRequestSHA256) - } - - private func subscribeActualSHA256Updates() { - globalRobotManager.$sha256 - .receive(on: DispatchQueue.main) - .sink { value in - guard let value = value else { return } - - if value == self.defaultValue { - return - } - - self.isFileValid = value == globalFirmwareManager.sha256 - self.process(event: .fileVerificationReceived) - } - .store(in: &cancellables) - } - - private func readRequestSHA256() { - globalRobotManager.robotPeripheral?.peripheral - .readValue( - forCharacteristic: BLESpecs.FileExchange.Characteristics.fileSHA256, - inService: BLESpecs.FileExchange.service - ) - .receive(on: DispatchQueue.main) - .sink( - receiveCompletion: { _ in - // nothing to do - }, - receiveValue: { data in - // nothing to do - } - ) - .store(in: &cancellables) - } - -} - private class StateApplyingUpdate: GKState, StateEventProcessor { private var cancellables: Set = []