puda-drivers 0.0.12__py3-none-any.whl → 0.0.13__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.
@@ -1,5 +1,6 @@
1
1
  from .serialcontroller import SerialController, list_serial_ports
2
2
  from .logging import setup_logging
3
3
  from .position import Position
4
+ from .nats_machine_client import NATSMachineClient, ExecutionState
4
5
 
5
- __all__ = ["SerialController", "list_serial_ports", "setup_logging", "Position"]
6
+ __all__ = ["SerialController", "list_serial_ports", "setup_logging", "Position", "NATSMachineClient", "ExecutionState"]
@@ -159,8 +159,34 @@ class First:
159
159
  self.camera.disconnect()
160
160
  self._logger.info("Machine shutdown complete")
161
161
 
162
+ def get_position(self) -> Dict[str, float]:
163
+ """
164
+ Get the current position of the machine. Both QuBot and Sartorius are queried.
165
+
166
+ Args:
167
+ None
168
+ Returns:
169
+ Dictionary containing the current position of the machine and it's components.
170
+ """
171
+ qubot_position = self.qubot.get_position()
172
+ sartorius_position = self.pipette.get_position()
173
+
174
+ return {
175
+ "qubot": qubot_position.to_dict(),
176
+ "pipette": sartorius_position,
177
+ }
178
+
162
179
  def load_labware(self, slot: str, labware_name: str):
163
- """Load a labware object into a slot."""
180
+ """
181
+ Load a labware object into a slot.
182
+
183
+ Args:
184
+ slot: Slot name (e.g., 'A1', 'B2')
185
+ labware_name: Name of the labware class to load
186
+
187
+ Raises:
188
+ KeyError: If slot is not found in deck
189
+ """
164
190
  self._logger.info("Loading labware '%s' into slot '%s'", labware_name, slot)
165
191
  self.deck.load_labware(slot=slot, labware_name=labware_name)
166
192
  self._logger.debug("Labware '%s' loaded into slot '%s'", labware_name, slot)
@@ -186,10 +212,20 @@ class First:
186
212
  self._logger.info("Deck layout loaded successfully")
187
213
 
188
214
  def attach_tip(self, slot: str, well: Optional[str] = None):
189
- """Attach a tip from a slot."""
215
+ """
216
+ Attach a tip from a slot.
217
+
218
+ Args:
219
+ slot: Slot name (e.g., 'A1', 'B2')
220
+ well: Optional well name within the slot (e.g., 'A1' for a well in a tiprack)
221
+
222
+ Note:
223
+ This method is idempotent - if a tip is already attached, it will
224
+ log a warning and return successfully without raising an error.
225
+ """
190
226
  if self.pipette.is_tip_attached():
191
- self._logger.error("Cannot attach tip: tip already attached")
192
- raise ValueError("Tip already attached")
227
+ self._logger.warning("Tip already attached - skipping attachment (idempotent operation)")
228
+ return
193
229
 
194
230
  self._logger.info("Attaching tip from slot '%s'%s", slot, f", well '{well}'" if well else "")
195
231
  pos = self.get_absolute_z_position(slot, well)
@@ -361,6 +397,13 @@ class First:
361
397
  def get_absolute_a_position(self, slot: str, well: Optional[str] = None) -> Position:
362
398
  """
363
399
  Get the absolute position for a slot (and optionally a well within that slot) based on the origin
400
+
401
+ Args:
402
+ slot: Slot name (e.g., 'A1', 'B2')
403
+ well: Optional well name within the slot (e.g., 'A1' for a well in a tiprack)
404
+
405
+ Returns:
406
+ Position with absolute coordinates
364
407
  """
365
408
  pos = self.get_slot_origin(slot)
366
409
 
@@ -508,9 +508,9 @@ class GCodeController(SerialController):
508
508
 
509
509
  return self._current_position
510
510
 
