add logging
This commit is contained in:
@ -222,10 +222,11 @@ class SerialLineSource:
|
|||||||
class SerialChunkReader:
|
class SerialChunkReader:
|
||||||
"""Быстрое неблокирующее чтение чанков из serial/raw TTY для максимального дренажа буфера."""
|
"""Быстрое неблокирующее чтение чанков из serial/raw TTY для максимального дренажа буфера."""
|
||||||
|
|
||||||
def __init__(self, src: SerialLineSource):
|
def __init__(self, src: SerialLineSource, error_counter: Optional[list] = None):
|
||||||
self._src = src
|
self._src = src
|
||||||
self._ser = src._pyserial
|
self._ser = src._pyserial
|
||||||
self._fd: Optional[int] = None
|
self._fd: Optional[int] = None
|
||||||
|
self._error_counter = error_counter # Список с 1 элементом для передачи по ссылке
|
||||||
if self._ser is not None:
|
if self._ser is not None:
|
||||||
# Неблокирующий режим для быстрой откачки
|
# Неблокирующий режим для быстрой откачки
|
||||||
try:
|
try:
|
||||||
@ -248,11 +249,15 @@ class SerialChunkReader:
|
|||||||
try:
|
try:
|
||||||
n = int(getattr(self._ser, "in_waiting", 0))
|
n = int(getattr(self._ser, "in_waiting", 0))
|
||||||
except Exception:
|
except Exception:
|
||||||
|
if self._error_counter:
|
||||||
|
self._error_counter[0] += 1
|
||||||
n = 0
|
n = 0
|
||||||
if n > 0:
|
if n > 0:
|
||||||
try:
|
try:
|
||||||
return self._ser.read(n)
|
return self._ser.read(n)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
if self._error_counter:
|
||||||
|
self._error_counter[0] += 1
|
||||||
return b""
|
return b""
|
||||||
return b""
|
return b""
|
||||||
if self._fd is None:
|
if self._fd is None:
|
||||||
@ -269,6 +274,8 @@ class SerialChunkReader:
|
|||||||
except BlockingIOError:
|
except BlockingIOError:
|
||||||
break
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
|
if self._error_counter:
|
||||||
|
self._error_counter[0] += 1
|
||||||
break
|
break
|
||||||
return bytes(out)
|
return bytes(out)
|
||||||
|
|
||||||
@ -297,6 +304,12 @@ class SweepReader(threading.Thread):
|
|||||||
self._n_valid_hist = deque()
|
self._n_valid_hist = deque()
|
||||||
# Счетчик потерь данных (выброшенных свипов из-за переполнения очереди)
|
# Счетчик потерь данных (выброшенных свипов из-за переполнения очереди)
|
||||||
self._dropped_sweeps: int = 0
|
self._dropped_sweeps: int = 0
|
||||||
|
# Диагностика потери точек внутри свипа
|
||||||
|
self._total_lines_received: int = 0 # Всего принято строк с данными
|
||||||
|
self._total_parse_errors: int = 0 # Ошибок парсинга строк
|
||||||
|
self._total_empty_lines: int = 0 # Пустых строк
|
||||||
|
self._max_buf_size: int = 0 # Максимальный размер буфера парсинга
|
||||||
|
self._read_errors: int = 0 # Ошибок чтения из порта
|
||||||
|
|
||||||
def _finalize_current(self, xs, ys):
|
def _finalize_current(self, xs, ys):
|
||||||
if not xs:
|
if not xs:
|
||||||
@ -381,6 +394,10 @@ class SweepReader(threading.Thread):
|
|||||||
"std": std,
|
"std": std,
|
||||||
"dt_ms": dt_ms,
|
"dt_ms": dt_ms,
|
||||||
"dropped": self._dropped_sweeps,
|
"dropped": self._dropped_sweeps,
|
||||||
|
"lines": self._total_lines_received,
|
||||||
|
"parse_err": self._total_parse_errors,
|
||||||
|
"read_err": self._read_errors,
|
||||||
|
"max_buf": self._max_buf_size,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Кладём готовый свип (если очередь полна — выбрасываем самый старый)
|
# Кладём готовый свип (если очередь полна — выбрасываем самый старый)
|
||||||
@ -412,12 +429,19 @@ class SweepReader(threading.Thread):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Быстрый неблокирующий дренаж порта с разбором по байтам
|
# Быстрый неблокирующий дренаж порта с разбором по байтам
|
||||||
chunk_reader = SerialChunkReader(self._src)
|
# Передаем счетчик ошибок чтения как список для изменения по ссылке
|
||||||
|
error_counter = [0]
|
||||||
|
chunk_reader = SerialChunkReader(self._src, error_counter)
|
||||||
buf = bytearray()
|
buf = bytearray()
|
||||||
while not self._stop.is_set():
|
while not self._stop.is_set():
|
||||||
data = chunk_reader.read_available()
|
data = chunk_reader.read_available()
|
||||||
|
# Обновляем счетчик ошибок чтения
|
||||||
|
self._read_errors = error_counter[0]
|
||||||
if data:
|
if data:
|
||||||
buf += data
|
buf += data
|
||||||
|
# Отслеживаем максимальный размер буфера парсинга
|
||||||
|
if len(buf) > self._max_buf_size:
|
||||||
|
self._max_buf_size = len(buf)
|
||||||
else:
|
else:
|
||||||
# Короткая уступка CPU, если нет новых данных (уменьшена до 0.1ms)
|
# Короткая уступка CPU, если нет новых данных (уменьшена до 0.1ms)
|
||||||
time.sleep(0.0001)
|
time.sleep(0.0001)
|
||||||
@ -433,6 +457,7 @@ class SweepReader(threading.Thread):
|
|||||||
if line.endswith(b"\r"):
|
if line.endswith(b"\r"):
|
||||||
line = line[:-1]
|
line = line[:-1]
|
||||||
if not line:
|
if not line:
|
||||||
|
self._total_empty_lines += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if line.startswith(b"Sweep_start"):
|
if line.startswith(b"Sweep_start"):
|
||||||
@ -449,9 +474,17 @@ class SweepReader(threading.Thread):
|
|||||||
x = int(parts[1], 10)
|
x = int(parts[1], 10)
|
||||||
y = int(parts[2], 10) # поддержка знака: "+…" и "-…"
|
y = int(parts[2], 10) # поддержка знака: "+…" и "-…"
|
||||||
except Exception:
|
except Exception:
|
||||||
|
self._total_parse_errors += 1
|
||||||
continue
|
continue
|
||||||
xs.append(x)
|
xs.append(x)
|
||||||
ys.append(y)
|
ys.append(y)
|
||||||
|
self._total_lines_received += 1
|
||||||
|
else:
|
||||||
|
# Строка не в формате "s X Y"
|
||||||
|
self._total_parse_errors += 1
|
||||||
|
else:
|
||||||
|
# Строка слишком короткая
|
||||||
|
self._total_parse_errors += 1
|
||||||
|
|
||||||
# Защита от переполнения буфера при отсутствии переводов строки (снижен порог)
|
# Защита от переполнения буфера при отсутствии переводов строки (снижен порог)
|
||||||
if len(buf) > 262144:
|
if len(buf) > 262144:
|
||||||
|
|||||||
Reference in New Issue
Block a user