puda-drivers 0.0.6__py3-none-any.whl → 0.0.8__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,15 +9,13 @@ Reference: https://api.sartorius.com/document-hub/dam/download/34901/Sartorius-r
9
9
 
10
10
  import logging
11
11
  from typing import Optional
12
-
13
12
  from puda_drivers.core.serialcontroller import SerialController
14
-
15
13
  from .constants import STATUS_CODES
16
14
 
17
-
18
15
  class SartoriusDeviceError(Exception):
19
16
  """Custom exception raised when the Sartorius device reports an error."""
20
-
17
+
18
+ pass
21
19
 
22
20
  class SartoriusController(SerialController):
23
21
  """
@@ -71,8 +69,10 @@ class SartoriusController(SerialController):
71
69
  baudrate,
72
70
  timeout,
73
71
  )
72
+ self._tip_attached: bool
73
+ self._volume: int = 0
74
74
 
75
- def _build_command(self, command_code: str, value: str = "") -> str:
75
+ def _build_command(self, command: str, value: Optional[str] = None) -> str:
76
76
  """
77
77
  Build a command string according to the Sartorius protocol.
78
78
 
@@ -88,28 +88,11 @@ class SartoriusController(SerialController):
88
88
  return (
89
89
  self.PROTOCOL_SOH
90
90
  + self.SLAVE_ADDRESS
91
- + "R"
92
- + command_code
93
- + value
91
+ + command
92
+ + (f"{value}" if value else "")
94
93
  + self.PROTOCOL_TERMINATOR
95
94
  )
96
95
 
97
- def _check_response_error(self, response: str, operation: str) -> None:
98
- """
99
- Check if a response contains an error and raise an exception if so.
100
-
101
- Args:
102
- response: Response string from the device
103
- operation: Description of the operation being performed (for error message)
104
-
105
- Raises:
106
- SartoriusDeviceError: If the response contains an error
107
- """
108
- if self.ERROR_RESPONSE in response.lower():
109
- raise SartoriusDeviceError(
110
- f"{operation} failed. Device returned error: {response}"
111
- )
112
-
113
96
  def _validate_speed(self, speed: int, direction: str = "speed") -> None:
114
97
  """
115
98
  Validate that a speed value is within the allowed range.
@@ -149,29 +132,6 @@ class SartoriusController(SerialController):
149
132
  )
150
133
  return value_str
151
134
 
152
- def _execute_command(
153
- self, command_code: str, value: str = "", operation: str = ""
154
- ) -> str:
155
- """
156
- Execute a command and return the response.
157
-
158
- Args:
159
- command_code: Command code to execute
160
- value: Optional value for the command
161
- operation: Description of the operation (for logging and error messages)
162
-
163
- Returns:
164
- Response string from the device
165
-
166
- Raises:
167
- SartoriusDeviceError: If the device returns an error
168
- """
169
- command = self._build_command(command_code, value)
170
- self._send_command(command)
171
- response = self._read_response()
172
- self._check_response_error(response, operation or f"Command {command_code}")
173
- return response
174
-
175
135
  def initialize(self) -> None:
176
136
  """
177
137
  Initialize the pipette unit (RZ command).
@@ -183,8 +143,9 @@ class SartoriusController(SerialController):
183
143
  SartoriusDeviceError: If initialization fails
184
144
  """
185
145
  self._logger.info("** Initializing Pipette Head (RZ) **")
186
- self._execute_command("Z", operation="Pipette initialization")
187
- self._logger.info("** Pipette Initialization Complete **")
146
+ self.execute(command="RZ")
147
+ self._logger.info("** Pipette Initialization Complete **\n")
148
+ self.set_tip_attached(attached=False)
188
149
 
189
150
  def get_inward_speed(self) -> int:
190
151
  """
@@ -197,7 +158,7 @@ class SartoriusController(SerialController):
197
158
  SartoriusDeviceError: If the query fails