511
- def query_position(self) -> Position:
511
+ def get_position(self) -> Position:
512
512
  """
513
- Query the current machine position (M114 command).
513
+ Get the current machine position (M114 command).
514
514
 
515
515
  Returns:
516
516
  Position containing X, Y, Z, and A positions
@@ -560,7 +560,7 @@ class GCodeController(SerialController):
560
560
  self._logger.info("Starting position synchronization check (M114).")
561
561
 
562
562
  # Query the actual machine position
563
- queried_position = self.query_position()
563
+ queried_position = self.get_position()
564
564
 
565
565
  if not queried_position.get_axes():
566
566
  self._logger.error("Query position failed. Cannot synchronize.")
@@ -1,2 +1,2 @@
1
- from .sartorius import SartoriusController, SartoriusDeviceError
1
+ from .rLine import SartoriusController, SartoriusDeviceError
2
2
  from .constants import STATUS_CODES
@@ -7,6 +7,7 @@ pipettes and robotic dispensers via serial communication.
7
7
  Reference: https://api.sartorius.com/document-hub/dam/download/34901/Sartorius-rLine-technical-user-manual-v1.1.pdf
8
8
  """
9
9
 
10
+ import json
10
11
  import logging
11
12
  from typing import Optional
12
13
  from puda_drivers.core.serialcontroller import SerialController
@@ -62,14 +63,14 @@ class SartoriusController(SerialController):
62
63
  timeout: Timeout in seconds for operations. Defaults to 10.
