Добавить протокол DEIF, диагностику CAN и сохранение сессий

This commit is contained in:
2026-10-04 23:34:26 +03:00
parent 6c08eece61
commit 0e3d89e1ee
8 changed files with 321 additions and 47 deletions

View File

@@ -17,12 +17,31 @@ class CanLogChannel:
name: str
address: int
points: deque
decoded: bool = False
def _values(frame: BoardFrame):
if frame.rtr:
return
prefix = f"{'EXT' if frame.extended else 'STD'} {frame.identifier:08X}"
if not frame.extended:
from .deif_xdi import decode as decode_deif
from .can_transport import build_frame
packet = decode_deif(build_frame(frame.identifier, frame.data, ide=False))
fields = dict(packet.fields)
if "RAW INT16 LE" in fields:
# Invalid measurements must not become numeric spikes or raw-word curves.
if "Статус" in fields or packet.warnings:
return
address = int(fields["Индекс объекта"], 16)
subindex = int(fields["Подиндекс"], 16)
label = f"{frame.source} · {prefix} · {packet.device_label} · 0x{address:04X}:{subindex:02X} · {fields['Параметр']}"
raw = int(fields["RAW INT16 LE"])
yield (label + " · RAW", address, raw, True)
if "Значение" in fields:
number, unit = fields["Значение"].split(" ", 1)
yield (label + " · " + unit, address, float(number), True)
return
if frame.extended:
for project in PROJECTS:
if project.format is not LegacyCanFormat.ROTATING_THREE_WORDS:
@@ -34,13 +53,13 @@ def _values(frame: BoardFrame):
if packet is not None:
for address, value in packet.present_values:
# Keep both CAN directions and nodes in separate curves.
yield (f"{prefix} · {node.name} · R{address:04X}", address, value)
yield (f"{prefix} · {node.name} · R{address:04X}", address, value, False)
return
# Unknown protocols stay explicitly raw; never guess a register layout.
for offset in range(0, len(frame.data), 2):
part = frame.data[offset:offset + 2]
label = f"BE16 [{offset}:{offset + 1}]" if len(part) == 2 else f"байт [{offset}]"
yield (f"{prefix} · {label}", offset, int.from_bytes(part, "big"))
yield (f"{prefix} · {label}", offset, int.from_bytes(part, "big"), False)
def read_can_log(lines, *, reference_timestamp: float, capacity: int,
@@ -49,16 +68,16 @@ def read_can_log(lines, *, reference_timestamp: float, capacity: int,
imported = parse_sd_log(lines, reference_timestamp, limit=frame_limit)
channels = {}
for frame in imported.frames:
for name, address, _value in _values(frame):
channels.setdefault(name, address)
for name, address, _value, decoded in _values(frame):
channels.setdefault(name, (address, decoded))
if len(channels) > 4096:
raise ValueError("CAN CSV: слишком много каналов (максимум 4096)")
if not channels:
raise ValueError("CAN CSV: нет данных для графиков")
capacity = max(1, min(capacity, max_total_points // len(channels)))
result = {name: CanLogChannel(name, address, deque(maxlen=capacity))
for name, address in channels.items()}
result = {name: CanLogChannel(name, address, deque(maxlen=capacity), decoded)
for name, (address, decoded) in channels.items()}
for frame in imported.frames:
for name, _address, value in _values(frame):
for name, _address, value, _decoded in _values(frame):
result[name].points.append((frame.timestamp * 1000, value))
return list(result.values()), imported