# firmware_test: TUI app current state
This commit is contained in:
parent
054bbd70f5
commit
55cd9e8273
25 changed files with 3104 additions and 168 deletions
|
|
@ -16,6 +16,10 @@ FLASHLOADER_PID=0073
|
||||||
SERVICE_CDC_VID=1996
|
SERVICE_CDC_VID=1996
|
||||||
SERVICE_CDC_PID=00ad
|
SERVICE_CDC_PID=00ad
|
||||||
|
|
||||||
|
|
||||||
|
# Тип сборки firmware_test для TUI (Debug | Release)
|
||||||
|
FIRMWARE_BUILD_TYPE=Debug
|
||||||
|
|
||||||
# --- Paths ---
|
# --- Paths ---
|
||||||
# BUILD_DIR и TOOLS_DIR задаются абсолютно в корневом justfile
|
# BUILD_DIR и TOOLS_DIR задаются абсолютно в корневом justfile
|
||||||
# через justfile_directory(), здесь можно переопределить если нужно
|
# через justfile_directory(), здесь можно переопределить если нужно
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@
|
||||||
#define USB_DEVICE_DEMO_BCD_VERSION (0x0101U)
|
#define USB_DEVICE_DEMO_BCD_VERSION (0x0101U)
|
||||||
|
|
||||||
/* ---- VID / PID --------------------------------------------------------- */
|
/* ---- VID / PID --------------------------------------------------------- */
|
||||||
#define USB_DEVICE_VID (0x1996U) /* TODO: заменить на свой */
|
#define USB_DEVICE_VID (0x1996U)
|
||||||
#define USB_DEVICE_PID (0x00ADU) /* TODO: заменить на свой */
|
#define USB_DEVICE_PID (0x00ADU)
|
||||||
|
|
||||||
/* ---- CDC коды классов -------------------------------------------------- */
|
/* ---- CDC коды классов -------------------------------------------------- */
|
||||||
#define CDC_COMM_CLASS (0x02U)
|
#define CDC_COMM_CLASS (0x02U)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
# firmware/test/CMakeLists.txt Тестовая прошивка — входной контроль платы на
|
# firmware/test/CMakeLists.txt Тестовая прошивка — входной контроль платы на
|
||||||
# производстве
|
# производстве
|
||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
project(
|
||||||
|
firmware_test
|
||||||
|
VERSION 0.0.1
|
||||||
|
LANGUAGES C ASM)
|
||||||
|
|
||||||
set(TARGET_NAME firmware_test)
|
set(TARGET_NAME firmware_test)
|
||||||
|
|
||||||
|
# Генерация version.h из шаблона
|
||||||
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/generated/version.h" @ONLY)
|
||||||
|
|
||||||
add_subdirectory(fatfs)
|
add_subdirectory(fatfs)
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
|
|
@ -25,6 +34,8 @@ add_executable(
|
||||||
|
|
||||||
target_include_directories(firmware_test PRIVATE src/)
|
target_include_directories(firmware_test PRIVATE src/)
|
||||||
|
|
||||||
|
target_include_directories(firmware_test
|
||||||
|
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||||
# __STARTUP_INITIALIZE_RAMFUNCTION - очистка секции .ram_function и копирование
|
# __STARTUP_INITIALIZE_RAMFUNCTION - очистка секции .ram_function и копирование
|
||||||
# туда данных из __ram_function_flash_start; __STARTUP_INITIALIZE_NONCACHEDATA -
|
# туда данных из __ram_function_flash_start; __STARTUP_INITIALIZE_NONCACHEDATA -
|
||||||
# инициализация некешируемое секции нулями
|
# инициализация некешируемое секции нулями
|
||||||
|
|
|
||||||
|
|
@ -331,6 +331,12 @@ static void handle_cmd(const char *p_line)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmp(cmd_name, "get_version") == 0)
|
||||||
|
{
|
||||||
|
protocol_send_version_response();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(cmd_name, "list_tests") == 0)
|
if (strcmp(cmd_name, "list_tests") == 0)
|
||||||
{
|
{
|
||||||
test_runner_send_list();
|
test_runner_send_list();
|
||||||
|
|
|
||||||
|
|
@ -158,3 +158,12 @@ void protocol_send_uid_response(const uint8_t *p_uid)
|
||||||
(unsigned int) p_uid[6U], (unsigned int) p_uid[7U]);
|
(unsigned int) p_uid[6U], (unsigned int) p_uid[7U]);
|
||||||
cli_send(buf);
|
cli_send(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void protocol_send_version_response(void)
|
||||||
|
{
|
||||||
|
char buf[PROTO_BUF_SIZE];
|
||||||
|
(void) snprintf(buf, sizeof(buf),
|
||||||
|
"{\"type\":\"version_response\","
|
||||||
|
"\"fw\":\"" FIRMWARE_TEST_VERSION "\"}\n");
|
||||||
|
cli_send(buf);
|
||||||
|
}
|
||||||
|
|
@ -19,12 +19,14 @@
|
||||||
#define PROTOCOL_H_
|
#define PROTOCOL_H_
|
||||||
|
|
||||||
#include "test_module.h"
|
#include "test_module.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/** @brief Строка версии прошивки, вставляемая в session_start. */
|
/** @brief Строка версии прошивки, вставляемая в session_start. */
|
||||||
#define FIRMWARE_TEST_VERSION "0.1.4"
|
#define FIRMWARE_TEST_VERSION FIRMWARE_TEST_VERSION_STR
|
||||||
|
|
||||||
/** @brief Таймаут подтверждения по умолчанию, мс. */
|
/** @brief Таймаут подтверждения по умолчанию, мс. */
|
||||||
#define PROTOCOL_CONFIRM_TIMEOUT_MS 30000U
|
#define PROTOCOL_CONFIRM_TIMEOUT_MS 30000U
|
||||||
|
|
@ -100,4 +102,11 @@ void protocol_send_error(const char *p_code);
|
||||||
*/
|
*/
|
||||||
void protocol_send_uid_response(const uint8_t *p_uid);
|
void protocol_send_uid_response(const uint8_t *p_uid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Отправить ответ на команду get_version.
|
||||||
|
*
|
||||||
|
* Формат: {"type":"version_response","fw":"X.Y.Z"}
|
||||||
|
*/
|
||||||
|
void protocol_send_version_response(void);
|
||||||
|
|
||||||
#endif /* PROTOCOL_H_ */
|
#endif /* PROTOCOL_H_ */
|
||||||
25
firmware/test/src/version.h.in
Normal file
25
firmware/test/src/version.h.in
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* @file version.h
|
||||||
|
* @brief Версия firmware_test — генерируется CMake из CMakeLists.txt.
|
||||||
|
*
|
||||||
|
* НЕ редактировать вручную. Версию менять в firmware/test/CMakeLists.txt:
|
||||||
|
* project(firmware_test VERSION X.Y.Z)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef VERSION_H_
|
||||||
|
#define VERSION_H_
|
||||||
|
|
||||||
|
/** @brief Мажорная версия. */
|
||||||
|
#define FIRMWARE_TEST_VERSION_MAJOR @firmware_test_VERSION_MAJOR@
|
||||||
|
|
||||||
|
/** @brief Минорная версия. */
|
||||||
|
#define FIRMWARE_TEST_VERSION_MINOR @firmware_test_VERSION_MINOR@
|
||||||
|
|
||||||
|
/** @brief Патч-версия. */
|
||||||
|
#define FIRMWARE_TEST_VERSION_PATCH @firmware_test_VERSION_PATCH@
|
||||||
|
|
||||||
|
/** @brief Версия строкой для протокола: "X.Y.Z". */
|
||||||
|
#define FIRMWARE_TEST_VERSION_STR \
|
||||||
|
"@firmware_test_VERSION_MAJOR@.@firmware_test_VERSION_MINOR@.@firmware_test_VERSION_PATCH@"
|
||||||
|
|
||||||
|
#endif /* VERSION_H_ */
|
||||||
|
|
@ -5,33 +5,68 @@ TUI-приложение для диагностики и прошивки пл
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tools/production/
|
||||||
|
├── main.py ← точка входа (10 строк)
|
||||||
|
├── pyproject.toml ← зависимости uv
|
||||||
|
├── uv.lock
|
||||||
|
└── app/
|
||||||
|
├── app.py ← ServiceApp — роутинг экранов, жизненный цикл клиентов
|
||||||
|
├── models.py ← все типы данных (dataclass/Enum)
|
||||||
|
├── firmware_client.py ← async USB CDC клиент firmware_test
|
||||||
|
├── m5_client.py ← async M5StampPLC клиент (Serial JSON-lines)
|
||||||
|
├── flasher.py ← subprocess-обёртка над tools/host/flash_usb.py
|
||||||
|
├── orchestrator.py ← маршрутизация confirm_request
|
||||||
|
└── screens/
|
||||||
|
├── __init__.py ← реэкспорт: WaitingScreen, FlashScreen, DiagScreen
|
||||||
|
├── waiting.py ← WaitingScreen — ожидание USB
|
||||||
|
├── flash.py ← FlashScreen — прошивка / chip erase
|
||||||
|
└── diag/
|
||||||
|
├── __init__.py ← DiagScreen — координатор диагностики
|
||||||
|
├── test_list.py ← TestListPanel — чекбоксы тестов
|
||||||
|
├── results.py ← ResultsPanel — строки результатов
|
||||||
|
├── confirm_panel.py ← ConfirmPanel — prompt оператора + countdown
|
||||||
|
└── styles/
|
||||||
|
├── waiting.tcss
|
||||||
|
├── flash.tcss
|
||||||
|
└── diag.tcss
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Концепция
|
## Концепция
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph LR
|
graph LR
|
||||||
subgraph PC["Сервисный ПК"]
|
subgraph PC["Сервисный ПК"]
|
||||||
TUI["service-tui\n(Textual App)"]
|
TUI["service-tui\n(Textual App)"]
|
||||||
subgraph tools["tools/"]
|
subgraph app["app/"]
|
||||||
FC["firmware_client.py\nUSB CDC ACM"]
|
FC["firmware_client.py\nUSB CDC ACM"]
|
||||||
M5["m5_client.py\nSerial JSON-lines"]
|
M5["m5_client.py\nSerial JSON-lines"]
|
||||||
FL["flasher.py\n→ tools/host/flash_usb.py"]
|
FL["flasher.py\nsubprocess"]
|
||||||
|
OR["orchestrator.py\nconfirm router"]
|
||||||
end
|
end
|
||||||
TUI --> FC
|
TUI --> FC & M5 & FL & OR
|
||||||
TUI --> M5
|
|
||||||
TUI --> FL
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Board["Плата TFT (MIMXRT1052)"]
|
subgraph Board["Плата TFT (MIMXRT1052)"]
|
||||||
FW["firmware_test\n(USB CDC)"]
|
FW["firmware_test\n(USB CDC)"]
|
||||||
ROM["BootROM\n(SDP 1FC9:0130)"]
|
ROM["BootROM SDP\n(1FC9:0130)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph HIL["HIL стенд (опционально)"]
|
subgraph HIL["HIL стенд (опционально)"]
|
||||||
M5HW["M5StampPLC\nRLY1–4 + CAN"]
|
M5HW["M5StampPLC\nRLY1–4 + CAN"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
subgraph Host["tools/host/"]
|
||||||
|
FU["flash_usb.py\nsdphost + blhost"]
|
||||||
|
end
|
||||||
|
|
||||||
FC <-->|"JSON-lines\nVID:PID 1996:00AD"| FW
|
FC <-->|"JSON-lines\nVID:PID 1996:00AD"| FW
|
||||||
FL -->|"sdphost + blhost\nVID:PID 1FC9:0130"| ROM
|
FL -->|"subprocess uv run"| FU
|
||||||
|
FU -->|"sdphost + blhost\nVID:PID 1FC9:0130"| ROM
|
||||||
M5 <-->|"JSON-lines\nSerial"| M5HW
|
M5 <-->|"JSON-lines\nSerial"| M5HW
|
||||||
M5HW -->|"RLY1–4"| Board
|
M5HW -->|"RLY1–4"| Board
|
||||||
```
|
```
|
||||||
|
|
@ -40,165 +75,535 @@ graph LR
|
||||||
|
|
||||||
## Два режима работы
|
## Два режима работы
|
||||||
|
|
||||||
Режим определяется автодетектом USB при старте и меняется динамически.
|
Режим определяется автодетектом USB и меняется динамически без перезапуска TUI.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
stateDiagram-v2
|
stateDiagram-v2
|
||||||
[*] --> WAITING : запуск TUI
|
[*] --> WAITING : запуск TUI
|
||||||
|
|
||||||
WAITING --> FLASHING : обнаружен VID:PID 1FC9:0130\n(BootROM SDP)
|
WAITING --> FLASHING : VID:PID 1FC9:0130\n(BootROM SDP)
|
||||||
WAITING --> DIAGNOSING : обнаружен VID:PID 1996:00AD\n+ session_start по CDC
|
WAITING --> DIAGNOSING : VID:PID 1996:00AD\n+ ping→pong по CDC
|
||||||
|
|
||||||
FLASHING --> WAITING : прошивка завершена / плата отключена
|
FLASHING --> WAITING : FlashDone / ESC / плата отключена
|
||||||
FLASHING --> DIAGNOSING: плата перезагружена в нормальный режим
|
FLASHING --> DIAGNOSING: плата перезагружена после прошивки
|
||||||
|
|
||||||
DIAGNOSING --> WAITING : плата отключена
|
DIAGNOSING --> WAITING : DiagDone / ESC / плата отключена
|
||||||
DIAGNOSING --> FLASHING: плата переведена в SDP (перемычка)
|
DIAGNOSING --> FLASHING: плата переведена в SDP (перемычка BOOT_MOD)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Режим A — Прошивка
|
### Режим A — Прошивка
|
||||||
|
|
||||||
Триггер: обнаружен BootROM SDP (`1FC9:0130`).
|
Триггер: BootROM SDP `1FC9:0130` виден в `serial.tools.list_ports`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
┌─ Прошивка платы ─────────────────────────────────┐
|
┌─ Прошивка платы ─────────────────────────────────┐
|
||||||
│ Обнаружен BootROM (SDP режим) │
|
│ ⚡ BootROM SDP обнаружен │
|
||||||
│ │
|
│ │
|
||||||
│ Что прошить? │
|
│ Что прошить? │
|
||||||
│ ◉ firmware_test (диагностика) │
|
│ ◉ firmware_test (диагностическая прошивка) │
|
||||||
│ ○ Production (bootloader + tft_app) │
|
│ ○ Production (bootloader + tft_app) │
|
||||||
|
│ ○ Кастомный бинарь... │
|
||||||
│ │
|
│ │
|
||||||
│ ████████████░░░░░░ 64% Запись во Flash... │
|
│ [ ▶ Прошить ] [ ⚠ Chip Erase ] │
|
||||||
|
│ │
|
||||||
|
│ ████████████░░░░░░ 64% blhost 64% │
|
||||||
|
│ ┌────────────────────────────────────────────┐ │
|
||||||
|
│ │ ▶ Прошивка: firmware_test │ │
|
||||||
|
│ │ $ blhost -u 0x15A2,0x0073 -- write-memory… │ │
|
||||||
|
│ └────────────────────────────────────────────┘ │
|
||||||
└────────────────────────────────────────────────────┘
|
└────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
### Режим B — Диагностика
|
### Режим B — Диагностика
|
||||||
|
|
||||||
Триггер: CDC-порт `1996:00AD` обнаружен и `ping→pong` прошёл.
|
Триггер: CDC-порт `1996:00AD` виден + `ping→pong` прошёл.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
┌─ Диагностика fw:0.1.4 UID:A1B2C3D4E5F60011 ─────┐
|
┌─ Диагностика fw:0.1.4 UID:A1B2C3D4E5F60011 ────────────────┐
|
||||||
│ M5StampPLC: ✓ │ Результаты: │
|
│ M5: ✓ подключён │
|
||||||
├───────────────────────────┤ sdram ✓ PASS │
|
├────────────────────────────┬───────────────────────────────────┤
|
||||||
│ ☑ SDRAM 32 MB │ qspi ✓ PASS │
|
│ Тесты │ Результаты │
|
||||||
│ ☑ QSPI Flash │ usd ✗ FAIL │
|
│ ☑ SDRAM 32 MB │ sdram ✓ PASS │
|
||||||
│ ☑ microSD │ mount failed: 5 │
|
│ ☑ QSPI Flash │ qspi ✓ PASS │
|
||||||
|
│ ☑ microSD │ usd ✗ FAIL mount err: 5 │
|
||||||
│ ☑ TFT Display │ display ✓ PASS │
|
│ ☑ TFT Display │ display ✓ PASS │
|
||||||
│ ☑ Кнопки │ buttons ✓ PASS │
|
│ ☑ Кнопки │ buttons ✓ PASS │
|
||||||
│ ☑ MQS Audio │ mqs ✓ PASS │
|
│ ☑ MQS Audio │ mqs ✓ PASS │
|
||||||
│ ☑ CAN loopback [HIL] │ can … running │
|
│ ☑ CAN loopback [HIL] │ can … running │
|
||||||
│ ☑ Оптовходы [HIL] │ opto ○ pending │
|
│ ☑ Оптовходы [HIL] │ opto pending │
|
||||||
├───────────────────────────┴─────────────────────────┤
|
├────────────────────────────┴───────────────────────────────────┤
|
||||||
│ [ Запустить выбранные ] [ Все тесты ] │
|
│ [ ▶ Запустить выбранные ] [ ▶▶ Все тесты ] │
|
||||||
│ ████████████████░░░░ 80% Тест: can │
|
│ ████████████████░░░░ 80% Тест: can │
|
||||||
├─────────────────────────────────────────────────────┤
|
├────────────────────────────────────────────────────────────────┤
|
||||||
│ ⚠ Экран залит красным цветом? │
|
│ ⚠ Экран залит красным цветом? 28с │
|
||||||
│ [ ✓ Да ] [ ✗ Нет ] │
|
│ [ ✓ Да ] [ ✗ Нет ] │
|
||||||
└─────────────────────────────────────────────────────┘
|
└────────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
HIL-тесты без M5StampPLC отображаются серыми и не выбираются автоматически.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Архитектура приложения
|
## Диаграмма классов
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
classDiagram
|
||||||
subgraph TUI["tui.py — Textual App"]
|
direction TB
|
||||||
WS["WaitingScreen"]
|
|
||||||
FS["FlashScreen"]
|
|
||||||
DS["DiagScreen"]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph Core["app/"]
|
%% ── Точка входа ──────────────────────────────────────────
|
||||||
OR["orchestrator.py\nмаршрутизация confirm_request"]
|
class ServiceApp {
|
||||||
FW["firmware_client.py\nasync CDC клиент"]
|
-_fw: FirmwareClient
|
||||||
M5["m5_client.py\nasync M5 клиент"]
|
-_m5: M5Client
|
||||||
FL["flasher.py\nsubprocess flash_usb.py"]
|
+on_mount()
|
||||||
MD["models.py\nTestInfo · TestResult\nSessionState · ConfirmRequest"]
|
+_on_device_detected(event)
|
||||||
end
|
+_on_flash_done(event)
|
||||||
|
+_on_diag_done()
|
||||||
|
+_connect_and_diagnose()
|
||||||
|
+_disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
DS --> OR
|
%% ── Экраны ───────────────────────────────────────────────
|
||||||
OR --> FW
|
class WaitingScreen {
|
||||||
OR --> M5
|
-_spinner_idx: int
|
||||||
FS --> FL
|
-_detect_timer: Timer
|
||||||
DS --> MD
|
-_spin_timer: Timer
|
||||||
OR --> MD
|
+on_mount()
|
||||||
|
+on_unmount()
|
||||||
|
-_poll_usb()
|
||||||
|
-_spin()
|
||||||
|
-_stop_timers()
|
||||||
|
}
|
||||||
|
class WaitingScreen.DeviceDetected {
|
||||||
|
+mode: AppMode
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlashScreen {
|
||||||
|
-_flasher: Flasher
|
||||||
|
-_flashing: bool
|
||||||
|
+compose()
|
||||||
|
-_on_radio_changed(event)
|
||||||
|
-_on_flash_pressed()
|
||||||
|
-_on_erase_pressed()
|
||||||
|
-_do_flash(target, bin_path)
|
||||||
|
-_do_erase()
|
||||||
|
-_resolve_target()
|
||||||
|
-_on_progress(progress)
|
||||||
|
-_set_busy(busy)
|
||||||
|
}
|
||||||
|
class FlashScreen.FlashDone {
|
||||||
|
+success: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
class DiagScreen {
|
||||||
|
-_fw: FirmwareClient
|
||||||
|
-_m5: M5Client
|
||||||
|
-_orchestrator: Orchestrator
|
||||||
|
-_session: SessionState
|
||||||
|
-_running: bool
|
||||||
|
+on_mount()
|
||||||
|
-_init_session()
|
||||||
|
-_update_header()
|
||||||
|
-_on_run_selected()
|
||||||
|
-_on_run_all()
|
||||||
|
-_on_confirmed(event)
|
||||||
|
-_start_run(test_ids)
|
||||||
|
-_run_worker(test_ids)
|
||||||
|
-_handle_event(event, total, done)
|
||||||
|
-_on_summary(summary)
|
||||||
|
}
|
||||||
|
class DiagScreen.DiagDone
|
||||||
|
|
||||||
|
%% ── Виджеты DiagScreen ───────────────────────────────────
|
||||||
|
class TestListPanel {
|
||||||
|
-_checkboxes: dict
|
||||||
|
+populate(tests, m5_connected)
|
||||||
|
+get_selected_ids() list
|
||||||
|
+set_enabled(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResultsPanel {
|
||||||
|
+populate(tests)
|
||||||
|
+set_running(test_id)
|
||||||
|
+set_result(result)
|
||||||
|
+reset()
|
||||||
|
-_update(test_id, status, detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfirmPanel {
|
||||||
|
-_timer: Timer
|
||||||
|
-_remaining: int
|
||||||
|
+show_operator(prompt, timeout_ms)
|
||||||
|
+show_buttons_hint(prompt)
|
||||||
|
+hide()
|
||||||
|
-_tick()
|
||||||
|
-_start_timer()
|
||||||
|
-_stop_timer()
|
||||||
|
}
|
||||||
|
class ConfirmPanel.Confirmed {
|
||||||
|
+confirmed: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
%% ── Клиенты ──────────────────────────────────────────────
|
||||||
|
class FirmwareClient {
|
||||||
|
-_port: str
|
||||||
|
-_ser: Serial
|
||||||
|
-_lock: Lock
|
||||||
|
+connect()
|
||||||
|
+disconnect()
|
||||||
|
+ping() bool
|
||||||
|
+list_tests() list
|
||||||
|
+run_selected(test_ids) AsyncGenerator
|
||||||
|
+send_confirm(id, confirmed)
|
||||||
|
+get_uid() str
|
||||||
|
+auto_connect(vid, pid)$
|
||||||
|
+find_port(vid, pid)$
|
||||||
|
}
|
||||||
|
|
||||||
|
class M5Client {
|
||||||
|
-_port: str
|
||||||
|
-_ser: Serial
|
||||||
|
-_lock: Lock
|
||||||
|
+connect()
|
||||||
|
+disconnect()
|
||||||
|
+ping() bool
|
||||||
|
+relay_set(relay, state) bool
|
||||||
|
+relay_get(relay) bool
|
||||||
|
+can_send(id, data) bool
|
||||||
|
+can_recv(timeout_ms) dict
|
||||||
|
+auto_connect()$
|
||||||
|
+find_port()$
|
||||||
|
}
|
||||||
|
|
||||||
|
class Flasher {
|
||||||
|
-_proc: Process
|
||||||
|
+detect_sdp()$ bool
|
||||||
|
+detect_cdc()$ bool
|
||||||
|
+flash(target, progress_cb, bin_path) bool
|
||||||
|
+erase_chip(progress_cb) bool
|
||||||
|
-_run_flash(firmware, build_type, cb)
|
||||||
|
-_run_flash_bin(bin_path, cb)
|
||||||
|
-_run_cmd(cmd, label, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
%% ── Оркестратор ──────────────────────────────────────────
|
||||||
|
class Orchestrator {
|
||||||
|
-_fw: FirmwareClient
|
||||||
|
-_m5: M5Client
|
||||||
|
-_operator_queue: Queue
|
||||||
|
+run_tests(test_ids) AsyncGenerator
|
||||||
|
+resolve_operator_confirm(confirmed)
|
||||||
|
-_handle_confirm(confirm)
|
||||||
|
-_handle_hil_opto(confirm)
|
||||||
|
-_handle_hil_can_rx(confirm)
|
||||||
|
-_handle_hil_can_tx(confirm)
|
||||||
|
-_handle_operator_confirm(confirm)
|
||||||
|
}
|
||||||
|
|
||||||
|
%% ── Модели ───────────────────────────────────────────────
|
||||||
|
class SessionState {
|
||||||
|
+fw_version: str
|
||||||
|
+chip_uid: str
|
||||||
|
+m5_connected: bool
|
||||||
|
+tests: list
|
||||||
|
+results: dict
|
||||||
|
+get_result(id)
|
||||||
|
+set_result(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrchestratorEvent {
|
||||||
|
+type: OrchestratorEventType
|
||||||
|
+test_id: str
|
||||||
|
+result: TestResult
|
||||||
|
+confirm: ConfirmRequest
|
||||||
|
+summary: dict
|
||||||
|
+message: str
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestInfo {
|
||||||
|
+id: str
|
||||||
|
+name: str
|
||||||
|
+critical: bool
|
||||||
|
+requires_hil: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestResult {
|
||||||
|
+id: str
|
||||||
|
+status: TestStatus
|
||||||
|
+duration_ms: int
|
||||||
|
+detail: str
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfirmRequest {
|
||||||
|
+id: str
|
||||||
|
+prompt: str
|
||||||
|
+timeout_ms: int
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlashProgress {
|
||||||
|
+phase: str
|
||||||
|
+percent: int
|
||||||
|
+message: str
|
||||||
|
}
|
||||||
|
|
||||||
|
%% ── Enum ─────────────────────────────────────────────────
|
||||||
|
class AppMode {
|
||||||
|
<<enumeration>>
|
||||||
|
WAITING
|
||||||
|
FLASHING
|
||||||
|
DIAGNOSING
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestStatus {
|
||||||
|
<<enumeration>>
|
||||||
|
PENDING
|
||||||
|
RUNNING
|
||||||
|
PASS
|
||||||
|
FAIL
|
||||||
|
SKIP
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlashTarget {
|
||||||
|
<<enumeration>>
|
||||||
|
FIRMWARE_TEST
|
||||||
|
PRODUCTION
|
||||||
|
CUSTOM
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrchestratorEventType {
|
||||||
|
<<enumeration>>
|
||||||
|
TEST_BEGIN
|
||||||
|
TEST_RESULT
|
||||||
|
CONFIRM_NEEDED
|
||||||
|
CONFIRM_RESOLVED
|
||||||
|
BUTTONS_PROMPT
|
||||||
|
SUMMARY
|
||||||
|
ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
%% ── Связи ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
%% App → экраны
|
||||||
|
ServiceApp --> WaitingScreen : push/switch
|
||||||
|
ServiceApp --> FlashScreen : switch
|
||||||
|
ServiceApp --> DiagScreen : switch
|
||||||
|
ServiceApp --> FirmwareClient : создаёт
|
||||||
|
ServiceApp --> M5Client : создаёт
|
||||||
|
|
||||||
|
%% Сообщения от экранов
|
||||||
|
WaitingScreen ..> WaitingScreen.DeviceDetected : posts
|
||||||
|
FlashScreen ..> FlashScreen.FlashDone : posts
|
||||||
|
DiagScreen ..> DiagScreen.DiagDone : posts
|
||||||
|
ConfirmPanel ..> ConfirmPanel.Confirmed : posts
|
||||||
|
|
||||||
|
%% App слушает сообщения
|
||||||
|
ServiceApp ..> WaitingScreen.DeviceDetected : on()
|
||||||
|
ServiceApp ..> FlashScreen.FlashDone : on()
|
||||||
|
ServiceApp ..> DiagScreen.DiagDone : on()
|
||||||
|
|
||||||
|
%% Экраны → компоненты
|
||||||
|
FlashScreen --> Flasher : использует
|
||||||
|
DiagScreen --> Orchestrator : создаёт
|
||||||
|
DiagScreen --> TestListPanel : монтирует
|
||||||
|
DiagScreen --> ResultsPanel : монтирует
|
||||||
|
DiagScreen --> ConfirmPanel : монтирует
|
||||||
|
DiagScreen --> SessionState : владеет
|
||||||
|
DiagScreen ..> ConfirmPanel.Confirmed : on()
|
||||||
|
|
||||||
|
%% Оркестратор
|
||||||
|
Orchestrator --> FirmwareClient : вызывает
|
||||||
|
Orchestrator --> M5Client : вызывает
|
||||||
|
Orchestrator ..> OrchestratorEvent : yields
|
||||||
|
|
||||||
|
%% Flasher детект
|
||||||
|
WaitingScreen --> Flasher : detect_sdp/cdc
|
||||||
|
|
||||||
|
%% Модели
|
||||||
|
Flasher --> FlashProgress
|
||||||
|
Orchestrator --> ConfirmRequest
|
||||||
|
Orchestrator --> TestResult
|
||||||
|
DiagScreen --> OrchestratorEvent
|
||||||
|
SessionState --> TestInfo
|
||||||
|
SessionState --> TestResult
|
||||||
|
|
||||||
|
%% Enum использование
|
||||||
|
ServiceApp --> AppMode
|
||||||
|
WaitingScreen.DeviceDetected --> AppMode
|
||||||
|
TestResult --> TestStatus
|
||||||
|
Flasher --> FlashTarget
|
||||||
|
Orchestrator --> OrchestratorEventType
|
||||||
|
OrchestratorEvent --> OrchestratorEventType
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Обработка confirm_request
|
## Обработка confirm_request
|
||||||
|
|
||||||
Маршрутизация определяется по `id` поля `confirm_request`:
|
Маршрутизация реализована в `Orchestrator._handle_confirm()` по значению `confirm_request.id`:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
CR["confirm_request\nот firmware_test"]
|
CR["confirm_request\nот firmware_test"]
|
||||||
|
|
||||||
CR --> R{confirm_request.id}
|
CR --> R{confirm_request.id}
|
||||||
|
|
||||||
R -->|"opto_*"| HIL_OPTO["HIL: M5 relay_set\n→ settle → confirm"]
|
R -->|"opto_in1_active\nopto_in1_inactive\nopto_in2_active\nopto_in2_inactive\nopto_rs_active\nopto_rs_inactive"| HIL_OPTO
|
||||||
R -->|"can_rx_ready"| HIL_CAN_RX["HIL: M5 can_send\n→ confirm"]
|
R -->|"can_rx_ready"| HIL_CAN_RX
|
||||||
R -->|"can_tx_verify"| HIL_CAN_TX["HIL: M5 can_recv\n→ verify → confirm"]
|
R -->|"can_tx_verify"| HIL_CAN_TX
|
||||||
R -->|"btn*"| BTN["показать инструкцию\nне отправлять confirm\nждать test_result"]
|
R -->|"btn*"| BTN
|
||||||
R -->|"всё остальное"| OP["показать оператору\nprompt + OK/FAIL\n+ countdown"]
|
R -->|"всё остальное"| OP
|
||||||
|
|
||||||
HIL_OPTO --> AUTO["CONFIRM_RESOLVED\n(автоматически)"]
|
HIL_OPTO["M5: relay_set(rly, state)\nsleep(settle)\nsend_confirm(true/false)"]
|
||||||
HIL_CAN_RX --> AUTO
|
HIL_CAN_RX["M5: can_send(0x100, data)\nsend_confirm(ok)"]
|
||||||
HIL_CAN_TX --> AUTO
|
HIL_CAN_TX["M5: can_recv()\nverify id+data\nsend_confirm(verified)"]
|
||||||
OP --> WAIT["CONFIRM_NEEDED\nждём resolve_operator_confirm()"]
|
BTN["DiagScreen: show_buttons_hint()\nНЕ отправлять confirm\nтаргет сам детектирует нажатие"]
|
||||||
WAIT --> SEND["send_confirm(id, confirmed)"]
|
OP["DiagScreen: show_operator()\nCountdown таймер\nОжидать resolve_operator_confirm()"]
|
||||||
AUTO --> SEND
|
|
||||||
|
HIL_OPTO --> RAUTO["CONFIRM_RESOLVED → прогресс"]
|
||||||
|
HIL_CAN_RX --> RAUTO
|
||||||
|
HIL_CAN_TX --> RAUTO
|
||||||
|
OP --> RNEED["CONFIRM_NEEDED → UI панель\nОператор нажимает OK/Нет"]
|
||||||
|
RNEED --> RESOLVE["ConfirmPanel.Confirmed\n→ resolve_operator_confirm()\n→ send_confirm()"]
|
||||||
```
|
```
|
||||||
|
|
||||||
| confirm id | Кто отвечает | Действие TUI |
|
| `confirm_request.id` | Кто отвечает | Действие TUI | Реле M5 |
|
||||||
| --------------- | ------------ | -------------------------------------- |
|
| -------------------- | ------------ | -------------------- | ---------- |
|
||||||
| `opto_*` | M5 авто | показать прогресс |
|
| `opto_in1_active` | M5 авто | прогресс | RLY3 ON |
|
||||||
| `can_rx_ready` | M5 авто | показать прогресс |
|
| `opto_in1_inactive` | M5 авто | прогресс | RLY3 OFF |
|
||||||
| `can_tx_verify` | M5 авто | показать прогресс |
|
| `opto_in2_active` | M5 авто | прогресс | RLY4 ON |
|
||||||
| `btn*` | физика | показать инструкцию, ждать test_result |
|
| `opto_in2_inactive` | M5 авто | прогресс | RLY4 OFF |
|
||||||
| всё остальное | оператор | prompt + OK/FAIL + countdown |
|
| `opto_rs_active` | M5 авто | прогресс | RLY2 ON |
|
||||||
|
| `opto_rs_inactive` | M5 авто | прогресс | RLY2 OFF |
|
||||||
|
| `can_rx_ready` | M5 авто | прогресс | — (CAN TX) |
|
||||||
|
| `can_tx_verify` | M5 авто | прогресс | — (CAN RX) |
|
||||||
|
| `btn*` | физика | инструкция оператору | — |
|
||||||
|
| всё остальное | оператор | prompt + countdown | — |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Структура
|
## Архитектура экранов
|
||||||
|
|
||||||
```bash
|
```mermaid
|
||||||
tools/production/
|
graph TB
|
||||||
├── pyproject.toml ← зависимости: textual, pyserial, python-dotenv, pyinstaller
|
subgraph ServiceApp["ServiceApp (app.py)"]
|
||||||
├── uv.lock
|
direction LR
|
||||||
├── main.py ← точка входа: asyncio + Textual App
|
WS["WaitingScreen"]
|
||||||
├── app/
|
FS["FlashScreen"]
|
||||||
│ ├── tui.py ← Textual App, экраны (WaitingScreen, FlashScreen, DiagScreen)
|
DS["DiagScreen"]
|
||||||
│ ├── firmware_client.py ← async USB CDC клиент firmware_test
|
end
|
||||||
│ ├── m5_client.py ← async M5StampPLC клиент
|
|
||||||
│ ├── flasher.py ← subprocess → tools/host/flash_usb.py
|
subgraph DiagInternals["DiagScreen (screens/diag/)"]
|
||||||
│ ├── orchestrator.py ← confirm_request маршрутизатор
|
TL["TestListPanel\ntest_list.py"]
|
||||||
│ └── models.py ← AppMode, TestInfo, TestResult, SessionState, …
|
RP["ResultsPanel\nresults.py"]
|
||||||
└── README.md ← этот файл
|
CP["ConfirmPanel\nconfirm_panel.py"]
|
||||||
|
OR["Orchestrator\norchestrator.py"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Clients["Клиенты"]
|
||||||
|
FC["FirmwareClient"]
|
||||||
|
M5["M5Client"]
|
||||||
|
FL["Flasher"]
|
||||||
|
end
|
||||||
|
|
||||||
|
WS -->|"DeviceDetected(FLASHING)"| FS
|
||||||
|
WS -->|"DeviceDetected(DIAGNOSING)"| DS
|
||||||
|
FS -->|"FlashDone"| WS
|
||||||
|
DS -->|"DiagDone"| WS
|
||||||
|
|
||||||
|
FS --> FL
|
||||||
|
DS --> OR
|
||||||
|
DS --> TL
|
||||||
|
DS --> RP
|
||||||
|
DS --> CP
|
||||||
|
CP -->|"Confirmed"| DS
|
||||||
|
OR --> FC
|
||||||
|
OR --> M5
|
||||||
|
WS --> FL
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Жизненный цикл сессии
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant OP as Оператор
|
||||||
|
participant TUI as ServiceApp
|
||||||
|
participant WS as WaitingScreen
|
||||||
|
participant DS as DiagScreen
|
||||||
|
participant FW as firmware_test
|
||||||
|
participant M5 as M5StampPLC
|
||||||
|
|
||||||
|
OP->>TUI: запустить service_tui
|
||||||
|
TUI->>WS: push_screen()
|
||||||
|
WS->>WS: poll USB каждые 1.5 с
|
||||||
|
|
||||||
|
OP->>FW: подключить плату USB
|
||||||
|
WS->>TUI: DeviceDetected(DIAGNOSING)
|
||||||
|
TUI->>FW: auto_connect() → ping→pong
|
||||||
|
TUI->>M5: auto_connect() (опционально)
|
||||||
|
TUI->>DS: switch_screen()
|
||||||
|
|
||||||
|
DS->>FW: list_tests() → TestInfo×8
|
||||||
|
DS->>FW: get_uid() → "A1B2C3D4..."
|
||||||
|
DS->>DS: populate TestListPanel + ResultsPanel
|
||||||
|
|
||||||
|
OP->>DS: выбрать тесты → Запустить
|
||||||
|
DS->>FW: run_selected([...])
|
||||||
|
|
||||||
|
loop Для каждого теста
|
||||||
|
FW-->>DS: test_begin
|
||||||
|
DS->>DS: ResultsPanel.set_running()
|
||||||
|
|
||||||
|
alt HIL confirm (opto / can)
|
||||||
|
FW-->>DS: confirm_request
|
||||||
|
DS->>M5: relay_set() / can_send() / can_recv()
|
||||||
|
DS->>FW: send_confirm(true/false)
|
||||||
|
DS->>DS: прогресс CONFIRM_RESOLVED
|
||||||
|
else Оператор (display / mqs)
|
||||||
|
FW-->>DS: confirm_request
|
||||||
|
DS->>DS: ConfirmPanel.show_operator()
|
||||||
|
OP->>DS: OK / Нет
|
||||||
|
DS->>FW: send_confirm(true/false)
|
||||||
|
else Кнопки
|
||||||
|
FW-->>DS: confirm_request
|
||||||
|
DS->>DS: ConfirmPanel.show_buttons_hint()
|
||||||
|
OP->>FW: физическое нажатие
|
||||||
|
end
|
||||||
|
|
||||||
|
FW-->>DS: test_result
|
||||||
|
DS->>DS: ResultsPanel.set_result()
|
||||||
|
end
|
||||||
|
|
||||||
|
FW-->>DS: summary
|
||||||
|
DS->>DS: показать итог PASS / FAIL
|
||||||
|
OP->>DS: ESC → DiagDone
|
||||||
|
TUI->>WS: switch_screen()
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Конфигурация (`.env`)
|
## Конфигурация (`.env`)
|
||||||
|
|
||||||
Файл `.env` в корне репозитория — единый источник конфигурации.
|
Файл `.env` в корне репозитория — единый источник. Загружается через `python-dotenv` в `main.py` до импорта app-модулей.
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# USB VID:PID — BootROM SDP (менять нельзя, NXP ROM)
|
# USB VID:PID — BootROM SDP (константы NXP, не менять)
|
||||||
BOOTROM_VID=1fc9
|
BOOTROM_VID=1fc9
|
||||||
BOOTROM_PID=0130
|
BOOTROM_PID=0130
|
||||||
|
|
||||||
|
# USB VID:PID — Flashloader (константы NXP, не менять)
|
||||||
|
FLASHLOADER_VID=15a2
|
||||||
|
FLASHLOADER_PID=0073
|
||||||
|
|
||||||
# USB VID:PID — firmware_test CDC (наше устройство)
|
# USB VID:PID — firmware_test CDC (наше устройство)
|
||||||
SERVICE_CDC_VID=1996
|
SERVICE_CDC_VID=1996
|
||||||
SERVICE_CDC_PID=00ad
|
SERVICE_CDC_PID=00ad
|
||||||
|
|
||||||
# Пути к бинарям (опционально, TUI ищет в build/ автоматически)
|
# Опционально: путь к директории лога TUI
|
||||||
FIRMWARE_TEST_BIN=build/Release/firmware_test_hab.bin
|
# SERVICE_LOG_DIR=/tmp
|
||||||
PRODUCTION_BIN_BOOT=build/Release/bootloader_hab.bin
|
|
||||||
PRODUCTION_BIN_APP=build/Release/tft_app_hab.bin
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Пути к бинарям `flash_usb.py` вычисляет автоматически из `BUILD_DIR` (также из `.env`).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Запуск
|
## Запуск
|
||||||
|
|
@ -206,7 +611,7 @@ PRODUCTION_BIN_APP=build/Release/tft_app_hab.bin
|
||||||
### Из монорепозитория (разработчик)
|
### Из монорепозитория (разработчик)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Установить зависимости
|
# Установить зависимости tools/production/
|
||||||
just host::service-setup
|
just host::service-setup
|
||||||
|
|
||||||
# Запустить TUI
|
# Запустить TUI
|
||||||
|
|
@ -215,7 +620,8 @@ just host::service-tui
|
||||||
|
|
||||||
### Standalone-бинарь (сервисник)
|
### Standalone-бинарь (сервисник)
|
||||||
|
|
||||||
Скачать `service_tui` из [GitHub Releases](https://github.com/OSabuser/tft_manufacture_test/releases) и запустить двойным кликом.
|
Скачать `service_tui` из [GitHub Releases](https://github.com/OSabuser/tft_manufacture_test/releases) и запустить двойным кликом — Python не требуется.
|
||||||
|
|
||||||
Для сборки из исходников:
|
Для сборки из исходников:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -223,52 +629,40 @@ just host::service-build
|
||||||
# → tools/production/dist/service_tui
|
# → tools/production/dist/service_tui
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Важно:** standalone-бинарь не включает `tools/host/`. Перед сборкой убедитесь что `tools/host/` инициализирован (`just host::setup-tools`) и доступен рядом с бинарём, либо измените `_FLASH_USB_SCRIPT` в `flasher.py` на абсолютный путь.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Рабочий процесс сервисника
|
## Рабочие процессы сервисника
|
||||||
|
|
||||||
```mermaid
|
### Диагностика (firmware_test уже прошит)
|
||||||
sequenceDiagram
|
|
||||||
participant OP as Оператор
|
|
||||||
participant TUI as service-tui
|
|
||||||
participant FW as firmware_test (CDC)
|
|
||||||
participant M5 as M5StampPLC
|
|
||||||
|
|
||||||
OP->>TUI: запустить service_tui
|
```bash
|
||||||
OP->>TUI: подключить плату USB (нормальный режим)
|
1. BOOT_MOD_1 → GND, сбросить плату
|
||||||
TUI->>FW: ping → pong (CDC автодетект)
|
2. Подключить USB к сервисному ПК
|
||||||
TUI->>FW: list_tests
|
3. TUI: WaitingScreen → обнаружен CDC 1996:00AD → DiagScreen
|
||||||
FW-->>TUI: TestInfo × 8
|
4. Выбрать тесты (или Все тесты) → Запустить
|
||||||
TUI-->>OP: показать список тестов
|
5. Ответить на интерактивные запросы (display, mqs)
|
||||||
|
6. Получить итог PASS / FAIL
|
||||||
|
```
|
||||||
|
|
||||||
OP->>TUI: выбрать тесты → Запустить
|
### Перепрошивка firmware_test
|
||||||
TUI->>FW: run_selected([...])
|
|
||||||
|
|
||||||
loop Для каждого теста
|
```bash
|
||||||
FW-->>TUI: test_begin
|
1. BOOT_MOD_1 → 3V3, сбросить плату
|
||||||
TUI-->>OP: прогресс
|
2. Подключить USB → TUI: FlashScreen
|
||||||
|
3. Выбрать firmware_test → Прошить
|
||||||
|
4. BOOT_MOD_1 → GND, сбросить плату
|
||||||
|
5. TUI автоматически переходит в DiagScreen
|
||||||
|
```
|
||||||
|
|
||||||
alt HIL тест (opto/can)
|
### Chip Erase (сброс Flash в FF)
|
||||||
FW-->>TUI: confirm_request
|
|
||||||
TUI->>M5: relay_set / can_send
|
|
||||||
TUI->>FW: confirm(true)
|
|
||||||
else Интерактивный (display/mqs)
|
|
||||||
FW-->>TUI: confirm_request
|
|
||||||
TUI-->>OP: показать prompt + countdown
|
|
||||||
OP->>TUI: OK / FAIL
|
|
||||||
TUI->>FW: confirm(true/false)
|
|
||||||
else Кнопки
|
|
||||||
FW-->>TUI: confirm_request (инструкция)
|
|
||||||
TUI-->>OP: "Нажмите кнопку..."
|
|
||||||
OP->>FW: физическое нажатие
|
|
||||||
end
|
|
||||||
|
|
||||||
FW-->>TUI: test_result
|
```bash
|
||||||
TUI-->>OP: результат теста
|
1. Плата в SDP-режиме (BOOT_MOD_1 → 3V3)
|
||||||
end
|
2. FlashScreen → Chip Erase (~30 с)
|
||||||
|
3. После erase: BootROM не загрузит прошивку —
|
||||||
FW-->>TUI: summary
|
необходимо перепрошить (пункт выше)
|
||||||
TUI-->>OP: итог: PASS / FAIL
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -282,5 +676,19 @@ sequenceDiagram
|
||||||
| `python-dotenv` | ≥ 1.0 | загрузка `.env` |
|
| `python-dotenv` | ≥ 1.0 | загрузка `.env` |
|
||||||
| `pyinstaller` | ≥ 6.0 | сборка standalone-бинаря |
|
| `pyinstaller` | ≥ 6.0 | сборка standalone-бинаря |
|
||||||
|
|
||||||
**Runtime зависимость (не в pyproject.toml):**
|
**Runtime-зависимость (не в `pyproject.toml`):**
|
||||||
`tools/host/flash_usb.py` вызывается через `subprocess` с `uv run` — `tools/host/` uv-проект должен быть инициализирован (`just host::setup-tools`).
|
`flasher.py` вызывает `tools/host/flash_usb.py` через `uv run` — uv-окружение `tools/host/` должно быть инициализировано командой `just host::setup-tools`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Логирование
|
||||||
|
|
||||||
|
TUI логирует в файл (не в stdout — Textual захватывает терминал):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tools/production/service_tui.log ← по умолчанию
|
||||||
|
$SERVICE_LOG_DIR/service_tui.log ← если задан в .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Уровень: `DEBUG` для всех модулей, `WARNING` для textual.
|
||||||
|
При standalone-запуске лог создаётся рядом с исполняемым файлом.
|
||||||
|
|
|
||||||
209
tools/production/TUI_REPORT.md
Normal file
209
tools/production/TUI_REPORT.md
Normal file
File diff suppressed because one or more lines are too long
87
tools/production/app/app.py
Normal file
87
tools/production/app/app.py
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
"""
|
||||||
|
app.py — корневое Textual приложение.
|
||||||
|
|
||||||
|
Управляет сменой экранов и жизненным циклом клиентов
|
||||||
|
(FirmwareClient, M5Client).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from textual import on, work
|
||||||
|
from textual.app import App
|
||||||
|
from textual.binding import Binding
|
||||||
|
|
||||||
|
from .firmware_client import FirmwareClient
|
||||||
|
from .m5_client import M5Client
|
||||||
|
from .models import AppMode
|
||||||
|
from .screens import DiagScreen, FlashScreen, WaitingScreen
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_CDC_VID = int(os.environ.get("SERVICE_CDC_VID", "0x1996"), 16)
|
||||||
|
_CDC_PID = int(os.environ.get("SERVICE_CDC_PID", "0x00ad"), 16)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceApp(App):
|
||||||
|
"""Корневое приложение service-tui."""
|
||||||
|
|
||||||
|
TITLE = "TFT Board Service Tool"
|
||||||
|
BINDINGS = [
|
||||||
|
Binding("ctrl+c", "quit", "Выход", show=True),
|
||||||
|
Binding("ctrl+q", "quit", "Выход"),
|
||||||
|
]
|
||||||
|
CSS_PATH = "app.tcss"
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._fw: Optional[FirmwareClient] = None
|
||||||
|
self._m5: Optional[M5Client] = None
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
self.push_screen(WaitingScreen())
|
||||||
|
|
||||||
|
# ── Переходы между экранами ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
@on(WaitingScreen.DeviceDetected)
|
||||||
|
def _on_device_detected(self, event: WaitingScreen.DeviceDetected) -> None:
|
||||||
|
if event.mode == AppMode.FLASHING:
|
||||||
|
self.switch_screen(FlashScreen())
|
||||||
|
elif event.mode == AppMode.DIAGNOSING:
|
||||||
|
self._connect_and_diagnose()
|
||||||
|
|
||||||
|
@on(FlashScreen.FlashDone)
|
||||||
|
def _on_flash_done(self, _event: FlashScreen.FlashDone) -> None:
|
||||||
|
"""После прошивки — вернуться на WaitingScreen."""
|
||||||
|
self.switch_screen(WaitingScreen())
|
||||||
|
|
||||||
|
@on(DiagScreen.DiagDone)
|
||||||
|
def _on_diag_done(self) -> None:
|
||||||
|
"""После диагностики — отключиться, вернуться в Waiting."""
|
||||||
|
self._disconnect()
|
||||||
|
self.switch_screen(WaitingScreen())
|
||||||
|
|
||||||
|
# ── Подключение к firmware_test ───────────────────────────────────────────
|
||||||
|
|
||||||
|
@work(thread=False)
|
||||||
|
async def _connect_and_diagnose(self) -> None:
|
||||||
|
try:
|
||||||
|
self._fw = await FirmwareClient.auto_connect(vid=_CDC_VID, pid=_CDC_PID)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error("CDC connect failed: %s", exc)
|
||||||
|
self.switch_screen(WaitingScreen())
|
||||||
|
return
|
||||||
|
|
||||||
|
self._m5 = await M5Client.auto_connect()
|
||||||
|
self.switch_screen(DiagScreen(firmware=self._fw, m5=self._m5))
|
||||||
|
|
||||||
|
def _disconnect(self) -> None:
|
||||||
|
if self._fw is not None:
|
||||||
|
self.call_later(self._fw.disconnect)
|
||||||
|
self._fw = None
|
||||||
|
if self._m5 is not None:
|
||||||
|
self.call_later(self._m5.disconnect)
|
||||||
|
self._m5 = None
|
||||||
283
tools/production/app/app.tcss
Normal file
283
tools/production/app/app.tcss
Normal file
|
|
@ -0,0 +1,283 @@
|
||||||
|
/* ── WaitingScreen ───────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
WaitingScreen {
|
||||||
|
align: center middle;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitingScreen > Static {
|
||||||
|
content-align: center middle;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#waiting-logo {
|
||||||
|
width: 52;
|
||||||
|
height: 7;
|
||||||
|
border: round $primary;
|
||||||
|
padding: 1 2;
|
||||||
|
content-align: center middle;
|
||||||
|
text-style: bold;
|
||||||
|
color: $primary;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#waiting-hint {
|
||||||
|
margin-top: 2;
|
||||||
|
color: $text-muted;
|
||||||
|
content-align: center middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#waiting-spinner {
|
||||||
|
margin-top: 1;
|
||||||
|
color: $accent;
|
||||||
|
content-align: center middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── FlashScreen ─────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
FlashScreen {
|
||||||
|
padding: 1 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flash-title {
|
||||||
|
text-style: bold;
|
||||||
|
color: $warning;
|
||||||
|
margin-bottom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flash-target-group {
|
||||||
|
border: round $panel;
|
||||||
|
padding: 1 2;
|
||||||
|
margin-bottom: 1;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#flash-custom-path {
|
||||||
|
margin-top: 1;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flash-custom-path.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flash-custom-input {
|
||||||
|
width: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flash-btn-row {
|
||||||
|
height: auto;
|
||||||
|
margin-top: 1;
|
||||||
|
align: left middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flash-btn-flash {
|
||||||
|
margin-right: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flash-btn-flash:disabled,
|
||||||
|
#flash-btn-erase:disabled {
|
||||||
|
background: $panel;
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flash-progress-bar {
|
||||||
|
margin-top: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flash-progress-label {
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flash-log {
|
||||||
|
height: 1fr;
|
||||||
|
margin-top: 1;
|
||||||
|
border: round $panel;
|
||||||
|
background: $surface-darken-1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── DiagScreen ──────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
DiagScreen {
|
||||||
|
layout: grid;
|
||||||
|
grid-size: 1;
|
||||||
|
grid-rows: 3 1fr 1 1 3 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Шапка */
|
||||||
|
#diag-header {
|
||||||
|
height: 3;
|
||||||
|
background: $panel;
|
||||||
|
padding: 0 2;
|
||||||
|
align: left middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diag-header-fw {
|
||||||
|
color: $text-muted;
|
||||||
|
margin-right: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diag-header-uid {
|
||||||
|
color: $text-muted;
|
||||||
|
margin-right: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diag-header-m5 {
|
||||||
|
color: $success;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diag-header-m5.m5-absent {
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Рабочая зона */
|
||||||
|
#diag-main {
|
||||||
|
height: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Прогресс */
|
||||||
|
#diag-progress-bar {
|
||||||
|
height: 1;
|
||||||
|
margin: 0 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diag-progress-label {
|
||||||
|
height: 1;
|
||||||
|
color: $text-muted;
|
||||||
|
padding: 0 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Кнопки запуска */
|
||||||
|
#diag-btn-row {
|
||||||
|
height: 3;
|
||||||
|
align: left middle;
|
||||||
|
padding: 0 2;
|
||||||
|
background: $panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diag-btn-run-selected {
|
||||||
|
margin-right: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diag-btn-run-selected:disabled,
|
||||||
|
#diag-btn-run-all:disabled {
|
||||||
|
background: $panel;
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── TestListPanel ───────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
TestListPanel {
|
||||||
|
width: 38;
|
||||||
|
border-right: solid $panel;
|
||||||
|
padding: 1 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestListPanel .section-title {
|
||||||
|
text-style: bold;
|
||||||
|
color: $text-muted;
|
||||||
|
padding: 0 0 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-row {
|
||||||
|
align: left middle;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-row-hil-badge {
|
||||||
|
width: 6;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-row-hil-badge.hil-disabled {
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── ResultsPanel ────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
ResultsPanel {
|
||||||
|
width: 1fr;
|
||||||
|
padding: 1 2;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultsPanel .section-title {
|
||||||
|
text-style: bold;
|
||||||
|
color: $text-muted;
|
||||||
|
padding: 0 0 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-row {
|
||||||
|
height: 1;
|
||||||
|
align: left middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-id {
|
||||||
|
color: $text-muted;
|
||||||
|
width: 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status-pass {
|
||||||
|
color: $success;
|
||||||
|
width: 8;
|
||||||
|
text-style: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status-fail {
|
||||||
|
color: $error;
|
||||||
|
width: 8;
|
||||||
|
text-style: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status-running {
|
||||||
|
color: $warning;
|
||||||
|
width: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status-skip {
|
||||||
|
color: $text-muted;
|
||||||
|
width: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-detail {
|
||||||
|
color: $text-muted;
|
||||||
|
width: 1fr;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── ConfirmPanel ────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
ConfirmPanel {
|
||||||
|
height: auto;
|
||||||
|
background: $warning 15%;
|
||||||
|
border-top: solid $warning;
|
||||||
|
padding: 1 2;
|
||||||
|
align: left middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmPanel.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirm-prompt {
|
||||||
|
width: 1fr;
|
||||||
|
color: $warning;
|
||||||
|
text-style: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirm-countdown {
|
||||||
|
color: $text-muted;
|
||||||
|
margin-right: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirm-btn-ok {
|
||||||
|
margin-right: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirm-btn-ok.hidden,
|
||||||
|
#confirm-btn-fail.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
@ -28,7 +28,7 @@ from typing import AsyncGenerator, Optional
|
||||||
import serial
|
import serial
|
||||||
import serial.tools.list_ports
|
import serial.tools.list_ports
|
||||||
|
|
||||||
from .models import ConfirmRequest, TestInfo, TestResult, TestStatus
|
from .models import TestInfo, TestStatus
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -92,7 +92,7 @@ class FirmwareClient:
|
||||||
|
|
||||||
async def connect(self) -> None:
|
async def connect(self) -> None:
|
||||||
"""Открыть порт и проверить связь через ping→pong."""
|
"""Открыть порт и проверить связь через ping→pong."""
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_running_loop()
|
||||||
await loop.run_in_executor(None, self._open)
|
await loop.run_in_executor(None, self._open)
|
||||||
ok = await self.ping()
|
ok = await self.ping()
|
||||||
if not ok:
|
if not ok:
|
||||||
|
|
@ -110,7 +110,7 @@ class FirmwareClient:
|
||||||
|
|
||||||
async def disconnect(self) -> None:
|
async def disconnect(self) -> None:
|
||||||
"""Закрыть порт."""
|
"""Закрыть порт."""
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_running_loop()
|
||||||
await loop.run_in_executor(None, self._close)
|
await loop.run_in_executor(None, self._close)
|
||||||
|
|
||||||
def _close(self) -> None:
|
def _close(self) -> None:
|
||||||
|
|
@ -119,7 +119,9 @@ class FirmwareClient:
|
||||||
self._ser = None
|
self._ser = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def auto_connect(cls, vid: int, pid: int, baudrate: int = 115200) -> "FirmwareClient":
|
async def auto_connect(
|
||||||
|
cls, vid: int, pid: int, baudrate: int = 115200
|
||||||
|
) -> "FirmwareClient":
|
||||||
"""
|
"""
|
||||||
Найти CDC-порт по VID/PID и подключиться.
|
Найти CDC-порт по VID/PID и подключиться.
|
||||||
|
|
||||||
|
|
@ -152,7 +154,7 @@ class FirmwareClient:
|
||||||
|
|
||||||
async def _send(self, obj: dict) -> None:
|
async def _send(self, obj: dict) -> None:
|
||||||
"""Отправить JSON-команду (async wrapper)."""
|
"""Отправить JSON-команду (async wrapper)."""
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_running_loop()
|
||||||
async with self._lock:
|
async with self._lock:
|
||||||
await loop.run_in_executor(None, self._write_line, obj)
|
await loop.run_in_executor(None, self._write_line, obj)
|
||||||
|
|
||||||
|
|
@ -165,7 +167,7 @@ class FirmwareClient:
|
||||||
Читать события до получения одного из stop_types или таймаута.
|
Читать события до получения одного из stop_types или таймаута.
|
||||||
Генератор — yield каждого полученного события.
|
Генератор — yield каждого полученного события.
|
||||||
"""
|
"""
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_running_loop()
|
||||||
deadline = loop.time() + timeout_s
|
deadline = loop.time() + timeout_s
|
||||||
while loop.time() < deadline:
|
while loop.time() < deadline:
|
||||||
event = await loop.run_in_executor(None, self._read_line)
|
event = await loop.run_in_executor(None, self._read_line)
|
||||||
|
|
@ -182,7 +184,7 @@ class FirmwareClient:
|
||||||
async def ping(self) -> bool:
|
async def ping(self) -> bool:
|
||||||
"""Отправить ping, ждать pong. Вернуть True при успехе."""
|
"""Отправить ping, ждать pong. Вернуть True при успехе."""
|
||||||
await self._send({"type": "cmd", "cmd": "ping"})
|
await self._send({"type": "cmd", "cmd": "ping"})
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_running_loop()
|
||||||
deadline = loop.time() + _PING_TIMEOUT_S
|
deadline = loop.time() + _PING_TIMEOUT_S
|
||||||
while loop.time() < deadline:
|
while loop.time() < deadline:
|
||||||
event = await loop.run_in_executor(None, self._read_line)
|
event = await loop.run_in_executor(None, self._read_line)
|
||||||
|
|
@ -191,6 +193,14 @@ class FirmwareClient:
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def get_version(self) -> str:
|
||||||
|
"""Запросить версию firmware_test. Вернуть строку X.Y.Z или ''."""
|
||||||
|
await self._send({"type": "cmd", "cmd": "get_version"})
|
||||||
|
async for event in self._recv_until({"version_response"}, timeout_s=3.0):
|
||||||
|
if event.get("type") == "version_response":
|
||||||
|
return event.get("fw", "")
|
||||||
|
return ""
|
||||||
|
|
||||||
async def list_tests(self) -> list[TestInfo]:
|
async def list_tests(self) -> list[TestInfo]:
|
||||||
"""Запросить список тестов. Вернуть list[TestInfo]."""
|
"""Запросить список тестов. Вернуть list[TestInfo]."""
|
||||||
await self._send({"type": "cmd", "cmd": "list_tests"})
|
await self._send({"type": "cmd", "cmd": "list_tests"})
|
||||||
|
|
@ -207,9 +217,7 @@ class FirmwareClient:
|
||||||
]
|
]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
async def run_selected(
|
async def run_selected(self, test_ids: list[str]) -> AsyncGenerator[dict, None]:
|
||||||
self, test_ids: list[str]
|
|
||||||
) -> AsyncGenerator[dict, None]:
|
|
||||||
"""
|
"""
|
||||||
Запустить выбранные тесты. Возвращает async generator событий:
|
Запустить выбранные тесты. Возвращает async generator событий:
|
||||||
test_begin, test_result, confirm_request, summary, error.
|
test_begin, test_result, confirm_request, summary, error.
|
||||||
|
|
@ -218,7 +226,9 @@ class FirmwareClient:
|
||||||
send_confirm() не дожидаясь следующего события.
|
send_confirm() не дожидаясь следующего события.
|
||||||
"""
|
"""
|
||||||
await self._send({"type": "cmd", "cmd": "run_selected", "tests": test_ids})
|
await self._send({"type": "cmd", "cmd": "run_selected", "tests": test_ids})
|
||||||
async for event in self._recv_until({"summary"}, timeout_s=_TEST_EVENT_TIMEOUT_S):
|
async for event in self._recv_until(
|
||||||
|
{"summary"}, timeout_s=_TEST_EVENT_TIMEOUT_S
|
||||||
|
):
|
||||||
yield event
|
yield event
|
||||||
|
|
||||||
async def send_confirm(self, confirm_id: str, confirmed: bool) -> None:
|
async def send_confirm(self, confirm_id: str, confirmed: bool) -> None:
|
||||||
|
|
|
||||||
|
|
@ -40,12 +40,32 @@ _HOST_TOOLS_DIR = _FLASH_USB_SCRIPT.parent
|
||||||
_RE_PERCENT = re.compile(r"(\d{1,3})\s*%")
|
_RE_PERCENT = re.compile(r"(\d{1,3})\s*%")
|
||||||
_RE_PHASE = re.compile(r"(sdphost|blhost|Writing|Erasing|Verifying)", re.IGNORECASE)
|
_RE_PHASE = re.compile(r"(sdphost|blhost|Writing|Erasing|Verifying)", re.IGNORECASE)
|
||||||
|
|
||||||
|
_FIRMWARE_BUILD_TYPE = os.environ.get("FIRMWARE_BUILD_TYPE", "Debug")
|
||||||
|
|
||||||
ProgressCallback = Callable[[FlashProgress], Awaitable[None]]
|
ProgressCallback = Callable[[FlashProgress], Awaitable[None]]
|
||||||
|
|
||||||
|
|
||||||
def _detect_usb(vid: int, pid: int) -> bool:
|
def _detect_usb(vid: int, pid: int) -> bool:
|
||||||
"""Проверить наличие USB-устройства по VID/PID (синхронно)."""
|
"""
|
||||||
|
Проверить наличие USB-устройства по VID/PID (синхронно).
|
||||||
|
|
||||||
|
Два метода детекта:
|
||||||
|
1. pyusb (usb.core) — видит все USB-устройства включая SDP bulk/HID
|
||||||
|
(на macOS SDP не создаёт serial-порт и невидим через list_ports).
|
||||||
|
2. serial.tools.list_ports — fallback для CDC ACM устройств
|
||||||
|
если pyusb недоступен.
|
||||||
|
"""
|
||||||
|
# Метод 1: pyusb — работает для SDP и CDC
|
||||||
|
try:
|
||||||
|
import usb.core
|
||||||
|
|
||||||
|
dev = usb.core.find(idVendor=vid, idProduct=pid)
|
||||||
|
if dev is not None:
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Метод 2: serial list_ports — fallback для CDC ACM
|
||||||
try:
|
try:
|
||||||
import serial.tools.list_ports
|
import serial.tools.list_ports
|
||||||
|
|
||||||
|
|
@ -54,6 +74,7 @@ def _detect_usb(vid: int, pid: int) -> bool:
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -127,11 +148,11 @@ class Flasher:
|
||||||
if target == FlashTarget.FIRMWARE_TEST:
|
if target == FlashTarget.FIRMWARE_TEST:
|
||||||
if bin_path is not None:
|
if bin_path is not None:
|
||||||
return await self._run_flash_bin(bin_path, progress_cb)
|
return await self._run_flash_bin(bin_path, progress_cb)
|
||||||
return await self._run_flash("firmware_test", "Release", progress_cb)
|
return await self._run_flash("firmware_test", progress_cb)
|
||||||
elif target == FlashTarget.PRODUCTION:
|
elif target == FlashTarget.PRODUCTION:
|
||||||
ok = await self._run_flash("bootloader", "Release", progress_cb)
|
ok = await self._run_flash("bootloader", progress_cb)
|
||||||
if ok:
|
if ok:
|
||||||
ok = await self._run_flash("app", "Release", progress_cb)
|
ok = await self._run_flash("app", progress_cb)
|
||||||
return ok
|
return ok
|
||||||
elif target == FlashTarget.CUSTOM:
|
elif target == FlashTarget.CUSTOM:
|
||||||
if bin_path is None:
|
if bin_path is None:
|
||||||
|
|
@ -142,7 +163,6 @@ class Flasher:
|
||||||
async def _run_flash( # прошивка стандартного firmware по имени
|
async def _run_flash( # прошивка стандартного firmware по имени
|
||||||
self,
|
self,
|
||||||
firmware: str,
|
firmware: str,
|
||||||
build_type: str,
|
|
||||||
progress_cb: Optional[ProgressCallback],
|
progress_cb: Optional[ProgressCallback],
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Запустить flash_usb.py для одного бинаря."""
|
"""Запустить flash_usb.py для одного бинаря."""
|
||||||
|
|
@ -156,7 +176,7 @@ class Flasher:
|
||||||
"--firmware",
|
"--firmware",
|
||||||
firmware,
|
firmware,
|
||||||
"--build-type",
|
"--build-type",
|
||||||
build_type,
|
_FIRMWARE_BUILD_TYPE,
|
||||||
]
|
]
|
||||||
return await self._run_cmd(cmd, firmware, progress_cb)
|
return await self._run_cmd(cmd, firmware, progress_cb)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ class M5Client:
|
||||||
|
|
||||||
async def connect(self) -> None:
|
async def connect(self) -> None:
|
||||||
"""Открыть порт и проверить связь через ping."""
|
"""Открыть порт и проверить связь через ping."""
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_running_loop()
|
||||||
await loop.run_in_executor(None, self._open)
|
await loop.run_in_executor(None, self._open)
|
||||||
ok = await self.ping()
|
ok = await self.ping()
|
||||||
if not ok:
|
if not ok:
|
||||||
|
|
@ -103,7 +103,7 @@ class M5Client:
|
||||||
self._ser.reset_input_buffer()
|
self._ser.reset_input_buffer()
|
||||||
|
|
||||||
async def disconnect(self) -> None:
|
async def disconnect(self) -> None:
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_running_loop()
|
||||||
await loop.run_in_executor(None, self._close)
|
await loop.run_in_executor(None, self._close)
|
||||||
|
|
||||||
def _close(self) -> None:
|
def _close(self) -> None:
|
||||||
|
|
@ -133,7 +133,7 @@ class M5Client:
|
||||||
|
|
||||||
async def _cmd(self, cmd: dict) -> Optional[dict]:
|
async def _cmd(self, cmd: dict) -> Optional[dict]:
|
||||||
"""Async wrapper над _send_recv."""
|
"""Async wrapper над _send_recv."""
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_running_loop()
|
||||||
async with self._lock:
|
async with self._lock:
|
||||||
return await loop.run_in_executor(None, self._send_recv, cmd)
|
return await loop.run_in_executor(None, self._send_recv, cmd)
|
||||||
|
|
||||||
|
|
|
||||||
7
tools/production/app/screens/__init__.py
Normal file
7
tools/production/app/screens/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
"""screens — публичный экспорт экранов TUI."""
|
||||||
|
|
||||||
|
from .diag import DiagScreen
|
||||||
|
from .flash import FlashScreen
|
||||||
|
from .waiting import WaitingScreen
|
||||||
|
|
||||||
|
__all__ = ["WaitingScreen", "FlashScreen", "DiagScreen"]
|
||||||
261
tools/production/app/screens/diag/__init__.py
Normal file
261
tools/production/app/screens/diag/__init__.py
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
"""
|
||||||
|
diag/__init__.py — экран диагностики (режим B).
|
||||||
|
|
||||||
|
DiagScreen координирует три виджета:
|
||||||
|
TestListPanel — выбор тестов (левая колонка)
|
||||||
|
ResultsPanel — результаты (правая колонка)
|
||||||
|
ConfirmPanel — confirm_request оператора (нижняя панель)
|
||||||
|
|
||||||
|
и Orchestrator — маршрутизатор confirm_request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from textual import on, work
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.binding import Binding
|
||||||
|
from textual.containers import Horizontal
|
||||||
|
from textual.css.query import NoMatches
|
||||||
|
from textual.message import Message
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.widgets import Button, Label, ProgressBar, Static
|
||||||
|
|
||||||
|
from ...firmware_client import FirmwareClient
|
||||||
|
from ...m5_client import M5Client
|
||||||
|
from ...models import SessionState
|
||||||
|
from ...orchestrator import Orchestrator, OrchestratorEvent, OrchestratorEventType
|
||||||
|
from .confirm_panel import ConfirmPanel
|
||||||
|
from .results import ResultsPanel
|
||||||
|
from .test_list import TestListPanel
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DiagScreen(Screen):
|
||||||
|
"""
|
||||||
|
Экран диагностики.
|
||||||
|
|
||||||
|
Messages:
|
||||||
|
DiagDone() — сессия завершена (плата отключена или summary получен)
|
||||||
|
"""
|
||||||
|
|
||||||
|
BINDINGS = [
|
||||||
|
Binding("escape", "go_back", "Отключиться"),
|
||||||
|
Binding("r", "run_all", "Все тесты"),
|
||||||
|
Binding("s", "run_selected", "Выбранные"),
|
||||||
|
]
|
||||||
|
|
||||||
|
class DiagDone(Message):
|
||||||
|
"""Сессия диагностики завершена."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
firmware: FirmwareClient,
|
||||||
|
m5: Optional[M5Client] = None,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self._fw = firmware
|
||||||
|
self._m5 = m5
|
||||||
|
self._orchestrator = Orchestrator(firmware, m5)
|
||||||
|
self._session = SessionState()
|
||||||
|
self._running = False
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
# Шапка
|
||||||
|
with Horizontal(id="diag-header"):
|
||||||
|
yield Static("fw: —", id="diag-header-fw")
|
||||||
|
yield Static("UID: —", id="diag-header-uid")
|
||||||
|
yield Static("M5: —", id="diag-header-m5")
|
||||||
|
|
||||||
|
# Рабочая зона: список тестов + результаты
|
||||||
|
with Horizontal(id="diag-main"):
|
||||||
|
yield TestListPanel(id="diag-test-list")
|
||||||
|
yield ResultsPanel(id="diag-results")
|
||||||
|
|
||||||
|
# Прогресс
|
||||||
|
yield ProgressBar(id="diag-progress-bar", show_eta=False)
|
||||||
|
yield Label("", id="diag-progress-label")
|
||||||
|
|
||||||
|
# Кнопки запуска
|
||||||
|
with Horizontal(id="diag-btn-row"):
|
||||||
|
yield Button(
|
||||||
|
"▶ Запустить выбранные",
|
||||||
|
id="diag-btn-run-selected",
|
||||||
|
variant="primary",
|
||||||
|
disabled=True,
|
||||||
|
)
|
||||||
|
yield Button(
|
||||||
|
"▶▶ Все тесты",
|
||||||
|
id="diag-btn-run-all",
|
||||||
|
variant="default",
|
||||||
|
disabled=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Панель confirm
|
||||||
|
yield ConfirmPanel(id="diag-confirm", classes="hidden")
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
self._init_session()
|
||||||
|
|
||||||
|
# ── Инициализация сессии ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@work(thread=False)
|
||||||
|
async def _init_session(self) -> None:
|
||||||
|
try:
|
||||||
|
tests = await self._fw.list_tests()
|
||||||
|
uid = await self._fw.get_uid()
|
||||||
|
version = await self._fw.get_version()
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error("Session init failed: %s", exc)
|
||||||
|
self.post_message(self.DiagDone())
|
||||||
|
return
|
||||||
|
|
||||||
|
self._session.tests = tests
|
||||||
|
self._session.chip_uid = uid
|
||||||
|
self._session.fw_version = version
|
||||||
|
self._session.m5_connected = self._m5 is not None
|
||||||
|
|
||||||
|
self._update_header()
|
||||||
|
|
||||||
|
test_list = self.query_one("#diag-test-list", TestListPanel)
|
||||||
|
test_list.populate(tests, m5_connected=self._session.m5_connected)
|
||||||
|
|
||||||
|
self.query_one("#diag-results", ResultsPanel).populate(tests)
|
||||||
|
self._set_run_buttons(enabled=True)
|
||||||
|
|
||||||
|
def _update_header(self) -> None:
|
||||||
|
self.query_one("#diag-header-fw", Static).update(
|
||||||
|
f"fw: {self._session.fw_version or '?'}"
|
||||||
|
)
|
||||||
|
uid_short = self._session.chip_uid[:16] if self._session.chip_uid else "—"
|
||||||
|
self.query_one("#diag-header-uid", Static).update(f"UID: {uid_short}")
|
||||||
|
|
||||||
|
m5_widget = self.query_one("#diag-header-m5", Static)
|
||||||
|
if self._session.m5_connected:
|
||||||
|
m5_widget.update("M5: ✓ подключён")
|
||||||
|
m5_widget.remove_class("m5-absent")
|
||||||
|
else:
|
||||||
|
m5_widget.update("M5: — нет")
|
||||||
|
m5_widget.add_class("m5-absent")
|
||||||
|
|
||||||
|
# ── Кнопки ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@on(Button.Pressed, "#diag-btn-run-selected")
|
||||||
|
def _on_run_selected(self) -> None:
|
||||||
|
ids = self.query_one("#diag-test-list", TestListPanel).get_selected_ids()
|
||||||
|
if ids:
|
||||||
|
self._start_run(ids)
|
||||||
|
|
||||||
|
@on(Button.Pressed, "#diag-btn-run-all")
|
||||||
|
def _on_run_all(self) -> None:
|
||||||
|
ids = [t.id for t in self._session.tests]
|
||||||
|
if ids:
|
||||||
|
self._start_run(ids)
|
||||||
|
|
||||||
|
def action_go_back(self) -> None:
|
||||||
|
if not self._running:
|
||||||
|
self.post_message(self.DiagDone())
|
||||||
|
|
||||||
|
def action_run_all(self) -> None:
|
||||||
|
self._on_run_all()
|
||||||
|
|
||||||
|
def action_run_selected(self) -> None:
|
||||||
|
self._on_run_selected()
|
||||||
|
|
||||||
|
# ── Confirm ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@on(ConfirmPanel.Confirmed)
|
||||||
|
def _on_confirmed(self, event: ConfirmPanel.Confirmed) -> None:
|
||||||
|
"""Оператор ответил — передать в оркестратор."""
|
||||||
|
self.app.call_later(
|
||||||
|
self._orchestrator.resolve_operator_confirm, event.confirmed
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Запуск тестов ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _start_run(self, test_ids: list[str]) -> None:
|
||||||
|
results = self.query_one("#diag-results", ResultsPanel)
|
||||||
|
results.reset()
|
||||||
|
self._session.results.clear()
|
||||||
|
self._update_progress(0, len(test_ids), "")
|
||||||
|
self._set_run_buttons(enabled=False)
|
||||||
|
self._running = True
|
||||||
|
self._run_worker(test_ids)
|
||||||
|
|
||||||
|
@work(exclusive=True, thread=False)
|
||||||
|
async def _run_worker(self, test_ids: list[str]) -> None:
|
||||||
|
total = len(test_ids)
|
||||||
|
done = 0
|
||||||
|
try:
|
||||||
|
async for event in self._orchestrator.run_tests(test_ids):
|
||||||
|
self._handle_event(event, total, done)
|
||||||
|
if event.type == OrchestratorEventType.TEST_RESULT:
|
||||||
|
done += 1
|
||||||
|
if event.type == OrchestratorEventType.SUMMARY:
|
||||||
|
break
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error("run_worker error: %s", exc)
|
||||||
|
finally:
|
||||||
|
self._running = False
|
||||||
|
self._set_run_buttons(enabled=True)
|
||||||
|
try:
|
||||||
|
self.query_one("#diag-confirm", ConfirmPanel).hide()
|
||||||
|
except NoMatches:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _handle_event(self, event: OrchestratorEvent, total: int, done: int) -> None:
|
||||||
|
results = self.query_one("#diag-results", ResultsPanel)
|
||||||
|
confirm = self.query_one("#diag-confirm", ConfirmPanel)
|
||||||
|
|
||||||
|
if event.type == OrchestratorEventType.TEST_BEGIN:
|
||||||
|
results.set_running(event.test_id)
|
||||||
|
self._update_progress(done, total, f"Тест: {event.test_id}")
|
||||||
|
|
||||||
|
elif event.type == OrchestratorEventType.TEST_RESULT:
|
||||||
|
assert event.result is not None
|
||||||
|
self._session.set_result(event.result)
|
||||||
|
results.set_result(event.result)
|
||||||
|
|
||||||
|
elif event.type == OrchestratorEventType.CONFIRM_NEEDED:
|
||||||
|
assert event.confirm is not None
|
||||||
|
confirm.show_operator(
|
||||||
|
prompt=event.confirm.prompt,
|
||||||
|
timeout_ms=event.confirm.timeout_ms,
|
||||||
|
)
|
||||||
|
|
||||||
|
elif event.type == OrchestratorEventType.CONFIRM_RESOLVED:
|
||||||
|
self._update_progress(done, total, f"HIL: {event.message}")
|
||||||
|
|
||||||
|
elif event.type == OrchestratorEventType.BUTTONS_PROMPT:
|
||||||
|
assert event.confirm is not None
|
||||||
|
confirm.show_buttons_hint(event.confirm.prompt)
|
||||||
|
|
||||||
|
elif event.type == OrchestratorEventType.SUMMARY:
|
||||||
|
self._on_summary(event.summary or {})
|
||||||
|
|
||||||
|
elif event.type == OrchestratorEventType.ERROR:
|
||||||
|
self._update_progress(done, total, f"⚠ {event.message}")
|
||||||
|
|
||||||
|
def _on_summary(self, summary: dict) -> None:
|
||||||
|
overall = summary.get("overall", "fail")
|
||||||
|
passed = summary.get("passed", 0)
|
||||||
|
failed = summary.get("failed", 0)
|
||||||
|
icon = "✅" if overall == "pass" else "❌"
|
||||||
|
self.query_one("#diag-progress-label", Label).update(
|
||||||
|
f"{icon} Итог: {passed} прошли, {failed} не прошли"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Утилиты ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _update_progress(self, done: int, total: int, msg: str) -> None:
|
||||||
|
pct = int(done / total * 100) if total else 0
|
||||||
|
self.query_one("#diag-progress-bar", ProgressBar).update(progress=pct)
|
||||||
|
self.query_one("#diag-progress-label", Label).update(msg)
|
||||||
|
|
||||||
|
def _set_run_buttons(self, enabled: bool) -> None:
|
||||||
|
self.query_one("#diag-btn-run-selected", Button).disabled = not enabled
|
||||||
|
self.query_one("#diag-btn-run-all", Button).disabled = not enabled
|
||||||
140
tools/production/app/screens/diag/confirm_panel.py
Normal file
140
tools/production/app/screens/diag/confirm_panel.py
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
"""
|
||||||
|
confirm_panel.py — виджет панели подтверждения оператора.
|
||||||
|
|
||||||
|
Показывается при confirm_request от firmware_test требующем ответа оператора.
|
||||||
|
Скрыт по умолчанию (CSS-класс 'hidden').
|
||||||
|
|
||||||
|
Messages:
|
||||||
|
ConfirmPanel.Confirmed(confirmed: bool) — оператор нажал OK или Нет
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from textual import on
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.css.query import NoMatches
|
||||||
|
from textual.message import Message
|
||||||
|
from textual.timer import Timer
|
||||||
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Button, Static
|
||||||
|
|
||||||
|
_TICK_S = 1.0
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmPanel(Widget):
|
||||||
|
"""
|
||||||
|
Панель подтверждения оператора.
|
||||||
|
|
||||||
|
Использование::
|
||||||
|
|
||||||
|
panel = ConfirmPanel()
|
||||||
|
panel.show(prompt="Экран залит красным?", timeout_ms=30000)
|
||||||
|
# Слушать ConfirmPanel.Confirmed в родительском экране
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Confirmed(Message):
|
||||||
|
"""Оператор ответил на confirm_request."""
|
||||||
|
|
||||||
|
def __init__(self, confirmed: bool) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.confirmed = confirmed
|
||||||
|
|
||||||
|
DEFAULT_CSS = "" # стили в diag.tcss
|
||||||
|
|
||||||
|
def __init__(self, **kwargs) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self._timer: Timer | None = None
|
||||||
|
self._remaining: int = 0
|
||||||
|
self._operator_mode: bool = True # False → режим buttons (нет кнопок)
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Static("", id="confirm-prompt")
|
||||||
|
yield Static("", id="confirm-countdown")
|
||||||
|
yield Button("✓ Да", id="confirm-btn-ok", variant="success")
|
||||||
|
yield Button("✗ Нет", id="confirm-btn-fail", variant="error")
|
||||||
|
|
||||||
|
# ── Public API ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def show_operator(self, prompt: str, timeout_ms: int) -> None:
|
||||||
|
"""Показать панель с кнопками OK/Нет и countdown."""
|
||||||
|
self._operator_mode = True
|
||||||
|
self._remaining = timeout_ms // 1000
|
||||||
|
self._set_prompt(prompt)
|
||||||
|
self._set_countdown(self._remaining)
|
||||||
|
self._show_buttons(True)
|
||||||
|
self.remove_class("hidden")
|
||||||
|
self._start_timer()
|
||||||
|
|
||||||
|
def show_buttons_hint(self, prompt: str) -> None:
|
||||||
|
"""
|
||||||
|
Показать инструкцию для теста кнопок.
|
||||||
|
Без кнопок OK/Нет — оператор только читает, нажимает физическую кнопку.
|
||||||
|
"""
|
||||||
|
self._operator_mode = False
|
||||||
|
self._stop_timer()
|
||||||
|
self._set_prompt(f"⌨ {prompt}")
|
||||||
|
self._set_countdown("")
|
||||||
|
self._show_buttons(False)
|
||||||
|
self.remove_class("hidden")
|
||||||
|
|
||||||
|
def hide(self) -> None:
|
||||||
|
"""Скрыть панель, остановить таймер."""
|
||||||
|
self._stop_timer()
|
||||||
|
self.add_class("hidden")
|
||||||
|
self._show_buttons(True) # восстановить на следующий раз
|
||||||
|
|
||||||
|
# ── Обработчики ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@on(Button.Pressed, "#confirm-btn-ok")
|
||||||
|
def _on_ok(self) -> None:
|
||||||
|
self.hide()
|
||||||
|
self.post_message(self.Confirmed(confirmed=True))
|
||||||
|
|
||||||
|
@on(Button.Pressed, "#confirm-btn-fail")
|
||||||
|
def _on_fail(self) -> None:
|
||||||
|
self.hide()
|
||||||
|
self.post_message(self.Confirmed(confirmed=False))
|
||||||
|
|
||||||
|
# ── Таймер countdown ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _start_timer(self) -> None:
|
||||||
|
self._stop_timer()
|
||||||
|
self._timer = self.set_interval(_TICK_S, self._tick)
|
||||||
|
|
||||||
|
def _stop_timer(self) -> None:
|
||||||
|
if self._timer is not None:
|
||||||
|
self._timer.stop()
|
||||||
|
self._timer = None
|
||||||
|
|
||||||
|
def _tick(self) -> None:
|
||||||
|
self._remaining -= 1
|
||||||
|
self._set_countdown(self._remaining)
|
||||||
|
if self._remaining <= 0:
|
||||||
|
self.hide()
|
||||||
|
self.post_message(self.Confirmed(confirmed=False))
|
||||||
|
|
||||||
|
# ── Утилиты ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _set_prompt(self, text: str) -> None:
|
||||||
|
try:
|
||||||
|
self.query_one("#confirm-prompt", Static).update(f"⚠ {text}")
|
||||||
|
except NoMatches:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _set_countdown(self, value: int | str) -> None:
|
||||||
|
text = f"{value}с" if isinstance(value, int) and value > 0 else ""
|
||||||
|
try:
|
||||||
|
self.query_one("#confirm-countdown", Static).update(text)
|
||||||
|
except NoMatches:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _show_buttons(self, visible: bool) -> None:
|
||||||
|
for btn_id in ("#confirm-btn-ok", "#confirm-btn-fail"):
|
||||||
|
try:
|
||||||
|
btn = self.query_one(btn_id, Button)
|
||||||
|
if visible:
|
||||||
|
btn.remove_class("hidden")
|
||||||
|
else:
|
||||||
|
btn.add_class("hidden")
|
||||||
|
except NoMatches:
|
||||||
|
pass
|
||||||
115
tools/production/app/screens/diag/results.py
Normal file
115
tools/production/app/screens/diag/results.py
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
"""
|
||||||
|
results.py — виджет правой колонки DiagScreen.
|
||||||
|
|
||||||
|
Отображает строки результатов тестов: id | статус | detail.
|
||||||
|
Обновляется по событиям от Orchestrator через DiagScreen.
|
||||||
|
|
||||||
|
Публичный API:
|
||||||
|
ResultsPanel.populate(tests) — инициализировать пустые строки
|
||||||
|
ResultsPanel.set_running(test_id) — показать "выполняется"
|
||||||
|
ResultsPanel.set_result(result) — показать финальный результат
|
||||||
|
ResultsPanel.reset() — сбросить все строки
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.containers import Horizontal
|
||||||
|
from textual.css.query import NoMatches
|
||||||
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Label, Static
|
||||||
|
|
||||||
|
from ...models import TestResult, TestStatus
|
||||||
|
|
||||||
|
|
||||||
|
def _status_display(status: TestStatus) -> tuple[str, str]:
|
||||||
|
"""Вернуть (текст, css-класс) для статуса."""
|
||||||
|
return {
|
||||||
|
TestStatus.PASS: ("✓ PASS", "result-status-pass"),
|
||||||
|
TestStatus.FAIL: ("✗ FAIL", "result-status-fail"),
|
||||||
|
TestStatus.RUNNING: ("…", "result-status-running"),
|
||||||
|
TestStatus.SKIP: ("SKIP", "result-status-skip"),
|
||||||
|
TestStatus.PENDING: ("", "result-status-skip"),
|
||||||
|
}[status]
|
||||||
|
|
||||||
|
|
||||||
|
# CSS-классы статусов — для очистки перед сменой
|
||||||
|
_STATUS_CLASSES = (
|
||||||
|
"result-status-pass",
|
||||||
|
"result-status-fail",
|
||||||
|
"result-status-running",
|
||||||
|
"result-status-skip",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ResultsPanel(Widget):
|
||||||
|
"""Правая колонка DiagScreen — результаты тестов."""
|
||||||
|
|
||||||
|
DEFAULT_CSS = "" # стили в diag.tcss
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Label("Результаты", classes="section-title")
|
||||||
|
|
||||||
|
# ── Public API ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def populate(self, tests: list) -> None:
|
||||||
|
"""
|
||||||
|
Инициализировать пустые строки результатов.
|
||||||
|
|
||||||
|
:param tests: list[TestInfo]
|
||||||
|
"""
|
||||||
|
for child in list(self.children):
|
||||||
|
if not child.has_class("section-title"):
|
||||||
|
child.remove()
|
||||||
|
|
||||||
|
for test in tests:
|
||||||
|
row = Horizontal(classes="result-row", id=f"result-row-{test.id}")
|
||||||
|
id_lbl = Static(test.id, classes="result-id")
|
||||||
|
status_lbl = Static(
|
||||||
|
"", classes="result-status-skip", id=f"result-status-{test.id}"
|
||||||
|
)
|
||||||
|
detail_lbl = Static(
|
||||||
|
"", classes="result-detail", id=f"result-detail-{test.id}"
|
||||||
|
)
|
||||||
|
self.mount(row)
|
||||||
|
row.mount(id_lbl)
|
||||||
|
row.mount(status_lbl)
|
||||||
|
row.mount(detail_lbl)
|
||||||
|
|
||||||
|
def set_running(self, test_id: str) -> None:
|
||||||
|
"""Пометить тест как выполняющийся."""
|
||||||
|
self._update(test_id, TestStatus.RUNNING, "")
|
||||||
|
|
||||||
|
def set_result(self, result: TestResult) -> None:
|
||||||
|
"""Показать финальный результат теста."""
|
||||||
|
detail = result.detail if result.status == TestStatus.FAIL else ""
|
||||||
|
self._update(result.id, result.status, detail)
|
||||||
|
|
||||||
|
def reset(self) -> None:
|
||||||
|
"""Сбросить все строки в пустое состояние."""
|
||||||
|
for widget in self.query(Static):
|
||||||
|
wid = widget.id or ""
|
||||||
|
if wid.startswith("result-status-"):
|
||||||
|
for cls in _STATUS_CLASSES:
|
||||||
|
widget.remove_class(cls)
|
||||||
|
widget.add_class("result-status-skip")
|
||||||
|
widget.update("")
|
||||||
|
elif wid.startswith("result-detail-"):
|
||||||
|
widget.update("")
|
||||||
|
|
||||||
|
# ── Internal ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _update(self, test_id: str, status: TestStatus, detail: str) -> None:
|
||||||
|
try:
|
||||||
|
status_w = self.query_one(f"#result-status-{test_id}", Static)
|
||||||
|
detail_w = self.query_one(f"#result-detail-{test_id}", Static)
|
||||||
|
except NoMatches:
|
||||||
|
return
|
||||||
|
|
||||||
|
for cls in _STATUS_CLASSES:
|
||||||
|
status_w.remove_class(cls)
|
||||||
|
|
||||||
|
text, css = _status_display(status)
|
||||||
|
status_w.update(text)
|
||||||
|
status_w.add_class(css)
|
||||||
|
detail_w.update(detail)
|
||||||
83
tools/production/app/screens/diag/test_list.py
Normal file
83
tools/production/app/screens/diag/test_list.py
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
"""
|
||||||
|
test_list.py — виджет списка тестов с чекбоксами.
|
||||||
|
|
||||||
|
Отображает тесты из list_tests, отмечает HIL-тесты,
|
||||||
|
серит недоступные (HIL без M5).
|
||||||
|
|
||||||
|
Публичный API:
|
||||||
|
TestListPanel.populate(tests, m5_connected) — заполнить список
|
||||||
|
TestListPanel.get_selected_ids() — список выбранных id
|
||||||
|
TestListPanel.set_enabled(enabled) — блокировать во время прогона
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.containers import Horizontal
|
||||||
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Checkbox, Label
|
||||||
|
|
||||||
|
|
||||||
|
class TestListPanel(Widget):
|
||||||
|
"""Левая колонка DiagScreen — список тестов с чекбоксами."""
|
||||||
|
|
||||||
|
DEFAULT_CSS = "" # стили в diag.tcss
|
||||||
|
|
||||||
|
def __init__(self, **kwargs) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
# test_id → Checkbox для быстрого доступа
|
||||||
|
self._checkboxes: dict[str, Checkbox] = {}
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Label("Тесты", classes="section-title")
|
||||||
|
|
||||||
|
# ── Public API ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def populate(self, tests: list, m5_connected: bool) -> None:
|
||||||
|
"""
|
||||||
|
Заполнить список тестами.
|
||||||
|
|
||||||
|
:param tests: list[TestInfo]
|
||||||
|
:param m5_connected: True если M5StampPLC подключён
|
||||||
|
"""
|
||||||
|
# Удалить старые строки (кроме заголовка)
|
||||||
|
for child in list(self.children):
|
||||||
|
if not child.has_class("section-title"):
|
||||||
|
child.remove()
|
||||||
|
self._checkboxes.clear()
|
||||||
|
|
||||||
|
for test in tests:
|
||||||
|
hil_unavailable = test.requires_hil and not m5_connected
|
||||||
|
|
||||||
|
cb = Checkbox(
|
||||||
|
test.name,
|
||||||
|
value=not hil_unavailable,
|
||||||
|
disabled=hil_unavailable,
|
||||||
|
id=f"cb-{test.id}",
|
||||||
|
classes="-textual-compact",
|
||||||
|
)
|
||||||
|
self._checkboxes[test.id] = cb
|
||||||
|
|
||||||
|
hil_css = (
|
||||||
|
"test-row-hil-badge hil-disabled"
|
||||||
|
if hil_unavailable
|
||||||
|
else "test-row-hil-badge"
|
||||||
|
)
|
||||||
|
badge_text = "[HIL]" if test.requires_hil else ""
|
||||||
|
|
||||||
|
row = Horizontal(classes="test-row")
|
||||||
|
self.mount(row)
|
||||||
|
row.mount(cb)
|
||||||
|
row.mount(Label(badge_text, classes=hil_css))
|
||||||
|
|
||||||
|
def get_selected_ids(self) -> list[str]:
|
||||||
|
"""Вернуть список id выбранных (checked + not disabled) тестов."""
|
||||||
|
return [
|
||||||
|
tid for tid, cb in self._checkboxes.items() if cb.value and not cb.disabled
|
||||||
|
]
|
||||||
|
|
||||||
|
def set_enabled(self, enabled: bool) -> None:
|
||||||
|
"""Разрешить/запретить изменение чекбоксов во время прогона."""
|
||||||
|
for cb in self._checkboxes.values():
|
||||||
|
if not cb.disabled: # не трогать серые HIL-без-M5
|
||||||
|
cb.disabled = not enabled
|
||||||
197
tools/production/app/screens/flash.py
Normal file
197
tools/production/app/screens/flash.py
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
"""
|
||||||
|
flash.py — экран прошивки (режим A).
|
||||||
|
|
||||||
|
Активен когда обнаружен BootROM SDP (1FC9:0130).
|
||||||
|
Поддерживает три варианта прошивки и chip erase.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from textual import on, work
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.binding import Binding
|
||||||
|
from textual.containers import Horizontal, Vertical
|
||||||
|
from textual.css.query import NoMatches
|
||||||
|
from textual.message import Message
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.widgets import (
|
||||||
|
Button,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
Log,
|
||||||
|
ProgressBar,
|
||||||
|
RadioButton,
|
||||||
|
RadioSet,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ..flasher import Flasher
|
||||||
|
from ..models import FlashProgress, FlashTarget
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FlashScreen(Screen):
|
||||||
|
"""
|
||||||
|
Экран прошивки.
|
||||||
|
|
||||||
|
Messages:
|
||||||
|
FlashDone(success) — прошивка завершена
|
||||||
|
"""
|
||||||
|
|
||||||
|
BINDINGS = [
|
||||||
|
Binding("escape", "go_back", "Назад"),
|
||||||
|
]
|
||||||
|
|
||||||
|
class FlashDone(Message):
|
||||||
|
def __init__(self, success: bool) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.success = success
|
||||||
|
|
||||||
|
def __init__(self, **kwargs) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self._flasher = Flasher()
|
||||||
|
self._flashing = False
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
with Vertical():
|
||||||
|
yield Label(
|
||||||
|
"⚡ Прошивка платы — BootROM SDP обнаружен",
|
||||||
|
id="flash-title",
|
||||||
|
)
|
||||||
|
|
||||||
|
with Vertical(id="flash-target-group"):
|
||||||
|
yield Label("Что прошить?", classes="section-title")
|
||||||
|
with RadioSet(id="flash-radio"):
|
||||||
|
yield RadioButton(
|
||||||
|
"firmware_test (диагностическая прошивка)",
|
||||||
|
id="radio-fw-test",
|
||||||
|
value=True,
|
||||||
|
)
|
||||||
|
yield RadioButton(
|
||||||
|
"Production (bootloader + tft_app)",
|
||||||
|
id="radio-production",
|
||||||
|
)
|
||||||
|
yield RadioButton(
|
||||||
|
"Кастомный бинарь...",
|
||||||
|
id="radio-custom",
|
||||||
|
)
|
||||||
|
with Horizontal(id="flash-custom-path", classes="hidden"):
|
||||||
|
yield Input(
|
||||||
|
placeholder="Путь к HAB-бинарю (.bin)",
|
||||||
|
id="flash-custom-input",
|
||||||
|
)
|
||||||
|
|
||||||
|
with Horizontal(id="flash-btn-row"):
|
||||||
|
yield Button(
|
||||||
|
"▶ Прошить",
|
||||||
|
id="flash-btn-flash",
|
||||||
|
variant="warning",
|
||||||
|
)
|
||||||
|
yield Button(
|
||||||
|
"⚠ Chip Erase",
|
||||||
|
id="flash-btn-erase",
|
||||||
|
variant="error",
|
||||||
|
)
|
||||||
|
|
||||||
|
yield ProgressBar(id="flash-progress-bar", show_eta=False)
|
||||||
|
yield Label("", id="flash-progress-label")
|
||||||
|
yield Log(id="flash-log", auto_scroll=True)
|
||||||
|
|
||||||
|
# ── Обработчики ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@on(RadioSet.Changed, "#flash-radio")
|
||||||
|
def _on_radio_changed(self, event: RadioSet.Changed) -> None:
|
||||||
|
is_custom = event.pressed.id == "radio-custom"
|
||||||
|
path_row = self.query_one("#flash-custom-path")
|
||||||
|
if is_custom:
|
||||||
|
path_row.remove_class("hidden")
|
||||||
|
else:
|
||||||
|
path_row.add_class("hidden")
|
||||||
|
|
||||||
|
@on(Button.Pressed, "#flash-btn-flash")
|
||||||
|
def _on_flash_pressed(self) -> None:
|
||||||
|
if self._flashing:
|
||||||
|
return
|
||||||
|
target, bin_path = self._resolve_target()
|
||||||
|
if target is None:
|
||||||
|
self._log("⚠ Укажите путь к бинарю")
|
||||||
|
return
|
||||||
|
self._do_flash(target, bin_path)
|
||||||
|
|
||||||
|
@on(Button.Pressed, "#flash-btn-erase")
|
||||||
|
def _on_erase_pressed(self) -> None:
|
||||||
|
if self._flashing:
|
||||||
|
return
|
||||||
|
self._do_erase()
|
||||||
|
|
||||||
|
def action_go_back(self) -> None:
|
||||||
|
if not self._flashing:
|
||||||
|
self.post_message(self.FlashDone(success=False))
|
||||||
|
|
||||||
|
# ── Workers ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@work(exclusive=True, thread=False)
|
||||||
|
async def _do_flash(self, target: FlashTarget, bin_path: Optional[Path]) -> None:
|
||||||
|
self._set_busy(True)
|
||||||
|
self._log(f"▶ Прошивка: {target.value}")
|
||||||
|
ok = await self._flasher.flash(
|
||||||
|
target=target,
|
||||||
|
bin_path=bin_path,
|
||||||
|
progress_cb=self._on_progress,
|
||||||
|
)
|
||||||
|
self._set_busy(False)
|
||||||
|
self._log("✅ Готово" if ok else "❌ Ошибка")
|
||||||
|
self.post_message(self.FlashDone(success=ok))
|
||||||
|
|
||||||
|
@work(exclusive=True, thread=False)
|
||||||
|
async def _do_erase(self) -> None:
|
||||||
|
self._set_busy(True)
|
||||||
|
self._log("⚠ Chip erase (~30 с)...")
|
||||||
|
ok = await self._flasher.erase_chip(progress_cb=self._on_progress)
|
||||||
|
self._set_busy(False)
|
||||||
|
self._log("✅ Chip erase завершён" if ok else "❌ Chip erase: ошибка")
|
||||||
|
|
||||||
|
# ── Вспомогательные ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _resolve_target(self) -> tuple[Optional[FlashTarget], Optional[Path]]:
|
||||||
|
radio = self.query_one("#flash-radio", RadioSet)
|
||||||
|
pressed_id = radio.pressed_button.id if radio.pressed_button else None
|
||||||
|
|
||||||
|
if pressed_id == "radio-fw-test":
|
||||||
|
return FlashTarget.FIRMWARE_TEST, None
|
||||||
|
if pressed_id == "radio-production":
|
||||||
|
return FlashTarget.PRODUCTION, None
|
||||||
|
if pressed_id == "radio-custom":
|
||||||
|
raw = self.query_one("#flash-custom-input", Input).value.strip()
|
||||||
|
if not raw:
|
||||||
|
return None, None
|
||||||
|
p = Path(raw)
|
||||||
|
if not p.exists():
|
||||||
|
self._log(f"⚠ Файл не найден: {p}")
|
||||||
|
return None, None
|
||||||
|
return FlashTarget.CUSTOM, p
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
async def _on_progress(self, progress: FlashProgress) -> None:
|
||||||
|
self.query_one("#flash-progress-bar", ProgressBar).update(
|
||||||
|
progress=progress.percent
|
||||||
|
)
|
||||||
|
self.query_one("#flash-progress-label", Label).update(
|
||||||
|
f"{progress.phase} {progress.percent}%"
|
||||||
|
)
|
||||||
|
self._log(progress.message)
|
||||||
|
|
||||||
|
def _set_busy(self, busy: bool) -> None:
|
||||||
|
self._flashing = busy
|
||||||
|
self.query_one("#flash-btn-flash", Button).disabled = busy
|
||||||
|
self.query_one("#flash-btn-erase", Button).disabled = busy
|
||||||
|
|
||||||
|
def _log(self, msg: str) -> None:
|
||||||
|
try:
|
||||||
|
self.query_one("#flash-log", Log).write_line(msg)
|
||||||
|
except NoMatches:
|
||||||
|
pass
|
||||||
83
tools/production/app/screens/waiting.py
Normal file
83
tools/production/app/screens/waiting.py
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
"""
|
||||||
|
waiting.py — экран ожидания подключения платы.
|
||||||
|
|
||||||
|
Опрашивает USB каждые _DETECT_INTERVAL_S секунд через Flasher.
|
||||||
|
При обнаружении SDP или CDC отправляет DeviceDetected message в App.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.css.query import NoMatches
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.timer import Timer
|
||||||
|
from textual.widgets import Static
|
||||||
|
from textual.message import Message
|
||||||
|
from ..flasher import Flasher
|
||||||
|
from ..models import AppMode
|
||||||
|
|
||||||
|
_DETECT_INTERVAL_S = 1.5
|
||||||
|
_SPIN_INTERVAL_S = 0.1
|
||||||
|
_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
||||||
|
|
||||||
|
|
||||||
|
class WaitingScreen(Screen):
|
||||||
|
"""
|
||||||
|
Экран ожидания.
|
||||||
|
|
||||||
|
Messages:
|
||||||
|
DeviceDetected(mode) — плата обнаружена, mode: FLASHING | DIAGNOSING
|
||||||
|
"""
|
||||||
|
|
||||||
|
class DeviceDetected(Message):
|
||||||
|
"""Плата обнаружена."""
|
||||||
|
|
||||||
|
def __init__(self, mode: AppMode) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.mode = mode
|
||||||
|
|
||||||
|
def __init__(self, **kwargs) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self._spinner_idx: int = 0
|
||||||
|
self._detect_timer: Timer | None = None
|
||||||
|
self._spin_timer: Timer | None = None
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Static("TFT Indicator Board\nService Tool", id="waiting-logo")
|
||||||
|
yield Static("Подключите плату к USB...", id="waiting-hint")
|
||||||
|
yield Static(_SPINNER_FRAMES[0], id="waiting-spinner")
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
self._detect_timer = self.set_interval(_DETECT_INTERVAL_S, self._poll_usb)
|
||||||
|
self._spin_timer = self.set_interval(_SPIN_INTERVAL_S, self._spin)
|
||||||
|
|
||||||
|
def on_unmount(self) -> None:
|
||||||
|
if self._detect_timer:
|
||||||
|
self._detect_timer.stop()
|
||||||
|
if self._spin_timer:
|
||||||
|
self._spin_timer.stop()
|
||||||
|
|
||||||
|
# ── Internal ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _spin(self) -> None:
|
||||||
|
self._spinner_idx = (self._spinner_idx + 1) % len(_SPINNER_FRAMES)
|
||||||
|
try:
|
||||||
|
self.query_one("#waiting-spinner", Static).update(
|
||||||
|
_SPINNER_FRAMES[self._spinner_idx]
|
||||||
|
)
|
||||||
|
except NoMatches:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _poll_usb(self) -> None:
|
||||||
|
if Flasher.detect_sdp():
|
||||||
|
self._stop_timers()
|
||||||
|
self.post_message(self.DeviceDetected(AppMode.FLASHING))
|
||||||
|
elif Flasher.detect_cdc():
|
||||||
|
self._stop_timers()
|
||||||
|
self.post_message(self.DeviceDetected(AppMode.DIAGNOSING))
|
||||||
|
|
||||||
|
def _stop_timers(self) -> None:
|
||||||
|
if self._detect_timer:
|
||||||
|
self._detect_timer.stop()
|
||||||
|
if self._spin_timer:
|
||||||
|
self._spin_timer.stop()
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""main.py — точка входа service-tui."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
_ENV_FILE = _REPO_ROOT / ".env"
|
||||||
|
|
||||||
|
try:
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
if _ENV_FILE.exists():
|
||||||
|
load_dotenv(_ENV_FILE)
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _setup_logging() -> None:
|
||||||
|
log_dir = Path(os.environ.get("SERVICE_LOG_DIR", str(Path(__file__).parent)))
|
||||||
|
log_file = log_dir / "service_tui.log"
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format="%(asctime)s %(levelname)-8s %(name)s %(message)s",
|
||||||
|
handlers=[logging.FileHandler(log_file, encoding="utf-8")],
|
||||||
|
)
|
||||||
|
logging.getLogger("textual").setLevel(logging.WARNING)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
_setup_logging()
|
||||||
|
from app.app import ServiceApp
|
||||||
|
|
||||||
|
ServiceApp().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main() or 0)
|
||||||
|
|
@ -8,6 +8,7 @@ dependencies = [
|
||||||
"pyserial>=3.5",
|
"pyserial>=3.5",
|
||||||
"python-dotenv>=1.0.0",
|
"python-dotenv>=1.0.0",
|
||||||
"pyinstaller>=6.0.0",
|
"pyinstaller>=6.0.0",
|
||||||
|
"pyusb>=1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|
|
||||||
662
tools/production/service_tui.log
Normal file
662
tools/production/service_tui.log
Normal file
|
|
@ -0,0 +1,662 @@
|
||||||
|
2026-06-29 15:20:18,732 DEBUG asyncio Using selector: EpollSelector
|
||||||
|
2026-06-29 18:21:10,465 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:21:14,987 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:21:53,409 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:22:16,437 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:28:38,572 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:36:00,529 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:36:17,980 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --erase-chip
|
||||||
|
2026-06-29 18:36:18,002 DEBUG app.flasher flash_usb [erase]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 18:36:18,002 DEBUG app.flasher flash_usb [erase]: warning: Ignoring existing virtual environment linked to non-existent Python interpreter: .venv/bin/python3 -> python
|
||||||
|
2026-06-29 18:36:18,046 DEBUG app.flasher flash_usb [erase]: Using CPython 3.10.11 interpreter at: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10
|
||||||
|
2026-06-29 18:36:18,224 DEBUG app.flasher flash_usb [erase]: Removed virtual environment at: .venv
|
||||||
|
2026-06-29 18:36:18,224 DEBUG app.flasher flash_usb [erase]: Creating virtual environment at: .venv
|
||||||
|
2026-06-29 18:36:18,344 DEBUG app.flasher flash_usb [erase]: Installed 66 packages in 93ms
|
||||||
|
2026-06-29 18:36:23,001 DEBUG app.flasher flash_usb [erase]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 18:36:23,001 DEBUG app.flasher flash_usb [erase]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 18:36:23,177 DEBUG app.flasher flash_usb [erase]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]: Операция: chip erase
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:36:23,197 DEBUG app.flasher flash_usb [erase]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 18:36:23,198 DEBUG app.flasher flash_usb [erase]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 18:36:23,198 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 18:36:24,586 DEBUG app.flasher flash_usb [erase]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:36:24,760 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:36:59,315 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:36:59,571 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:36:59,591 DEBUG app.flasher flash_usb [erase]: OK (1с)
|
||||||
|
2026-06-29 18:36:59,591 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 18:36:59,591 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:36:59,591 DEBUG app.flasher flash_usb [erase]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 18:36:59,591 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:36:59,591 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 18:36:59,591 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: Полная очистка Flash (chip erase, ~30 с)
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: ⚠️ После chip erase BootROM не сможет загрузить прошивку.
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: ⚠️ Используй flash_usb.py для восстановления.
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: $ blhost -t 100000 -u 0x15A2,0x73 -- flash-erase-all 9
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: Reset
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 18:36:59,592 DEBUG app.flasher flash_usb [erase]: ✅ Chip erase завершён
|
||||||
|
2026-06-29 18:37:20,822 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --firmware firmware_test --build-type Release
|
||||||
|
2026-06-29 18:37:20,836 DEBUG app.flasher flash_usb [firmware_test]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 18:37:21,381 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 18:37:21,381 DEBUG app.flasher flash_usb [firmware_test]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 18:37:21,559 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: Прошивка: firmware_test [Release]
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Release/firmware_test_hab.bin
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:21,582 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 18:37:21,583 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 18:37:21,583 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:22,972 DEBUG app.flasher flash_usb [firmware_test]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:23,148 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:23,979 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:24,183 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:24,357 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:24,534 DEBUG app.flasher flash_usb [firmware_test]: Writing memory
|
||||||
|
2026-06-29 18:37:24,594 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:24,594 DEBUG app.flasher flash_usb [firmware_test]: Response word 1 = 22616 (0x5858)
|
||||||
|
2026-06-29 18:37:24,779 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:24,798 DEBUG app.flasher flash_usb [firmware_test]: OK (1с)
|
||||||
|
2026-06-29 18:37:24,798 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:24,798 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:24,798 DEBUG app.flasher flash_usb [firmware_test]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 18:37:24,798 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:24,798 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: Прошивка Flash: firmware_test_hab.bin
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Release/firmware_test_hab.bin
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: Размер: 22616 байт
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: Адрес: 0x60001000
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: Стирание: 0x60000000 .. +28672 байт
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- flash-erase-region 0x60000000 28672 0
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: Запись FCB в Flash[0x60000000]
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xF000000F word
|
||||||
|
2026-06-29 18:37:24,799 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 18:37:24,800 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- write-memory 0x60001000 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Release/firmware_test_hab.bin 0
|
||||||
|
2026-06-29 18:37:24,800 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:24,800 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:24,800 DEBUG app.flasher flash_usb [firmware_test]: Reset
|
||||||
|
2026-06-29 18:37:24,800 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:24,800 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 18:37:24,800 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:24,800 DEBUG app.flasher flash_usb [firmware_test]: ✅ Прошивка завершена успешно
|
||||||
|
2026-06-29 18:37:46,867 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --firmware firmware_test --build-type Release
|
||||||
|
2026-06-29 18:37:46,883 DEBUG app.flasher flash_usb [firmware_test]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 18:37:47,377 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 18:37:47,377 DEBUG app.flasher flash_usb [firmware_test]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 18:37:47,548 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: Прошивка: firmware_test [Release]
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Release/firmware_test_hab.bin
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:47,568 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 18:37:47,569 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 18:37:47,569 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:48,972 DEBUG app.flasher flash_usb [firmware_test]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:49,154 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:49,987 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:50,206 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:50,386 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:50,560 DEBUG app.flasher flash_usb [firmware_test]: Writing memory
|
||||||
|
2026-06-29 18:37:50,621 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:50,621 DEBUG app.flasher flash_usb [firmware_test]: Response word 1 = 22616 (0x5858)
|
||||||
|
2026-06-29 18:37:50,800 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:37:50,818 DEBUG app.flasher flash_usb [firmware_test]: OK (1с)
|
||||||
|
2026-06-29 18:37:50,818 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:50,818 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:50,818 DEBUG app.flasher flash_usb [firmware_test]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 18:37:50,818 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:50,818 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: Прошивка Flash: firmware_test_hab.bin
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Release/firmware_test_hab.bin
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: Размер: 22616 байт
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: Адрес: 0x60001000
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: Стирание: 0x60000000 .. +28672 байт
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- flash-erase-region 0x60000000 28672 0
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: Запись FCB в Flash[0x60000000]
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xF000000F word
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- write-memory 0x60001000 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Release/firmware_test_hab.bin 0
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:50,819 DEBUG app.flasher flash_usb [firmware_test]: Reset
|
||||||
|
2026-06-29 18:37:50,820 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:37:50,820 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 18:37:50,820 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:37:50,820 DEBUG app.flasher flash_usb [firmware_test]: ✅ Прошивка завершена успешно
|
||||||
|
2026-06-29 18:38:46,907 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:40:26,915 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:40:28,552 INFO app.m5_client M5StampPLC не найден
|
||||||
|
2026-06-29 18:41:26,213 ERROR app.orchestrator HIL confirm без M5: opto_in1_active
|
||||||
|
2026-06-29 18:44:38,127 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:44:39,837 INFO app.m5_client M5StampPLC не найден
|
||||||
|
2026-06-29 18:45:28,221 ERROR app.orchestrator HIL confirm без M5: opto_in1_active
|
||||||
|
2026-06-29 18:46:57,397 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:46:58,957 INFO app.m5_client M5StampPLC не найден
|
||||||
|
2026-06-29 18:50:48,270 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:51:01,877 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --firmware firmware_test --build-type Release
|
||||||
|
2026-06-29 18:51:01,900 DEBUG app.flasher flash_usb [firmware_test]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 18:51:01,900 DEBUG app.flasher flash_usb [firmware_test]: warning: Ignoring existing virtual environment linked to non-existent Python interpreter: .venv/bin/python3 -> python
|
||||||
|
2026-06-29 18:51:01,900 DEBUG app.flasher flash_usb [firmware_test]: Using CPython 3.10.11 interpreter at: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10
|
||||||
|
2026-06-29 18:51:02,032 DEBUG app.flasher flash_usb [firmware_test]: Removed virtual environment at: .venv
|
||||||
|
2026-06-29 18:51:02,032 DEBUG app.flasher flash_usb [firmware_test]: Creating virtual environment at: .venv
|
||||||
|
2026-06-29 18:51:02,130 DEBUG app.flasher flash_usb [firmware_test]: Installed 66 packages in 75ms
|
||||||
|
2026-06-29 18:51:06,727 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 18:51:06,727 DEBUG app.flasher flash_usb [firmware_test]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 18:51:06,903 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 18:51:06,923 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:51:06,923 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 18:51:06,923 DEBUG app.flasher flash_usb [firmware_test]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 18:51:06,923 DEBUG app.flasher flash_usb [firmware_test]: Прошивка: firmware_test [Release]
|
||||||
|
2026-06-29 18:51:06,923 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Release/firmware_test_hab.bin
|
||||||
|
2026-06-29 18:51:06,923 DEBUG app.flasher flash_usb [firmware_test]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 18:51:06,923 DEBUG app.flasher flash_usb [firmware_test]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 18:51:06,923 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 18:51:06,923 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:51:06,924 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:51:06,924 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:51:06,924 DEBUG app.flasher flash_usb [firmware_test]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 18:51:06,924 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:51:06,924 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 18:51:06,924 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 18:51:06,924 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:51:08,331 DEBUG app.flasher flash_usb [firmware_test]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:51:08,506 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:51:09,215 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:51:09,429 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:51:09,621 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:51:09,804 DEBUG app.flasher flash_usb [firmware_test]: Writing memory
|
||||||
|
2026-06-29 18:51:09,985 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:51:09,985 DEBUG app.flasher flash_usb [firmware_test]: Response word 1 = 72116 (0x119b4)
|
||||||
|
2026-06-29 18:51:10,173 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 18:51:10,191 DEBUG app.flasher flash_usb [firmware_test]: OK (1с)
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: Прошивка Flash: firmware_test_hab.bin
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Release/firmware_test_hab.bin
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: Размер: 72116 байт
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: Адрес: 0x60001000
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: Стирание: 0x60000000 .. +77824 байт
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- flash-erase-region 0x60000000 77824 0
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: Запись FCB в Flash[0x60000000]
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:51:10,192 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xF000000F word
|
||||||
|
2026-06-29 18:51:10,193 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 18:51:10,193 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- write-memory 0x60001000 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Release/firmware_test_hab.bin 0
|
||||||
|
2026-06-29 18:51:10,193 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:51:10,193 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:51:10,193 DEBUG app.flasher flash_usb [firmware_test]: Reset
|
||||||
|
2026-06-29 18:51:10,193 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 18:51:10,193 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 18:51:10,193 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 18:51:10,193 DEBUG app.flasher flash_usb [firmware_test]: ✅ Прошивка завершена успешно
|
||||||
|
2026-06-29 18:51:36,969 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:57:30,698 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 18:57:32,409 INFO app.m5_client M5StampPLC не найден
|
||||||
|
2026-06-29 19:00:37,411 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 19:00:38,953 INFO app.m5_client M5StampPLC не найден
|
||||||
|
2026-06-29 19:04:04,441 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 19:04:12,903 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --erase-chip
|
||||||
|
2026-06-29 19:04:12,918 DEBUG app.flasher flash_usb [erase]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 19:04:12,919 DEBUG app.flasher flash_usb [erase]: warning: Ignoring existing virtual environment linked to non-existent Python interpreter: .venv/bin/python3 -> python
|
||||||
|
2026-06-29 19:04:12,921 DEBUG app.flasher flash_usb [erase]: Using CPython 3.10.11 interpreter at: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10
|
||||||
|
2026-06-29 19:04:13,106 DEBUG app.flasher flash_usb [erase]: Removed virtual environment at: .venv
|
||||||
|
2026-06-29 19:04:13,106 DEBUG app.flasher flash_usb [erase]: Creating virtual environment at: .venv
|
||||||
|
2026-06-29 19:04:13,225 DEBUG app.flasher flash_usb [erase]: Installed 66 packages in 93ms
|
||||||
|
2026-06-29 19:04:17,826 DEBUG app.flasher flash_usb [erase]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:04:17,826 DEBUG app.flasher flash_usb [erase]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 19:04:18,001 DEBUG app.flasher flash_usb [erase]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:04:18,019 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:04:18,019 DEBUG app.flasher flash_usb [erase]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]: Операция: chip erase
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 19:04:18,020 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:04:19,412 DEBUG app.flasher flash_usb [erase]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:04:19,586 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:04:54,910 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:04:55,150 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:04:55,168 DEBUG app.flasher flash_usb [erase]: OK (1с)
|
||||||
|
2026-06-29 19:04:55,168 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:04:55,168 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:04:55,168 DEBUG app.flasher flash_usb [erase]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 19:04:55,168 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:04:55,168 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 19:04:55,169 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:04:55,169 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:04:55,169 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:04:55,169 DEBUG app.flasher flash_usb [erase]: Полная очистка Flash (chip erase, ~30 с)
|
||||||
|
2026-06-29 19:04:55,169 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:04:55,169 DEBUG app.flasher flash_usb [erase]: ⚠️ После chip erase BootROM не сможет загрузить прошивку.
|
||||||
|
2026-06-29 19:04:55,169 DEBUG app.flasher flash_usb [erase]: ⚠️ Используй flash_usb.py для восстановления.
|
||||||
|
2026-06-29 19:04:55,170 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:04:55,170 DEBUG app.flasher flash_usb [erase]: $ blhost -t 100000 -u 0x15A2,0x73 -- flash-erase-all 9
|
||||||
|
2026-06-29 19:04:55,170 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:04:55,170 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:04:55,170 DEBUG app.flasher flash_usb [erase]: Reset
|
||||||
|
2026-06-29 19:04:55,170 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:04:55,170 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 19:04:55,170 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:04:55,170 DEBUG app.flasher flash_usb [erase]: ✅ Chip erase завершён
|
||||||
|
2026-06-29 19:04:58,515 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --firmware firmware_test --build-type Debug
|
||||||
|
2026-06-29 19:04:58,542 DEBUG app.flasher flash_usb [firmware_test]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 19:04:58,955 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:04:58,955 DEBUG app.flasher flash_usb [firmware_test]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 19:04:59,131 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:04:59,150 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: Прошивка: firmware_test [Debug]
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 19:04:59,151 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:05:00,537 DEBUG app.flasher flash_usb [firmware_test]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:05:00,713 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:05:01,631 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:05:01,832 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:05:02,011 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:05:02,191 DEBUG app.flasher flash_usb [firmware_test]: Writing memory
|
||||||
|
2026-06-29 19:05:02,527 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:05:02,527 DEBUG app.flasher flash_usb [firmware_test]: Response word 1 = 135240 (0x21048)
|
||||||
|
2026-06-29 19:05:02,734 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:05:02,752 DEBUG app.flasher flash_usb [firmware_test]: OK (1с)
|
||||||
|
2026-06-29 19:05:02,752 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:05:02,752 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:05:02,752 DEBUG app.flasher flash_usb [firmware_test]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 19:05:02,752 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:05:02,752 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 19:05:02,752 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: Прошивка Flash: firmware_test_hab.bin
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: Размер: 135240 байт
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: Адрес: 0x60001000
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: Стирание: 0x60000000 .. +143360 байт
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- flash-erase-region 0x60000000 143360 0
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: Запись FCB в Flash[0x60000000]
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xF000000F word
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- write-memory 0x60001000 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin 0
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: Reset
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 19:05:02,753 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:05:02,754 DEBUG app.flasher flash_usb [firmware_test]: ✅ Прошивка завершена успешно
|
||||||
|
2026-06-29 19:05:23,969 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 19:05:25,545 INFO app.m5_client M5StampPLC не найден
|
||||||
|
2026-06-29 19:07:57,549 WARNING app.firmware_client _recv_until timeout after 120.0 s
|
||||||
|
2026-06-29 19:17:54,908 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 19:18:31,735 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --erase-chip
|
||||||
|
2026-06-29 19:18:31,752 DEBUG app.flasher flash_usb [erase]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 19:18:31,752 DEBUG app.flasher flash_usb [erase]: warning: Ignoring existing virtual environment linked to non-existent Python interpreter: .venv/bin/python3 -> python
|
||||||
|
2026-06-29 19:18:31,755 DEBUG app.flasher flash_usb [erase]: Using CPython 3.10.11 interpreter at: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10
|
||||||
|
2026-06-29 19:18:31,934 DEBUG app.flasher flash_usb [erase]: Removed virtual environment at: .venv
|
||||||
|
2026-06-29 19:18:31,935 DEBUG app.flasher flash_usb [erase]: Creating virtual environment at: .venv
|
||||||
|
2026-06-29 19:18:32,066 DEBUG app.flasher flash_usb [erase]: Installed 66 packages in 98ms
|
||||||
|
2026-06-29 19:18:36,600 DEBUG app.flasher flash_usb [erase]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:18:36,600 DEBUG app.flasher flash_usb [erase]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 19:18:36,776 DEBUG app.flasher flash_usb [erase]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]: Операция: chip erase
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 19:18:36,796 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:18:36,797 DEBUG app.flasher flash_usb [erase]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 19:18:36,797 DEBUG app.flasher flash_usb [erase]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 19:18:36,797 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:18:38,238 DEBUG app.flasher flash_usb [erase]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:18:38,414 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:19:13,145 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:19:13,403 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:19:13,422 DEBUG app.flasher flash_usb [erase]: OK (1с)
|
||||||
|
2026-06-29 19:19:13,422 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:19:13,422 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:19:13,422 DEBUG app.flasher flash_usb [erase]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 19:19:13,422 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:19:13,422 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 19:19:13,423 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:19:13,423 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:19:13,423 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:19:13,423 DEBUG app.flasher flash_usb [erase]: Полная очистка Flash (chip erase, ~30 с)
|
||||||
|
2026-06-29 19:19:13,423 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:19:13,423 DEBUG app.flasher flash_usb [erase]: ⚠️ После chip erase BootROM не сможет загрузить прошивку.
|
||||||
|
2026-06-29 19:19:13,424 DEBUG app.flasher flash_usb [erase]: ⚠️ Используй flash_usb.py для восстановления.
|
||||||
|
2026-06-29 19:19:13,424 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:19:13,424 DEBUG app.flasher flash_usb [erase]: $ blhost -t 100000 -u 0x15A2,0x73 -- flash-erase-all 9
|
||||||
|
2026-06-29 19:19:13,424 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:19:13,424 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:19:13,424 DEBUG app.flasher flash_usb [erase]: Reset
|
||||||
|
2026-06-29 19:19:13,424 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:19:13,424 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 19:19:13,424 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:19:13,424 DEBUG app.flasher flash_usb [erase]: ✅ Chip erase завершён
|
||||||
|
2026-06-29 19:20:01,708 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --firmware firmware_test --build-type Debug
|
||||||
|
2026-06-29 19:20:01,724 DEBUG app.flasher flash_usb [firmware_test]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 19:20:02,300 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:20:02,300 DEBUG app.flasher flash_usb [firmware_test]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 19:20:02,473 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: Прошивка: firmware_test [Debug]
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 19:20:02,493 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:20:03,887 DEBUG app.flasher flash_usb [firmware_test]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:20:04,068 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:20:04,968 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:20:05,182 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:20:05,355 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:20:05,534 DEBUG app.flasher flash_usb [firmware_test]: Writing memory
|
||||||
|
2026-06-29 19:20:05,873 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:20:05,873 DEBUG app.flasher flash_usb [firmware_test]: Response word 1 = 135240 (0x21048)
|
||||||
|
2026-06-29 19:20:06,062 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: OK (1с)
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: Прошивка Flash: firmware_test_hab.bin
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: Размер: 135240 байт
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: Адрес: 0x60001000
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: Стирание: 0x60000000 .. +143360 байт
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- flash-erase-region 0x60000000 143360 0
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: Запись FCB в Flash[0x60000000]
|
||||||
|
2026-06-29 19:20:06,081 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:20:06,082 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xF000000F word
|
||||||
|
2026-06-29 19:20:06,082 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:20:06,082 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- write-memory 0x60001000 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin 0
|
||||||
|
2026-06-29 19:20:06,082 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:20:06,082 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:20:06,082 DEBUG app.flasher flash_usb [firmware_test]: Reset
|
||||||
|
2026-06-29 19:20:06,082 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:20:06,082 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 19:20:06,082 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:20:06,082 DEBUG app.flasher flash_usb [firmware_test]: ✅ Прошивка завершена успешно
|
||||||
|
2026-06-29 19:20:59,572 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --firmware firmware_test --build-type Debug
|
||||||
|
2026-06-29 19:20:59,595 DEBUG app.flasher flash_usb [firmware_test]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 19:21:00,152 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:21:00,152 DEBUG app.flasher flash_usb [firmware_test]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 19:21:00,330 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:21:00,350 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:21:00,350 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:21:00,350 DEBUG app.flasher flash_usb [firmware_test]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 19:21:00,350 DEBUG app.flasher flash_usb [firmware_test]: Прошивка: firmware_test [Debug]
|
||||||
|
2026-06-29 19:21:00,350 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin
|
||||||
|
2026-06-29 19:21:00,350 DEBUG app.flasher flash_usb [firmware_test]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 19:21:00,351 DEBUG app.flasher flash_usb [firmware_test]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 19:21:00,351 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:21:00,351 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:21:00,351 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:21:00,351 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:00,351 DEBUG app.flasher flash_usb [firmware_test]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 19:21:00,351 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:00,351 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 19:21:00,351 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 19:21:00,351 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:21:01,734 DEBUG app.flasher flash_usb [firmware_test]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:01,904 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:02,832 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:03,049 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:03,224 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:03,447 DEBUG app.flasher flash_usb [firmware_test]: Writing memory
|
||||||
|
2026-06-29 19:21:03,786 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:03,787 DEBUG app.flasher flash_usb [firmware_test]: Response word 1 = 135240 (0x21048)
|
||||||
|
2026-06-29 19:21:04,008 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:04,028 DEBUG app.flasher flash_usb [firmware_test]: OK (1с)
|
||||||
|
2026-06-29 19:21:04,028 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:21:04,028 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:04,028 DEBUG app.flasher flash_usb [firmware_test]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 19:21:04,028 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:04,028 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: Прошивка Flash: firmware_test_hab.bin
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: Размер: 135240 байт
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: Адрес: 0x60001000
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: Стирание: 0x60000000 .. +143360 байт
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- flash-erase-region 0x60000000 143360 0
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: Запись FCB в Flash[0x60000000]
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xF000000F word
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- write-memory 0x60001000 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin 0
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: Reset
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:21:04,029 DEBUG app.flasher flash_usb [firmware_test]: ✅ Прошивка завершена успешно
|
||||||
|
2026-06-29 19:21:20,423 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --erase-chip
|
||||||
|
2026-06-29 19:21:20,437 DEBUG app.flasher flash_usb [erase]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 19:21:20,936 DEBUG app.flasher flash_usb [erase]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:21:20,936 DEBUG app.flasher flash_usb [erase]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 19:21:21,112 DEBUG app.flasher flash_usb [erase]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]: Операция: chip erase
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:21,133 DEBUG app.flasher flash_usb [erase]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 19:21:21,134 DEBUG app.flasher flash_usb [erase]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 19:21:21,134 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:21:22,510 DEBUG app.flasher flash_usb [erase]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:22,685 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:57,783 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:58,044 DEBUG app.flasher flash_usb [erase]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: OK (1с)
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: Полная очистка Flash (chip erase, ~30 с)
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: ⚠️ После chip erase BootROM не сможет загрузить прошивку.
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: ⚠️ Используй flash_usb.py для восстановления.
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:21:58,064 DEBUG app.flasher flash_usb [erase]: $ blhost -t 100000 -u 0x15A2,0x73 -- flash-erase-all 9
|
||||||
|
2026-06-29 19:21:58,065 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:21:58,065 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:58,065 DEBUG app.flasher flash_usb [erase]: Reset
|
||||||
|
2026-06-29 19:21:58,065 DEBUG app.flasher flash_usb [erase]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:21:58,065 DEBUG app.flasher flash_usb [erase]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 19:21:58,065 DEBUG app.flasher flash_usb [erase]:
|
||||||
|
2026-06-29 19:21:58,065 DEBUG app.flasher flash_usb [erase]: ✅ Chip erase завершён
|
||||||
|
2026-06-29 19:31:03,319 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 19:31:12,383 INFO app.flasher Running: uv run --directory /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host python /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/flash_usb.py --firmware firmware_test --build-type Debug
|
||||||
|
2026-06-29 19:31:12,399 DEBUG app.flasher flash_usb [firmware_test]: warning: `VIRTUAL_ENV=/Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/production/.venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
|
||||||
|
2026-06-29 19:31:12,928 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:31:12,928 DEBUG app.flasher flash_usb [firmware_test]: Response status = 2290649224 (0x88888888) Write File Success.
|
||||||
|
2026-06-29 19:31:13,096 DEBUG app.flasher flash_usb [firmware_test]: Status (HAB mode) = 1450735702 (0x56787856) Hab Is Disabled (Unlocked).
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: MIMXRT1052 Flash Tool (USB SDP)
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: Прошивка: firmware_test [Debug]
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: SDP USB: 0x1FC9,0x130
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: BL USB: 0x15A2,0x73
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: ════════════════════════════════════════════════════════════
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: Загрузка Flashloader через SDP (1FC9:0130)
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:31:13,117 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- write-file 0x20001C00 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/host/dcd/ivt_flashloader.bin
|
||||||
|
2026-06-29 19:31:13,118 DEBUG app.flasher flash_usb [firmware_test]: $ sdphost -u 0x1FC9,0x130 -j -- jump-address 0x20001C00
|
||||||
|
2026-06-29 19:31:13,118 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:31:14,509 DEBUG app.flasher flash_usb [firmware_test]: Ожидание Flashloader (до 10с)...Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:31:14,679 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:31:15,624 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:31:15,830 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:31:16,009 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:31:16,183 DEBUG app.flasher flash_usb [firmware_test]: Writing memory
|
||||||
|
2026-06-29 19:31:16,518 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:31:16,518 DEBUG app.flasher flash_usb [firmware_test]: Response word 1 = 135240 (0x21048)
|
||||||
|
2026-06-29 19:31:16,724 DEBUG app.flasher flash_usb [firmware_test]: Response status = 0 (0x0) Success.
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: OK (1с)
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: Конфигурация FlexSPI NOR (инициализация контроллера)
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xC0000007 word
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: Прошивка Flash: firmware_test_hab.bin
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: Образ: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: Размер: 135240 байт
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: Адрес: 0x60001000
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: Стирание: 0x60000000 .. +143360 байт
|
||||||
|
2026-06-29 19:31:16,744 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- flash-erase-region 0x60000000 143360 0
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: Запись FCB в Flash[0x60000000]
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- fill-memory 0x2000 4 0xF000000F word
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- configure-memory 9 0x2000
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- write-memory 0x60001000 /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin 0
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: Reset
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: ────────────────────────────────────────────────────────────
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: $ blhost -u 0x15A2,0x73 -- reset
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]:
|
||||||
|
2026-06-29 19:31:16,745 DEBUG app.flasher flash_usb [firmware_test]: ✅ Прошивка завершена успешно
|
||||||
|
2026-06-29 19:31:45,595 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 19:31:54,684 INFO app.m5_client M5StampPLC не найден
|
||||||
|
2026-06-29 19:31:57,741 WARNING app.firmware_client _recv_until timeout after 3.0 s
|
||||||
|
2026-06-29 19:34:42,219 ERROR app.orchestrator HIL confirm без M5: opto_in1_active
|
||||||
|
2026-06-29 19:36:35,663 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 19:36:37,208 INFO app.m5_client M5StampPLC не найден
|
||||||
|
2026-06-29 19:36:40,255 WARNING app.firmware_client _recv_until timeout after 3.0 s
|
||||||
|
2026-06-29 19:36:47,180 DEBUG asyncio Using selector: KqueueSelector
|
||||||
|
2026-06-29 19:41:53,210 INFO app.m5_client M5StampPLC не найден
|
||||||
|
2026-06-29 19:41:56,267 WARNING app.firmware_client _recv_until timeout after 3.0 s
|
||||||
264
tools/production/uv.lock
Normal file
264
tools/production/uv.lock
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "altgraph"
|
||||||
|
version = "0.17.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/7e/f8/97fdf103f38fed6792a1601dbc16cc8aac56e7459a9fff08c812d8ae177a/altgraph-0.17.5.tar.gz", hash = "sha256:c87b395dd12fabde9c99573a9749d67da8d29ef9de0125c7f536699b4a9bc9e7", size = 48428, upload-time = "2025-11-21T20:35:50.583Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/ba/000a1996d4308bc65120167c21241a3b205464a2e0b58deda26ae8ac21d1/altgraph-0.17.5-py2.py3-none-any.whl", hash = "sha256:f3a22400bce1b0c701683820ac4f3b159cd301acab067c51c653e06961600597", size = 21228, upload-time = "2025-11-21T20:35:49.444Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linkify-it-py"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "uc-micro-py" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/2e/c9/06ea13676ef354f0af6169587ae292d3e2406e212876a413bf9eece4eb23/linkify_it_py-2.1.0.tar.gz", hash = "sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b", size = 29158, upload-time = "2026-03-01T07:48:47.683Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b4/de/88b3be5c31b22333b3ca2f6ff1de4e863d8fe45aaea7485f591970ec1d3e/linkify_it_py-2.1.0-py3-none-any.whl", hash = "sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e", size = 19878, upload-time = "2026-03-01T07:48:46.098Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "macholib"
|
||||||
|
version = "1.16.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "altgraph" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/10/2f/97589876ea967487978071c9042518d28b958d87b17dceb7cdc1d881f963/macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362", size = 59427, upload-time = "2025-11-22T08:28:38.373Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/d1/a9f36f8ecdf0fb7c9b1e78c8d7af12b8c8754e74851ac7b94a8305540fc7/macholib-1.16.4-py2.py3-none-any.whl", hash = "sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea", size = 38117, upload-time = "2025-11-22T08:28:36.939Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markdown-it-py"
|
||||||
|
version = "4.2.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "mdurl" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.optional-dependencies]
|
||||||
|
linkify = [
|
||||||
|
{ name = "linkify-it-py" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mdit-py-plugins"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "markdown-it-py" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/69/6da5581c6a7fede7dc261bf4e67d6adca4196f176b43288b55b3db395b6e/mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d", size = 66663, upload-time = "2026-05-13T09:03:37.76Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mdurl"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "26.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pefile"
|
||||||
|
version = "2024.8.26"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/03/4f/2750f7f6f025a1507cd3b7218691671eecfd0bbebebe8b39aa0fe1d360b8/pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632", size = 76008, upload-time = "2024-08-26T20:58:38.155Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/16/12b82f791c7f50ddec566873d5bdd245baa1491bac11d15ffb98aecc8f8b/pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f", size = 74766, upload-time = "2024-08-26T21:01:02.632Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platformdirs"
|
||||||
|
version = "4.10.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pygments"
|
||||||
|
version = "2.20.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyinstaller"
|
||||||
|
version = "6.21.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "altgraph" },
|
||||||
|
{ name = "macholib", marker = "sys_platform == 'darwin'" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pefile", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "pyinstaller-hooks-contrib" },
|
||||||
|
{ name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "setuptools" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d5/4d/ec706c3fcf39e26888c35b39615ff4d5865d184069666c47492cff1fbe50/pyinstaller-6.21.0.tar.gz", hash = "sha256:bb9fab705983e393a2d1cac77d6972513057ad800215fd861dc15ff5272e98fd", size = 4061519, upload-time = "2026-06-13T14:15:06.25Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/4a/53cf98bf66daed012dc9cd78c8203f19a675d696f2fc12afcf8c5049a0e0/pyinstaller-6.21.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:327d132389f37912609e01be62810cf96b5aa95b613903e4b8692e0d12fb0eda", size = 1052350, upload-time = "2026-06-13T14:13:55.88Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/83/b591295c352ef464c50b4c6ffff1c4f771d875c9e833f578d1b9f564f6b3/pyinstaller-6.21.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7071d4b094d5b40deeef5fa3d3b98a1b846087f7562b49209663d5f9281fe251", size = 748477, upload-time = "2026-06-13T14:14:00.327Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/8f/88fff4e403873b1e22286911350e75ff00db014aa08e57045da9d4328993/pyinstaller-6.21.0-py3-none-manylinux2014_i686.whl", hash = "sha256:6b6374d652107dd4a2eeece903ff82bb4045bb5e1006c5a158a6dcdbefe84bf2", size = 760877, upload-time = "2026-06-13T14:14:04.836Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8a/13/f0e48fbdfd1d05d948157121cea8b1b823dcb89efe6934b71fdd8bdb3f0f/pyinstaller-6.21.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:4e3108b3f02384560da70e39b8bf22b0ad597d02bd68a40d76ea91c1cfa00cad", size = 759194, upload-time = "2026-06-13T14:14:10.61Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dd/d5/ea7878cf9924ed30d946d8288777424e6d069d94f5bde56b4d0890069664/pyinstaller-6.21.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:697532279f535ad572bda613db4f821540e235c7854ca6da4d3bf0373f4415ee", size = 754979, upload-time = "2026-06-13T14:14:15.226Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9f/09/51b8905714b733bac66dbc041a7821372d70d888d273ae474c4037d4202d/pyinstaller-6.21.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:605169523a6b5ace39f13dfbff21add9f2bc43df99c7daf9394fefb2c45e8b6f", size = 754812, upload-time = "2026-06-13T14:14:20.264Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/43/d77779439d8c6c2e27a77bcfbd1d5cc0f568ebb611bb472b11af81b5f177/pyinstaller-6.21.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5fa56746c1e76f93634d018502301378a2d0c382553d37d8c3c34ff436c12dd1", size = 753887, upload-time = "2026-06-13T14:14:25.268Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/51/8f/c22df1f6837784ac349057ba693f08e7b1ca7a0e06f9c33c63bc6280007b/pyinstaller-6.21.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:42395ec76df8e8120c36b13339d9db8cab83e316a12839ee303cc00fc941bb74", size = 753779, upload-time = "2026-06-13T14:14:29.445Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/76/1ce8a27ce62ba8cf3a87c9ce6d575610f4e55d7cb0123e7512fc3f4b921a/pyinstaller-6.21.0-py3-none-win32.whl", hash = "sha256:c6b28d30d8fd99ce162ff3aab5013ed44dbfb747566b1f01b9bed7964d7c14e9", size = 1336462, upload-time = "2026-06-13T14:14:35.785Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/fa/ca1d7e5257dd8566a9dfc0dfb02f8a8075eeb53d4b2d3c579f1276759042/pyinstaller-6.21.0-py3-none-win_amd64.whl", hash = "sha256:7fae06c494ce0ebfe6bd3055c0e409def884f63af2e3705d06bd431ad9237fc7", size = 1397487, upload-time = "2026-06-13T14:14:42.328Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dc/75/21b51523ce8d96629b71311775a0a65f5f5a872124ab0de33e5c848f8bff/pyinstaller-6.21.0-py3-none-win_arm64.whl", hash = "sha256:f13c95c9c03fb567217135919f93815c305813126780b0ed6e0123cb8acaf025", size = 1346094, upload-time = "2026-06-13T14:14:48.914Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyinstaller-hooks-contrib"
|
||||||
|
version = "2026.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "packaging" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/94/5b/c9fe0db5e83ee1c39b2258fa21d23b15e1a60786b6c5990ee5074ead8bb6/pyinstaller_hooks_contrib-2026.6.tar.gz", hash = "sha256:bef5002c32f4f50bd55b005da12cff64eca8783e7eaf86a06a62410164bab725", size = 173354, upload-time = "2026-06-08T22:37:16.152Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e7/31/f2d7343d8ed5f7c4678377886f6ce533e6eaaa131b252ce950114c2a7efa/pyinstaller_hooks_contrib-2026.6-py3-none-any.whl", hash = "sha256:fd13b8ac126b35361175edacd41a0d97080b75dd5f4b594ecefefff969509dd3", size = 457159, upload-time = "2026-06-08T22:37:14.722Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyserial"
|
||||||
|
version = "3.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dotenv"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyusb"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/00/6b/ce3727395e52b7b76dfcf0c665e37d223b680b9becc60710d4bc08b7b7cb/pyusb-1.3.1.tar.gz", hash = "sha256:3af070b607467c1c164f49d5b0caabe8ac78dbed9298d703a8dbf9df4052d17e", size = 77281, upload-time = "2025-01-08T23:45:01.866Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/28/b8/27e6312e86408a44fe16bd28ee12dd98608b39f7e7e57884a24e8f29b573/pyusb-1.3.1-py3-none-any.whl", hash = "sha256:bf9b754557af4717fe80c2b07cc2b923a9151f5c08d17bdb5345dac09d6a0430", size = 58465, upload-time = "2025-01-08T23:45:00.029Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pywin32-ctypes"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rich"
|
||||||
|
version = "15.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "markdown-it-py" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "service-tui"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "pyinstaller" },
|
||||||
|
{ name = "pyserial" },
|
||||||
|
{ name = "python-dotenv" },
|
||||||
|
{ name = "pyusb" },
|
||||||
|
{ name = "textual" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "pyinstaller", specifier = ">=6.0.0" },
|
||||||
|
{ name = "pyserial", specifier = ">=3.5" },
|
||||||
|
{ name = "python-dotenv", specifier = ">=1.0.0" },
|
||||||
|
{ name = "pyusb", specifier = ">=1.0.0" },
|
||||||
|
{ name = "textual", specifier = ">=0.80.0" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "setuptools"
|
||||||
|
version = "82.0.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textual"
|
||||||
|
version = "8.2.7"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "markdown-it-py", extra = ["linkify"] },
|
||||||
|
{ name = "mdit-py-plugins" },
|
||||||
|
{ name = "platformdirs" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
{ name = "rich" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/9b/7a/c519db0aba5024f86e71e9631810bfdd6866ed2c8695bd7fa34b90e7ef59/textual-8.2.7.tar.gz", hash = "sha256:658f568ff81e30ed43890c3e07520390e5cf1b4763822006e060656b0a88f105", size = 1859249, upload-time = "2026-05-19T10:52:49.531Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a8/f5/c1e18bc0707300a0e90204343abbf7d7acd6fb7ebe03a6d4893b99a234b8/textual-8.2.7-py3-none-any.whl", hash = "sha256:4caaa13a90bc4cf9c6c862c067ccd34fe84e9c161710a2a907a8026313b6bd73", size = 731129, upload-time = "2026-05-19T10:52:51.773Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typing-extensions"
|
||||||
|
version = "4.15.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uc-micro-py"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/78/67/9a363818028526e2d4579334460df777115bdec1bb77c08f9db88f6389f2/uc_micro_py-2.0.0.tar.gz", hash = "sha256:c53691e495c8db60e16ffc4861a35469b0ba0821fe409a8a7a0a71864d33a811", size = 6611, upload-time = "2026-03-01T06:31:27.526Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/61/73/d21edf5b204d1467e06500080a50f79d49ef2b997c79123a536d4a17d97c/uc_micro_py-2.0.0-py3-none-any.whl", hash = "sha256:3603a3859af53e5a39bc7677713c78ea6589ff188d70f4fee165db88e22b242c", size = 6383, upload-time = "2026-03-01T06:31:26.257Z" },
|
||||||
|
]
|
||||||
Loading…
Reference in a new issue