fix: authenticate after firmware update

- удалена повторная авторизация перед отправкой команды 04

- полный AES-256 handshake запускается только после успешного OTA и перезагрузки контроллера

- обновлены сообщения интерфейса и документация порядка обновления

- тесты теперь проверяют, что команда 04 предшествует команде авторизации 07

- версия приложения повышена до 1.5.1

- локальные каталоги релизных артефактов исключены из Git
This commit is contained in:
Данил Ярош 2026-09-15 13:33:38 +03:00
parent c34cb0cd7e
commit e163b63666
6 changed files with 15 additions and 45 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@ dist/
build-linux/
dist-linux/
.venv-linux/
release-builds/
*.manifest
*.spec.bak

View File

@ -118,9 +118,10 @@
- Зеленый — подключено, плата готова к работе.
Кнопка переподключения (↻) позволяет быстро восстановить связь при обрыве.
Перед запуском OTA и после успешного обновления приложение автоматически
выполняет полную AES-256 авторизацию платы. В терминале команд доступна
отдельная кнопка ручной авторизации.
После успешного OTA и перезагрузки контроллера приложение автоматически
выполняет полную AES-256 авторизацию платы. Перед командой `04` повторная
авторизация не выполняется. В терминале команд доступна отдельная кнопка
ручной авторизации.
---

View File

@ -1,3 +1,3 @@
"""Plate Config App package."""
__version__ = "1.5.0"
__version__ = "1.5.1"

View File

@ -1,4 +1,4 @@
"""Авторизованный запуск OTA-обновления контроллера."""
"""Запуск OTA с повторной авторизацией после обновления контроллера."""
from __future__ import annotations
@ -12,7 +12,6 @@ from .auth_service import (
READY_TIMEOUT_SECONDS,
STARTUP_BANNER_TIMEOUT_SECONDS,
STARTUP_DELAY_SECONDS,
perform_handshake,
perform_startup_handshake,
)
from .com_service import ComConnection
@ -24,7 +23,6 @@ OTA_TIMEOUT_SECONDS = 60.0
class FirmwareUpdateResult:
success: bool
detail: str
pre_auth: AuthResult | None = None
post_auth: AuthResult | None = None
@ -39,29 +37,11 @@ def perform_authenticated_ota(
ready_timeout: float = READY_TIMEOUT_SECONDS,
sleep_fn: Callable[[float], None] = time.sleep,
) -> FirmwareUpdateResult:
"""Повторно авторизоваться, выполнить ``04`` и авторизоваться после OTA."""
"""Выполнить ``04`` и полностью авторизоваться после успешного OTA."""
emit = log_message or (lambda _message: None)
if not conn.is_connected:
return FirmwareUpdateResult(False, "Порт не подключён")
emit("Авторизация перед запуском OTA…")
try:
pre_auth = perform_handshake(
conn,
timeout=handshake_timeout,
ready_timeout=ready_timeout,
)
except Exception as exc:
return FirmwareUpdateResult(False, f"Ошибка авторизации перед OTA: {exc}")
if pre_auth.status != AuthStatus.OK:
return FirmwareUpdateResult(
False,
f"Авторизация перед OTA не выполнена: {pre_auth.detail}",
pre_auth=pre_auth,
)
emit("✓ Авторизация перед OTA выполнена.")
try:
conn.send_command("04")
emit("→ 04")
@ -82,7 +62,6 @@ def perform_authenticated_ota(
return FirmwareUpdateResult(
False,
"Таймаут ожидания подтверждения успешного обновления",
pre_auth=pre_auth,
)
emit("✓ Обновление подтверждено. Ожидание перезагрузки и авторизации…")
@ -98,7 +77,6 @@ def perform_authenticated_ota(
return FirmwareUpdateResult(
False,
f"Авторизация после OTA не выполнена: {post_auth.detail}",
pre_auth=pre_auth,
post_auth=post_auth,
)
@ -106,14 +84,12 @@ def perform_authenticated_ota(
return FirmwareUpdateResult(
True,
"Прошивка обновлена, плата перезагружена и авторизована",
pre_auth=pre_auth,
post_auth=post_auth,
)
except Exception as exc:
return FirmwareUpdateResult(
False,
f"Ошибка OTA: {exc}",
pre_auth=pre_auth,
)
finally:
conn.release()

View File

@ -244,7 +244,7 @@ class FirmwareDialog(StyledDialog):
self._flash_btn.setEnabled(False)
self._upload_btn.setEnabled(False)
self._log.append("Запуск авторизованного OTA…")
self._log.append("Запуск OTA (04)…")
from PySide6.QtCore import QCoreApplication

View File

@ -50,11 +50,8 @@ class AuthenticatedOtaTests(unittest.TestCase):
sleep_fn=lambda _seconds: None,
)
def test_authenticates_before_04_and_after_successful_update(self) -> None:
def test_sends_04_before_authentication_after_successful_update(self) -> None:
conn = FakeConnection([
self._CHAL,
self._RAES,
"READY!",
"Writing 100%",
"Successful update",
"periphery controller ver_2.4.0 release",
@ -66,27 +63,22 @@ class AuthenticatedOtaTests(unittest.TestCase):
result = self._run(conn)
self.assertTrue(result.success)
self.assertEqual(AuthStatus.OK, result.pre_auth.status)
self.assertEqual(AuthStatus.OK, result.post_auth.status)
send_events = [event for event in conn.events if event.startswith("send:")]
self.assertEqual("send:04", send_events[1])
self.assertTrue(send_events[0].startswith("send:07"))
self.assertTrue(send_events[2].startswith("send:07"))
self.assertEqual("send:04", send_events[0])
self.assertTrue(send_events[1].startswith("send:07"))
def test_does_not_send_04_when_pre_authentication_fails(self) -> None:
def test_sends_04_without_pre_authentication(self) -> None:
conn = FakeConnection([None])
result = self._run(conn)
self.assertFalse(result.success)
self.assertEqual(AuthStatus.TIMEOUT, result.pre_auth.status)
self.assertFalse(any(event == "send:04" for event in conn.events))
self.assertEqual("send:04", conn.events[0])
self.assertFalse(any(event.startswith("send:07") for event in conn.events))
def test_reports_post_authentication_failure_after_update(self) -> None:
conn = FakeConnection([
self._CHAL,
self._RAES,
"READY!",
"Successful update",
None,
])