puda-drivers 0.0.8__tar.gz → 0.0.10__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.
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/.gitignore +3 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/PKG-INFO +57 -118
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/README.md +55 -117
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/pyproject.toml +2 -1
- puda_drivers-0.0.10/src/puda_drivers/cv/__init__.py +4 -0
- puda_drivers-0.0.10/src/puda_drivers/cv/camera.py +434 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/machines/first.py +66 -7
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/tests/first.py +6 -1
- puda_drivers-0.0.10/tests/webcam.py +28 -0
- puda_drivers-0.0.10/uv.lock +209 -0
- puda_drivers-0.0.8/uv.lock +0 -23
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/LICENSE +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/__init__.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/core/__init__.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/core/logging.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/core/position.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/core/serialcontroller.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/labware/__init__.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/labware/labware.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/labware/opentrons_96_tiprack_300ul.json +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/labware/polyelectric_8_wellplate_30000ul.json +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/labware/trash_bin.json +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/machines/__init__.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/move/__init__.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/move/deck.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/move/gcode.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/move/grbl/__init__.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/move/grbl/api.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/move/grbl/constants.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/py.typed +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/transfer/liquid/sartorius/__init__.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/transfer/liquid/sartorius/api.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/transfer/liquid/sartorius/constants.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/src/puda_drivers/transfer/liquid/sartorius/sartorius.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/tests/example.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/tests/pipette.py +0 -0
- {puda_drivers-0.0.8 → puda_drivers-0.0.10}/tests/qubot.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: puda-drivers
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.10
|
|
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
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
15
15
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
16
|
Classifier: Topic :: System :: Hardware
|
|
17
17
|
Requires-Python: >=3.8
|
|
18
|
+
Requires-Dist: opencv-python>=4.12.0.88
|
|
18
19
|
Requires-Dist: pyserial~=3.5
|
|
19
20
|
Description-Content-Type: text/markdown
|
|
20
21
|
|
|
@@ -79,160 +80,96 @@ setup_logging(
|
|
|
79
80
|
|
|
80
81
|
When file logging is enabled, logs are saved to timestamped files (unless a custom name is provided) in the `logs/` folder. The logs folder is created automatically if it doesn't exist.
|
|
81
82
|
|
|
82
|
-
###
|
|
83
|
+
### First Machine Example
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
from puda_drivers.move import GCodeController
|
|
86
|
-
|
|
87
|
-
# Initialize and connect to a G-code device
|
|
88
|
-
gantry = GCodeController(port_name="/dev/ttyACM0", feed=3000)
|
|
89
|
-
gantry.connect()
|
|
90
|
-
|
|
91
|
-
# Configure axis limits for safety (recommended)
|
|
92
|
-
gantry.set_axis_limits("X", 0, 200)
|
|
93
|
-
gantry.set_axis_limits("Y", -200, 0)
|
|
94
|
-
gantry.set_axis_limits("Z", -100, 0)
|
|
95
|
-
gantry.set_axis_limits("A", -180, 180)
|
|
96
|
-
|
|
97
|
-
# Home the gantry
|
|
98
|
-
gantry.home()
|
|
85
|
+
The `First` machine integrates motion control, deck management, liquid handling, and camera capabilities:
|
|
99
86
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
gantry.move_relative(x=20.0, y=-10.0)
|
|
105
|
-
|
|
106
|
-
# Query current position
|
|
107
|
-
position = gantry.query_position()
|
|
108
|
-
print(f"Current position: {position}")
|
|
87
|
+
```python
|
|
88
|
+
import logging
|
|
89
|
+
from puda_drivers.machines import First
|
|
90
|
+
from puda_drivers.core.logging import setup_logging
|
|
109
91
|
|
|
110
|
-
#
|
|
111
|
-
|
|
112
|
-
|
|
92
|
+
# Configure logging
|
|
93
|
+
setup_logging(
|
|
94
|
+
enable_file_logging=False,
|
|
95
|
+
log_level=logging.DEBUG,
|
|
96
|
+
)
|
|
113
97
|
|
|
114
|
-
|
|
98
|
+
# Initialize the First machine
|
|
99
|
+
machine = First(
|
|
100
|
+
qubot_port="/dev/ttyACM0",
|
|
101
|
+
sartorius_port="/dev/ttyUSB0",
|
|
102
|
+
camera_index=0,
|
|
103
|
+
)
|
|
115
104
|
|
|
116
|
-
|
|
105
|
+
# Connect all devices
|
|
106
|
+
machine.connect()
|
|
117
107
|
|
|
118
|
-
|
|
119
|
-
|
|
108
|
+
# Home the gantry
|
|
109
|
+
machine.qubot.home()
|
|
120
110
|
|
|
121
|
-
# Initialize
|
|
122
|
-
pipette
|
|
123
|
-
pipette.connect()
|
|
124
|
-
pipette.initialize()
|
|
111
|
+
# Initialize the pipette
|
|
112
|
+
machine.pipette.initialize()
|
|
125
113
|
|
|
126
|
-
#
|
|
127
|
-
|
|
114
|
+
# Load labware onto the deck
|
|
115
|
+
machine.load_deck({
|
|
116
|
+
"C1": "trash_bin",
|
|
117
|
+
"C2": "polyelectric_8_wellplate_30000ul",
|
|
118
|
+
"A3": "opentrons_96_tiprack_300ul",
|
|
119
|
+
})
|
|
128
120
|
|
|
129
|
-
#
|
|
130
|
-
|
|
121
|
+
# Start video recording
|
|
122
|
+
machine.camera.start_video_recording()
|
|
131
123
|
|
|
132
|
-
#
|
|
133
|
-
|
|
124
|
+
# Perform liquid handling operations
|
|
125
|
+
machine.attach_tip(slot="A3", well="G8")
|
|
126
|
+
machine.aspirate_from(slot="C2", well="A1", amount=100)
|
|
127
|
+
machine.dispense_to(slot="C2", well="B4", amount=100)
|
|
128
|
+
machine.drop_tip(slot="C1", well="A1")
|
|
134
129
|
|
|
135
|
-
#
|
|
136
|
-
|
|
130
|
+
# Stop video recording
|
|
131
|
+
machine.camera.stop_video_recording()
|
|
137
132
|
|
|
138
|
-
# Disconnect
|
|
139
|
-
|
|
133
|
+
# Disconnect all devices
|
|
134
|
+
machine.disconnect()
|
|
140
135
|
```
|
|
141
136
|
|
|
142
|
-
|
|
137
|
+
**Discovering Available Methods**: To explore what methods are available on any class instance, you can use Python's built-in `help()` function:
|
|
143
138
|
|
|
144
139
|
```python
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
#
|
|
149
|
-
|
|
150
|
-
pipette = SartoriusController(port_name="/dev/ttyUSB0")
|
|
151
|
-
|
|
152
|
-
gantry.connect()
|
|
153
|
-
pipette.connect()
|
|
154
|
-
|
|
155
|
-
# Move to source well
|
|
156
|
-
gantry.move_absolute(x=50.0, y=-50.0, z=-20.0)
|
|
157
|
-
pipette.aspirate(amount=50.0)
|
|
158
|
-
|
|
159
|
-
# Move to destination well
|
|
160
|
-
gantry.move_absolute(x=150.0, y=-150.0, z=-20.0)
|
|
161
|
-
pipette.dispense(amount=50.0)
|
|
162
|
-
|
|
163
|
-
# Cleanup
|
|
164
|
-
pipette.eject_tip()
|
|
165
|
-
gantry.disconnect()
|
|
166
|
-
pipette.disconnect()
|
|
140
|
+
machine = First()
|
|
141
|
+
help(machine) # See methods for the First machine
|
|
142
|
+
help(machine.qubot) # See GCodeController methods
|
|
143
|
+
help(machine.pipette) # See SartoriusController methods
|
|
144
|
+
help(machine.camera) # See CameraController methods
|
|
167
145
|
```
|
|
168
146
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
### Motion Systems
|
|
147
|
+
Alternatively, you can read the source code directly in the `src/puda_drivers/` directory.
|
|
172
148
|
|
|
173
|
-
|
|
174
|
-
- Supports X, Y, Z, and A axes
|
|
175
|
-
- Configurable feed rates
|
|
176
|
-
- Position synchronization and homing
|
|
177
|
-
- Automatic axis limit validation for safe operation
|
|
149
|
+
## Device Support
|
|
178
150
|
|
|
179
|
-
|
|
151
|
+
The following device types are supported:
|
|
180
152
|
|
|
153
|
+
- **GCode** - G-code compatible motion systems (e.g., QuBot)
|
|
181
154
|
- **Sartorius rLINE®** - Electronic pipettes and robotic dispensers
|
|
182
|
-
|
|
183
|
-
- Tip attachment and ejection
|
|
184
|
-
- Configurable speeds and volumes
|
|
155
|
+
- **Camera** - Webcams and USB cameras for image and video capture
|
|
185
156
|
|
|
186
|
-
##
|
|
187
|
-
|
|
188
|
-
### Axis Limit Validation
|
|
189
|
-
|
|
190
|
-
Both `move_absolute()` and `move_relative()` validate positions against configured axis limits before executing any movement. If a position is outside the limits, a `ValueError` is raised:
|
|
191
|
-
|
|
192
|
-
```python
|
|
193
|
-
from puda_drivers.move import GCodeController
|
|
194
|
-
|
|
195
|
-
gantry = GCodeController(port_name="/dev/ttyACM0")
|
|
196
|
-
gantry.connect()
|
|
197
|
-
|
|
198
|
-
# Set axis limits
|
|
199
|
-
gantry.set_axis_limits("X", 0, 200)
|
|
200
|
-
gantry.set_axis_limits("Y", -200, 0)
|
|
201
|
-
|
|
202
|
-
try:
|
|
203
|
-
# This will raise ValueError: Value 250 outside axis limits [0, 200]
|
|
204
|
-
gantry.move_absolute(x=250.0, y=-50.0)
|
|
205
|
-
except ValueError as e:
|
|
206
|
-
print(f"Move rejected: {e}")
|
|
207
|
-
|
|
208
|
-
# Relative moves are also validated after conversion to absolute positions
|
|
209
|
-
try:
|
|
210
|
-
# If current X is 150, moving 100 more would exceed the limit
|
|
211
|
-
gantry.move_relative(x=100.0)
|
|
212
|
-
except ValueError as e:
|
|
213
|
-
print(f"Move rejected: {e}")
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
Validation errors are automatically logged at the ERROR level before the exception is raised.
|
|
217
|
-
|
|
218
|
-
### Logging Best Practices
|
|
157
|
+
## Logging Best Practices
|
|
219
158
|
|
|
220
159
|
For production applications, configure logging at the start of your script:
|
|
221
160
|
|
|
222
161
|
```python
|
|
223
162
|
import logging
|
|
224
163
|
from puda_drivers.core.logging import setup_logging
|
|
225
|
-
from puda_drivers.move import GCodeController
|
|
226
164
|
|
|
227
165
|
# Configure logging first, before initializing devices
|
|
228
166
|
setup_logging(
|
|
229
167
|
enable_file_logging=True,
|
|
230
168
|
log_level=logging.INFO,
|
|
231
|
-
log_file_name="
|
|
169
|
+
log_file_name="experiment"
|
|
232
170
|
)
|
|
233
171
|
|
|
234
172
|
# Now all device operations will be logged
|
|
235
|
-
gantry = GCodeController(port_name="/dev/ttyACM0")
|
|
236
173
|
# ... rest of your code
|
|
237
174
|
```
|
|
238
175
|
|
|
@@ -264,6 +201,8 @@ sartorius_ports = list_serial_ports(filter_desc="Sartorius")
|
|
|
264
201
|
|
|
265
202
|
### Setup Development Environment
|
|
266
203
|
|
|
204
|
+
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.
|
|
205
|
+
|
|
267
206
|
```bash
|
|
268
207
|
# Create virtual environment
|
|
269
208
|
uv venv
|
|
@@ -59,160 +59,96 @@ setup_logging(
|
|
|
59
59
|
|
|
60
60
|
When file logging is enabled, logs are saved to timestamped files (unless a custom name is provided) in the `logs/` folder. The logs folder is created automatically if it doesn't exist.
|
|
61
61
|
|
|
62
|
-
###
|
|
62
|
+
### First Machine Example
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
from puda_drivers.move import GCodeController
|
|
66
|
-
|
|
67
|
-
# Initialize and connect to a G-code device
|
|
68
|
-
gantry = GCodeController(port_name="/dev/ttyACM0", feed=3000)
|
|
69
|
-
gantry.connect()
|
|
70
|
-
|
|
71
|
-
# Configure axis limits for safety (recommended)
|
|
72
|
-
gantry.set_axis_limits("X", 0, 200)
|
|
73
|
-
gantry.set_axis_limits("Y", -200, 0)
|
|
74
|
-
gantry.set_axis_limits("Z", -100, 0)
|
|
75
|
-
gantry.set_axis_limits("A", -180, 180)
|
|
76
|
-
|
|
77
|
-
# Home the gantry
|
|
78
|
-
gantry.home()
|
|
64
|
+
The `First` machine integrates motion control, deck management, liquid handling, and camera capabilities:
|
|
79
65
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
gantry.move_relative(x=20.0, y=-10.0)
|
|
85
|
-
|
|
86
|
-
# Query current position
|
|
87
|
-
position = gantry.query_position()
|
|
88
|
-
print(f"Current position: {position}")
|
|
66
|
+
```python
|
|
67
|
+
import logging
|
|
68
|
+
from puda_drivers.machines import First
|
|
69
|
+
from puda_drivers.core.logging import setup_logging
|
|
89
70
|
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
|
|
71
|
+
# Configure logging
|
|
72
|
+
setup_logging(
|
|
73
|
+
enable_file_logging=False,
|
|
74
|
+
log_level=logging.DEBUG,
|
|
75
|
+
)
|
|
93
76
|
|
|
94
|
-
|
|
77
|
+
# Initialize the First machine
|
|
78
|
+
machine = First(
|
|
79
|
+
qubot_port="/dev/ttyACM0",
|
|
80
|
+
sartorius_port="/dev/ttyUSB0",
|
|
81
|
+
camera_index=0,
|
|
82
|
+
)
|
|
95
83
|
|
|
96
|
-
|
|
84
|
+
# Connect all devices
|
|
85
|
+
machine.connect()
|
|
97
86
|
|
|
98
|
-
|
|
99
|
-
|
|
87
|
+
# Home the gantry
|
|
88
|
+
machine.qubot.home()
|
|
100
89
|
|
|
101
|
-
# Initialize
|
|
102
|
-
pipette
|
|
103
|
-
pipette.connect()
|
|
104
|
-
pipette.initialize()
|
|
90
|
+
# Initialize the pipette
|
|
91
|
+
machine.pipette.initialize()
|
|
105
92
|
|
|
106
|
-
#
|
|
107
|
-
|
|
93
|
+
# Load labware onto the deck
|
|
94
|
+
machine.load_deck({
|
|
95
|
+
"C1": "trash_bin",
|
|
96
|
+
"C2": "polyelectric_8_wellplate_30000ul",
|
|
97
|
+
"A3": "opentrons_96_tiprack_300ul",
|
|
98
|
+
})
|
|
108
99
|
|
|
109
|
-
#
|
|
110
|
-
|
|
100
|
+
# Start video recording
|
|
101
|
+
machine.camera.start_video_recording()
|
|
111
102
|
|
|
112
|
-
#
|
|
113
|
-
|
|
103
|
+
# Perform liquid handling operations
|
|
104
|
+
machine.attach_tip(slot="A3", well="G8")
|
|
105
|
+
machine.aspirate_from(slot="C2", well="A1", amount=100)
|
|
106
|
+
machine.dispense_to(slot="C2", well="B4", amount=100)
|
|
107
|
+
machine.drop_tip(slot="C1", well="A1")
|
|
114
108
|
|
|
115
|
-
#
|
|
116
|
-
|
|
109
|
+
# Stop video recording
|
|
110
|
+
machine.camera.stop_video_recording()
|
|
117
111
|
|
|
118
|
-
# Disconnect
|
|
119
|
-
|
|
112
|
+
# Disconnect all devices
|
|
113
|
+
machine.disconnect()
|
|
120
114
|
```
|
|
121
115
|
|
|
122
|
-
|
|
116
|
+
**Discovering Available Methods**: To explore what methods are available on any class instance, you can use Python's built-in `help()` function:
|
|
123
117
|
|
|
124
118
|
```python
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
pipette = SartoriusController(port_name="/dev/ttyUSB0")
|
|
131
|
-
|
|
132
|
-
gantry.connect()
|
|
133
|
-
pipette.connect()
|
|
134
|
-
|
|
135
|
-
# Move to source well
|
|
136
|
-
gantry.move_absolute(x=50.0, y=-50.0, z=-20.0)
|
|
137
|
-
pipette.aspirate(amount=50.0)
|
|
138
|
-
|
|
139
|
-
# Move to destination well
|
|
140
|
-
gantry.move_absolute(x=150.0, y=-150.0, z=-20.0)
|
|
141
|
-
pipette.dispense(amount=50.0)
|
|
142
|
-
|
|
143
|
-
# Cleanup
|
|
144
|
-
pipette.eject_tip()
|
|
145
|
-
gantry.disconnect()
|
|
146
|
-
pipette.disconnect()
|
|
119
|
+
machine = First()
|
|
120
|
+
help(machine) # See methods for the First machine
|
|
121
|
+
help(machine.qubot) # See GCodeController methods
|
|
122
|
+
help(machine.pipette) # See SartoriusController methods
|
|
123
|
+
help(machine.camera) # See CameraController methods
|
|
147
124
|
```
|
|
148
125
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
### Motion Systems
|
|
126
|
+
Alternatively, you can read the source code directly in the `src/puda_drivers/` directory.
|
|
152
127
|
|
|
153
|
-
|
|
154
|
-
- Supports X, Y, Z, and A axes
|
|
155
|
-
- Configurable feed rates
|
|
156
|
-
- Position synchronization and homing
|
|
157
|
-
- Automatic axis limit validation for safe operation
|
|
128
|
+
## Device Support
|
|
158
129
|
|
|
159
|
-
|
|
130
|
+
The following device types are supported:
|
|
160
131
|
|
|
132
|
+
- **GCode** - G-code compatible motion systems (e.g., QuBot)
|
|
161
133
|
- **Sartorius rLINE®** - Electronic pipettes and robotic dispensers
|
|
162
|
-
|
|
163
|
-
- Tip attachment and ejection
|
|
164
|
-
- Configurable speeds and volumes
|
|
134
|
+
- **Camera** - Webcams and USB cameras for image and video capture
|
|
165
135
|
|
|
166
|
-
##
|
|
167
|
-
|
|
168
|
-
### Axis Limit Validation
|
|
169
|
-
|
|
170
|
-
Both `move_absolute()` and `move_relative()` validate positions against configured axis limits before executing any movement. If a position is outside the limits, a `ValueError` is raised:
|
|
171
|
-
|
|
172
|
-
```python
|
|
173
|
-
from puda_drivers.move import GCodeController
|
|
174
|
-
|
|
175
|
-
gantry = GCodeController(port_name="/dev/ttyACM0")
|
|
176
|
-
gantry.connect()
|
|
177
|
-
|
|
178
|
-
# Set axis limits
|
|
179
|
-
gantry.set_axis_limits("X", 0, 200)
|
|
180
|
-
gantry.set_axis_limits("Y", -200, 0)
|
|
181
|
-
|
|
182
|
-
try:
|
|
183
|
-
# This will raise ValueError: Value 250 outside axis limits [0, 200]
|
|
184
|
-
gantry.move_absolute(x=250.0, y=-50.0)
|
|
185
|
-
except ValueError as e:
|
|
186
|
-
print(f"Move rejected: {e}")
|
|
187
|
-
|
|
188
|
-
# Relative moves are also validated after conversion to absolute positions
|
|
189
|
-
try:
|
|
190
|
-
# If current X is 150, moving 100 more would exceed the limit
|
|
191
|
-
gantry.move_relative(x=100.0)
|
|
192
|
-
except ValueError as e:
|
|
193
|
-
print(f"Move rejected: {e}")
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Validation errors are automatically logged at the ERROR level before the exception is raised.
|
|
197
|
-
|
|
198
|
-
### Logging Best Practices
|
|
136
|
+
## Logging Best Practices
|
|
199
137
|
|
|
200
138
|
For production applications, configure logging at the start of your script:
|
|
201
139
|
|
|
202
140
|
```python
|
|
203
141
|
import logging
|
|
204
142
|
from puda_drivers.core.logging import setup_logging
|
|
205
|
-
from puda_drivers.move import GCodeController
|
|
206
143
|
|
|
207
144
|
# Configure logging first, before initializing devices
|
|
208
145
|
setup_logging(
|
|
209
146
|
enable_file_logging=True,
|
|
210
147
|
log_level=logging.INFO,
|
|
211
|
-
log_file_name="
|
|
148
|
+
log_file_name="experiment"
|
|
212
149
|
)
|
|
213
150
|
|
|
214
151
|
# Now all device operations will be logged
|
|
215
|
-
gantry = GCodeController(port_name="/dev/ttyACM0")
|
|
216
152
|
# ... rest of your code
|
|
217
153
|
```
|
|
218
154
|
|
|
@@ -244,6 +180,8 @@ sartorius_ports = list_serial_ports(filter_desc="Sartorius")
|
|
|
244
180
|
|
|
245
181
|
### Setup Development Environment
|
|
246
182
|
|
|
183
|
+
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.
|
|
184
|
+
|
|
247
185
|
```bash
|
|
248
186
|
# Create virtual environment
|
|
249
187
|
uv venv
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "puda-drivers"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.10"
|
|
4
4
|
description = "Hardware drivers for the PUDA platform."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -19,6 +19,7 @@ classifiers = [
|
|
|
19
19
|
license = "MIT"
|
|
20
20
|
license-files = ["LICEN[CS]E*"]
|
|
21
21
|
dependencies = [
|
|
22
|
+
"opencv-python>=4.12.0.88",
|
|
22
23
|
"pyserial~=3.5",
|
|
23
24
|
]
|
|
24
25
|
|