puda-drivers 0.0.19__tar.gz → 0.0.20__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 (42) hide show
  1. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/PKG-INFO +2 -1
  2. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/pyproject.toml +2 -1
  3. puda_drivers-0.0.20/src/puda_drivers/machines/__init__.py +4 -0
  4. puda_drivers-0.0.20/src/puda_drivers/machines/biologic.py +357 -0
  5. puda_drivers-0.0.19/src/puda_drivers/machines/__init__.py +0 -3
  6. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/.gitignore +0 -0
  7. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/LICENSE +0 -0
  8. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/README.md +0 -0
  9. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/__init__.py +0 -0
  10. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/core/__init__.py +0 -0
  11. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/core/logging.py +0 -0
  12. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/core/position.py +0 -0
  13. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/core/serialcontroller.py +0 -0
  14. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/cv/__init__.py +0 -0
  15. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/cv/camera.py +0 -0
  16. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/labware/MEA_cell_MTP.json +0 -0
  17. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/labware/__init__.py +0 -0
  18. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/labware/labware.py +0 -0
  19. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/labware/opentrons_96_tiprack_300ul.json +0 -0
  20. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/labware/polyelectric_8_wellplate_30000ul.json +0 -0
  21. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/labware/trash_bin.json +0 -0
  22. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/machines/first.py +0 -0
  23. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/measure/electrode.py +0 -0
  24. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/move/__init__.py +0 -0
  25. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/move/deck.py +0 -0
  26. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/move/gcode.py +0 -0
  27. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/move/grbl/__init__.py +0 -0
  28. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/move/grbl/api.py +0 -0
  29. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/move/grbl/constants.py +0 -0
  30. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/py.typed +0 -0
  31. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/transfer/liquid/sartorius/__init__.py +0 -0
  32. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/transfer/liquid/sartorius/api.py +0 -0
  33. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/transfer/liquid/sartorius/constants.py +0 -0
  34. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/src/puda_drivers/transfer/liquid/sartorius/rLine.py +0 -0
  35. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/tests/example.py +0 -0
  36. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/tests/first.py +0 -0
  37. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/tests/pipette.py +0 -0
  38. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/tests/poll_position.py +0 -0
  39. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/tests/qubot.py +0 -0
  40. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/tests/test_deck.py +0 -0
  41. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/tests/test_position_polling.py +0 -0
  42. {puda_drivers-0.0.19 → puda_drivers-0.0.20}/tests/webcam.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: puda-drivers
3
- Version: 0.0.19
3
+ Version: 0.0.20
4
4
  Summary: Hardware drivers for the PUDA platform.
5
5
  Project-URL: Homepage, https://github.com/PUDAP/puda
6
6
  Project-URL: Issues, https://github.com/PUDAP/puda/issues
@@ -16,6 +16,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
16
  Classifier: Topic :: System :: Hardware
17
17
  Requires-Python: >=3.10
18
18
  Requires-Dist: nats-py>=2.12.0
19
+ Requires-Dist: numpy==2.4.2
19
20
  Requires-Dist: opencv-python>=4.12.0.88
20
21
  Requires-Dist: pyserial~=3.5
21
22
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "puda-drivers"
3
- version = "0.0.19"
3
+ version = "0.0.20"
4
4
  description = "Hardware drivers for the PUDA platform."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -21,6 +21,7 @@ license-files = ["LICEN[CS]E*"]
21
21
 
22
22
  dependencies = [
23
23
  "nats-py>=2.12.0",
24
+ "numpy==2.4.2", # Explicit numpy to ensure proper Windows wheels (avoids MINGW-W64 builds)
24
25
  "opencv-python>=4.12.0.88",
25
26
  "pyserial~=3.5",
26
27
  ]
