puda-drivers 0.0.11__py3-none-any.whl → 0.0.12__py3-none-any.whl

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.
@@ -9,7 +9,9 @@ This class demonstrates the integration of:
9
9
 
10
10
  import logging
11
11
  import time
12
+ from pathlib import Path
12
13
  from typing import Optional, Dict, Tuple, Type, Union
14
+ import numpy as np
13
15
  from puda_drivers.move import GCodeController, Deck
14
16
  from puda_drivers.core import Position
15
17
  from puda_drivers.transfer.liquid.sartorius import SartoriusController
@@ -118,21 +120,44 @@ class First:
118
120
  camera_index if camera_index is not None else self.DEFAULT_CAMERA_INDEX,
119
121
  )
120
122
 
121
- def connect(self):
122
- """Connect all controllers."""
123
- self._logger.info("Connecting all controllers")
123
+ def startup(self):
124
+ """
125
+ Start up the machine by connecting all controllers and initializing subsystems.
126
+
127
+ This method:
128
+ - Connects to all controllers (gantry, pipette, camera)
129
+ - Homes the gantry to establish a known position
130
+ - Initializes the pipette to reset it to a known state
131
+
132
+ The machine is ready for operations after this method completes.
133
+ """
134
+ self._logger.info("Starting up machine and connecting all controllers")
124
135
  self.qubot.connect()
125
136
  self.pipette.connect()
126
137
  self.camera.connect()
127
138
  self._logger.info("All controllers connected successfully")
128
139
 
129
- def disconnect(self):
130
- """Disconnect all controllers."""
131
- self._logger.info("Disconnecting all controllers")
140
+ # Home the gantry to establish known position
141
+ self._logger.info("Homing gantry...")
142
+ self.qubot.home()
143
+
144
+ # Initialize the pipette (all pipette operations need to wait 5 seconds for completion)
145
+ self._logger.info("Initializing pipette...")
146
+ self.pipette.initialize()
147
+ time.sleep(5)
148
+ self._logger.info("Machine startup complete - ready for operations")
149
+
150
+ def shutdown(self):
151
+ """
152
+ Gracefully shut down the machine by disconnecting all controllers.
153
+
154
+ This method ensures all connections are properly closed and resources are released.
155
+ """
156
+ self._logger.info("Shutting down machine and disconnecting all controllers")
132
157
  self.qubot.disconnect()
133
158
  self.pipette.disconnect()
134
159
  self.camera.disconnect()
135
- self._logger.info("All controllers disconnected successfully")
160
+ self._logger.info("Machine shutdown complete")
136
161
 
137
162
  def load_labware(self, slot: str, labware_name: str):
138
163
  """Load a labware object into a slot."""
@@ -350,3 +375,86 @@ class First:
350
375
  else:
351
376
  self._logger.debug("Absolute A position for slot '%s': %s", slot, pos)
352
377
  return pos
378
+
379
+ def start_video_recording(
380
+ self,
381
+ filename: Optional[Union[str, Path]] = None,
382
+ fps: Optional[float] = None
383
+ ) -> Path:
384
+ """
385
+ Start recording a video.
386
+
387
+ Args:
388
+ filename: Optional filename for the video. If not provided, a timestamped
389
+ filename will be generated. If provided without extension, .mp4 will be added.
390
+ fps: Optional frames per second for the video. Defaults to 30.0 if not specified.
391
+
392
+ Returns:
393
+ Path to the video file where recording is being saved
394
+
395
+ Raises:
396
+ IOError: If camera is not connected or recording fails to start
397
+ ValueError: If already recording
398
+ """
399
+ return self.camera.start_video_recording(filename=filename, fps=fps)
400
+
401
+ def stop_video_recording(self) -> Optional[Path]:
402
+ """
403
+ Stop recording a video.
404
+
405
+ Returns:
406
+ Path to the saved video file, or None if no recording was in progress
407
+
408
+ Raises:
409
+ IOError: If video writer fails to release
410
+ """
411
+ return self.camera.stop_video_recording()
412
+
413
+ def record_video(
414
+ self,
415
+ duration_seconds: float,
416
+ filename: Optional[Union[str, Path]] = None,
417
+ fps: Optional[float] = None
418
+ ) -> Path:
419
+ """
420
+ Record a video for a specified duration.
421
+
422
+ Args:
423
+ duration_seconds: Duration of the video in seconds
424
+ filename: Optional filename for the video. If not provided, a timestamped
425
+ filename will be generated. If provided without extension, .mp4 will be added.
426
+ fps: Optional frames per second for the video. Defaults to 30.0 if not specified.
427
+
428
+ Returns:
429
+ Path to the saved video file
430
+
431
+ Raises:
432
+ IOError: If camera is not connected or recording fails
433
+ """
434
+ return self.camera.record_video(
435
+ duration_seconds=duration_seconds,
436
+ filename=filename,
437
+ fps=fps
438
+ )
439
+
440
+ def capture_image(
441
+ self,
442
+ save: bool = False,
443
+ filename: Optional[Union[str, Path]] = None
444
+ ) -> np.ndarray:
445
+ """
446
+ Capture a single image from the camera.
447
+
448
+ Args:
449
+ save: If True, save the image to the captures folder
450
+ filename: Optional filename for the saved image. If not provided and save=True,
451
+ a timestamped filename will be generated. If provided without extension,
452
+ .jpg will be added.
453
+
454
+ Returns:
455
+ Captured image as a numpy array (BGR format)
456
+
457
+ Raises:
458
+ IOError: If camera is not connected or capture fails
459
+ """
460
+ return self.camera.capture_image(save=save, filename=filename)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: puda-drivers
3
- Version: 0.0.11
3
+ Version: 0.0.12
4
4
  Summary: Hardware drivers for the PUDA platform.
