diff options
| author | Christian Kolset <christian.kolset@gmail.com> | 2026-04-20 16:55:57 -0600 |
|---|---|---|
| committer | Christian Kolset <christian.kolset@gmail.com> | 2026-04-20 16:55:57 -0600 |
| commit | d5acb04b88373d33b038bb59945fb5ab8b4f543b (patch) | |
| tree | 21c37f2b54258ef20edc1b9b7226327d5f5b1a3a /core/acquisition.py | |
| parent | 425ba78ee1f760978b23a09fe8acbbc9b8b5dae4 (diff) | |
V8
Diffstat (limited to 'core/acquisition.py')
| -rw-r--r-- | core/acquisition.py | 83 |
1 files changed, 25 insertions, 58 deletions
diff --git a/core/acquisition.py b/core/acquisition.py index 640e6d5..afccb56 100644 --- a/core/acquisition.py +++ b/core/acquisition.py @@ -3,7 +3,7 @@ core/acquisition.py Background acquisition engine. Polls all connected devices, buffers data, fires Qt signals, -writes CSV logs, and checks alarm thresholds. +and writes CSV logs. """ import csv @@ -18,7 +18,7 @@ from PyQt6.QtCore import QObject, pyqtSignal from devices.base_device import BaseDevice, DeviceStatus -MAX_BUFFER = 20_000 # samples per channel +MAX_BUFFER = 20_000 class ChannelBuffer: @@ -33,7 +33,6 @@ class ChannelBuffer: self.values.append(v) def window(self, seconds: float) -> Tuple[List[float], List[float]]: - """Return the last `seconds` worth of data.""" if not self.times: return [], [] cutoff = self.times[-1] - seconds @@ -63,35 +62,29 @@ class AcquisitionEngine(QObject): Signals ------- new_data(device_id, channel_id, timestamp, value) - alarm_triggered(device_id, channel_id, value, kind) kind: "low"|"high" device_status_changed(device_id, status_str) log_started(filepath) log_stopped(filepath) """ - new_data = pyqtSignal(str, str, float, float) - alarm_triggered = pyqtSignal(str, str, float, str) - device_status_changed = pyqtSignal(str, str) - log_started = pyqtSignal(str) - log_stopped = pyqtSignal(str) + new_data = pyqtSignal(str, str, float, float) + device_status_changed = pyqtSignal(str, str) + log_started = pyqtSignal(str) + log_stopped = pyqtSignal(str) def __init__(self, poll_interval_ms: int = 100): super().__init__() self._interval = poll_interval_ms / 1000.0 - self._devices: List[BaseDevice] = [] - self._buffers: Dict[str, Dict[str, ChannelBuffer]] = {} - self._running = False - self._thread: Optional[threading.Thread] = None - self._t0 = 0.0 - - # Logging - self._logging = False - self._log_path = "" - self._csv_file = None - self._csv_writer = None - - # Alarm dedup - self._alarm_state: Dict[str, bool] = {} + self._devices: List[BaseDevice] = [] + self._buffers: Dict[str, Dict[str, ChannelBuffer]] = {} + self._running = False + self._thread: Optional[threading.Thread] = None + self._t0 = 0.0 + + self._logging = False + self._log_path = "" + self._csv_file = None + self._csv_writer = None # ── Device management ──────────────────────────────────────────────── @@ -128,10 +121,11 @@ class AcquisitionEngine(QObject): # ── Logging ────────────────────────────────────────────────────────── def start_logging(self, filepath: str = "") -> str: - if not filepath: - os.makedirs("logs", exist_ok=True) - ts = datetime.now().strftime("%Y%m%d_%H%M%S") - filepath = f"logs/daq_{ts}.csv" + if not filepath or filepath.endswith(os.sep) or filepath.endswith("/"): + dir_ = filepath if filepath else "logs" + os.makedirs(dir_, exist_ok=True) + ts = datetime.now().strftime("%Y%m%d_%H%M%S") + filepath = os.path.join(dir_, f"daq_{ts}.csv") self._log_path = filepath self._csv_file = open(filepath, "w", newline="") headers = ["elapsed_s"] @@ -163,9 +157,9 @@ class AcquisitionEngine(QObject): def _loop(self): while self._running: - t_start = time.time() - elapsed = t_start - self._t0 - log_row = [f"{elapsed:.4f}"] + t_start = time.time() + elapsed = t_start - self._t0 + log_row = [f"{elapsed:.4f}"] for dev in list(self._devices): active = dev.status in (DeviceStatus.CONNECTED, DeviceStatus.SIMULATED) @@ -180,15 +174,11 @@ class AcquisitionEngine(QObject): if val is None: log_row.append("") continue - # Buffer buf = self._buffers.get(dev.info.device_id, {}).get(ch.channel_id) if buf is not None: buf.append(elapsed, float(val)) - # Signal self.new_data.emit(dev.info.device_id, ch.channel_id, elapsed, float(val)) log_row.append(f"{val:.5f}") - # Alarms - self._check_alarm(dev.info.device_id, ch, float(val)) if self._logging and self._csv_writer: try: @@ -196,30 +186,7 @@ class AcquisitionEngine(QObject): except Exception: pass - # Sleep remainder of interval - dt = time.time() - t_start + dt = time.time() - t_start sleep = self._interval - dt if sleep > 0: time.sleep(sleep) - - # ── Alarm logic ─────────────────────────────────────────────────────── - - def _check_alarm(self, device_id: str, ch, val: float): - lo_key = f"{device_id}.{ch.channel_id}.lo" - hi_key = f"{device_id}.{ch.channel_id}.hi" - - if ch.alarm_low is not None: - if val < ch.alarm_low: - if not self._alarm_state.get(lo_key): - self._alarm_state[lo_key] = True - self.alarm_triggered.emit(device_id, ch.channel_id, val, "low") - else: - self._alarm_state[lo_key] = False - - if ch.alarm_high is not None: - if val > ch.alarm_high: - if not self._alarm_state.get(hi_key): - self._alarm_state[hi_key] = True - self.alarm_triggered.emit(device_id, ch.channel_id, val, "high") - else: - self._alarm_state[hi_key] = False |
