puda-drivers 0.0.16__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.
Files changed (40) hide show
  1. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/PKG-INFO +62 -19
  2. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/README.md +59 -16
  3. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/pyproject.toml +8 -3
  4. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/core/serialcontroller.py +21 -30
  5. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/labware/labware.py +1 -2
  6. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/labware/trash_bin.json +2 -1
  7. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/machines/first.py +138 -89
  8. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/move/deck.py +35 -1
  9. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/move/gcode.py +2 -2
  10. puda_drivers-0.0.18/tests/poll_position.py +126 -0
  11. puda_drivers-0.0.18/tests/test_deck.py +172 -0
  12. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/tests/test_position_polling.py +3 -3
  13. puda_drivers-0.0.18/tests/webcam.py +59 -0
  14. puda_drivers-0.0.16/tests/webcam.py +0 -28
  15. puda_drivers-0.0.16/uv.lock +0 -114
  16. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/.gitignore +0 -0
  17. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/LICENSE +0 -0
  18. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/__init__.py +0 -0
  19. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/core/__init__.py +0 -0
  20. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/core/logging.py +0 -0
  21. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/core/position.py +0 -0
  22. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/cv/__init__.py +0 -0
  23. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/cv/camera.py +0 -0
  24. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/labware/__init__.py +0 -0
  25. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/labware/opentrons_96_tiprack_300ul.json +0 -0
  26. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/labware/polyelectric_8_wellplate_30000ul.json +0 -0
  27. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/machines/__init__.py +0 -0
  28. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/move/__init__.py +0 -0
  29. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/move/grbl/__init__.py +0 -0
  30. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/move/grbl/api.py +0 -0
  31. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/move/grbl/constants.py +0 -0
  32. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/py.typed +0 -0
  33. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/__init__.py +0 -0
  34. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/api.py +0 -0
  35. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/constants.py +0 -0
  36. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/src/puda_drivers/transfer/liquid/sartorius/rLine.py +0 -0
  37. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/tests/example.py +0 -0
  38. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/tests/first.py +0 -0
  39. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/tests/pipette.py +0 -0
  40. {puda_drivers-0.0.16 → puda_drivers-0.0.18}/tests/qubot.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: puda-drivers
3
- Version: 0.0.16
3
+ Version: 0.0.18
4
4
  Summary: Hardware drivers for the PUDA platform.
5
- Project-URL: Homepage, https://github.com/zhao-bears/puda-drivers
6
- Project-URL: Issues, https://github.com/zhao-bears/puda-drivers/issues
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
@@ -40,14 +40,6 @@ Hardware drivers for the PUDA (Physical Unified Device Architecture) platform. T
40
40
  pip install puda-drivers
41
41
  ```
42
42
 
43
- ### From Source
44
-
45
- ```bash
46
- git clone https://github.com/zhao-bears/puda-drivers.git
47
- cd puda-drivers
48
- pip install -e .
49
- ```
50
-
51
43
  ## Quick Start
52
44
 
53
45
  ### Logging Configuration
@@ -197,28 +189,79 @@ sartorius_ports = list_serial_ports(filter_desc="Sartorius")
197
189
 
198
190
  ### Setup Development Environment
199
191
 
200
- First, install `uv` if you haven't already. See the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/) for platform-specific instructions.
192
+ This package is part of a UV workspace monorepo. First, install `uv` if you haven't already. See the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/) for platform-specific instructions.
193
+
194
+ **From the repository root:**
201
195
 
202
196
  ```bash
203
- # Create virtual environment
204
- uv venv
197
+ # Or install dependencies for all workspace packages
198
+ uv sync --all-packages
199
+ ```
200
+
201
+ This will:
202
+ - Create a virtual environment at the repository root (`.venv/`)
203
+ - Install all dependencies for all workspace packages
204
+ - Install `puda-drivers` and other workspace packages in editable mode automatically
205
205
 
206
- # Activate virtual environment
206
+ **Using the package:**
207
+
208
+ ```bash
209
+ # Run Python scripts with workspace context (recommended, works from anywhere in the workspace)
210
+ uv run python your_script.py
211
+
212
+ # Or activate the virtual environment (from repository root where .venv is located)
207
213
  source .venv/bin/activate # On Windows: .venv\Scripts\activate
