Skip to content

Commit 2a3d90d

Browse files
committed
Support real pump controllers and hybrid execution mode
Update files for real pump controller compatibility and enable hybrid (simulation + hardware) API execution
1 parent 15d5060 commit 2a3d90d

7 files changed

Lines changed: 307 additions & 93 deletions

File tree

pi-deployment/requirements-api.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# API-specific dependencies (pinned)
22
# These are additive to the base requirements; install when running the API.
3-
fastapi==0.95.2
3+
4+
fastapi>=0.103.0
45
uvicorn[standard]==0.21.1
5-
labthings-fastapi==0.0.6
6-
pydantic==1.10.14
6+
labthings-fastapi>=0.0.14
7+
pydantic>=2.0.0
78
typing_extensions>=4.7.0
89
PyYAML>=6.0
9-

software/README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ The repository includes **3 requirements files** for different deployment scenar
125125
2. **Run the application in simulation mode**:
126126
```bash
127127
# Default port (5000)
128+
# set RIO_SIMULATION=true (Windows)
128129
export RIO_SIMULATION=true
129130
python main.py
130131

@@ -155,16 +156,25 @@ To run without hardware (for testing on a Mac/PC):
155156

156157
**Option 1: Quick setup and run**
157158
```bash
158-
cd software
159+
160+
# NOTE: These scripts are written for conda/mamba environments. If you are using virtualenv, you will need to modify the scripts.
161+
162+
# on Linux/Mac
159163
./scripts/dev/setup-simulation.sh # First time setup
160164
./scripts/dev/run-simulation.sh # Run simulation
165+
166+
# on Windows (Git Bash)
167+
bash ./scripts/dev/setup-simulation.sh
168+
bash ./scripts/dev/run-simulation.sh
169+
161170
```
162171

163172
Need custom parameters for simulation (frame size, ROI defaults, feature flags)? See `configurations/README.md` for the environment-variable profiles and examples you can export before running.
164173

165174
**Option 2: Manual setup**
166175
```bash
167176
cd software
177+
# set RIO_SIMULATION=true (Windows)
168178
export RIO_SIMULATION=true
169179
python main.py
170180
```

software/api/client/notebooks/.ipynb_checkpoints/tutorial-checkpoint.ipynb

Lines changed: 132 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,22 @@
2525
},
2626
{
2727
"cell_type": "code",
28-
"execution_count": null,
29-
"metadata": {},
30-
"outputs": [],
28+
"execution_count": 1,
29+
"metadata": {
30+
"jupyter": {
31+
"source_hidden": true
32+
}
33+
},
34+
"outputs": [
35+
{
36+
"name": "stdout",
37+
"output_type": "stream",
38+
"text": [
39+
"✅ Rio API client initialized\n",
40+
" Base URL: http://localhost:8000\n"
41+
]
42+
}
43+
],
3144
"source": [
3245
"# Configuration\n",
3346
"import os\n",
@@ -42,23 +55,23 @@
4255
"import sys\n",
4356
"from pathlib import Path\n",
4457
"\n",
45-
"# Add software/client to path for client import\n",
46-
"# This works whether running from repo root or software/client/notebooks/\n",
58+
"# Add software/api/client to path for client import\n",
59+
"# This works whether running from repo root or software/api/client/notebooks/\n",
4760
"repo_root = Path.cwd()\n",
48-
"if (repo_root / \"software\" / \"client\").exists():\n",
61+
"if (repo_root / \"software\" / \"api\" / \"client\").exists():\n",
4962
" # Running from repo root\n",
5063
" sys.path.insert(0, str(repo_root / \"software\"))\n",
51-
"elif (repo_root.parent.parent / \"software\" / \"client\").exists():\n",
52-
" # Running from software/client/notebooks/\n",
64+
"elif (repo_root.parent.parent / \"software\" / \"api\" / \"client\").exists():\n",
65+
" # Running from software/api/client/notebooks/\n",
5366
" sys.path.insert(0, str(repo_root.parent.parent))\n",
5467
"else:\n",
5568
" # Fallback: try to find software directory\n",
5669
" for parent in repo_root.parents:\n",
57-
" if (parent / \"software\" / \"client\").exists():\n",
70+
" if (parent / \"software\" / \"api\" / \"client\").exists():\n",
5871
" sys.path.insert(0, str(parent / \"software\"))\n",
5972
" break\n",
6073
"\n",
61-
"from client import RioClient, RioStreamClient\n",
74+
"from api.client import RioClient, RioStreamClient\n",
6275
"import requests\n",
6376
"import json\n",
6477
"import time\n",
@@ -72,12 +85,14 @@
7285
"client = RioClient(base_url=API_BASE_URL)\n",
7386
"\n",
7487
"print(\"✅ Rio API client initialized\")\n",
75-
"print(f\" Base URL: {API_BASE_URL}\")\n"
88+
"print(f\" Base URL: {API_BASE_URL}\")"
7689
]
7790
},
7891
{
7992
"cell_type": "markdown",
80-
"metadata": {},
93+
"metadata": {
94+
"jp-MarkdownHeadingCollapsed": true
95+
},
8196
"source": [
8297
"## 1. System Health and Capabilities\n",
8398
"\n",
@@ -86,9 +101,27 @@
86101
},
87102
{
88103
"cell_type": "code",
89-
"execution_count": null,
104+
"execution_count": 2,
90105
"metadata": {},
91-
"outputs": [],
106+
"outputs": [
107+
{
108+
"name": "stdout",
109+
"output_type": "stream",
110+
"text": [
111+
"Status: ok\n",
112+
"Simulation mode: True\n",
113+
"\n",
114+
"Available modules:\n",
115+
" ✅ flow: True\n",
116+
" ✅ pressure: True\n",
117+
" ✅ heater: True\n",
118+
" ❌ strobe: False\n",
119+
" ❌ camera: False\n",
120+
" ❌ droplet: False\n",
121+
" ✅ pump: True\n"
122+
]
123+
}
124+
],
92125
"source": [
93126
"# Check health\n",
94127
"health = client.health()\n",
@@ -100,12 +133,14 @@
100133
"print(\"\\nAvailable modules:\")\n",
101134
"for module, available in capabilities['modules'].items():\n",
102135
" status = \"\" if available else \"\"\n",
103-
" print(f\" {status} {module}: {available}\")\n"
136+
" print(f\" {status} {module}: {available}\")"
104137
]
105138
},
106139
{
107140
"cell_type": "markdown",
108-
"metadata": {},
141+
"metadata": {
142+
"jp-MarkdownHeadingCollapsed": true
143+
},
109144
"source": [
110145
"## 2. Channel Configuration\n",
111146
"\n",
@@ -140,7 +175,9 @@
140175
},
141176
{
142177
"cell_type": "markdown",
143-
"metadata": {},
178+
"metadata": {
179+
"jp-MarkdownHeadingCollapsed": true
180+
},
144181
"source": [
145182
"## 3. Flow/Pressure Control\n",
146183
"\n",
@@ -174,7 +211,9 @@
174211
},
175212
{
176213
"cell_type": "markdown",
177-
"metadata": {},
214+
"metadata": {
215+
"jp-MarkdownHeadingCollapsed": true
216+
},
178217
"source": [
179218
"## 4. Heater Control\n",
180219
"\n",
@@ -210,7 +249,9 @@
210249
},
211250
{
212251
"cell_type": "markdown",
213-
"metadata": {},
252+
"metadata": {
253+
"jp-MarkdownHeadingCollapsed": true
254+
},
214255
"source": [
215256
"## 5. Camera Snapshot\n",
216257
"\n",
@@ -234,7 +275,9 @@
234275
},
235276
{
236277
"cell_type": "markdown",
237-
"metadata": {},
278+
"metadata": {
279+
"jp-MarkdownHeadingCollapsed": true
280+
},
238281
"source": [
239282
"## 6. WebSocket Telemetry Streaming\n",
240283
"\n",
@@ -277,7 +320,9 @@
277320
},
278321
{
279322
"cell_type": "markdown",
280-
"metadata": {},
323+
"metadata": {
324+
"jp-MarkdownHeadingCollapsed": true
325+
},
281326
"source": [
282327
"## 7. Plot Streaming Data\n",
283328
"\n",
@@ -332,7 +377,9 @@
332377
},
333378
{
334379
"cell_type": "markdown",
335-
"metadata": {},
380+
"metadata": {
381+
"jp-MarkdownHeadingCollapsed": true
382+
},
336383
"source": [
337384
"## 8. On-Demand Data Capture\n",
338385
"\n",
@@ -373,7 +420,9 @@
373420
},
374421
{
375422
"cell_type": "markdown",
376-
"metadata": {},
423+
"metadata": {
424+
"jp-MarkdownHeadingCollapsed": true
425+
},
377426
"source": [
378427
"## 9. Droplet Detection (if available)\n",
379428
"\n",
@@ -411,6 +460,50 @@
411460
" print(\"❌ Droplet detection not available\")\n"
412461
]
413462
},
463+
{
464+
"cell_type": "markdown",
465+
"metadata": {},
466+
"source": [
467+
"## 10. Syringe Pump Control (if available)\n",
468+
"\n",
469+
"This section uses the lightweight pump client. It supports the WoT pump Thing at `/pump/` and falls back to legacy `/api/control/pump/*` endpoints if present.\n"
470+
]
471+
},
472+
{
473+
"cell_type": "code",
474+
"execution_count": 5,
475+
"metadata": {},
476+
"outputs": [
477+
{
478+
"name": "stdout",
479+
"output_type": "stream",
480+
"text": [
481+
"Pump D state: {'pump': 'D', 'flow': 2500.0, 'diameter': 8.17, 'direction': 1, 'state': False, 'unit': 'UL/HR', 'gearbox': '100:1', 'microstep': '1/8', 'threadrod': '1-START', 'enabled': False}\n"
482+
]
483+
}
484+
],
485+
"source": [
486+
"from api.client.syringe_pump_api import SyringePumpAPI, PumpAPIError\n",
487+
"\n",
488+
"pump_api = SyringePumpAPI(base_url=API_BASE_URL, use_wot=False)\n",
489+
"\n",
490+
"try:\n",
491+
" pump=\"D\"\n",
492+
" state = pump_api.get_state(pump)\n",
493+
" print(f\"Pump {pump} state: {state}\")\n",
494+
" #print(state)\n",
495+
"\n",
496+
" # Example commands (uncomment when pump backend is available):\n",
497+
" # pump_api.set_diameter(pump, 8.17)\n",
498+
" # pump_api.set_direction(pump, \"infuse\")\n",
499+
" # pump_api.set_flow(pump, 100.0)\n",
500+
" # pump_api.set_state(pump, \"run\")\n",
501+
"except PumpAPIError as e:\n",
502+
" print(f\"❌ Pump API error: {e}\")\n",
503+
"except Exception as e:\n",
504+
" print(f\"❌ Pump not available: {e}\")"
505+
]
506+
},
414507
{
415508
"cell_type": "markdown",
416509
"metadata": {},
@@ -426,10 +519,24 @@
426519
}
427520
],
428521
"metadata": {
522+
"kernelspec": {
523+
"display_name": "Python 3 (ipykernel)",
524+
"language": "python",
525+
"name": "python3"
526+
},
429527
"language_info": {
430-
"name": "python"
528+
"codemirror_mode": {
529+
"name": "ipython",
530+
"version": 3
531+
},
532+
"file_extension": ".py",
533+
"mimetype": "text/x-python",
534+
"name": "python",
535+
"nbconvert_exporter": "python",
536+
"pygments_lexer": "ipython3",
537+
"version": "3.11.4"
431538
}
432539
},
433540
"nbformat": 4,
434-
"nbformat_minor": 2
541+
"nbformat_minor": 4
435542
}

0 commit comments

Comments
 (0)