feat: smarter firmware confirm via reconnect

This commit is contained in:
2026-05-04 14:40:13 +02:00
parent 16690dc216
commit bcccd03ecc
2 changed files with 172 additions and 17 deletions

View File

@ -196,15 +196,11 @@ class FirmwareUpdateService {
);
_emitProgress(state: DfuUpdateState.finishing);
final finishStatus = await _writeControlAndWaitForStatus(
BootloaderDfuProtocol.encodeFinishPayload(normalizedSessionId),
timeout: effectiveStatusTimeout,
);
_requireOkStatus(
finishStatus,
await _writeFinishAndWaitForReset(
sessionId: normalizedSessionId,
expectedOffset: imageBytes.length,
operation: 'FINISH',
statusTimeout: effectiveStatusTimeout,
resetTimeout: effectivePostFinishResetTimeout,
);
await _statusSubscription?.cancel();
@ -212,15 +208,6 @@ class FirmwareUpdateService {
_emitProgress(
state: DfuUpdateState.rebooting, sentBytes: imageBytes.length);
final resetDisconnectResult =
await _transport.waitForBootloaderDisconnect(
timeout: effectivePostFinishResetTimeout,
);
if (resetDisconnectResult.isErr()) {
throw _DfuFailure(
'Bootloader did not perform the expected post-FINISH reset disconnect: ${resetDisconnectResult.unwrapErr()}',
);
}
if (!verifyAfterFinish) {
_emitProgress(
@ -554,6 +541,76 @@ class FirmwareUpdateService {
);
}
Future<void> _writeFinishAndWaitForReset({
required int sessionId,
required int expectedOffset,
required Duration statusTimeout,
required Duration resetTimeout,
}) async {
final eventCount = _statusEventCount;
final result = await _transport.writeControl(
BootloaderDfuProtocol.encodeFinishPayload(sessionId),
);
if (result.isErr()) {
throw _DfuFailure(
'Failed to write bootloader control command: ${result.unwrapErr()}',
);
}
final deadline = DateTime.now().add(resetTimeout);
var observedEvents = eventCount;
while (true) {
_throwIfCancelled();
_throwIfStatusStreamErrored();
if (_statusEventCount > observedEvents && _latestStatus != null) {
final status = _latestStatus!;
_requireOkStatus(
status,
sessionId: sessionId,
expectedOffset: expectedOffset,
operation: 'FINISH',
);
final remaining = deadline.difference(DateTime.now());
final resetDisconnectResult =
await _transport.waitForBootloaderDisconnect(
timeout: remaining > Duration.zero ? remaining : Duration.zero,
);
if (resetDisconnectResult.isErr()) {
throw _DfuFailure(
'Bootloader did not perform the expected post-FINISH reset disconnect: ${resetDisconnectResult.unwrapErr()}',
);
}
return;
}
final disconnectResult = await _transport.waitForBootloaderDisconnect(
timeout: Duration.zero,
);
if (disconnectResult.isOk()) {
return;
}
final remaining = deadline.difference(DateTime.now());
if (remaining <= Duration.zero) {
throw _DfuFailure(
'Bootloader did not perform the expected post-FINISH reset disconnect within ${resetTimeout.inMilliseconds}ms.',
);
}
final waitDuration =
remaining < statusTimeout ? remaining : statusTimeout;
final gotEvent = await _waitForNextStatusEvent(
afterEventCount: observedEvents,
timeout: waitDuration,
recoverable: false,
);
if (gotEvent) {
observedEvents = _statusEventCount - 1;
}
}
}
Future<DfuBootloaderStatus> _waitForStatus({
required int afterEventCount,
required Duration timeout,