Phase 1c: prune remaining dead functions, unused imports, quarantine Genesis

- uc480_camera: drop never-called _capture_paused/get_framerate (the
  hardware question _capture_paused encoded is now in KNOWN_ISSUES.md)
- t3r_protocol: drop read_reg/write_reg/decode_reg/Reg (commands never
  wired into the driver)
- bbd20x: drop _update0x0212 (never dispatched) and 8 of 9 unused
  trigger convenience wrappers; apt_constants: drop TriggerBitsStepper
  (servo-only rig)
- ruff --fix: 35 unused imports across all apps; drop unused T3R_BAUD
- genesis_core.py: quarantine warning header; docs/genesis_verification.md
  bench checklist for the 7 divergences vs tools/genesis_laser_gui.py

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Thomas Ales
2026-07-28 10:08:48 -05:00
parent 5148f0bca2
commit 67aabde4b6
17 changed files with 83 additions and 153 deletions
+10 -1
View File
@@ -2,6 +2,16 @@
Genesis SLM MX 532 Laser Core Hardware Control Module
======================================================
.. warning::
QUARANTINED — do not modify semantics or dedupe against
``tools/genesis_laser_gui.py`` until the bench checklist in
``docs/genesis_verification.md`` has been run. This module was
extracted from that GUI but diverges from it in ways only hardware can
adjudicate: ADS7828 command byte (0x84 vs 0x8C), LDD enable polarity
(inverted vs not), shutter semantics (manual vs bit-controlled),
dropped median-of-3 ADC filtering, dropped Amps/Watts scaling, dropped
temperature reads and pre-flight check.
This module provides low-level hardware control for the Genesis SLM MX 532 laser
using NXP I2C-over-serial protocol. It contains reusable classes for serial
communication, I2C protocol handling, device control, and laser operations.
@@ -34,7 +44,6 @@ from typing import Optional, List
from enum import IntEnum
import serial
from serial.tools import list_ports
# ============================================================================
-2
View File
@@ -1,9 +1,7 @@
"""pybbd202 - Thorlabs BBD202 servo stage driver (pyserial-based)"""
from .bbd20x import ThorlabsServoDriver
from .apt_constants import TriggerBitsServo, StatusBits
# Axis address constants
AXIS_X = 0x21
AXIS_Y = 0x22
CONTROLLER = 0x11
-10
View File
@@ -32,16 +32,6 @@ class StatusBits(IntFlag):
MOT_SB_COMMUTATIONERROR | MOT_SB_OVERLOAD |
MOT_SB_ERROR | MOT_SB_INSTRERROR)
class TriggerBitsStepper(IntFlag):
TRIGIN_ENABLE = 0x01,
TRIGOUT_ENABLE = 0x02,
TRIGOUT_MODEFOLLOW = 0x04,
TRIGOUT_MODEMOVEEND = 0x08,
TRIG_RELMOVE = 0x10,
TRIG_ABSMOVE = 0x20,
TRIG_HOMEMOVE = 0x40,
TRIGOUT_NOTRIGIN = 0x80
class TriggerBitsServo(IntFlag):
TRIGIN_HIGH = 0x01
TRIGIN_RELMOVE = 0x02
-1
View File
@@ -4,7 +4,6 @@
Version 1
'''
import struct
from .apt_constants import StatusBits as sb
class APTProtocol():
ADDRESSES = { 'HOST_PC': 0x01, 'CONTROLLER': 0x11,
-51
View File
@@ -288,25 +288,6 @@ class ThorlabsServoDriver():
self.am_moving[ch] = False
return
def _update0x0212(self, msg):
'''
_update0x0212 - internal function that listens for CHANENABLESTATE
messages.
'''
if msg['source'] == 0x21:
ch = 0
elif msg['source'] == 0x22:
ch = 1
else:
raise ValueError("Wherever this message came from, it's WRONG!")
if msg['enable_state'] == 0x01:
self.am_enabled[ch] = True # enabled
elif msg['enable_state'] == 0x02:
self.am_enabled[ch] = False # disabled
else:
raise ValueError("Am I a joke to you? WTF did this even come from?!")
# ── Axis control ─────────────────────────────────────────────
def enable_axis(self, axis):
@@ -473,38 +454,6 @@ class ThorlabsServoDriver():
destination=axis, source=0x01)
return TriggerBitsServo(result['mode'])
def set_trigger_trigin_high(self, axis):
'''Set trigger input to logic high.'''
self.set_trigger(axis, TriggerBitsServo.TRIGIN_HIGH)
def set_trigger_trigin_relmove(self, axis):
'''Set trigger input to initiate a relative move.'''
self.set_trigger(axis, TriggerBitsServo.TRIGIN_RELMOVE)
def set_trigger_trigin_absmove(self, axis):
'''Set trigger input to initiate an absolute move.'''
self.set_trigger(axis, TriggerBitsServo.TRIGIN_ABSMOVE)
def set_trigger_trigin_homemove(self, axis):
'''Set trigger input to initiate a home move.'''
self.set_trigger(axis, TriggerBitsServo.TRIGIN_HOMEMOVE)
def set_trigger_trigout_high(self, axis):
'''Set trigger output to logic high.'''
self.set_trigger(axis, TriggerBitsServo.TRIGOUT_HIGH)
def set_trigger_trigout_inmotion(self, axis):
'''Set trigger output high while axis is in motion.'''
self.set_trigger(axis, TriggerBitsServo.TRIGOUT_INMOTION)
def set_trigger_trigout_motioncomplete(self, axis):
'''Set trigger output to pulse when motion completes.'''
self.set_trigger(axis, TriggerBitsServo.TRIGOUT_MOTIONCOMPLETE)
def set_trigger_trigout_maxvelocity(self, axis):
'''Set trigger output to pulse at max velocity.'''
self.set_trigger(axis, TriggerBitsServo.TRIGOUT_MAXVELOCITY)
def set_trigger_trigout_maxv(self, axis):
'''Set trigger output high + pulse at max velocity (TRIGOUT_MAXV).'''
self.set_trigger(axis, TriggerBitsServo.TRIGOUT_MAXV)
-22
View File
@@ -193,14 +193,6 @@ def set_position(ch: int, position: int) -> bytes:
return build_frame(CMD_SET_POSITION, struct.pack("<Bi", ch, position))
def read_reg(ch: int, reg: int) -> bytes:
return build_frame(CMD_READ_REG, struct.pack("<BB", ch, reg))
def write_reg(ch: int, reg: int, value: int) -> bytes:
return build_frame(CMD_WRITE_REG, struct.pack("<BBI", ch, reg, value))
# ---------------------------------------------------------------------------
# Response / event decoders. Each returns a dataclass (or None on bad length).
# ---------------------------------------------------------------------------
@@ -261,13 +253,6 @@ class Position:
position: int
@dataclass
class Reg:
ch: int
reg: int
value: int
def decode_pong(p: bytes):
if len(p) < 4:
return None
@@ -304,13 +289,6 @@ def decode_position(p: bytes):
return Position(ch, pos)
def decode_reg(p: bytes):
if len(p) < 6:
return None
ch, reg, value = struct.unpack_from("<BBI", p, 0)
return Reg(ch, reg, value)
def decode_event_position(p: bytes):
"""MOTION_DONE / STOPPED share the (ch, position) layout."""
return decode_position(p)
-42
View File
@@ -14,7 +14,6 @@ from PyQt6.QtCore import QThread, pyqtSignal, QObject
from PyQt6.QtGui import QImage
import logging
import threading
from contextlib import contextmanager
from typing import List, Optional, Tuple
logger = logging.getLogger(__name__)
@@ -331,29 +330,6 @@ class UC480Camera(QObject):
logger.info("Video capture stopped")
return True
@contextmanager
def _capture_paused(self):
"""
Context manager that temporarily stops live video while a camera
parameter is being changed, then restarts it. Many IDS cameras
return IS_CANT_COMMUNICATE_WITH_DRIVER (17) or IS_NO_SUCCESS (-1)
when gain/exposure commands are issued during active capture.
"""
with self._settings_lock:
was_capturing = self.is_capturing
if was_capturing:
ueye.is_StopLiveVideo(self.h_cam, ueye.IS_WAIT)
self.is_capturing = False
try:
yield
finally:
if was_capturing:
ret = ueye.is_CaptureVideo(self.h_cam, ueye.IS_DONT_WAIT)
if ret == ueye.IS_SUCCESS:
self.is_capturing = True
else:
logger.error(f"Failed to restart capture after settings change: {ret}")
def get_frame(self) -> Optional[QImage]:
"""
Capture a single frame from the camera.
@@ -535,24 +511,6 @@ class UC480Camera(QObject):
logger.error(f"Failed to set framerate: {ret}")
return False
def get_framerate(self) -> Optional[float]:
"""
Get current framerate.
Returns:
Framerate in fps, or None if failed
"""
if not self.is_initialized:
return None
fps = ueye.c_double()
ret = ueye.is_GetFramesPerSecond(self.h_cam, fps)
if ret == ueye.IS_SUCCESS:
return fps.value
else:
return None
def set_gain(self, master_gain: int) -> bool:
"""
Set camera master gain.