-
Notifications
You must be signed in to change notification settings - Fork 41
[S30-130][fix] Store e-beam current and voltage as metadata #3490
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,7 +25,7 @@ | |
| import numpy | ||
|
|
||
| from odemis import model | ||
| from odemis.driver import static | ||
| from odemis.driver import simsem, static | ||
| from odemis.model import Microscope | ||
| from odemis.odemisd.mdupdater import MetadataUpdater | ||
| from odemis.util import mock | ||
|
|
@@ -88,5 +88,89 @@ def fake_get_component(name): | |
| mdup.terminate() | ||
|
|
||
|
|
||
| class EbeamMDUpdaterTest(unittest.TestCase): | ||
| """ | ||
| Tests for e-beam metadata propagation via MetadataUpdater, using mock components. | ||
| """ | ||
|
|
||
| @classmethod | ||
| def setUpClass(cls): | ||
| # Create a minimal sparc2 microscope | ||
| cls.mic = Microscope("Fake SPARC2", "sparc2") | ||
|
|
||
| # Create a SimSEM with an e-beam scanner and an se-detector child | ||
| cls.sem = simsem.SimSEM( | ||
| "SEM", | ||
| "sem", | ||
| children={ | ||
| "scanner": {"name": "e-beam scanner", "role": "e-beam"}, | ||
| "detector0": {"name": "se-detector", "role": "se-detector"}, | ||
| } | ||
| ) | ||
| cls.ebeam = next(c for c in cls.sem.children.value if c.role == "e-beam") | ||
|
|
||
| # Create a fake CCD that will be affected by the e-beam | ||
| img = model.DataArray(numpy.empty((512, 768), dtype=numpy.uint16)) | ||
| cls.ccd = mock.FakeCCD(img) | ||
|
|
||
| cls.ebeam.affects.value = [cls.ccd.name, "se-detector"] | ||
|
|
||
|
Comment on lines
+116
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate metadata propagation on the actual The test configures two affected targets ( Suggested patch cls.ebeam = next(c for c in cls.sem.children.value if c.role == "e-beam")
+ cls.se_detector = next(c for c in cls.sem.children.value if c.role == "se-detector")
@@
md = self.ccd.getMetadata()
+ md_se = self.se_detector.getMetadata()
@@
self.assertAlmostEqual(md[model.MD_BEAM_CURRENT], self.ebeam.probeCurrent.value)
self.assertAlmostEqual(md[model.MD_BEAM_VOLTAGE], self.ebeam.accelVoltage.value)
+ self.assertAlmostEqual(md_se[model.MD_BEAM_CURRENT], self.ebeam.probeCurrent.value)
+ self.assertAlmostEqual(md_se[model.MD_BEAM_VOLTAGE], self.ebeam.accelVoltage.value)
@@
md = self.ccd.getMetadata()
+ md_se = self.se_detector.getMetadata()
self.assertAlmostEqual(md[model.MD_BEAM_CURRENT], new_current)
+ self.assertAlmostEqual(md_se[model.MD_BEAM_CURRENT], new_current)
@@
md = self.ccd.getMetadata()
+ md_se = self.se_detector.getMetadata()
self.assertAlmostEqual(md[model.MD_BEAM_VOLTAGE], new_voltage)
+ self.assertAlmostEqual(md_se[model.MD_BEAM_VOLTAGE], new_voltage)Also applies to: 139-173 🤖 Prompt for AI Agents |
||
| # Mock model.getComponent() so MetadataUpdater can resolve component names | ||
| all_comps = list(cls.sem.children.value) + [cls.sem, cls.ccd] | ||
|
|
||
| def fake_get_component(name): | ||
| for c in all_comps: | ||
| if c.name == name: | ||
| return c | ||
| raise LookupError(f"no component {name}") | ||
|
|
||
| cls._patch_get_component = unittest.mock.patch.object(model, "getComponent", fake_get_component) | ||
| cls._patch_get_component.start() | ||
|
|
||
| cls.mdup = MetadataUpdater("MDUpdater", cls.mic) | ||
| cls.mic.alive.value = set(cls.sem.children.value) | {cls.ccd} | ||
|
|
||
| @classmethod | ||
| def tearDownClass(cls): | ||
| cls.mdup.terminate() | ||
| cls.sem.terminate() | ||
| cls._patch_get_component.stop() | ||
|
Comment on lines
+97
to
+137
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add required type hints and function docstrings on new test methods/helpers.
Suggested patch class EbeamMDUpdaterTest(unittest.TestCase):
@@
`@classmethod`
- def setUpClass(cls):
+ def setUpClass(cls) -> None:
+ """
+ Build a simulated microscope setup and start metadata updater wiring.
+ """
@@
- def fake_get_component(name):
+ def fake_get_component(name: str):
+ """
+ Resolve a component by its name from the local simulated component list.
+ """
for c in all_comps:
if c.name == name:
return c
raise LookupError(f"no component {name}")
@@
`@classmethod`
- def tearDownClass(cls):
+ def tearDownClass(cls) -> None:
+ """
+ Tear down updater, simulated hardware, and patches created for this test class.
+ """
cls.mdup.terminate()
cls.sem.terminate()
cls._patch_get_component.stop()
@@
- def test_ebeam_beam_params_on_detector(self):
+ def test_ebeam_beam_params_on_detector(self) -> None:As per coding guidelines: Also applies to: 139-139 🧰 Tools🪛 Ruff (0.15.15)[warning] 119-119: Consider Replace with (RUF005) 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
|
|
||
| def test_ebeam_beam_params_on_detector(self): | ||
| """ | ||
| Verify that when probeCurrent or accelVoltage change on the e-beam, | ||
| MD_BEAM_CURRENT and MD_BEAM_VOLTAGE are updated on the affected detectors. | ||
| """ | ||
| # Both VAs must be present on the simulated e-beam | ||
| self.assertTrue(model.hasVA(self.ebeam, "probeCurrent"), | ||
| "e-beam has no probeCurrent VA") | ||
| self.assertTrue(model.hasVA(self.ebeam, "accelVoltage"), | ||
| "e-beam has no accelVoltage VA") | ||
|
|
||
| # After setup, the metadata should already be populated (init=True on subscribe) | ||
| md = self.ccd.getMetadata() | ||
| self.assertIn(model.MD_BEAM_CURRENT, md, | ||
| "MD_BEAM_CURRENT not set on CCD after startup") | ||
| self.assertIn(model.MD_BEAM_VOLTAGE, md, | ||
| "MD_BEAM_VOLTAGE not set on CCD after startup") | ||
| self.assertAlmostEqual(md[model.MD_BEAM_CURRENT], self.ebeam.probeCurrent.value) | ||
| self.assertAlmostEqual(md[model.MD_BEAM_VOLTAGE], self.ebeam.accelVoltage.value) | ||
|
|
||
| # Change probeCurrent => MD_BEAM_CURRENT must be updated | ||
| new_current = self.ebeam.probeCurrent.choices - {self.ebeam.probeCurrent.value} | ||
| new_current = next(iter(new_current)) # pick any value different from current | ||
| self.ebeam.probeCurrent.value = new_current | ||
|
pieleric marked this conversation as resolved.
|
||
| md = self.ccd.getMetadata() | ||
| self.assertAlmostEqual(md[model.MD_BEAM_CURRENT], new_current) | ||
|
|
||
| # Change accelVoltage => MD_BEAM_VOLTAGE must be updated | ||
| orig_voltage = self.ebeam.accelVoltage.value | ||
| voltage_range = self.ebeam.accelVoltage.range | ||
| new_voltage = voltage_range[1] if orig_voltage == voltage_range[0] else voltage_range[0] | ||
| self.ebeam.accelVoltage.value = new_voltage | ||
| md = self.ccd.getMetadata() | ||
| self.assertAlmostEqual(md[model.MD_BEAM_VOLTAGE], new_voltage) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() | ||
Uh oh!
There was an error while loading. Please reload this page.