Files
templates/python/set_devices/can_bridge.py

103 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Служебный уровень моста 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