198
159
  """
199
160
  self._logger.info("** Querying Inward Speed (DI) **")
200
- response = self._execute_command("DI", operation="Inward speed query")
161
+ response = self.execute(command="DI")
201
162
 
202
163
  if len(response) < 2:
203
164
  raise SartoriusDeviceError(
@@ -205,7 +166,7 @@ class SartoriusController(SerialController):
205
166
  )
206
167
 
207
168
  speed = int(response[1])
208
- self._logger.info("** Current Inward Speed: %s **", speed)
169
+ self._logger.info("** Current Inward Speed: %s **\n", speed)
209
170
  return speed
210
171
 
211
172
  def set_inward_speed(self, speed: int) -> None:
@@ -221,8 +182,8 @@ class SartoriusController(SerialController):
221
182
  """
222
183
  self._validate_speed(speed, "Inward")
223
184
  self._logger.info("** Setting Inward Speed (SI, Speed: %s) **", speed)
224
- self._execute_command("I", value=str(speed), operation="Setting inward speed")
225
- self._logger.info("** Inward Speed Set to %s Successfully **", speed)
185
+ self.execute(command="SI", value=str(speed))
186
+ self._logger.info("** Inward Speed Set to %s Successfully **\n", speed)
226
187
 
227
188
  def get_outward_speed(self) -> int:
228
189
  """
@@ -235,7 +196,7 @@ class SartoriusController(SerialController):
235
196
  SartoriusDeviceError: If the query fails
236
197
  """
237
198
  self._logger.info("** Querying Outward Speed (DO) **")
238
- response = self._execute_command("DO", operation="Outward speed query")
199
+ response = self.execute(command="DO")
239
200
 
240
201
  if len(response) < 2:
241
202
  raise SartoriusDeviceError(
@@ -243,7 +204,7 @@ class SartoriusController(SerialController):
243
204
  )
244
205
 
245
206
  speed = int(response[1])
246
- self._logger.info("** Current Outward Speed: %s **", speed)
207
+ self._logger.info("** Current Outward Speed: %s **\n", speed)
247
208
  return speed
248
209
 
249
210
  def set_outward_speed(self, speed: int) -> None:
@@ -259,8 +220,8 @@ class SartoriusController(SerialController):
259
220
  """
260
221
  self._validate_speed(speed, "Outward")
261
222
  self._logger.info("** Setting Outward Speed (SO, Speed: %s) **", speed)
262
- self._execute_command("O", value=str(speed), operation="Setting outward speed")
263
- self._logger.info("** Outward Speed Set to %s Successfully **", speed)
223
+ self.execute(command="SO", value=str(speed))
224
+ self._logger.info("** Outward Speed Set to %s Successfully **\n", speed)
264
225
 
265
226
  def run_to_position(self, position: int) -> None:
266
227
  """
@@ -275,10 +236,11 @@ class SartoriusController(SerialController):
275
236
  """
276
237
  position_str = self._validate_no_leading_zeros(position, "RP")
277
238
  self._logger.info("** Run to absolute Position (RP, Position: %s) **", position)
278
- self._execute_command("P", value=position_str, operation="Run to position")
279
- self._logger.info("** Reached Position %s Successfully **", position)
280
-
281
- def aspirate(self, amount: float) -> None:
239
+ self.execute(command="RP", value=position_str)
240
+ self._logger.info("** Reached Position %s Successfully **\n", position)
241
+
242
+ # instead of run_inward, use aspirate
243
+ def aspirate(self, amount: int) -> None:
282
244
  """
283
245
  Aspirate fluid from the current location.
284
246
 
@@ -294,10 +256,12 @@ class SartoriusController(SerialController):
294
256
 
295
257
  steps = int(amount / self.MICROLITER_PER_STEP)
296
258
  self._logger.info("** Aspirating %s uL (RI%s steps) **", amount, steps)
297
- self._execute_command("I", value=str(steps), operation="Aspirate")
298
- self._logger.info("** Aspirated %s uL Successfully **", amount)
259
+ self.execute(command="RI", value=str(steps))
260
+ self._logger.info("** Aspirated %s uL Successfully **\n", amount)
261
+ self._volume += amount
299
262
 