63
64
  """
64
65
  super().__init__(port_name, baudrate, timeout)
65
- self._logger = logging.getLogger(__name__)
66
+ self._logger = logging.getLogger('puda_drivers.transfer.liquid.sartorius')
66
67
  self._logger.info(
67
68
  "Sartorius Controller initialized with port='%s', baudrate=%s, timeout=%s",
68
69
  port_name,
69
70
  baudrate,
70
71
  timeout,
71
72
  )
72
- self._tip_attached: bool
73
+ self._tip_attached: bool = False
73
74
  self._volume: int = 0
74
75
 
75
76
  def _build_command(self, command: str, value: Optional[str] = None) -> str:
@@ -337,7 +338,10 @@ class SartoriusController(SerialController):
337
338
  Query the current status of the pipette (DS command).
338
339
 
339
340
  Returns:
340
- Status code character (single character string)
341
+ JSON string containing status information with keys:
342
+ - status_code: Status code character
343
+ - status_message: Human-readable status message (if known)
344
+ - is_known: Boolean indicating if status code is recognized
341
345
 
342
346
  Raises:
343
347
  SartoriusDeviceError: If the status query fails
@@ -351,15 +355,22 @@ class SartoriusController(SerialController):
351
355
  )
352
356
 
353
357
  status_code = response[1]
358
+ status_data = {
359
+ "status_code": status_code,
360
+ "is_known": status_code in STATUS_CODES
361
+ }
362
+
354
363
  if status_code in STATUS_CODES:
355
364
  status_message = STATUS_CODES[status_code]
365
+ status_data["status_message"] = status_message
356
366
  self._logger.info("Pipette Status Code [%s]: %s\n", status_code, status_message)
357
367
  else:
368
+ status_data["status_message"] = None
358
369
  self._logger.warning(
359
370
  "Pipette Status Code [%s]: Unknown Status Code\n", status_code
360
371
  )
361
372
 
362
- return status_code
373
+ return json.dumps(status_data)
363
374
 
364
375
  def get_position(self) -> int:
365
376
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: puda-drivers
3
- Version: 0.0.12
3
+ Version: 0.0.13
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
@@ -14,7 +14,8 @@ Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.14
15
15
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
16
  Classifier: Topic :: System :: Hardware
17
- Requires-Python: >=3.8
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: nats-py>=2.12.0
18
19
  Requires-Dist: opencv-python>=4.12.0.88
19
20
  Requires-Dist: pyserial~=3.5
20
21
  Description-Content-Type: text/markdown
@@ -188,7 +189,7 @@ sartorius_ports = list_serial_ports(filter_desc="Sartorius")
188
189
 
189
190
  ## Requirements
190
191
 
191
- - Python >= 3.14
192
+ - Python >= 3.8
192
193
  - pyserial >= 3.5
193
194
  - See `pyproject.toml` for full dependency list
194
195
 
@@ -1,6 +1,6 @@
1
1
  puda_drivers/__init__.py,sha256=rcF5xCkMgyLlJLN3gWwJnUoW0ShPyISeyENvaqwg4Ik,503
2
2
  puda_drivers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- puda_drivers/core/__init__.py,sha256=XbCdXsU6NMDsmEAtavAGiSZZPla5d7zc2L7Qx9qKHdY,214
3
+ puda_drivers/core/__init__.py,sha256=1T5jL8SXz_4jz1TGa3tSupwF5vg-wuN2efhulVClHaI,320
4
4
  puda_drivers/core/logging.py,sha256=prOeJ3CGEbm37TtMRyAOTQQiMU5_ImZTRXmcUJxkenc,2892
5
5
  puda_drivers/core/position.py,sha256=f4efmDSrKKCtqrR-GUJxVitPG20MiuGSDOWt-9TVISk,12628
6
6
  puda_drivers/core/serialcontroller.py,sha256=38mKas1iJaOkAE0_V4tmqgZz7RxMMEWfGqA0Ma_Dt2A,8604
@@ -12,18 +12,18 @@ 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=yk_Kh9f_yjSlRHjYEOzvrMxADBvlTH0AIV1VLvm2sp8,18339
15
+ puda_drivers/machines/first.py,sha256=kpxn8__u2mVt0MROj8Tuqs9-guBAsjW8lDTDVuQL9XE,19683
16
16
  puda_drivers/move/__init__.py,sha256=NKIKckcqgyviPM0EGFcmIoaqkJM4qekR4babfdddRzM,96
17
17
  puda_drivers/move/deck.py,sha256=yq2B4WMqj0hQvHt8HoJskP10u1DUyKwUnjP2c9gJ174,1397
18
- puda_drivers/move/gcode.py,sha256=Aw7la4RkPw737hW5sKl6WiPCmmTnsjLvG4mOb-RwVSc,22592
18
+ puda_drivers/move/gcode.py,sha256=NAUBwhCI24NJlcZSQyKO6K7nP6ADcIybb4rrmYusKq0,22586
19
19
  puda_drivers/move/grbl/__init__.py,sha256=vBeeti8DVN2dACi1rLmHN_UGIOdo0s-HZX6mIepLV5I,98
20
20
  puda_drivers/move/grbl/api.py,sha256=loj8_Vap7S9qaD0ReHhgxr9Vkl6Wp7DGzyLkZyZ6v_k,16995
21
21
  puda_drivers/move/grbl/constants.py,sha256=4736CRDzLGWVqGscLajMlrIQMyubsHfthXi4RF1CHNg,9585
22
- puda_drivers/transfer/liquid/sartorius/__init__.py,sha256=QGpKz5YUwa8xCdSMXeZ0iRU-hRVqAWNPK0mlMTuzv8I,101
22
+ puda_drivers/transfer/liquid/sartorius/__init__.py,sha256=7fljIbu0KkumsI3NI3O64dl6JkMAQDG5-uGqPefDI7M,97
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
- puda_drivers/transfer/liquid/sartorius/sartorius.py,sha256=bW838jYOAfLlbUqtsKRipZ-RLjrNTcZ7riYV6I4w8G8,13728
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,,
25
+ puda_drivers/transfer/liquid/sartorius/rLine.py,sha256=Lzcxt-oHFHa2a-q1WFFrpHTeQrKQihRJrPcf29J5Rb0,14232
26
+ puda_drivers-0.0.13.dist-info/METADATA,sha256=6shREZadMDvMjcZTBlKd9SDfd0-IZOutmMtg5kOpspY,7102
27
+ puda_drivers-0.0.13.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
28
+ puda_drivers-0.0.13.dist-info/licenses/LICENSE,sha256=7EI8xVBu6h_7_JlVw-yPhhOZlpY9hP8wal7kHtqKT_E,1074
29
+ puda_drivers-0.0.13.dist-info/RECORD,,