Skip to content

Commit bfadc9a

Browse files
authored
Feat(hardware-testing): make gravimetric qc a protocol. (#18466)
<!-- Thanks for taking the time to open a Pull Request (PR)! Please make sure you've read the "Opening Pull Requests" section of our Contributing Guide: https://github.com/Opentrons/opentrons/blob/edge/CONTRIBUTING.md#opening-pull-requests GitHub provides robust markdown to format your PR. Links, diagrams, pictures, and videos along with text formatting make it possible to create a rich and informative PR. For more information on GitHub markdown, see: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax To ensure your code is reviewed quickly and thoroughly, please fill out the sections below to the best of your ability! --> # Overview Historically the Gravimetric and photometric fixtures in the factory have been running on scripts. This process is slow, hard to use, hard to update and not very flexible. This PR is the last in a long series of steps that will finally get us off the scripts and onto the protocol based version of this. This has been enabled by the recent features of LLD, Liquid Tracking, Liquid Classes, Run time parameters and error recovery. This PR also adds a new set of methods on the ot3api level that enables the hardware controller to easily pull sensor data from the instruments. <!-- Describe your PR at a high level. State acceptance criteria and how this PR fits into other work. Link issues, PRs, and other relevant resources. --> ## Test Plan and Hands on Testing This does not touch any existing features, and only adds functions to a non-public api level so there is no additional testing that needs to be added for the release. <!-- Describe your testing of the PR. Emphasize testing not reflected in the code. Attach protocols, logs, screenshots and any other assets that support your testing. --> ## Changelog <!-- List changes introduced by this PR considering future developers and the end user. Give careful thought and clear documentation to breaking changes. --> ## Review requests <!-- - What do you need from reviewers to feel confident this PR is ready to merge? - Ask questions. --> ## Risk assessment <!-- - Indicate the level of attention this PR needs. - Provide context to guide reviewers. - Discuss trade-offs, coupling, and side effects. - Look for the possibility, even if you think it's small, that your change may affect some other part of the system. - For instance, changing return tip behavior may also change the behavior of labware calibration. - How do your unit tests and on hands on testing mitigate this PR's risks and the risk of future regressions? - Especially in high risk PRs, explain how you know your testing is enough. -->
1 parent 26c3bf0 commit bfadc9a

File tree

17 files changed

+1052
-948
lines changed

17 files changed

+1052
-948
lines changed

api/src/opentrons/hardware_control/backends/flex_protocol.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from ..dev_types import OT3AttachedInstruments
4343
from .types import HWStopCondition
4444

45+
4546
Cls = TypeVar("Cls")
4647

4748

@@ -466,3 +467,27 @@ async def get_hepa_uv_state(self) -> Optional[HepaUVState]:
466467
async def increase_evo_disp_count(self, mount: OT3Mount) -> None:
467468
"""Tell a pipette to increase it's evo-tip-dispense-count in eeprom."""
468469
...
470+
471+
async def read_env_temp_sensor(
472+
self, mount: OT3Mount, primary: bool
473+
) -> Optional[float]:
474+
"""Read and return the current sensor information."""
475+
...
476+
477+
async def read_env_hum_sensor(
478+
self, mount: OT3Mount, primary: bool
479+
) -> Optional[float]:
480+
"""Read and return the current sensor information."""
481+
...
482+
483+
async def read_pressure_sensor(
484+
self, mount: OT3Mount, primary: bool
485+
) -> Optional[float]:
486+
"""Read and return the current sensor information."""
487+
...
488+
489+
async def read_capacitive_sensor(
490+
self, mount: OT3Mount, primary: bool
491+
) -> Optional[float]:
492+
"""Read and return the current sensor information."""
493+
...

api/src/opentrons/hardware_control/backends/ot3controller.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,13 @@
215215
from .types import HWStopCondition
216216
from .flex_protocol import FlexBackend
217217
from .status_bar_state import StatusBarStateController
218-
from opentrons_hardware.sensors.types import SensorDataType
218+
from opentrons_hardware.sensors.sensor_types import (
219+
EnvironmentSensor,
220+
CapacitiveSensor,
221+
PressureSensor,
222+
)
223+
from opentrons_hardware.sensors.types import SensorDataType, EnvironmentSensorDataType
224+
from opentrons_hardware.sensors.sensor_driver import SensorDriver
219225
from opentrons_hardware.sensors.utils import send_evo_dispense_count_increase
220226

221227
log = logging.getLogger(__name__)
@@ -1835,3 +1841,72 @@ async def increase_evo_disp_count(self, mount: OT3Mount) -> None:
18351841
await send_evo_dispense_count_increase(
18361842
self._messenger, sensor_node_for_pipette(OT3Mount(mount.value))
18371843
)
1844+
1845+
async def _read_env_sensor(
1846+
self, mount: OT3Mount, primary: bool
1847+
) -> Optional[EnvironmentSensorDataType]:
1848+
"""Read and return the current sensor information."""
1849+
sensor = EnvironmentSensor.build(
1850+
sensor_id=SensorId.S0 if primary else SensorId.S1,
1851+
node_id=sensor_node_for_mount(mount),
1852+
)
1853+
s_driver = SensorDriver()
1854+
sensor_data = await s_driver.read(
1855+
can_messenger=self._messenger,
1856+
sensor=sensor,
1857+
offset=False,
1858+
)
1859+
assert sensor_data is None or isinstance(sensor_data, EnvironmentSensorDataType)
1860+
return sensor_data
1861+
1862+
async def read_env_temp_sensor(
1863+
self, mount: OT3Mount, primary: bool
1864+
) -> Optional[float]:
1865+
"""Read and return the current sensor information."""
1866+
s_data = await self._read_env_sensor(mount, primary)
1867+
if s_data is None or s_data.temperature is None:
1868+
return None
1869+
return s_data.temperature.to_float()
1870+
1871+
async def read_env_hum_sensor(
1872+
self, mount: OT3Mount, primary: bool
1873+
) -> Optional[float]:
1874+
"""Read and return the current sensor information."""
1875+
s_data = await self._read_env_sensor(mount, primary)
1876+
if s_data is None or s_data.humidity is None:
1877+
return None
1878+
return s_data.humidity.to_float()
1879+
1880+
async def read_pressure_sensor(
1881+
self, mount: OT3Mount, primary: bool
1882+
) -> Optional[float]:
1883+
"""Read and return the current sensor information."""
1884+
sensor = PressureSensor.build(
1885+
sensor_id=SensorId.S0 if primary else SensorId.S1,
1886+
node_id=sensor_node_for_mount(mount),
1887+
)
1888+
s_driver = SensorDriver()
1889+
sensor_data = await s_driver.read(
1890+
can_messenger=self._messenger,
1891+
sensor=sensor,
1892+
offset=False,
1893+
)
1894+
assert sensor_data is None or isinstance(sensor_data, SensorDataType)
1895+
return sensor_data.to_float() if sensor_data else None
1896+
1897+
async def read_capacitive_sensor(
1898+
self, mount: OT3Mount, primary: bool
1899+
) -> Optional[float]:
1900+
"""Read and return the current sensor information."""
1901+
sensor = CapacitiveSensor.build(
1902+
sensor_id=SensorId.S0 if primary else SensorId.S1,
1903+
node_id=sensor_node_for_mount(mount),
1904+
)
1905+
s_driver = SensorDriver()
1906+
sensor_data = await s_driver.read(
1907+
can_messenger=self._messenger,
1908+
sensor=sensor,
1909+
offset=False,
1910+
)
1911+
assert sensor_data is None or isinstance(sensor_data, SensorDataType)
1912+
return sensor_data.to_float() if sensor_data else None

api/src/opentrons/hardware_control/backends/ot3simulator.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
FlexBackend,
6868
)
6969