214
+ python your_script.py
215
+ ```
216
+
217
+ **Adding dependencies:**
218
+
219
+ ```bash
220
+ # From the package directory
221
+ cd libs/drivers
222
+ uv add some-package
223
+
224
+ # Or from repository root
225
+ uv add --package puda-drivers some-package
226
+ ```
227
+
228
+ **Note:** Workspace packages are automatically installed in editable mode, so code changes are immediately available without reinstalling.
208
229
 
209
- # Install dependencies
210
- uv sync
230
+ ### Testing
231
+
232
+ Run tests using pytest with `uv run`:
233
+
234
+ ```bash
235
+ # Run all tests
236
+ uv run pytest tests/
211
237
 
212
- # Install package in editable mode
213
- pip install -e .
238
+ # Run a specific test file
239
+ uv run pytest tests/test_deck.py
240
+
241
+ # Run a specific test class
242
+ uv run pytest tests/test_deck.py::TestDeckToDict
243
+
244
+ # Run a specific test function
245
+ uv run pytest tests/test_deck.py::TestDeckToDict::test_to_dict_empty_deck
246
+
247
+ # Run with verbose output
248
+ uv run pytest tests/ -v
249
+
250
+ # Run with coverage report
251
+ uv run pytest tests/ --cov=puda_drivers --cov-report=html
214
252
  ```
215
253
 
254
+ **Note:** Make sure you're in the `libs/drivers` directory or use the full path to the tests directory when running pytest commands.
255
+
216
256
  ### Building and Publishing
217
257
 
218
258
  ```bash
219
259
  # Build distribution packages
220
260
  uv build
221
261
 
262
+ # cd to puda project root
263
+ cd ...
264
+
222
265
  # Publish to PyPI
223
266
  uv publish
224
267
  # Username: __token__
@@ -18,14 +18,6 @@ Hardware drivers for the PUDA (Physical Unified Device Architecture) platform. T
18
18
  pip install puda-drivers
19
19
  ```
20
20
 
21
- ### From Source
22
-
23
- ```bash
24
- git clone https://github.com/zhao-bears/puda-drivers.git
25
- cd puda-drivers
26
- pip install -e .
27
- ```
28
-
29
21
  ## Quick Start
30
22
 
31
23
  ### Logging Configuration
@@ -175,28 +167,79 @@ sartorius_ports = list_serial_ports(filter_desc="Sartorius")
175
167
 
176
168
  ### Setup Development Environment
177
169
 
178
- First, install `uv` if you haven't already. See the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/) for platform-specific instructions.
170
+ This package is part of a UV workspace monorepo. First, install `uv` if you haven't already. See the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/) for platform-specific instructions.
171
+
172
+ **From the repository root:**
179
173
 
180
174
  ```bash
181
- # Create virtual environment
182
- uv venv
175
+ # Or install dependencies for all workspace packages
176
+ uv sync --all-packages
177
+ ```
178
+
179
+ This will:
180
+ - Create a virtual environment at the repository root (`.venv/`)
181
+ - Install all dependencies for all workspace packages
182
+ - Install `puda-drivers` and other workspace packages in editable mode automatically
183
183
 
184
- # Activate virtual environment
184
+ **Using the package:**
185
+
186
+ ```bash
187
+ # Run Python scripts with workspace context (recommended, works from anywhere in the workspace)
188
+ uv run python your_script.py
189
+
190
+ # Or activate the virtual environment (from repository root where .venv is located)
185
191
  source .venv/bin/activate # On Windows: .venv\Scripts\activate