5
5
  Project-URL: Homepage, https://github.com/zhao-bears/puda-drivers
6
6
  Project-URL: Issues, https://github.com/zhao-bears/puda-drivers/issues
@@ -85,6 +85,7 @@ When file logging is enabled, logs are saved to timestamped files (unless a cust
85
85
  The `First` machine integrates motion control, deck management, liquid handling, and camera capabilities:
86
86
 
87
87
  ```python
88
+ import time
88
89
  import logging
89
90
  from puda_drivers.machines import First
90
91
  from puda_drivers.core.logging import setup_logging
@@ -102,14 +103,8 @@ machine = First(
102
103
  camera_index=0,
103
104
  )
104
105
 
105
- # Connect all devices
106
- machine.connect()
107
-
108
- # Home the gantry
109
- machine.qubot.home()
110
-
111
- # Initialize the pipette
112
- machine.pipette.initialize()
106
+ # Start up the machine (connects all controllers, homes gantry, and initializes pipette)
107
+ machine.startup()
113
108
 
114
109
  # Load labware onto the deck
115
110
  machine.load_deck({
@@ -119,19 +114,19 @@ machine.load_deck({
119
114
  })
120
115
 
121
116
  # Start video recording
122
- machine.camera.start_video_recording()
117
+ machine.start_video_recording()
123
118
 
124
119
  # Perform liquid handling operations
125
120
  machine.attach_tip(slot="A3", well="G8")
126
121
  machine.aspirate_from(slot="C2", well="A1", amount=100, height_from_bottom=10.0)
127
122
  machine.dispense_to(slot="C2", well="B4", amount=100, height_from_bottom=30.0)
128
- machine.drop_tip(slot="C1", well="A1")
123
+ machine.drop_tip(slot="C1", well="A1", height_from_bottom=10)
129
124
 
130
125
  # Stop video recording
131
- machine.camera.stop_video_recording()
126
+ machine.stop_video_recording()
132
127
 
133
- # Disconnect all devices
134
- machine.disconnect()
128
+ # Shutdown the machine (gracefully disconnects all controllers)
129
+ machine.shutdown()
135
130
  ```
136
131
 
137
132
  **Discovering Available Methods**: To explore what methods are available on any class instance, you can use Python's built-in `help()` function:
@@ -12,7 +12,7 @@ puda_drivers/labware/opentrons_96_tiprack_300ul.json,sha256=jmNaworu688GEgFdxMxN
12
12
  puda_drivers/labware/polyelectric_8_wellplate_30000ul.json,sha256=esu2tej0ORs7Pfd4HwoQVUpU5mPvp2AYzE3zsCC2FDk,3104
13
13
  puda_drivers/labware/trash_bin.json,sha256=Hk4MXO48P28jG7F87DUd9Ja4c_P7kAy3karPQ965i9Y,580
14
14
  puda_drivers/machines/__init__.py,sha256=zmIk_r2T8nbPA68h3Cko8N6oL7ncoBpmvhNcAqzHmc4,45
15
- puda_drivers/machines/first.py,sha256=F1D-Te25cUJamE-FG_BEcY2PgN0_6S81ImdhNDeupCk,14415
15
+ puda_drivers/machines/first.py,sha256=yk_Kh9f_yjSlRHjYEOzvrMxADBvlTH0AIV1VLvm2sp8,18339
16
16
  puda_drivers/move/__init__.py,sha256=NKIKckcqgyviPM0EGFcmIoaqkJM4qekR4babfdddRzM,96
17
17
  puda_drivers/move/deck.py,sha256=yq2B4WMqj0hQvHt8HoJskP10u1DUyKwUnjP2c9gJ174,1397
18
18
  puda_drivers/move/gcode.py,sha256=Aw7la4RkPw737hW5sKl6WiPCmmTnsjLvG4mOb-RwVSc,22592
@@ -23,7 +23,7 @@ puda_drivers/transfer/liquid/sartorius/__init__.py,sha256=QGpKz5YUwa8xCdSMXeZ0iR
23
23
  puda_drivers/transfer/liquid/sartorius/api.py,sha256=jxwIJmY2k1K2ts6NC2ZgFTe4MOiH8TGnJeqYOqNa3rE,28250
24
24
  puda_drivers/transfer/liquid/sartorius/constants.py,sha256=mcsjLrVBH-RSodH-pszstwcEL9wwbV0vOgHbGNxZz9w,2770
25
25
  puda_drivers/transfer/liquid/sartorius/sartorius.py,sha256=bW838jYOAfLlbUqtsKRipZ-RLjrNTcZ7riYV6I4w8G8,13728
26
- puda_drivers-0.0.11.dist-info/METADATA,sha256=b6LZSNkAD3RG4cjghtm2DcNwFEbK1nIgDJazlUfhYRQ,7041
27
- puda_drivers-0.0.11.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
28
- puda_drivers-0.0.11.dist-info/licenses/LICENSE,sha256=7EI8xVBu6h_7_JlVw-yPhhOZlpY9hP8wal7kHtqKT_E,1074
29
- puda_drivers-0.0.11.dist-info/RECORD,,
26
+ puda_drivers-0.0.12.dist-info/METADATA,sha256=saXg221vJnpqzC4t6Cn7Kcc1ZX0WPBLQkclr_vFaRgQ,7071
27
+ puda_drivers-0.0.12.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
28
+ puda_drivers-0.0.12.dist-info/licenses/LICENSE,sha256=7EI8xVBu6h_7_JlVw-yPhhOZlpY9hP8wal7kHtqKT_E,1074
29
+ puda_drivers-0.0.12.dist-info/RECORD,,