70+
7071
log = logging.getLogger(__name__)
7172

7273
AXIS_TO_SUBSYSTEM = {
@@ -874,3 +875,29 @@ def _update_tip_state(self, mount: OT3Mount, status: bool) -> None:
874875

875876
async def increase_evo_disp_count(self, mount: OT3Mount) -> None:
876877
pass
878+
879+
async def read_env_temp_sensor(
880+
self, mount: OT3Mount, primary: bool
881+
) -> Optional[float]:
882+
"""Read and return the current sensor information."""
883+
884+
return 0.0
885+
886+
async def read_env_hum_sensor(
887+
self, mount: OT3Mount, primary: bool
888+
) -> Optional[float]:
889+
"""Read and return the current sensor information."""
890+
891+
return 0.0
892+
893+
async def read_pressure_sensor(
894+
self, mount: OT3Mount, primary: bool
895+
) -> Optional[float]:
896+
"""Read and return the current sensor information."""
897+
return 0.0
898+
899+
async def read_capacitive_sensor(
900+
self, mount: OT3Mount, primary: bool
901+
) -> Optional[float]:
902+
"""Read and return the current sensor information."""
903+
return 0.0

api/src/opentrons/hardware_control/ot3api.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3140,3 +3140,35 @@ async def increase_evo_disp_count(
31403140
"""Tell a pipette to increase its evo-tip-dispense-count in eeprom."""
31413141
realmount = OT3Mount.from_mount(mount)
31423142
await self._backend.increase_evo_disp_count(realmount)
3143+
3144+
async def read_stem_temperature(
3145+
self, mount: Union[top_types.Mount, OT3Mount], primary: bool = True
3146+
) -> float:
3147+
"""Read and return the current stem temperature."""
3148+
realmount = OT3Mount.from_mount(mount)
3149+
s_data = await self._backend.read_env_temp_sensor(realmount, primary)
3150+
return s_data if s_data else 0.0
3151+
3152+
async def read_stem_humidity(
3153+
self, mount: Union[top_types.Mount, OT3Mount], primary: bool = True
3154+
) -> float:
3155+
"""Read and return the current primary stem humidity."""
3156+
realmount = OT3Mount.from_mount(mount)
3157+
s_data = await self._backend.read_env_hum_sensor(realmount, primary)
3158+
return s_data if s_data else 0.0
3159+
3160+
async def read_stem_pressure(
3161+
self, mount: Union[top_types.Mount, OT3Mount], primary: bool = True
3162+
) -> float:
3163+
"""Read and return the current primary stem pressure."""
3164+
realmount = OT3Mount.from_mount(mount)
3165+
s_data = await self._backend.read_pressure_sensor(realmount, primary)
3166+
return s_data if s_data else 0.0
3167+
3168+
async def read_stem_capacitance(
3169+
self, mount: Union[top_types.Mount, OT3Mount], primary: bool = True
3170+
) -> float:
3171+
"""Read and return the current primary stem capacitance."""
3172+
realmount = OT3Mount.from_mount(mount)
3173+
s_data = await self._backend.read_capacitive_sensor(realmount, primary)
3174+
return s_data if s_data else 0.0

hardware-testing/Makefile

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -85,23 +85,6 @@ test:
8585
test-cov:
8686
$(pytest) $(tests) $(test_opts) $(cov_opts)
8787

88-
.PHONY: test-photometric-single
89-
test-photometric-single:
90-
$(python) -m hardware_testing.gravimetric --photometric --simulate --pipette 50 --channels 1 --tip 50
91-
$(python) -m hardware_testing.gravimetric --photometric --simulate --pipette 50 --channels 1 --tip 50 --photoplate-col-offset 3
92-
$(python) -m hardware_testing.gravimetric --photometric --simulate --pipette 50 --channels 1 --tip 50 --dye-well-col-offset 3
93-
94-
.PHONY: test-photometric-multi
95-
test-photometric-multi:
96-
$(python) -m hardware_testing.gravimetric --photometric --simulate --pipette 50 --channels 8 --tip 50
97-
98-
.PHONY: test-photometric
99-
test-photometric:
100-
$(python) -m hardware_testing.gravimetric --photometric --simulate --pipette 1000 --channels 96 --tip 50 --trials 1
101-
$(python) -m hardware_testing.gravimetric --photometric --simulate --pipette 1000 --channels 96 --tip 200 --trials 1
102-
$(python) -m hardware_testing.gravimetric --photometric --simulate --pipette 200 --channels 96 --tip 50 --trials 1
103-
$(python) -m hardware_testing.gravimetric --photometric --simulate --pipette 200 --channels 96 --tip 20 --trials 1
104-
10588
.PHONY: test-gravimetric-single
10689
test-gravimetric-single:
10790
$(python) -m hardware_testing.gravimetric --simulate --pipette 1000 --channels 1
@@ -133,7 +116,6 @@ test-gravimetric:
133116
$(MAKE) test-gravimetric-single
134117
$(MAKE) test-gravimetric-multi
135118
$(MAKE) test-gravimetric-96
136-
$(MAKE) test-photometric
137119

138120
.PHONY: test-production-qc
139121
test-production-qc:

0 commit comments

Comments
 (0)