300
- def dispense(self, amount: float) -> None:
263
+ # instead of run_outward, use dispense
264
+ def dispense(self, amount: int) -> None:
301
265
  """
302
266
  Dispense fluid at the current location.
303
267
 
@@ -313,8 +277,9 @@ class SartoriusController(SerialController):
313
277
 
314
278
  steps = int(amount / self.MICROLITER_PER_STEP)
315
279
  self._logger.info("** Dispensing %s uL (RO%s steps) **", amount, steps)
316
- self._execute_command("O", value=str(steps), operation="Dispense")
317
- self._logger.info("** Dispensed %s uL Successfully **", amount)
280
+ self.execute(command="RO", value=str(steps))
281
+ self._logger.info("** Dispensed %s uL Successfully **\n", amount)
282
+ self._volume -= amount
318
283
 
319
284
  def eject_tip(self, return_position: int = 30) -> None:
320
285
  """
@@ -327,16 +292,17 @@ class SartoriusController(SerialController):
327
292
  ValueError: If return_position has leading zeros
328
293
  SartoriusDeviceError: If tip ejection fails
329
294
  """
330
- position_str = self._validate_no_leading_zeros(return_position, "RE")
295
+ position_str = self._validate_no_leading_zeros(
296
+ command_name="RE",
297
+ value=return_position
298
+ )
331
299
  self._logger.info(
332
300
  "** Ejecting Tip and returning to position %s (RE %s) **",
333
301
  return_position,
334
302
  return_position,
335
303
  )
336
- self._execute_command(
337
- "E", value=position_str, operation="Eject tip with return position"
338
- )
339
- self._logger.info("** Tip Ejection Complete **")
304
+ self.execute(command="RE", value=position_str)
305
+ self._logger.info("** Tip Ejection Complete **\n")
340
306
 
341
307
  def run_blowout(self, return_position: Optional[int] = None) -> None:
342
308
  """
@@ -359,14 +325,12 @@ class SartoriusController(SerialController):
359
325
  return_position,
360
326
  return_position,
361
327
  )
362
- self._execute_command(
363
- "B", value=position_str, operation="Blowout with return position"
364
- )
328
+ self.execute(command="RB", value=position_str)
365
329
  else:
366
330
  self._logger.info("** Running Blowout (RB) **")
367
- self._execute_command("B", operation="Blowout")
331
+ self.execute(command="RB")
368
332
 
369
- self._logger.info("** Blowout Complete **")
333
+ self._logger.info("** Blowout Complete **\n")
370
334
 
371
335
  def get_status(self) -> str:
372
336
  """
@@ -379,7 +343,7 @@ class SartoriusController(SerialController):
379
343
  SartoriusDeviceError: If the status query fails
380
344
  """
381
345
  self._logger.info("** Querying Pipette Status (DS) **")
382
- response = self._execute_command("DS", operation="Status query")
346
+ response = self.execute(command="DS")
383
347
 
384
348
  if len(response) < 2:
