diff --git a/RFG_ADC_dataplotter.py b/RFG_ADC_dataplotter.py index ade3d22..08c8624 100755 --- a/RFG_ADC_dataplotter.py +++ b/RFG_ADC_dataplotter.py @@ -222,10 +222,11 @@ class SerialLineSource: class SerialChunkReader: """Быстрое неблокирующее чтение чанков из serial/raw TTY для максимального дренажа буфера.""" - def __init__(self, src: SerialLineSource): + def __init__(self, src: SerialLineSource, error_counter: Optional[list] = None): self._src = src self._ser = src._pyserial self._fd: Optional[int] = None + self._error_counter = error_counter # Список с 1 элементом для передачи по ссылке if self._ser is not None: # Неблокирующий режим для быстрой откачки try: @@ -248,11 +249,15 @@ class SerialChunkReader: try: n = int(getattr(self._ser, "in_waiting", 0)) except Exception: + if self._error_counter: + self._error_counter[0] += 1 n = 0 if n > 0: try: return self._ser.read(n) except Exception: + if self._error_counter: + self._error_counter[0] += 1 return b"" return b"" if self._fd is None: @@ -269,6 +274,8 @@ class SerialChunkReader: except BlockingIOError: break except Exception: + if self._error_counter: + self._error_counter[0] += 1 break return bytes(out) @@ -297,6 +304,12 @@ class SweepReader(threading.Thread): self._n_valid_hist = deque() # Счетчик потерь данных (выброшенных свипов из-за переполнения очереди) 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): if not xs: @@ -381,6 +394,10 @@ class SweepReader(threading.Thread): "std": std, "dt_ms": dt_ms, "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: # Быстрый неблокирующий дренаж порта с разбором по байтам - chunk_reader = SerialChunkReader(self._src) + # Передаем счетчик ошибок чтения как список для изменения по ссылке + error_counter = [0] + chunk_reader = SerialChunkReader(self._src, error_counter) buf = bytearray() while not self._stop.is_set(): data = chunk_reader.read_available() + # Обновляем счетчик ошибок чтения + self._read_errors = error_counter[0] if data: buf += data + # Отслеживаем максимальный размер буфера парсинга + if len(buf) > self._max_buf_size: + self._max_buf_size = len(buf) else: # Короткая уступка CPU, если нет новых данных (уменьшена до 0.1ms) time.sleep(0.0001) @@ -433,6 +457,7 @@ class SweepReader(threading.Thread): if line.endswith(b"\r"): line = line[:-1] if not line: + self._total_empty_lines += 1 continue if line.startswith(b"Sweep_start"): @@ -449,9 +474,17 @@ class SweepReader(threading.Thread): x = int(parts[1], 10) y = int(parts[2], 10) # поддержка знака: "+…" и "-…" except Exception: + self._total_parse_errors += 1 continue xs.append(x) 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: