puda-drivers 0.0.17__tar.gz → 0.0.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/PKG-INFO +6 -4
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/README.md +3 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/pyproject.toml +7 -3
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/labware/labware.py +1 -2
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/machines/first.py +123 -104
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/.gitignore +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/LICENSE +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/__init__.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/core/__init__.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/core/logging.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/core/position.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/core/serialcontroller.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/cv/__init__.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/cv/camera.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/labware/__init__.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/labware/opentrons_96_tiprack_300ul.json +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/labware/polyelectric_8_wellplate_30000ul.json +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/labware/trash_bin.json +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/machines/__init__.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/move/__init__.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/move/deck.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/move/gcode.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/move/grbl/__init__.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/move/grbl/api.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/move/grbl/constants.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/py.typed +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/__init__.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/api.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/constants.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/rLine.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/tests/example.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/tests/first.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/tests/pipette.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/tests/poll_position.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/tests/qubot.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/tests/test_deck.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/tests/test_position_polling.py +0 -0
- {puda_drivers-0.0.17 → puda_drivers-0.0.18}/tests/webcam.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: puda-drivers
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.18
|
|
4
4
|
Summary: Hardware drivers for the PUDA platform.
|
|
5
|
-
Project-URL: Homepage, https://github.com/PUDAP/puda
|
|
6
|
-
Project-URL: Issues, https://github.com/PUDAP/puda
|
|
5
|
+
Project-URL: Homepage, https://github.com/PUDAP/puda
|
|
6
|
+
Project-URL: Issues, https://github.com/PUDAP/puda/issues
|
|
7
7
|
Author-email: zhao <20024592+agentzhao@users.noreply.github.com>
|
|
8
8
|
License-Expression: MIT
|
|
9
9
|
License-File: LICENSE
|
|
@@ -18,7 +18,6 @@ Requires-Python: >=3.10
|
|
|
18
18
|
Requires-Dist: nats-py>=2.12.0
|
|
19
19
|
Requires-Dist: opencv-python>=4.12.0.88
|
|
20
20
|
Requires-Dist: pyserial~=3.5
|
|
21
|
-
Requires-Dist: pytest>=9.0.2
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
23
22
|
|
|
24
23
|
# puda-drivers
|
|
@@ -260,6 +259,9 @@ uv run pytest tests/ --cov=puda_drivers --cov-report=html
|
|
|
260
259
|
# Build distribution packages
|
|
261
260
|
uv build
|
|
262
261
|
|
|
262
|
+
# cd to puda project root
|
|
263
|
+
cd ...
|
|
264
|
+
|
|
263
265
|
# Publish to PyPI
|
|
264
266
|
uv publish
|
|
265
267
|
# Username: __token__
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "puda-drivers"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.18"
|
|
4
4
|
description = "Hardware drivers for the PUDA platform."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -18,10 +18,14 @@ classifiers = [
|
|
|
18
18
|
]
|
|
19
19
|
license = "MIT"
|
|
20
20
|
license-files = ["LICEN[CS]E*"]
|
|
21
|
+
|
|
21
22
|
dependencies = [
|
|
22
23
|
"nats-py>=2.12.0",
|
|
23
24
|
"opencv-python>=4.12.0.88",
|
|
24
25
|
"pyserial~=3.5",
|
|
26
|
+
]
|
|
27
|
+
[dependency-groups]
|
|
28
|
+
dev = [
|
|
25
29
|
"pytest>=9.0.2",
|
|
26
30
|
]
|
|
27
31
|
|
|
@@ -30,5 +34,5 @@ requires = ["hatchling >= 1.26"]
|
|
|
30
34
|
build-backend = "hatchling.build"
|
|
31
35
|
|
|
32
36
|
[project.urls]
|
|
33
|
-
Homepage = "https://github.com/PUDAP/puda
|
|
34
|
-
Issues = "https://github.com/PUDAP/puda
|
|
37
|
+
Homepage = "https://github.com/PUDAP/puda"
|
|
38
|
+
Issues = "https://github.com/PUDAP/puda/issues"
|
|
@@ -141,10 +141,10 @@ class First:
|
|
|
141
141
|
self._logger.info("Homing gantry...")
|
|
142
142
|
self.qubot.home()
|
|
143
143
|
|
|
144
|
-
# Initialize the pipette
|
|
144
|
+
# Initialize the pipette
|
|
145
145
|
self._logger.info("Initializing pipette...")
|
|
146
146
|
self.pipette.initialize()
|
|
147
|
-
time.sleep(
|
|
147
|
+
time.sleep(3) # need to wait for the pipette to initialize
|
|
148
148
|
self._logger.info("Machine startup complete - ready for operations")
|
|
149
149
|
|
|
150
150
|
def shutdown(self):
|
|
@@ -159,6 +159,8 @@ class First:
|
|
|
159
159
|
self.camera.disconnect()
|
|
160
160
|
self._logger.info("Machine shutdown complete")
|
|
161
161
|
|
|
162
|
+
### Queue (public commands) ###
|
|
163
|
+
|
|
162
164
|
async def get_position(self) -> Dict[str, Union[Dict[str, float], int]]:
|
|
163
165
|
"""
|
|
164
166
|
Get the current position of the machine. Both QuBot and Sartorius are queried.
|
|
@@ -175,8 +177,18 @@ class First:
|
|
|
175
177
|
"qubot": qubot_position.to_dict(),
|
|
176
178
|
"pipette": sartorius_position,
|
|
177
179
|
}
|
|
180
|
+
|
|
181
|
+
def get_deck(self):
|
|
182
|
+
"""
|
|
183
|
+
Get the current deck layout.
|
|
178
184
|
|
|
179
|
-
|
|
185
|
+
Returns:
|
|
186
|
+
Dictionary mapping slot names (e.g., "A1") to labware classes.
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
None
|
|
190
|
+
"""
|
|
191
|
+
return self.deck.to_dict()
|
|
180
192
|
|
|
181
193
|
def load_labware(self, slot: str, labware_name: str):
|
|
182
194
|
"""
|
|
@@ -205,19 +217,6 @@ class First:
|
|
|
205
217
|
"""
|
|
206
218
|
self.deck.empty_slot(slot=slot)
|
|
207
219
|
self._logger.debug("Slot '%s' emptied", slot)
|
|
208
|
-
|
|
209
|
-
def get_deck(self):
|
|
210
|
-
"""
|
|
211
|
-
Get the current deck layout.
|
|
212
|
-
|
|
213
|
-
Returns:
|
|
214
|
-
Dictionary mapping slot names (e.g., "A1") to labware classes.
|
|
215
|
-
|
|
216
|
-
Raises:
|
|
217
|
-
None
|
|
218
|
-
"""
|
|
219
|
-
return self.deck.to_dict()
|
|
220
|
-
|
|
221
220
|
|
|
222
221
|
def load_deck(self, deck_layout: Dict[str, Type[StandardLabware]]):
|
|
223
222
|
"""
|
|
@@ -239,8 +238,6 @@ class First:
|
|
|
239
238
|
self.load_labware(slot=slot, labware_name=labware_name)
|
|
240
239
|
self._logger.info("Deck layout loaded successfully")
|
|
241
240
|
|
|
242
|
-
### Liquid handling ###
|
|
243
|
-
|
|
244
241
|
def attach_tip(self, slot: str, well: Optional[str] = None):
|
|
245
242
|
"""
|
|
246
243
|
Attach a tip from a slot.
|
|
@@ -258,7 +255,7 @@ class First:
|
|
|
258
255
|
return
|
|
259
256
|
|
|
260
257
|
self._logger.info("Attaching tip from slot '%s'%s", slot, f", well '{well}'" if well else "")
|
|
261
|
-
pos = self.
|
|
258
|
+
pos = self._get_absolute_z_position(slot, well)
|
|
262
259
|
self._logger.debug("Moving to position %s for tip attachment", pos)
|
|
263
260
|
# return the offset from the origin
|
|
264
261
|
self.qubot.move_absolute(position=pos)
|
|
@@ -280,7 +277,7 @@ class First:
|
|
|
280
277
|
self.qubot.home(axis="Z")
|
|
281
278
|
self._logger.debug("Z axis homed after tip attachment")
|
|
282
279
|
|
|
283
|
-
def drop_tip(self, slot: str, well: str, height_from_bottom: float = 0.0):
|
|
280
|
+
def drop_tip(self, *, slot: str, well: str, height_from_bottom: float = 0.0):
|
|
284
281
|
"""
|
|
285
282
|
Drop a tip into a slot.
|
|
286
283
|
|
|
@@ -301,7 +298,7 @@ class First:
|
|
|
301
298
|
raise ValueError("Tip not attached")
|
|
302
299
|
|
|
303
300
|
self._logger.info("Dropping tip into slot '%s', well '%s'", slot, well)
|
|
304
|
-
pos = self.
|
|
301
|
+
pos = self._get_absolute_z_position(slot, well)
|
|
305
302
|
# add height from bottom
|
|
306
303
|
pos += Position(z=height_from_bottom)
|
|
307
304
|
# move up by the tip length
|
|
@@ -315,7 +312,7 @@ class First:
|
|
|
315
312
|
self.pipette.set_tip_attached(attached=False)
|
|
316
313
|
self._logger.info("Tip dropped successfully")
|
|
317
314
|
|
|
318
|
-
def aspirate_from(self, slot:str, well:str, amount:int, height_from_bottom: float = 0.0):
|
|
315
|
+
def aspirate_from(self, *, slot: str, well: str, amount: int, height_from_bottom: float = 0.0):
|
|
319
316
|
"""
|
|
320
317
|
Aspirate a volume of liquid from a slot.
|
|
321
318
|
|
|
@@ -337,7 +334,7 @@ class First:
|
|
|
337
334
|
raise ValueError("Tip not attached")
|
|
338
335
|
|
|
339
336
|
self._logger.info("Aspirating %d µL from slot '%s', well '%s'", amount, slot, well)
|
|
340
|
-
pos = self.
|
|
337
|
+
pos = self._get_absolute_z_position(slot, well)
|
|
341
338
|
# add height from bottom
|
|
342
339
|
pos += Position(z=height_from_bottom)
|
|
343
340
|
self._logger.debug("Moving Z axis to position %s", pos)
|
|
@@ -348,7 +345,7 @@ class First:
|
|
|
348
345
|
time.sleep(5)
|
|
349
346
|
self._logger.info("Aspiration completed: %d µL from slot '%s', well '%s'", amount, slot, well)
|
|
350
347
|
|
|
351
|
-
def dispense_to(self, slot:str, well:str, amount:int, height_from_bottom: float = 0.0):
|
|
348
|
+
def dispense_to(self, *, slot: str, well: str, amount: int, height_from_bottom: float = 0.0):
|
|
352
349
|
"""
|
|
353
350
|
Dispense a volume of liquid to a slot.
|
|
354
351
|
|
|
@@ -370,7 +367,7 @@ class First:
|
|
|
370
367
|
raise ValueError("Tip not attached")
|
|
371
368
|
|
|
372
369
|
self._logger.info("Dispensing %d µL to slot '%s', well '%s'", amount, slot, well)
|
|
373
|
-
pos = self.
|
|
370
|
+
pos = self._get_absolute_z_position(slot, well)
|
|
374
371
|
# add height from bottom
|
|
375
372
|
pos += Position(z=height_from_bottom)
|
|
376
373
|
self._logger.debug("Moving Z axis to position %s", pos)
|
|
@@ -380,8 +377,93 @@ class First:
|
|
|
380
377
|
self.pipette.dispense(amount=amount)
|
|
381
378
|
time.sleep(5)
|
|
382
379
|
self._logger.info("Dispense completed: %d µL to slot '%s', well '%s'", amount, slot, well)
|
|
380
|
+
|
|
381
|
+
def start_video_recording(
|
|
382
|
+
self,
|
|
383
|
+
filename: Optional[Union[str, Path]] = None,
|
|
384
|
+
fps: Optional[float] = None
|
|
385
|
+
) -> Path:
|
|
386
|
+
"""
|
|
387
|
+
Start recording a video.
|
|
383
388
|
|
|
384
|
-
|
|
389
|
+
Args:
|
|
390
|
+
filename: Optional filename for the video. If not provided, a timestamped
|
|
391
|
+
filename will be generated. If provided without extension, .mp4 will be added.
|
|
392
|
+
fps: Optional frames per second for the video. Defaults to 30.0 if not specified.
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
Path to the video file where recording is being saved
|
|
396
|
+
|
|
397
|
+
Raises:
|
|
398
|
+
IOError: If camera is not connected or recording fails to start
|
|
399
|
+
ValueError: If already recording
|
|
400
|
+
"""
|
|
401
|
+
return self.camera.start_video_recording(filename=filename, fps=fps)
|
|
402
|
+
|
|
403
|
+
def stop_video_recording(self) -> Optional[Path]:
|
|
404
|
+
"""
|
|
405
|
+
Stop recording a video.
|
|
406
|
+
|
|
407
|
+
Returns:
|
|
408
|
+
Path to the saved video file, or None if no recording was in progress
|
|
409
|
+
|
|
410
|
+
Raises:
|
|
411
|
+
IOError: If video writer fails to release
|
|
412
|
+
"""
|
|
413
|
+
return self.camera.stop_video_recording()
|
|
414
|
+
|
|
415
|
+
def record_video(
|
|
416
|
+
self,
|
|
417
|
+
duration_seconds: float,
|
|
418
|
+
filename: Optional[Union[str, Path]] = None,
|
|
419
|
+
fps: Optional[float] = None
|
|
420
|
+
) -> Path:
|
|
421
|
+
"""
|
|
422
|
+
Record a video for a specified duration.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
duration_seconds: Duration of the video in seconds
|
|
426
|
+
filename: Optional filename for the video. If not provided, a timestamped
|
|
427
|
+
filename will be generated. If provided without extension, .mp4 will be added.
|
|
428
|
+
fps: Optional frames per second for the video. Defaults to 30.0 if not specified.
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
Path to the saved video file
|
|
432
|
+
|
|
433
|
+
Raises:
|
|
434
|
+
IOError: If camera is not connected or recording fails
|
|
435
|
+
"""
|
|
436
|
+
return self.camera.record_video(
|
|
437
|
+
duration_seconds=duration_seconds,
|
|
438
|
+
filename=filename,
|
|
439
|
+
fps=fps
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
def capture_image(
|
|
443
|
+
self,
|
|
444
|
+
save: bool = False,
|
|
445
|
+
filename: Optional[Union[str, Path]] = None
|
|
446
|
+
) -> np.ndarray:
|
|
447
|
+
"""
|
|
448
|
+
Capture a single image from the camera.
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
save: If True, save the image to the captures folder
|
|
452
|
+
filename: Optional filename for the saved image. If not provided and save=True,
|
|
453
|
+
a timestamped filename will be generated. If provided without extension,
|
|
454
|
+
.jpg will be added.
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
Captured image as a numpy array (BGR format)
|
|
458
|
+
|
|
459
|
+
Raises:
|
|
460
|
+
IOError: If camera is not connected or capture fails
|
|
461
|
+
"""
|
|
462
|
+
return self.camera.capture_image(save=save, filename=filename)
|
|
463
|
+
|
|
464
|
+
### Private methods ###
|
|
465
|
+
|
|
466
|
+
def _get_slot_origin(self, slot: str) -> Position:
|
|
385
467
|
"""
|
|
386
468
|
Get the origin coordinates of a slot.
|
|
387
469
|
|
|
@@ -402,7 +484,7 @@ class First:
|
|
|
402
484
|
self._logger.debug("Slot origin for '%s': %s", slot, pos)
|
|
403
485
|
return pos
|
|
404
486
|
|
|
405
|
-
def
|
|
487
|
+
def _get_absolute_z_position(self, slot: str, well: Optional[str] = None) -> Position:
|
|
406
488
|
"""
|
|
407
489
|
Get the absolute position for a slot (and optionally a well within that slot) based on the origin
|
|
408
490
|
|
|
@@ -417,7 +499,7 @@ class First:
|
|
|
417
499
|
ValueError: If well is specified but no labware is loaded in the slot
|
|
418
500
|
"""
|
|
419
501
|
# Get slot origin
|
|
420
|
-
pos = self.
|
|
502
|
+
pos = self._get_slot_origin(slot)
|
|
421
503
|
|
|
422
504
|
# relative well position from slot origin
|
|
423
505
|
if well:
|
|
@@ -435,7 +517,7 @@ class First:
|
|
|
435
517
|
self._logger.debug("Absolute Z position for slot '%s': %s", slot, pos)
|
|
436
518
|
return pos
|
|
437
519
|
|
|
438
|
-
def
|
|
520
|
+
def _get_absolute_a_position(self, slot: str, well: Optional[str] = None) -> Position:
|
|
439
521
|
"""
|
|
440
522
|
Get the absolute position for a slot (and optionally a well within that slot) based on the origin
|
|
441
523
|
|
|
@@ -449,7 +531,7 @@ class First:
|
|
|
449
531
|
Raises:
|
|
450
532
|
ValueError: If well is specified but no labware is loaded in the slot
|
|
451
533
|
"""
|
|
452
|
-
pos = self.
|
|
534
|
+
pos = self._get_slot_origin(slot)
|
|
453
535
|
|
|
454
536
|
if well:
|
|
455
537
|
labware = self.deck[slot]
|
|
@@ -467,85 +549,22 @@ class First:
|
|
|
467
549
|
self._logger.debug("Absolute A position for slot '%s': %s", slot, pos)
|
|
468
550
|
return pos
|
|
469
551
|
|
|
470
|
-
|
|
471
|
-
self,
|
|
472
|
-
filename: Optional[Union[str, Path]] = None,
|
|
473
|
-
fps: Optional[float] = None
|
|
474
|
-
) -> Path:
|
|
475
|
-
"""
|
|
476
|
-
Start recording a video.
|
|
477
|
-
|
|
478
|
-
Args:
|
|
479
|
-
filename: Optional filename for the video. If not provided, a timestamped
|
|
480
|
-
filename will be generated. If provided without extension, .mp4 will be added.
|
|
481
|
-
fps: Optional frames per second for the video. Defaults to 30.0 if not specified.
|
|
482
|
-
|
|
483
|
-
Returns:
|
|
484
|
-
Path to the video file where recording is being saved
|
|
485
|
-
|
|
486
|
-
Raises:
|
|
487
|
-
IOError: If camera is not connected or recording fails to start
|
|
488
|
-
ValueError: If already recording
|
|
489
|
-
"""
|
|
490
|
-
return self.camera.start_video_recording(filename=filename, fps=fps)
|
|
552
|
+
### Control (immediate commands) ###
|
|
491
553
|
|
|
492
|
-
def
|
|
554
|
+
def pause(self):
|
|
493
555
|
"""
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
Returns:
|
|
497
|
-
Path to the saved video file, or None if no recording was in progress
|
|
498
|
-
|
|
499
|
-
Raises:
|
|
500
|
-
IOError: If video writer fails to release
|
|
556
|
+
Pause the execution of queued commands.
|
|
501
557
|
"""
|
|
502
|
-
|
|
558
|
+
print("Pausing machine")
|
|
503
559
|
|
|
504
|
-
def
|
|
505
|
-
self,
|
|
506
|
-
duration_seconds: float,
|
|
507
|
-
filename: Optional[Union[str, Path]] = None,
|
|
508
|
-
fps: Optional[float] = None
|
|
509
|
-
) -> Path:
|
|
560
|
+
def resume(self):
|
|
510
561
|
"""
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
Args:
|
|
514
|
-
duration_seconds: Duration of the video in seconds
|
|
515
|
-
filename: Optional filename for the video. If not provided, a timestamped
|
|
516
|
-
filename will be generated. If provided without extension, .mp4 will be added.
|
|
517
|
-
fps: Optional frames per second for the video. Defaults to 30.0 if not specified.
|
|
518
|
-
|
|
519
|
-
Returns:
|
|
520
|
-
Path to the saved video file
|
|
521
|
-
|
|
522
|
-
Raises:
|
|
523
|
-
IOError: If camera is not connected or recording fails
|
|
562
|
+
Resume the execution of queued commands.
|
|
524
563
|
"""
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
fps=fps
|
|
529
|
-
)
|
|
530
|
-
|
|
531
|
-
def capture_image(
|
|
532
|
-
self,
|
|
533
|
-
save: bool = False,
|
|
534
|
-
filename: Optional[Union[str, Path]] = None
|
|
535
|
-
) -> np.ndarray:
|
|
564
|
+
print("Resuming machine")
|
|
565
|
+
|
|
566
|
+
def cancel(self):
|
|
536
567
|
"""
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
Args:
|
|
540
|
-
save: If True, save the image to the captures folder
|
|
541
|
-
filename: Optional filename for the saved image. If not provided and save=True,
|
|
542
|
-
a timestamped filename will be generated. If provided without extension,
|
|
543
|
-
.jpg will be added.
|
|
544
|
-
|
|
545
|
-
Returns:
|
|
546
|
-
Captured image as a numpy array (BGR format)
|
|
547
|
-
|
|
548
|
-
Raises:
|
|
549
|
-
IOError: If camera is not connected or capture fails
|
|
568
|
+
Cancel the execution of queued commands.
|
|
550
569
|
"""
|
|
551
|
-
|
|
570
|
+
print("Cancelling machine")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/labware/opentrons_96_tiprack_300ul.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/__init__.py
RENAMED
|
File without changes
|
{puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/api.py
RENAMED
|
File without changes
|
{puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/constants.py
RENAMED
|
File without changes
|
{puda_drivers-0.0.17 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/rLine.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|