192
+ python your_script.py
193
+ ```
194
+
195
+ **Adding dependencies:**
196
+
197
+ ```bash
198
+ # From the package directory
199
+ cd libs/drivers
200
+ uv add some-package
201
+
202
+ # Or from repository root
203
+ uv add --package puda-drivers some-package
204
+ ```
205
+
206
+ **Note:** Workspace packages are automatically installed in editable mode, so code changes are immediately available without reinstalling.
186
207
 
187
- # Install dependencies
188
- uv sync
208
+ ### Testing
209
+
210
+ Run tests using pytest with `uv run`:
211
+
212
+ ```bash
213
+ # Run all tests
214
+ uv run pytest tests/
189
215
 
190
- # Install package in editable mode
191
- pip install -e .
216
+ # Run a specific test file
217
+ uv run pytest tests/test_deck.py
218
+
219
+ # Run a specific test class
220
+ uv run pytest tests/test_deck.py::TestDeckToDict
221
+
222
+ # Run a specific test function
223
+ uv run pytest tests/test_deck.py::TestDeckToDict::test_to_dict_empty_deck
224
+
225
+ # Run with verbose output
226
+ uv run pytest tests/ -v
227
+
228
+ # Run with coverage report
229
+ uv run pytest tests/ --cov=puda_drivers --cov-report=html
192
230
  ```
193
231
 
232
+ **Note:** Make sure you're in the `libs/drivers` directory or use the full path to the tests directory when running pytest commands.
233
+
194
234
  ### Building and Publishing
195
235
 
196
236
  ```bash
197
237
  # Build distribution packages
198
238
  uv build
199
239
 
240
+ # cd to puda project root
241
+ cd ...
242
+
200
243
  # Publish to PyPI
201
244
  uv publish
202
245
  # Username: __token__
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "puda-drivers"
3
- version = "0.0.16"
3
+ version = "0.0.18"
4
4
  description = "Hardware drivers for the PUDA platform."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -18,16 +18,21 @@ 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",
25
26
  ]
27
+ [dependency-groups]
28
+ dev = [
29
+ "pytest>=9.0.2",
30
+ ]
26
31
 
27
32
  [build-system]
28
33
  requires = ["hatchling >= 1.26"]
29
34
  build-backend = "hatchling.build"
30
35
 
31
36
  [project.urls]
32
- Homepage = "https://github.com/zhao-bears/puda-drivers"
33
- Issues = "https://github.com/zhao-bears/puda-drivers/issues"
37
+ Homepage = "https://github.com/PUDAP/puda"
38
+ Issues = "https://github.com/PUDAP/puda/issues"
@@ -48,7 +48,7 @@ class SerialController(ABC):
48
48
  self._serial = None
49
49
  self.port_name = port_name
50
50
  self.baudrate = baudrate
51
- self.timeout = timeout
51
+ self._timeout = timeout
52
52
  self._logger = logger
53
53
 
54
54
  # lock to prevent concurrent access to the serial port
@@ -75,7 +75,7 @@ class SerialController(ABC):
75
75
  self._serial = serial.Serial(
76
76
  port=self.port_name,
77
77
  baudrate=self.baudrate,
78
- timeout=self.timeout,
78
+ timeout=self._timeout,
79
79
  )
80
80
  self._serial.flush()
81
81
  self._logger.info("Successfully connected to %s.", self.port_name)
@@ -107,19 +107,19 @@ class SerialController(ABC):
107
107
 
108
108
  def _send_command(self, command: str) -> None:
109
109
  """
110
- Sends a custom protocol command.
110
+ Sends a command to the device.
111
111
  Note: This method should be called while holding self._lock to ensure
112
112
  atomic command/response pairing.
113
113
  """
114
+ self._logger.info("-> Sending: %r", command)
115
+
114
116
  if not self.is_connected or not self._serial:
115
117
  self._logger.error(
116
118
  "Attempt to send command '%s' failed: Device not connected.",
117
119
  command,
118
120
  )
119
121
  # Retain raising an error for being disconnected, as that's a connection state issue
120
- raise serial.SerialException("Device not connected. Call connect() first.")
121
-
122
- self._logger.info("-> Sending: %r", command)
122
+ raise serial.SerialException("Device disconnected. Call connect() first.")
123
123
 
124
124
  try:
125
125
  self._serial.reset_input_buffer() # clear input buffer
@@ -140,7 +140,7 @@ class SerialController(ABC):
140
140
  )