385
349
  raise SartoriusDeviceError(
@@ -389,10 +353,55 @@ class SartoriusController(SerialController):
389
353
  status_code = response[1]
390
354
  if status_code in STATUS_CODES:
391
355
  status_message = STATUS_CODES[status_code]
392
- self._logger.info("Pipette Status Code [%s]: %s", status_code, status_message)
356
+ self._logger.info("Pipette Status Code [%s]: %s\n", status_code, status_message)
393
357
  else:
394
358
  self._logger.warning(
395
- "Pipette Status Code [%s]: Unknown Status Code", status_code
359
+ "Pipette Status Code [%s]: Unknown Status Code\n", status_code
396
360
  )
397
361
 
398
362
  return status_code
363
+
364
+ def get_position(self) -> int:
365
+ """
366
+ Query the current position of the pipette (DP command).
367
+
368
+ Returns:
369
+ Current position in steps
370
+ """
371
+ self._logger.info("** Querying Position (DP) **")
372
+ response = self.execute(command="DP")
373
+ self._logger.info("** Position: %s steps **\n", response)
374
+ return response
375
+
376
+ def get_liquid_level(self) -> int:
377
+ """
378
+ Query the current liquid level of the pipette (DN command).
379
+
380
+ Returns:
381
+ Current liquid level in microliters (µL)
382
+ """
383
+ # without tip 240 - 300
384
+ # incrase with tip attached and liquid
385
+ # 160 - 400
386
+ self._logger.info("** Querying Liquid Level (DN) **")
387
+ response = self.execute(command="DN")
388
+ self._logger.info("** Liquid Level: %s uL **\n", response)
389
+ return response
390
+
391
+ def is_tip_attached(self) -> bool:
392
+ """
393
+ Check if a tip is attached to the pipette (DS command).
394
+
395
+ Returns:
396
+ True if a tip is attached, False otherwise
397
+ """
398
+ return self._tip_attached
399
+
400
+ def set_tip_attached(self, attached: bool) -> None:
401
+ """
402
+ Set the tip attached state (DS command).
403
+
404
+ Args:
405
+ attached: True if a tip is attached, False otherwise
406
+ """
407
+ self._tip_attached = attached
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: puda-drivers
3
- Version: 0.0.6
3
+ Version: 0.0.8
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,7 @@ 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.14
17
+ Requires-Python: >=3.8
18
18
  Requires-Dist: pyserial~=3.5
19
19
  Description-Content-Type: text/markdown
20
20
 
@@ -0,0 +1,27 @@
1
+ puda_drivers/__init__.py,sha256=rcF5xCkMgyLlJLN3gWwJnUoW0ShPyISeyENvaqwg4Ik,503
2
+ puda_drivers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ puda_drivers/core/__init__.py,sha256=XbCdXsU6NMDsmEAtavAGiSZZPla5d7zc2L7Qx9qKHdY,214
4
+ puda_drivers/core/logging.py,sha256=prOeJ3CGEbm37TtMRyAOTQQiMU5_ImZTRXmcUJxkenc,2892
5
+ puda_drivers/core/position.py,sha256=f4efmDSrKKCtqrR-GUJxVitPG20MiuGSDOWt-9TVISk,12628
6
+ puda_drivers/core/serialcontroller.py,sha256=38mKas1iJaOkAE0_V4tmqgZz7RxMMEWfGqA0Ma_Dt2A,8604
7
+ puda_drivers/labware/__init__.py,sha256=RlRxrJn2zyzyxv4c1KGt8Gmxv2cRO8V4zZVnnyL-I00,288
8
+ puda_drivers/labware/labware.py,sha256=hZhOzSyb1GP_bm1LvQsREx4YSqLCWBTKNWDgDqfFavI,5317
9
+ puda_drivers/labware/opentrons_96_tiprack_300ul.json,sha256=jmNaworu688GEgFdxMxNRSaEp4ZOg9IumFk8bVHSzYY,19796
10
+ puda_drivers/labware/polyelectric_8_wellplate_30000ul.json,sha256=NwXMgHBYnIIFasmrthTDTTV7n1M5DYQBDWh-KYsq1gI,3104
11
+ puda_drivers/labware/trash_bin.json,sha256=X4TDNDzGbCtJSWlYgGYHUzdnVKj62SggGDNf7z5S0OE,581
12
+ puda_drivers/machines/__init__.py,sha256=zmIk_r2T8nbPA68h3Cko8N6oL7ncoBpmvhNcAqzHmc4,45
13
+ puda_drivers/machines/first.py,sha256=McnZ4q_ykv56aWFqphoZFqXqbhDGZAWCk40CPblgPsw,8492
14
+ puda_drivers/move/__init__.py,sha256=NKIKckcqgyviPM0EGFcmIoaqkJM4qekR4babfdddRzM,96
15
+ puda_drivers/move/deck.py,sha256=yq2B4WMqj0hQvHt8HoJskP10u1DUyKwUnjP2c9gJ174,1397
16
+ puda_drivers/move/gcode.py,sha256=Aw7la4RkPw737hW5sKl6WiPCmmTnsjLvG4mOb-RwVSc,22592
17
+ puda_drivers/move/grbl/__init__.py,sha256=vBeeti8DVN2dACi1rLmHN_UGIOdo0s-HZX6mIepLV5I,98
18
+ puda_drivers/move/grbl/api.py,sha256=loj8_Vap7S9qaD0ReHhgxr9Vkl6Wp7DGzyLkZyZ6v_k,16995
19
+ puda_drivers/move/grbl/constants.py,sha256=4736CRDzLGWVqGscLajMlrIQMyubsHfthXi4RF1CHNg,9585
20
+ puda_drivers/transfer/liquid/sartorius/__init__.py,sha256=QGpKz5YUwa8xCdSMXeZ0iRU-hRVqAWNPK0mlMTuzv8I,101
21
+ puda_drivers/transfer/liquid/sartorius/api.py,sha256=jxwIJmY2k1K2ts6NC2ZgFTe4MOiH8TGnJeqYOqNa3rE,28250
22
+ puda_drivers/transfer/liquid/sartorius/constants.py,sha256=mcsjLrVBH-RSodH-pszstwcEL9wwbV0vOgHbGNxZz9w,2770
23
+ puda_drivers/transfer/liquid/sartorius/sartorius.py,sha256=bW838jYOAfLlbUqtsKRipZ-RLjrNTcZ7riYV6I4w8G8,13728
24
+ puda_drivers-0.0.8.dist-info/METADATA,sha256=_dQYo29lMIclzyd3HGZBvmOidliS92UWTQPPO0g79V0,8597
25
+ puda_drivers-0.0.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
26
+ puda_drivers-0.0.8.dist-info/licenses/LICENSE,sha256=7EI8xVBu6h_7_JlVw-yPhhOZlpY9hP8wal7kHtqKT_E,1074
27
+ puda_drivers-0.0.8.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- puda_drivers/__init__.py,sha256=rcF5xCkMgyLlJLN3gWwJnUoW0ShPyISeyENvaqwg4Ik,503
2
- puda_drivers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- puda_drivers/core/__init__.py,sha256=yYsOLXl32msFNRGrXLqhNVl_OfNPFR4ED7pmgn7rPU0,171
4
- puda_drivers/core/logging.py,sha256=prOeJ3CGEbm37TtMRyAOTQQiMU5_ImZTRXmcUJxkenc,2892
5
- puda_drivers/core/serialcontroller.py,sha256=9Vgz884MWmq4Z7rQzIQ7DtPzygMuzBeNWj0JiUpAhGA,7996
6
- puda_drivers/move/__init__.py,sha256=i7G5VKD5FgnmC21TLxoASVtC88IrPUTLDJrTnp99u-0,35
7
- puda_drivers/move/gcode.py,sha256=egZw3D5m9d1R8P32L1wd3lDwiWcFMDGPHsFMFIYXkRA,22069
8
- puda_drivers/move/grbl/__init__.py,sha256=vBeeti8DVN2dACi1rLmHN_UGIOdo0s-HZX6mIepLV5I,98
9
- puda_drivers/move/grbl/api.py,sha256=loj8_Vap7S9qaD0ReHhgxr9Vkl6Wp7DGzyLkZyZ6v_k,16995
10
- puda_drivers/move/grbl/constants.py,sha256=4736CRDzLGWVqGscLajMlrIQMyubsHfthXi4RF1CHNg,9585
11
- puda_drivers/transfer/liquid/sartorius/__init__.py,sha256=QGpKz5YUwa8xCdSMXeZ0iRU-hRVqAWNPK0mlMTuzv8I,101
12
- puda_drivers/transfer/liquid/sartorius/api.py,sha256=jxwIJmY2k1K2ts6NC2ZgFTe4MOiH8TGnJeqYOqNa3rE,28250
13
- puda_drivers/transfer/liquid/sartorius/constants.py,sha256=mcsjLrVBH-RSodH-pszstwcEL9wwbV0vOgHbGNxZz9w,2770
14
- puda_drivers/transfer/liquid/sartorius/sartorius.py,sha256=iW3v-YHjj4ZAfGv0x0J-XV-Y0fAAhS6xmSg2ozQm4UI,13803
15
- puda_drivers-0.0.6.dist-info/METADATA,sha256=ELS1LZxhddh-dD-b0iKDrO5RnuJrrgITv6F6GeJ20wY,8598
16
- puda_drivers-0.0.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
17
- puda_drivers-0.0.6.dist-info/licenses/LICENSE,sha256=7EI8xVBu6h_7_JlVw-yPhhOZlpY9hP8wal7kHtqKT_E,1074
18
- puda_drivers-0.0.6.dist-info/RECORD,,