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.
- puda_drivers/core/__init__.py +2 -1
- puda_drivers/machines/first.py +47 -4
- puda_drivers/move/gcode.py +3 -3
- puda_drivers/transfer/liquid/sartorius/__init__.py +1 -1
- puda_drivers/transfer/liquid/sartorius/{sartorius.py → rLine.py} +15 -4
- {puda_drivers-0.0.12.dist-info → puda_drivers-0.0.13.dist-info}/METADATA +4 -3
- {puda_drivers-0.0.12.dist-info → puda_drivers-0.0.13.dist-info}/RECORD +9 -9
- {puda_drivers-0.0.12.dist-info → puda_drivers-0.0.13.dist-info}/WHEEL +0 -0
- {puda_drivers-0.0.12.dist-info → puda_drivers-0.0.13.dist-info}/licenses/LICENSE +0 -0
puda_drivers/core/__init__.py
CHANGED
|
@@ -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"]
|
puda_drivers/machines/first.py
CHANGED
|
@@ -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
|
-
"""
|
|
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
|
-
"""
|
|
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.
|
|
192
|
-
|
|
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
|
|
puda_drivers/move/gcode.py
CHANGED
|
@@ -508,9 +508,9 @@ class GCodeController(SerialController):
|
|
|
508
508
|
|
|
509
509
|
return self._current_position
|
|
510
510
|
|
|
511
|
-
def
|
|
511
|
+
def get_position(self) -> Position:
|
|
512
512
|
"""
|
|
513
|
-
|
|
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.
|
|
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 .
|
|
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(
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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/
|
|
26
|
-
puda_drivers-0.0.
|
|
27
|
-
puda_drivers-0.0.
|
|
28
|
-
puda_drivers-0.0.
|
|
29
|
-
puda_drivers-0.0.
|
|
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,,
|
|
File without changes
|
|
File without changes
|