Добавить протокол DEIF, диагностику CAN и сохранение сессий
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"""Shared CAN protocol selection and disk-backed session buffer (no Qt)."""
|
||||
import csv
|
||||
import os
|
||||
import tempfile
|
||||
from collections import Counter, deque
|
||||
from datetime import datetime
|
||||
@@ -8,10 +9,14 @@ from protocan import protocan as pc
|
||||
from .can_bridge import decode_frame
|
||||
|
||||
PROTOCOLS = (("auto", "Авто (ProtoCAN / Balsam)"),
|
||||
("devboard", "DevBoard_V1 / SETCAN"))
|
||||
("devboard", "DevBoard_V1 / SETCAN"),
|
||||
("deif", "DEIF XDi-192 / CANopen"))
|
||||
|
||||
|
||||
def decode_protocol(frame, protocol="auto"):
|
||||
if protocol == "deif":
|
||||
from .deif_xdi import decode
|
||||
return decode(frame)
|
||||
if protocol == "devboard":
|
||||
return pc.decode(frame.can_id, frame.data, ide=frame.ide, rtr=frame.rtr)
|
||||
return decode_frame(frame)
|
||||
@@ -40,7 +45,7 @@ def parse_filter(text):
|
||||
|
||||
|
||||
class CanSession:
|
||||
"""All frames spool to disk; only the latest 5000 stay in display memory.
|
||||
"""All frames spool to disk; retain recent frames and unique-frame totals.
|
||||
|
||||
Starting recording includes the current session buffer. Reset clears the
|
||||
buffer/counters but never removes frames already written to a recording.
|
||||
@@ -50,6 +55,7 @@ class CanSession:
|
||||
def __init__(self):
|
||||
self.recent = deque(maxlen=5000)
|
||||
self.counts = Counter()
|
||||
self.unique_frames = {}
|
||||
self.output = None
|
||||
self.path = None
|
||||
self.buffer = tempfile.TemporaryFile(mode="w+", encoding="utf-8", newline="")
|
||||
@@ -61,6 +67,11 @@ class CanSession:
|
||||
csv.writer(self.buffer, delimiter=";").writerow(row)
|
||||
self.recent.append((identifier, row))
|
||||
self.counts[identifier] += 1
|
||||
# Timestamp is not part of a frame's identity. Keep the latest arrival
|
||||
# and a session-wide count, including repeats outside the recent buffer.
|
||||
key = row[1:]
|
||||
previous = self.unique_frames.get(key)
|
||||
self.unique_frames[key] = (row, previous[1] + 1 if previous else 1)
|
||||
if self.output is not None:
|
||||
csv.writer(self.output, delimiter=";").writerow(row)
|
||||
|
||||
@@ -83,6 +94,28 @@ class CanSession:
|
||||
self.output, self.path = output, path
|
||||
return path
|
||||
|
||||
def save(self, path):
|
||||
"""Save a full buffer snapshot without stopping an active recording."""
|
||||
path = Path(path)
|
||||
if self.output is not None and (path.resolve() == self.path.resolve()
|
||||
or (path.exists() and path.samefile(self.path))):
|
||||
raise ValueError("Выберите другой файл: в этот CSV сейчас идёт запись.")
|
||||
temporary = None
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8-sig",
|
||||
newline="", dir=path.parent, delete=False) as output:
|
||||
temporary = Path(output.name)
|
||||
csv.writer(output, delimiter=";").writerow(self.HEADER)
|
||||
self.buffer.seek(0)
|
||||
while chunk := self.buffer.read(65536):
|
||||
output.write(chunk)
|
||||
os.replace(temporary, path)
|
||||
finally:
|
||||
self.buffer.seek(0, 2)
|
||||
if temporary is not None and temporary.exists():
|
||||
temporary.unlink()
|
||||
return path
|
||||
|
||||
def flush(self):
|
||||
if self.output is not None:
|
||||
self.output.flush()
|
||||
@@ -97,6 +130,7 @@ class CanSession:
|
||||
self.buffer.truncate()
|
||||
self.recent.clear()
|
||||
self.counts.clear()
|
||||
self.unique_frames.clear()
|
||||
|
||||
def close(self):
|
||||
self.stop()
|
||||
|
||||
Reference in New Issue
Block a user