diff --git a/WINDOWS_TEST.md b/WINDOWS_TEST.md deleted file mode 100644 index f5ba975..0000000 --- a/WINDOWS_TEST.md +++ /dev/null @@ -1,81 +0,0 @@ -# Gate 4a - -## Case 1 - -```bash -2026-07-06 10:07:16,955 DEBUG spsdk.mboot.protocol.bulk_protocol IN [1016]: 03, 00, 0C, 00, A0, 00, 00, 02, 00, 00, 00, 00, 11, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 -2026-07-06 10:07:16,955 DEBUG spsdk.mboot.mcuboot RX-PACKET: Tag=GenericResponse, Status=Success, Cmd=ConfigureMemory -2026-07-06 10:07:16,955 INFO spsdk.mboot.mcuboot CMD: Status: 0 (0x0) Success. -2026-07-06 10:07:16,956 INFO spsdk.mboot.mcuboot CMD: FlashEraseAll(mem_id=9) -2026-07-06 10:07:16,956 DEBUG spsdk.mboot.mcuboot TX-PACKET: Tag=FlashEraseAll, Flags=0x00, P[0]=0x00000009 -2026-07-06 10:07:16,956 DEBUG spsdk.mboot.protocol.bulk_protocol OUT[12]: 01, 00, 08, 00, 01, 00, 00, 01, 09, 00, 00, 00 -2026-07-06 10:07:16,956 DEBUG libusbsio.hidapi.dev HID device 3185030394352 writing[12]: 0x010008000100000109000000 -2026-07-06 10:07:16,957 DEBUG libusbsio.hidapi.dev HID device 3185030394352 wrote 1016 bytes -2026-07-06 10:07:21,297 DEBUG libusbsio.hidapi.dev HID device 3185030394352 read[-1]: None -2026-07-06 10:07:21,297 DEBUG spsdk.utils.interfaces.device.usb_device Cannot read from HID device, error=-1 -2026-07-06 10:07:21,297 DEBUG spsdk.mboot.mcuboot RX-PACKET: No Response, Timeout Error ! -2026-07-06 10:07:21,298 DEBUG spsdk.mboot.mcuboot RX-PACKET: Tag=0x01, Flags=0x00 [14, 27, 00, 00] -2026-07-06 10:07:21,298 INFO spsdk.mboot.mcuboot CMD: Status: 10004 (0x2714) No response packet from target device. -2026-07-06 10:07:21,299 INFO libusbsio Loading SIO library: C:\Development\IMXRT\tft_manufacture_test\tools\production\.venv\Lib\site-packages\libusbsio\bin\x64\libusbsio.dll -2026-07-06 10:07:21,347 INFO libusbsio HID enumeration[3185065344192]: initialized -2026-07-06 10:07:21,347 DEBUG libusbsio HID enumeration[3185065344192]: device #0: USB Receiver -2026-07-06 10:07:21,347 DEBUG libusbsio HID enumeration[3185065344192]: device #1: USB Receiver -2026-07-06 10:07:21,347 DEBUG libusbsio HID enumeration[3185065344192]: device #2: USB Receiver -2026-07-06 10:07:21,347 DEBUG libusbsio HID enumeration[3185065344192]: device #3: USB Receiver -2026-07-06 10:07:21,347 DEBUG libusbsio HID enumeration[3185065344192]: device #4: USB Receiver -2026-07-06 10:07:21,348 DEBUG libusbsio HID enumeration[3185065344192]: device #5: USB Receiver -2026-07-06 10:07:21,348 DEBUG libusbsio HID enumeration[3185065344192]: device #6: HIDI2C Device -2026-07-06 10:07:21,348 DEBUG libusbsio HID enumeration[3185065344192]: device #7: USB Receiver -2026-07-06 10:07:21,348 DEBUG libusbsio HID enumeration[3185065344192]: device #8: HIDI2C Device -2026-07-06 10:07:21,348 DEBUG libusbsio HID enumeration[3185065344192]: device #9: USB Receiver -2026-07-06 10:07:21,348 DEBUG libusbsio HID enumeration[3185065344192]: device #10: HIDI2C Device -2026-07-06 10:07:21,348 INFO libusbsio HID enumeration[3185065344192]: finished, total 11 devices -2026-07-06 10:07:21,348 INFO libusbsio SIO library unloaded -2026-07-06 10:07:21,348 INFO spsdk.mboot.mcuboot Closing: identifier='usb', device=USB COMPOSITE DEVICE (0x15A2, 0x0073)path=b'\\\\?\\hid#vid_15a2&pid_0073#7&100c291f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}' sn='' -2026-07-06 10:07:21,348 DEBUG spsdk.utils.interfaces.device.usb_device Closing the Interface: USB COMPOSITE DEVICE (0x15A2, 0x0073)path=b'\\\\?\\hid#vid_15a2&pid_0073#7&100c291f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}' sn='' -2026-07-06 10:07:21,348 INFO libusbsio.hidapi.dev HID device 0x2e5929af1f0 closed -2026-07-06 10:07:21,348 DEBUG spsdk.utils.interfaces.device.usb_device Closing the Interface: USB COMPOSITE DEVICE (0x15A2, 0x0073)path=b'\\\\?\\hid#vid_15a2&pid_0073#7&100c291f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}' sn='' -2026-07-06 10:07:21,349 ERROR app.flasher erase_chip: flash_erase_all вернул False (устройство пропало с шины) -2026-07-06 10:07:21,351 INFO libusbsio SIO library unloaded - -``` - -## Case 2 - -```bash -2026-07-06 11:11:37,016 DEBUG spsdk.mboot.protocol.bulk_protocol IN [1016]: 03, 00, 0C, 00, A0, 00, 00, 02, 00, 00, 00, 00, 11, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 -2026-07-06 11:11:37,016 DEBUG spsdk.mboot.mcuboot RX-PACKET: Tag=GenericResponse, Status=Success, Cmd=ConfigureMemory -2026-07-06 11:11:37,017 INFO spsdk.mboot.mcuboot CMD: Status: 0 (0x0) Success. -2026-07-06 11:11:37,018 INFO spsdk.mboot.mcuboot CMD: FlashEraseRegion(address=0x60000000, length=143360, mem_id=0) -2026-07-06 11:11:37,018 DEBUG spsdk.mboot.mcuboot TX-PACKET: Tag=FlashEraseRegion, Flags=0x00, P[0]=0x60000000, P[1]=0x00023000, P[2]=0x00000000 -2026-07-06 11:11:37,018 DEBUG spsdk.mboot.protocol.bulk_protocol OUT[20]: 01, 00, 10, 00, 02, 00, 00, 03, 00, 00, 00, 60, 00, 30, 02, 00, 00, 00, 00, 00 -2026-07-06 11:11:37,018 DEBUG libusbsio.hidapi.dev HID device 2020961928848 writing[20]: 0x0100100002000003000000600030020000000000 -2026-07-06 11:11:37,018 DEBUG libusbsio.hidapi.dev HID device 2020961928848 wrote 1016 bytes -2026-07-06 11:11:37,301 DEBUG libusbsio.hidapi.dev HID device 2020961928848 read[-1]: None -2026-07-06 11:11:37,301 DEBUG spsdk.utils.interfaces.device.usb_device Cannot read from HID device, error=-1 -2026-07-06 11:11:37,301 DEBUG spsdk.mboot.mcuboot RX-PACKET: No Response, Timeout Error ! -2026-07-06 11:11:37,301 DEBUG spsdk.mboot.mcuboot RX-PACKET: Tag=0x02, Flags=0x00 [14, 27, 00, 00] -2026-07-06 11:11:37,301 INFO spsdk.mboot.mcuboot CMD: Status: 10004 (0x2714) No response packet from target device. -2026-07-06 11:11:37,301 INFO libusbsio Loading SIO library: C:\Development\IMXRT\tft_manufacture_test\tools\production\.venv\Lib\site-packages\libusbsio\bin\x64\libusbsio.dll -2026-07-06 11:11:37,349 INFO libusbsio HID enumeration[2020961011232]: initialized -2026-07-06 11:11:37,349 DEBUG libusbsio HID enumeration[2020961011232]: device #0: USB Receiver -2026-07-06 11:11:37,349 DEBUG libusbsio HID enumeration[2020961011232]: device #1: USB Receiver -2026-07-06 11:11:37,349 DEBUG libusbsio HID enumeration[2020961011232]: device #2: USB Receiver -2026-07-06 11:11:37,349 DEBUG libusbsio HID enumeration[2020961011232]: device #3: USB Receiver -2026-07-06 11:11:37,349 DEBUG libusbsio HID enumeration[2020961011232]: device #4: USB Receiver -2026-07-06 11:11:37,349 DEBUG libusbsio HID enumeration[2020961011232]: device #5: USB Receiver -2026-07-06 11:11:37,349 DEBUG libusbsio HID enumeration[2020961011232]: device #6: HIDI2C Device -2026-07-06 11:11:37,349 DEBUG libusbsio HID enumeration[2020961011232]: device #7: USB Receiver -2026-07-06 11:11:37,350 DEBUG libusbsio HID enumeration[2020961011232]: device #8: HIDI2C Device -2026-07-06 11:11:37,350 DEBUG libusbsio HID enumeration[2020961011232]: device #9: USB Receiver -2026-07-06 11:11:37,350 DEBUG libusbsio HID enumeration[2020961011232]: device #10: HIDI2C Device -2026-07-06 11:11:37,350 INFO libusbsio HID enumeration[2020961011232]: finished, total 11 devices -2026-07-06 11:11:37,350 INFO libusbsio SIO library unloaded -2026-07-06 11:11:37,350 INFO spsdk.mboot.mcuboot Closing: identifier='usb', device=USB COMPOSITE DEVICE (0x15A2, 0x0073)path=b'\\\\?\\hid#vid_15a2&pid_0073#7&100c291f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}' sn='' -2026-07-06 11:11:37,350 DEBUG spsdk.utils.interfaces.device.usb_device Closing the Interface: USB COMPOSITE DEVICE (0x15A2, 0x0073)path=b'\\\\?\\hid#vid_15a2&pid_0073#7&100c291f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}' sn='' -2026-07-06 11:11:37,350 INFO libusbsio.hidapi.dev HID device 0x1d68ab7c690 closed -2026-07-06 11:11:37,350 DEBUG spsdk.utils.interfaces.device.usb_device Closing the Interface: USB COMPOSITE DEVICE (0x15A2, 0x0073)path=b'\\\\?\\hid#vid_15a2&pid_0073#7&100c291f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}' sn='' -2026-07-06 11:11:37,350 ERROR app.flasher flash: flash_erase_region вернул False (устройство пропало с шины) -2026-07-06 11:11:37,354 INFO libusbsio SIO library unloaded - -``` diff --git a/tools/production/app/app.py b/tools/production/app/app.py index 4adfe94..22f7dc2 100644 --- a/tools/production/app/app.py +++ b/tools/production/app/app.py @@ -64,16 +64,21 @@ class ServiceApp(App): """ После прошивки: - firmware_test + успех → PostFlashScreen (промпт смены BootMode) - - всё остальное (production/custom, либо ошибка) → WaitingScreen - - target=None — маркер потери соединения (см. ConnectionWatcherMixin) - → WaitingScreen с явной причиной возврата + - production/custom + успех → WaitingScreen + - target=None — обрыв USB (watcher в простое ИЛИ backend во время + активной операции, см. models.FlashResult, Фаза 4a вариант 2) + → WaitingScreen с причиной (конкретный текст, если есть, иначе + общий fallback) + + Логическая ошибка (плата на месте) сюда не долетает вовсе — + FlashScreen в этом случае не покидает себя (см. _do_flash/_do_erase). """ if event.preset is not None: self._last_flash_preset = event.preset if event.target is None and not event.success: - self.switch_screen( - WaitingScreen(disconnect_reason="Соединение с платой потеряно") + WaitingScreen( + disconnect_reason=event.error_message or "Соединение с платой потеряно" ) return diff --git a/tools/production/app/flasher.py b/tools/production/app/flasher.py index 060cb50..9b46aab 100644 --- a/tools/production/app/flasher.py +++ b/tools/production/app/flasher.py @@ -40,7 +40,7 @@ from pathlib import Path from typing import Awaitable, Callable, Optional from . import flash_backend -from .models import FcbVariant, FlashProgress, FlashTarget +from .models import FcbVariant, FlashProgress, FlashResult, FlashTarget logger = logging.getLogger(__name__) @@ -159,13 +159,14 @@ class Flasher: async_progress_cb: Optional[ProgressCallback], sync_progress_cb: Optional[flash_backend.ProgressCallback], **kwargs: object, - ) -> bool: + ) -> FlashResult: """Выполнить flash_backend.flash()/erase_chip() в потоке. flash_backend поднимает FlashBackendError (включая ConnectionLostError, см. Фазу 4) вместо возврата False — здесь это конвертируется обратно - в контракт Flasher (bool + событие phase="error"), который был у - subprocess-версии (ненулевой returncode). + в контракт Flasher (FlashResult + событие phase="error"). connection_lost + транслируется из exc.connection_lost (Фаза 4a, вариант 2) — экран + различает физический обрыв от логической ошибки без парсинга текста. Отдельный except Exception — safety net (Фаза 4, согласовано): любое непредвиденное исключение из worker-потока (не только @@ -176,7 +177,7 @@ class Flasher: """ try: await asyncio.to_thread(func, *args, progress_cb=sync_progress_cb, **kwargs) - return True + return FlashResult(ok=True) except flash_backend.FlashBackendError as exc: logger.error("%s: %s", getattr(func, "__name__", func), exc) if async_progress_cb is not None: @@ -185,7 +186,7 @@ class Flasher: phase="error", percent=0, message=_format_error_message(exc) ) ) - return False + return FlashResult(ok=False, connection_lost=exc.connection_lost) except Exception as exc: # noqa: BLE001 — safety net, см. docstring logger.exception( "%s: непредвиденная ошибка", getattr(func, "__name__", func) @@ -198,19 +199,19 @@ class Flasher: message=f"Непредвиденная ошибка: {exc}", ) ) - return False + return FlashResult(ok=False, connection_lost=False) # ── Erase ──────────────────────────────────────────────────────────── async def erase_chip( self, progress_cb: Optional[ProgressCallback] = None, - ) -> bool: + ) -> FlashResult: """ Chip erase Flash через USB SDP (flash_backend.erase_chip). Занимает ~30 с для W25Q128. FCB будет стёрт. - :return: True при успехе. + :return: FlashResult(ok, connection_lost) — см. models.FlashResult. """ loop = asyncio.get_running_loop() sync_cb = _make_sync_progress_cb(progress_cb, loop) @@ -229,7 +230,7 @@ class Flasher: bin_path: Optional[Path] = None, use_dcd: bool = False, fcb_variant: FcbVariant = FcbVariant.W25Q128, - ) -> bool: + ) -> FlashResult: """ Запустить прошивку через flash_backend. @@ -242,7 +243,7 @@ class Flasher: при сборке HAB-образа через HabImage. :param fcb_variant: Только для CUSTOM — какой явный FCB-блоб (dcd/*_fdcb.bin) записать в Flash[0x60000000] вместо auto-config. - :return: True при успехе. + :return: FlashResult(ok, connection_lost) — см. models.FlashResult. """ loop = asyncio.get_running_loop() sync_cb = _make_sync_progress_cb(progress_cb, loop) @@ -261,20 +262,20 @@ class Flasher: ) elif target == FlashTarget.PRODUCTION: - ok = await self._run_flash_op( + result = await self._run_flash_op( flash_backend.flash, _firmware_hab_path("bootloader"), async_progress_cb=progress_cb, sync_progress_cb=sync_cb, ) - if ok: - ok = await self._run_flash_op( + if result.ok: + result = await self._run_flash_op( flash_backend.flash, _firmware_hab_path("app"), async_progress_cb=progress_cb, sync_progress_cb=sync_cb, ) - return ok + return result elif target == FlashTarget.CUSTOM: if bin_path is None: @@ -283,7 +284,7 @@ class Flasher: bin_path, use_dcd, fcb_variant, progress_cb, sync_cb ) - return False + return FlashResult(ok=False) async def _flash_custom( self, @@ -292,7 +293,7 @@ class Flasher: fcb_variant: FcbVariant, progress_cb: Optional[ProgressCallback], sync_cb: Optional[flash_backend.ProgressCallback], - ) -> bool: + ) -> FlashResult: """ Прошить «сырой» (не-HAB) кастомный бинарник из custom_binaries/. @@ -311,7 +312,9 @@ class Flasher: raw_bin_path, use_dcd, progress_cb, sync_cb ) if hab_bin is None: - return False + # Ошибка сборки HAB — чисто локальная операция (HabImage, + # временный файл), к USB-соединению отношения не имеет. + return FlashResult(ok=False) fcb_path = flash_backend.fcb_blob_path(fcb_variant.fcb_filename) try: diff --git a/tools/production/app/models.py b/tools/production/app/models.py index 48d0757..199f2a6 100644 --- a/tools/production/app/models.py +++ b/tools/production/app/models.py @@ -135,3 +135,22 @@ class FlashProgress: # | "reset" | "hab_build" | "done" | "error" percent: int # 0..100 message: str + + +@dataclass(frozen=True) +class FlashResult: + """Результат Flasher.flash()/erase_chip() (Фаза 4a, вариант 2). + + Раньше публичный API Flasher() возвращал голый bool — экран не мог + отличить физический обрыв USB от логической ошибки (файл не найден, + битый custom-бинарь, ошибка сборки HAB) и одинаково уводил оператора + на WaitingScreen в обоих случаях, хотя при логической ошибке плата + остаётся на месте и разумнее остаться на FlashScreen. + + connection_lost транслируется из FlashBackendError.connection_lost + (см. flash_backend.py) без парсинга текста сообщения — тот же принцип, + что и там. + """ + + ok: bool + connection_lost: bool = False diff --git a/tools/production/app/screens/flash.py b/tools/production/app/screens/flash.py index efa2270..328060e 100644 --- a/tools/production/app/screens/flash.py +++ b/tools/production/app/screens/flash.py @@ -51,6 +51,18 @@ class FlashScreen(Screen, ConnectionWatcherMixin): №4 отчёта). Во время самой прошивки/erase мониторинг приостановлен — обрыв в этом случае обнаруживает сам flash_backend (SPSDKConnectionError → ConnectionLostError, см. Фазу 4) и репортит через #flash-log. + + FlashResult (Фаза 4a, вариант 2) различает физический обрыв от + логической ошибки без парсинга текста — см. models.FlashResult: + - connection_lost=True → уходим на WaitingScreen (тот же маркер + target=None, что и watcher-детект в простое), текст ошибки + прокидывается через FlashDone.error_message, чтобы не терять + конкретику ради общего "Соединение с платой потеряно". + - connection_lost=False → плата на месте, остаёмся на FlashScreen. + Раньше (до варианта 2) любая ошибка уводила на WaitingScreen, + который почти мгновенно переоткрывал FlashScreen заново (плата + на шине видна) и уничтожал #flash-log раньше, чем оператор + успевал прочитать (см. Гейт 4a, п.0 отчёта). """ BINDINGS = [ @@ -63,17 +75,24 @@ class FlashScreen(Screen, ConnectionWatcherMixin): success: bool, target: Optional[FlashTarget] = None, preset: Optional[FlashPreset] = None, + error_message: Optional[str] = None, ) -> None: super().__init__() self.success = success self.target = target self.preset = preset + # Заполняется только при connection_lost=True — конкретный текст + # обрыва вместо общего "Соединение с платой потеряно" в app.py. + # При логической ошибке экран никуда не уходит (см. _do_flash), + # и это поле не используется. + self.error_message = error_message def __init__(self, preset: Optional[FlashPreset] = None, **kwargs) -> None: super().__init__(**kwargs) self._flasher = Flasher() self._flashing = False self._preset = preset or FlashPreset() + self._last_error_message: Optional[str] = None def compose(self) -> ComposeResult: with AppFrame(id="flash-frame"): @@ -217,10 +236,11 @@ class FlashScreen(Screen, ConnectionWatcherMixin): async def _do_flash( self, target: FlashTarget, bin_path: Optional[Path], preset: FlashPreset ) -> None: + self._last_error_message = None self._set_busy(True) self._show_progress(True) self._log(f"▶ Прошивка: {target.value}") - ok = await self._flasher.flash( + result = await self._flasher.flash( target=target, bin_path=bin_path, use_dcd=preset.use_dcd, @@ -228,19 +248,47 @@ class FlashScreen(Screen, ConnectionWatcherMixin): progress_cb=self._on_progress, ) self._set_busy(False) - self._finish_progress(ok) - self._log("✅ Готово" if ok else "❌ Ошибка") - self.post_message(self.FlashDone(success=ok, target=target, preset=preset)) + self._finish_progress(result.ok) + self._log("✅ Готово" if result.ok else "❌ Ошибка") + + if result.ok: + self.post_message( + self.FlashDone(success=True, target=target, preset=preset) + ) + elif result.connection_lost: + # Обрыв во время активной прошивки — watcher в это время приглушён + # (см. _check_sdp_present), обрыв обнаружил сам backend. Тот же + # маркер target=None, что и у watcher-детекта в простое (Гейт 4a). + self.post_message( + self.FlashDone( + success=False, + target=None, + preset=preset, + error_message=self._last_error_message, + ) + ) + # else: логическая ошибка, плата на месте (вариант 2, Гейт 4a) — + # остаёмся на FlashScreen. Сообщение уже в #flash-log (_on_progress), + # кнопки уже разблокированы (_set_busy(False) выше). @work(exclusive=True, thread=False) async def _do_erase(self) -> None: + self._last_error_message = None self._set_busy(True) self._show_progress(True) self._log("⚠ Очистка памяти (~30 с)...") - ok = await self._flasher.erase_chip(progress_cb=self._on_progress) + result = await self._flasher.erase_chip(progress_cb=self._on_progress) self._set_busy(False) - self._finish_progress(ok) - self._log("✅ Очистка памяти завершена" if ok else "❌ Очистка памяти: ошибка") + self._finish_progress(result.ok) + self._log( + "✅ Очистка памяти завершена" if result.ok else "❌ Очистка памяти: ошибка" + ) + if result.connection_lost: + self.post_message( + self.FlashDone( + success=False, target=None, error_message=self._last_error_message + ) + ) # ── Вспомогательные ─────────────────────────────────────────────────────── @@ -273,6 +321,8 @@ class FlashScreen(Screen, ConnectionWatcherMixin): bar = self.query_one("#flash-progress-bar", ProgressBar) bar.update(total=100, progress=progress.percent) self._log(progress.message) + if progress.phase == "error": + self._last_error_message = progress.message def _show_progress(self, visible: bool) -> None: """Показать/скрыть прогресс-бар. Скрыт в простое — без анимации.""" diff --git a/tools/production/docs/MONOLITH_APP_PLAN.md b/tools/production/docs/MONOLITH_APP_PLAN.md index b999efa..2350860 100644 --- a/tools/production/docs/MONOLITH_APP_PLAN.md +++ b/tools/production/docs/MONOLITH_APP_PLAN.md @@ -449,4 +449,4 @@ service-tui-vX.Y.Z-/ документация, `.env.example`). **Старт (после подтверждения Р7 и, желательно, измерения О1):** -фаза 0 — diff `pyproject.toml` + `spike_hab.py` + `spike_flash.py`. \ No newline at end of file +фаза 0 — diff `pyproject.toml` + `spike_hab.py` + `spike_flash.py`.