From d507b84a76b3a9a976e0c91f549f781be6c3a68b Mon Sep 17 00:00:00 2001 From: Andrey Kruchinkin Date: Sat, 29 Aug 2026 22:18:53 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20Python-=D0=B4=D0=B5=D0=BA=D0=BE=D0=B4=D0=B5=D1=80=20Pr?= =?UTF-8?q?otoCAN=20Boot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/protocan/protocan.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/python/protocan/protocan.py b/python/protocan/protocan.py index ee174d6..540c44e 100644 --- a/python/protocan/protocan.py +++ b/python/protocan/protocan.py @@ -39,6 +39,11 @@ class MsgType(IntEnum): MODBUS_HOLDING = 0b0110 MODBUS_INPUT = 0b0111 ERROR = 0b1000 + BOOT_CONTROL = 0b1001 + BOOT_DATA_A = 0b1010 + BOOT_DATA_B = 0b1011 + BOOT_STATUS = 0b1100 + BOOT_DISCOVERY = 0b1101 PULSE = 0b1111 @@ -90,6 +95,11 @@ MSGTYPE_RU = { MsgType.MODBUS_HOLDING: "Modbus Holding", MsgType.MODBUS_INPUT: "Modbus Input", MsgType.ERROR: "Error", + MsgType.BOOT_CONTROL: "Boot Control", + MsgType.BOOT_DATA_A: "Boot Data A", + MsgType.BOOT_DATA_B: "Boot Data B", + MsgType.BOOT_STATUS: "Boot Status", + MsgType.BOOT_DISCOVERY: "Boot Discovery", MsgType.PULSE: "Pulse", } @@ -298,6 +308,11 @@ def decode(raw_id: int, data: bytes, ide: bool = True, rtr: bool = False) -> Dec MsgType.MODBUS_HOLDING: _decode_modbus_reg, MsgType.MODBUS_INPUT: _decode_modbus_reg, MsgType.ERROR: _decode_error, + MsgType.BOOT_CONTROL: _decode_boot, + MsgType.BOOT_DATA_A: _decode_boot, + MsgType.BOOT_DATA_B: _decode_boot, + MsgType.BOOT_STATUS: _decode_boot, + MsgType.BOOT_DISCOVERY: _decode_boot, MsgType.PULSE: _decode_pulse, } try: @@ -312,6 +327,28 @@ def decode(raw_id: int, data: bytes, ide: bool = True, rtr: bool = False) -> Dec return res +def _decode_boot(res: Decoded) -> None: + kind = MsgType(res.id.msg_type) + if kind == MsgType.BOOT_CONTROL: + command = res.id.msg_body & 0xFF + session = res.id.msg_body >> 8 + names = {1: "IDENTIFY", 2: "ENTER_BOOT", 3: "BEGIN_IMAGE", + 4: "BEGIN_COMPAT", 5: "ERASE", 6: "VERIFY", 7: "COMMIT", + 8: "CONFIRM", 9: "REBOOT", 10: "ABORT", 11: "QUERY_PROGRESS"} + res.summary = "BOOT CONTROL %s, session=%d" % ( + names.get(command, "0x%02X" % command), session) + elif kind in (MsgType.BOOT_DATA_A, MsgType.BOOT_DATA_B): + res.summary = "BOOT DATA slot %s, block=%d" % ( + "A" if kind == MsgType.BOOT_DATA_A else "B", res.id.msg_body) + elif kind == MsgType.BOOT_STATUS: + res.summary = "BOOT STATUS command=0x%02X, session=%d" % ( + res.id.msg_body & 0xFF, res.id.msg_body >> 8) + else: + res.summary = "BOOT DISCOVERY" + if res.data[:4] == b"PONG" and len(res.data) == 8: + res.summary = "PONG, FW %d.%d, protocol %d" % tuple(res.data[4:7]) + + def _decode_broadcast(res: Decoded) -> None: body, btype = split_broadcast(res.id.msg_body) res.fields.append((" Broadcast.Type", "0x%03X — %s" % (