@@ -0,0 +1,4 @@
1
+ from .first import First
2
+ from .biologic import Biologic
3
+
4
+ __all__ = ["First", "Biologic"]
@@ -0,0 +1,357 @@
1
+ """
2
+ BiologicMachine class for handling Biologic device commands.
3
+
4
+ This module provides a wrapper around easy_biologic that enables dynamic command
5
+ handling via getattr, allowing command-based execution patterns for Biologic
6
+ electrochemical testing devices.
7
+ """
8
+ import logging
9
+ import sys
10
+ from typing import Dict, Any, Type
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # Only import easy_biologic on Windows (allows class inspection on Linux)
15
+ if sys.platform == "win32":
16
+ import easy_biologic as ebl
17
+ import easy_biologic.base_programs as blp
18
+ else:
19
+ ebl = None
20
+ blp = None
21
+
22
+
23
+ class Biologic:
24
+ """
25
+ Wrapper around easy_biologic that provides command handlers for Biologic devices.
26
+
27
+ This class wraps easy_biologic.base_programs to allow for dynamic method lookup
28
+ via getattr, enabling command-based execution patterns. Each method (OCV, CA, PEIS, etc.)
29
+ wraps the corresponding base program class and provides a consistent interface.
30
+
31
+ Example:
32
+ machine = Biologic("192.168.1.2")
33
+ handler = getattr(machine, "OCV", None) # Get handler dynamically
34
+ result = handler(params={"time": 60}, channels=[1, 2])
35
+ """
36
+
37
+ def __init__(self, device_ip: str):
38
+ """
39
+ Initialize the Biologic machine.
40
+
41
+ Args:
42
+ device_ip: IP address of the Biologic device
43
+ """
44
+ self.device_ip = device_ip
45
+ self.device = None
46
+ logger.info("BiologicMachine initialized with IP: %s (not connected yet)", device_ip)
47
+
48
+ def startup(self):
49
+ """
50
+ Connect to the Biologic device.
51
+
52
+ This method should be called before executing any commands to establish
53
+ the connection to the device.
54
+
55
+ Raises:
56
+ OSError: If easy_biologic cannot be used (e.g., on non-Windows systems)
57
+ """
58
+ if ebl is None:
59
+ raise OSError("easy_biologic can only be used on Windows.")
60
+ if self.device is None:
61
+ self.device = ebl.BiologicDevice(self.device_ip)
62
+ logger.info("BiologicDevice connected with IP: %s", self.device_ip)
63
+ else:
64
+ logger.warning("BiologicDevice already connected")
65
+
66
+ def _run_base_program(
67
+ self,
68
+ program_class: Type[blp.BiologicProgram],
69
+ params: dict[str, Any],
70
+ **kwargs
71
+ ) -> Dict[str, Any]:
72
+ """
73
+ Generic helper method to run any base program.
74
+
75
+ This method handles the common pattern:
76
+ 1. Create the program instance with params and kwargs
77
+ 2. Run the program (with appropriate signature)
78
+ 3. Return the data
79
+
80
+ Args:
81
+ program_class: The base program class to instantiate (e.g., blp.OCV, blp.CA)
82
+ params: Dictionary of parameters for the program
83
+ **kwargs: Additional keyword arguments:
84
+ - For standard programs: retrieve_data [Default: True]
85
+ - For MPP/MPP_Cycles: data, by_channel, cv
86
+ - channels: Optional list of channel numbers (for constructor)
87
+
88
+ Returns:
89
+ Dictionary containing the program data
90
+
91
+ Raises:
92
+ RuntimeError: If startup() has not been called yet
93
+ """
94
+ if self.device is None:
95
+ raise RuntimeError("Device not connected. Call startup() before executing commands.")
96
+
97
+ # Check program class type to determine run signature
98
+ # MPP and MPP_Cycles use: data, by_channel, cv
99
+ # MPP_Tracking uses: folder, by_channel
100
+ # Standard programs use: retrieve_data
101
+ if issubclass(program_class, blp.MPP):
102
+ # MPP/MPP_Cycles style: extract run parameters
103
+ data = kwargs.pop('data', 'data')
104
+ by_channel = kwargs.pop('by_channel', False)
105
+ cv_params = kwargs.pop('cv', {})
106
+ run_kwargs = {'data': data, 'by_channel': by_channel, 'cv': cv_params}
107
+ elif program_class == blp.MPP_Tracking:
108
+ # MPP_Tracking style: extract run parameters
109
+ folder = kwargs.pop('folder', None)
110
+ by_channel = kwargs.pop('by_channel', False)
111
+ run_kwargs = {'folder': folder, 'by_channel': by_channel}
112
+ else:
113
+ # Standard program style: extract retrieve_data
114
+ retrieve_data = kwargs.pop('retrieve_data', True)
115
+ run_kwargs = {'retrieve_data': retrieve_data}
116
+
117
+ # Create program instance - pass all remaining kwargs directly to constructor
118
+ program = program_class(
119
+ device=self.device,
120
+ params=params,
121
+ **kwargs
122
+ )
123
+
124
+ # Run the program with appropriate signature
125
+ program.run(**run_kwargs)
126
+
127
+ return program.data
128
+
129
+ def OCV(
130
+ self,
131
+ params: dict[str, Any],
132
+ **kwargs
133
+ ) -> Dict[str, Any]:
134
+ """
135
+ Run OCV (Open Circuit Voltage) test.
136
+
137
+ Args:
138
+ params: Dictionary containing:
139
+ - time: Test duration in seconds
140
+ - time_interval: Maximum time between readings. [Default: 1]
141
+ - voltage_interval: Maximum interval between voltage readings. [Default: 0.01]
142
+ **kwargs: Additional keyword arguments passed to program constructor:
143
+ - channels: Optional list of channel numbers
144
+ - retrieve_data: Whether to automatically retrieve data after running [Default: True]
145
+
146
+ Returns:
147
+ Dictionary containing the OCV data (keyed by channel)
148
+ """
149
+ logger.info("Running OCV test: params=%s", params)
150
+ return self._run_base_program(blp.OCV, params, **kwargs)
151
+
152
+ def CA(
153
+ self,
154
+ params: dict[str, Any],
155
+ **kwargs
156
+ ) -> Dict[str, Any]:
157
+ """
158
+ Run CA (Chronoamperometry) test.
159
+
160
+ Args:
161
+ params: Dictionary containing:
162
+ - voltages: List of voltages.
163
+ - durations: List of times in seconds.
164
+ - vs_initial: If step is vs. initial or previous. [Default: False]
165
+ - time_interval: Maximum time interval between points. [Default: 1]
166
+ - current_interval: Maximum current change between points. [Default: 0.001]
167
+ - current_range: Current range. Use ec_lib.IRange. [Default: IRange.m10]
168
+ **kwargs: Additional keyword arguments passed to program constructor:
169
+ - channels: Optional list of channel numbers
170
+ - retrieve_data: Whether to automatically retrieve data after running [Default: True]
171
+
172
+ Returns:
173
+ Dictionary containing the CA data (keyed by channel)
174
+ """
175
+ logger.info("Running CA test: params=%s", params)
176
+ return self._run_base_program(blp.CA, params, **kwargs)
177
+
178
+
179
+ def PEIS(
180
+ self,
181
+ params: dict[str, Any],
182
+ **kwargs
183
+ ) -> Dict[str, Any]:
184
+ """
185
+ Run PEIS (Potentiostatic Electrochemical Impedance Spectroscopy) test.
186
+
187
+ Args:
188
+ params: Dictionary containing:
189
+ - voltage: Initial potential in Volts.
190
+ - amplitude_voltage: Sinus amplitude in Volts.
191
+ - initial_frequency: Initial frequency in Hertz.
192
+ - final_frequency: Final frequency in Hertz.
193
+ - frequency_number: Number of frequencies.
194
+ - duration: Overall duration in seconds.
195
+ - vs_initial: If step is vs. initial or previous. [Default: False]
196
+ - time_interval: Maximum time interval between points in seconds. [Default: 1]
197
+ - current_interval: Maximum time interval between points in Amps. [Default: 0.001]
198
+ - sweep: Defines whether the spacing between frequencies is logarithmic ('log') or linear ('lin'). [Default: 'log']
199
+ - repeat: Number of times to repeat the measurement and average the values for each frequency. [Default: 1]
200
+ - correction: Drift correction. [Default: False]
201
+ - wait: Adds a delay before the measurement at each frequency. The delay is expressed as a fraction of the period. [Default: 0]
202
+ **kwargs: Additional keyword arguments passed to program constructor:
203
+ - channels: Optional list of channel numbers
204
+ - retrieve_data: Whether to automatically retrieve data after running [Default: True]
205
+
206
+ Returns:
207
+ Dictionary containing the PEIS data (keyed by channel)
208
+ """
209
+ logger.info("Running PEIS test: params=%s", params)
210
+ return self._run_base_program(blp.PEIS, params, **kwargs)
211
+
212
+
213
+ def GEIS(
214
+ self,
215
+ params: dict[str, Any],
216
+ **kwargs
217
+ ) -> Dict[str, Any]:
218
+ """
219
+ Run GEIS (Galvanostatic Electrochemical Impedance Spectroscopy) test.
220
+
221
+ Args:
222
+ params: Dictionary containing:
223
+ - current: Initial current in Ampere.
224
+ - amplitude_current: Sinus amplitude in Ampere.
225
+ - initial_frequency: Initial frequency in Hertz.
226
+ - final_frequency: Final frequency in Hertz.
227
+ - frequency_number: Number of frequencies.
228
+ - duration: Overall duration in seconds.
229
+ - vs_initial: If step is vs. initial or previous. [Default: False]
230
+ - time_interval: Maximum time interval between points in seconds. [Default: 1]
231
+ - potential_interval: Maximum interval between points in Volts. [Default: 0.001]
232
+ - sweep: Defines whether the spacing between frequencies is logarithmic ('log') or linear ('lin'). [Default: 'log']
233
+ - repeat: Number of times to repeat the measurement and average the values for each frequency. [Default: 1]
234
+ - correction: Drift correction. [Default: False]
235
+ - wait: Adds a delay before the measurement at each frequency. The delay is expressed as a fraction of the period. [Default: 0]
236
+ **kwargs: Additional keyword arguments passed to program constructor:
237
+ - channels: Optional list of channel numbers
238
+ - retrieve_data: Whether to automatically retrieve data after running [Default: True]
239
+
240
+ Returns:
241
+ Dictionary containing the GEIS data (keyed by channel)
242
+ """
243
+ logger.info("Running GEIS test: params=%s", params)
244
+ return self._run_base_program(blp.GEIS, params, **kwargs)
245
+
246
+ def CV(
247
+ self,
248
+ params: dict[str, Any],
249
+ **kwargs
250
+ ) -> Dict[str, Any]:
251
+ """
252
+ Run CV (Cyclic Voltammetry) test.
253
+
254
+ Args:
255
+ params: Dictionary containing:
256
+ - start: Start voltage. [Default: 0]
257
+ - end: End voltage. Boundary voltage in forward scan. [Default: 0.5]
258
+ - E2: Boundary voltage in backward scan. [Default: 0]
259
+ - Ef: End voltage in the final cycle scan [Default: 0]
260
+ - step: Voltage step. dEN/1000 [Default: 0.01]
261
+ - rate: Scan rate in V/s. [Default: 0.01]
262
+ - average: Average over points. [Default: False]
263
+ - N_Cycles: Number of cycles. [Default: 0]
264
+ **kwargs: Additional keyword arguments passed to program constructor:
265
+ - channels: Optional list of channel numbers
266
+ - retrieve_data: Whether to automatically retrieve data after running [Default: True]
267
+
268
+ Returns:
269
+ Dictionary containing the CV data (keyed by channel)
270
+ """
271
+ logger.info("Running CV test: params=%s", params)
272
+ return self._run_base_program(blp.CV, params, **kwargs)
273
+
274
+ def MPP_Tracking(
275
+ self,
276
+ params: dict[str, Any],
277
+ **kwargs
278
+ ) -> Dict[str, Any]:
279
+ """
280
+ Run MPP_Tracking (Maximum Power Point Tracking) test.
281
+
282
+ Args:
283
+ params: Dictionary containing:
284
+ - run_time: Run time in seconds.
285
+ - init_vmpp: Initial v_mpp.
286
+ - probe_step: Voltage step for probe. [Default: 0.005 V]
287
+ - probe_points: Number of data points to collect for probe. [Default: 5]
288
+ - probe_interval: How often to probe in seconds. [Default: 2]
289
+ - record_interval: How often to record a data point in seconds. [Default: 1]
290
+ **kwargs: Additional keyword arguments:
291
+ - channels: Optional list of channel numbers
292
+ - folder: Folder or file for saving data [Default: None]
293
+ - by_channel: Save data by channel [Default: False]
294
+
295
+ Returns:
296
+ Dictionary containing the MPP_Tracking data (keyed by channel)
297
+ """
298
+ logger.info("Running MPP_Tracking test: params=%s", params)
299
+ return self._run_base_program(blp.MPP_Tracking, params, **kwargs)
300
+
301
+ def MPP(
302
+ self,
303
+ params: dict[str, Any],
304
+ **kwargs
305
+ ) -> Dict[str, Any]:
306
+ """
307
+ Run MPP (Maximum Power Point) test.
308
+
309
+ Makes a CV scan and Voc scan and runs MPP tracking.
310
+
311
+ Args:
312
+ params: Dictionary containing:
313
+ - run_time: Run time in seconds.
314
+ - probe_step: Voltage step for probe. [Default: 0.005 V]
315
+ - probe_points: Number of data points to collect for probe. [Default: 5]
316
+ - probe_interval: How often to probe in seconds. [Default: 2]
317
+ - record_interval: How often to record a data point in seconds. [Default: 1]
318
+ **kwargs: Additional keyword arguments:
319
+ - channels: Optional list of channel numbers
320
+ - data: Data folder path. [Default: 'data']
321
+ - by_channel: Save data by channel. [Default: False]
322
+ - cv: Parameters passed to CV to find initial MPP, or {} for default. [Default: {}]
323
+
324
+ Returns:
325
+ Dictionary containing the MPP data (keyed by channel)
326
+ """
327
+ logger.info("Running MPP test: params=%s", params)
328
+ return self._run_base_program(blp.MPP, params, **kwargs)
329
+
330
+ def MPP_Cycles(
331
+ self,
332
+ params: dict[str, Any],
333
+ **kwargs
334
+ ) -> Dict[str, Any]:
335
+ """
336
+ Run MPP_Cycles (MPP tracking with periodic CV scans) test.
337
+
338
+ Args:
339
+ params: Dictionary containing:
340
+ - run_time: Cycle run time in seconds.
341
+ - cycles: Number of cycles to perform.
342
+ - probe_step: Voltage step for probe. [Default: 0.01 V]
343
+ - probe_points: Number of data points to collect for probe. [Default: 5]
344
+ - probe_interval: How often to probe in seconds. [Default: 2]
345
+ - record_interval: How often to record a data point in seconds. [Default: 1]
346
+ **kwargs: Additional keyword arguments:
347
+ - channels: Optional list of channel numbers
348
+ - data: Data folder path. [Default: 'data']
349
+ - by_channel: Save data by channel. [Default: False]
350
+ - cv: Parameters for the CV. [Default: {}]
351
+
352
+ Returns:
353
+ Dictionary containing the MPP_Cycles data (keyed by channel)
354
+ """
355
+ logger.info("Running MPP_Cycles test: params=%s", params)
356
+ return self._run_base_program(blp.MPP_Cycles, params, **kwargs)
357
+
@@ -1,3 +0,0 @@
1
- from .first import First
2
-
3
- __all__ = ["First"]
File without changes
File without changes
File without changes