103 lines
4.9 KiB
Python
103 lines
4.9 KiB
Python
"""Служебный уровень моста CAN <-> RS485: его кадры и его регистры.
|
||
|
||
Мост пересылает кадры шины прозрачно, но два вида трафика принадлежат
|
||
ему самому: служебный кадр с ``FLAGS.ERR`` (состояние CAN и счётчики
|
||
потерь) и блок регистров ``0xFF00`` в общем адресном пространстве.
|
||
Разбор обоих собран здесь, чтобы вкладка шины не знала подробностей.
|
||
|
||
Модуль не импортирует Qt и проверяется host-тестами.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from . import can_transport as tr
|
||
from protocan import protocan as pc
|
||
from . import balsam_can
|
||
|
||
#: Расшифровка поля LEC регистра CAN_ESR.
|
||
CAN_ESR_LEC = {
|
||
0: "нет ошибок",
|
||
1: "stuff error",
|
||
2: "form error",
|
||
3: "acknowledgment error",
|
||
4: "bit recessive error",
|
||
5: "bit dominant error",
|
||
6: "CRC error",
|
||
7: "флаг сброшен ПО",
|
||
}
|
||
|
||
#: Имена диагностических регистров моста, см. CAN_to_RS485/fw/Inc/bridge.h.
|
||
GAS_NAMES = {
|
||
0xFF00: "версия транспорта",
|
||
0xFF01: "время работы, с",
|
||
0xFF02: "CAN_ESR, младшие 16 бит",
|
||
0xFF03: "CAN TEC — ошибки передачи",
|
||
0xFF04: "CAN REC — ошибки приёма",
|
||
0xFF05: "принято из CAN (младшие)",
|
||
0xFF06: "принято из CAN (старшие)",
|
||
0xFF07: "передано в CAN (младшие)",
|
||
0xFF08: "передано в CAN (старшие)",
|
||
0xFF09: "потеряно кадров CAN",
|
||
0xFF0A: "кадров принято с RS485",
|
||
0xFF0B: "ошибок CRC на RS485",
|
||
0xFF0C: "кадров не отдано в RS485",
|
||
0xFF0D: "кадров принято с UART GUI",
|
||
0xFF0E: "битрейт CAN, кбит/с",
|
||
0xFF0F: "скорость UART / 100",
|
||
}
|
||
|
||
#: Сколько регистров GAS помещается в один кадр шины (8 байт данных).
|
||
GAS_REGS_PER_FRAME = 4
|
||
|
||
|
||
def decode_frame(frame: tr.Frame) -> pc.Decoded:
|
||
"""@brief Разбирает кадр: служебные кадры моста — отдельно от ProtoCAN.
|
||
|
||
@param frame Кадр, снятый с полевого канала моста.
|
||
@return Разбор прикладного уровня либо расшифровка служебного кадра.
|
||
"""
|
||
if not frame.is_error:
|
||
decoded_balsam = balsam_can.decode(
|
||
frame.can_id, frame.data, ide=frame.ide, rtr=frame.rtr)
|
||
if decoded_balsam is not None:
|
||
return decoded_balsam
|
||
return pc.decode(frame.can_id, frame.data, ide=frame.ide, rtr=frame.rtr)
|
||
|
||
dec = pc.Decoded(id=pc.ProtoCanId.parse(frame.can_id), dlc=len(frame.data),
|
||
data=frame.data, ide=frame.ide, rtr=frame.rtr)
|
||
d = frame.data
|
||
if len(d) >= 7:
|
||
esr, tec, rec = d[0], d[1], d[2]
|
||
can_ovr = d[3] | (d[4] << 8)
|
||
rs_err = d[5] | (d[6] << 8)
|
||
lec = (esr >> 4) & 0x7
|
||
dec.summary = ("МОСТ: TEC=%d REC=%d, %s%s%s, потери CAN=%d, ошибки RS485=%d"
|
||
% (tec, rec, CAN_ESR_LEC.get(lec, "?"),
|
||
", BUS-OFF" if esr & 0x4 else "",
|
||
", passive" if esr & 0x2 else "",
|
||
can_ovr, rs_err))
|
||
dec.fields = [
|
||
("Источник", "служебный кадр моста (FLAGS.ERR)"),
|
||
("CAN_ESR[7:0]", "0x%02X" % esr),
|
||
(" Последняя ошибка", CAN_ESR_LEC.get(lec, "?")),
|
||
(" EWGF / EPVF / BOFF", "%d / %d / %d" % (esr & 1, (esr >> 1) & 1,
|
||
(esr >> 2) & 1)),
|
||
("Счётчик ошибок передачи (TEC)", str(tec)),
|
||
("Счётчик ошибок приёма (REC)", str(rec)),
|
||
("Потеряно кадров CAN", str(can_ovr)),
|
||
("Ошибок/потерь RS485", str(rs_err)),
|
||
]
|
||
if esr & 0x4:
|
||
dec.warnings.append("Шина CAN в состоянии BUS-OFF")
|
||
elif esr & 0x2:
|
||
dec.warnings.append("Шина CAN в состоянии error passive")
|
||
if can_ovr:
|
||
dec.warnings.append("Мост потерял %d кадр(ов) CAN из-за переполнения" % can_ovr)
|
||
if rs_err:
|
||
dec.warnings.append("На стороне RS485 %d ошибок/потерянных байт" % rs_err)
|
||
else:
|
||
dec.summary = "МОСТ: служебный кадр, %d байт" % len(d)
|
||
dec.fields = [("Источник", "служебный кадр моста (FLAGS.ERR)"),
|
||
("Данные", tr.hex_str(d))]
|
||
return dec
|