141
141
  return None
142
142
 
143
- def _read_response(self) -> str:
143
+ def _read_response(self, timeout: int = None) -> str:
144
144
  """
145
145
  Generic, blocking read that respects timeout and returns
146
146
  all data that arrived within the timeout period.
@@ -148,10 +148,13 @@ class SerialController(ABC):
148
148
  if not self.is_connected or not self._serial:
149
149
  raise serial.SerialException("Device not connected.")
150
150
 
151
+ if timeout is None:
152
+ timeout = self._timeout
153
+
151
154
  start_time = time.time()
152
155
  response = b""
153
156
 
154
- while time.time() - start_time < self.timeout:
157
+ while time.time() - start_time < timeout:
155
158
  if self._serial.in_waiting > 0:
156
159
  # Read all available bytes
157
160
  response += self._serial.read(self._serial.in_waiting)
@@ -168,14 +171,12 @@ class SerialController(ABC):
168
171
 
169
172
  # Timeout reached - check what we got
170
173
  if not response:
171
- self._logger.error("No response within %s seconds.", self.timeout)
174
+ self._logger.error("No response within %s seconds.", timeout)
172
175
  raise serial.SerialTimeoutException(
173
- f"No response received within {self.timeout} seconds."
176
+ f"No response received within {timeout} seconds."
174
177
  )
175
178
 
176
- # Decode once and check the decoded string
177
179
  decoded_response = response.decode("utf-8", errors="ignore").strip()
178
-
179
180
  if "ok" in decoded_response.lower():
180
181
  self._logger.debug("<- Received response: %r", decoded_response)
181
182
  elif "err" in decoded_response.lower():
@@ -193,10 +194,12 @@ class SerialController(ABC):
193
194
  def _build_command(self, command: str, value: Optional[str] = None) -> str:
194
195
  """
195
196
  Build a command string according to the device protocol.
197
+
198
+ There might be special starting and ending characters for devices
196
199
  """
197
- pass
200
+ raise NotImplementedError
198
201
 
199
- def execute(self, command: str, value: Optional[str] = None) -> str:
202
+ def execute(self, command: str, value: Optional[str] = None, timeout: int = None) -> str:
200
203
  """
201
204
  Send a command and read the response atomically.
202
205
 
@@ -218,22 +221,10 @@ class SerialController(ABC):
218
221
  serial.SerialException: If device is not connected or communication fails
219
222
  serial.SerialTimeoutException: If no response is received within timeout
220
223
  """
224
+ if timeout is None:
225
+ timeout = self._timeout
221
226
  # Hold the lock for the entire send+read operation to ensure atomicity
222
227
  # This prevents concurrent commands from mixing up responses
223
228
  with self._lock:
224
- # Increase timeout by 60 seconds for G28 (homing) command
225
- original_timeout = self.timeout
226
- if "G28" in command.upper():
227
- self.timeout = original_timeout + 60
228
- # Also update the serial connection's timeout if connected
229
- if self.is_connected and self._serial:
230
- self._serial.timeout = self.timeout
231
-
232
- try:
233
- self._send_command(self._build_command(command, value))
234
- return self._read_response()
235
- finally:
236
- # Restore original timeout
237
- self.timeout = original_timeout
238
- if self.is_connected and self._serial:
239
- self._serial.timeout = original_timeout
229
+ self._send_command(self._build_command(command, value))
230
+ return self._read_response(timeout=timeout)
@@ -3,9 +3,8 @@
3
3
  import json
4
4
  import inspect
5
5
  from pathlib import Path
6
- from typing import Dict, Any
6
+ from typing import Dict, Any, List
7
7
  from abc import ABC
8
- from typing import List
9
8
  from puda_drivers.core import Position
10
9
 
11
10
 
@@ -20,7 +20,8 @@
20
20
  "width": 10.0,
21
21
  "height": 10.0,
22
22
  "x": 60.0,
23
- "y": 42.0
23
+ "y": 42.0,
24
+ "z": 0.0
24
25
  }
25
26
  },
26
27
  "groups": [