Skip to content

Commit b2e91f9

Browse files
dccoteclaude
andcommitted
Millennia eV: status snapshot + preserve init error
Two small gaps on the eV driver that pushed work into downstream callers: * doInitializeDevice caught the failure and raised a bare PhysicalDevice.UnableToInitialize(), discarding the underlying serial error. Callers could no longer tell a busy port ("[Errno 16] Resource busy") from a missing one. Now re-raises UnableToInitialize(error) from error so the cause is preserved. * MillenniaEv25Device did not override doGetStatusUserInfo(), so the base returned None and PhysicalDevice.startBackgroundStatusUpdates() posted empty status notifications for the eV. Added an override returning {power, isLaserOn, isShutterOpen} — a one-call snapshot for background monitoring and for GUI polling. Adds a debug-device test for the status snapshot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6LVhfM3d9CcJdkuSMMqag
1 parent a30f969 commit b2e91f9

2 files changed

Lines changed: 28 additions & 2 deletions

File tree

hardwarelibrary/sources/millennia.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,13 @@ def doInitializeDevice(self):
8080
self.port = SerialPort(portPath=self.portPath)
8181
self.port.open(baudRate=self.defaultBaudRate)
8282
self.readIdentity()
83-
except Exception:
83+
except Exception as error:
8484
if self.port is not None and self.port.isOpen:
8585
self.port.close()
86-
raise PhysicalDevice.UnableToInitialize()
86+
# Preserve the underlying error (e.g. pyserial's "[Errno 16]
87+
# Resource busy") so callers can tell a busy port from a missing
88+
# one instead of seeing a bare, info-less UnableToInitialize.
89+
raise PhysicalDevice.UnableToInitialize(error) from error
8790

8891
def readIdentity(self):
8992
# The eV identifies via the SCPI-style *IDN? query. The ?IDN form in
@@ -197,6 +200,20 @@ def doGetPower(self) -> float:
197200
# token.
198201
return float(self.queryString("?P").split()[0])
199202

203+
# Status snapshot
204+
205+
def doGetStatusUserInfo(self) -> dict:
206+
# Single-call snapshot for PhysicalDevice.startBackgroundStatusUpdates()
207+
# (posted as PhysicalDeviceNotification.status) and for GUI polling: the
208+
# output power plus the diode (emission) and shutter states. Without this
209+
# override the base returns None, so the monitoring loop posts nothing
210+
# useful for the eV.
211+
return {
212+
"power": self.doGetPower(),
213+
"isLaserOn": self.doGetOnOffState(),
214+
"isShutterOpen": self.doGetShutterState(),
215+
}
216+
200217

201218
class DebugMillenniaEv25Device(MillenniaEv25Device):
202219
classIdVendor = 0xFFFF

hardwarelibrary/tests/testMillennia.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ def testShutterIsIndependentOfOnOff(self):
104104
self.assertFalse(self.laser.isLaserOn())
105105
self.assertTrue(self.laser.isShutterOpen()) # off, shutter unchanged
106106

107+
def testStatusUserInfoSnapshotsPowerOnOffAndShutter(self):
108+
self.laser.setPower(7.5)
109+
self.laser.turnOn()
110+
self.laser.openShutter()
111+
self.assertEqual(
112+
self.laser.doGetStatusUserInfo(),
113+
{"power": 7.5, "isLaserOn": True, "isShutterOpen": True},
114+
)
115+
107116

108117
class TestMillenniaEv25Device(unittest.TestCase):
109118
def setUp(self):

0 commit comments

Comments
 (0)