orion-bridge 2.0.0__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.
- orion_bridge-2.0.0/PKG-INFO +204 -0
- orion_bridge-2.0.0/README.md +155 -0
- orion_bridge-2.0.0/orion_bridge/__init__.py +4 -0
- orion_bridge-2.0.0/orion_bridge/__main__.py +3 -0
- orion_bridge-2.0.0/orion_bridge/cli.py +296 -0
- orion_bridge-2.0.0/orion_bridge/config.py +166 -0
- orion_bridge-2.0.0/orion_bridge/dispatcher.py +186 -0
- orion_bridge-2.0.0/orion_bridge/handlers/__init__.py +24 -0
- orion_bridge-2.0.0/orion_bridge/handlers/abb.py +202 -0
- orion_bridge-2.0.0/orion_bridge/handlers/base.py +784 -0
- orion_bridge-2.0.0/orion_bridge/handlers/plc.py +158 -0
- orion_bridge-2.0.0/orion_bridge/handlers/shell.py +183 -0
- orion_bridge-2.0.0/orion_bridge/handlers/xarm_gazebo.py +253 -0
- orion_bridge-2.0.0/orion_bridge/handlers/xarm_mujoco.py +246 -0
- orion_bridge-2.0.0/orion_bridge/handlers/xarm_physical.py +251 -0
- orion_bridge-2.0.0/orion_bridge/http/plc_api.py +89 -0
- orion_bridge-2.0.0/orion_bridge/logger.py +80 -0
- orion_bridge-2.0.0/orion_bridge/reactive.py +351 -0
- orion_bridge-2.0.0/orion_bridge/sim/__init__.py +7 -0
- orion_bridge-2.0.0/orion_bridge/sim/collision.py +334 -0
- orion_bridge-2.0.0/orion_bridge/sim/engine.py +241 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/base_link.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/end_tool.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/left_finger.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/left_inner_knuckle.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/left_outer_knuckle.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link1.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link2.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link3.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link4.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link5.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link6.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link7.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link_base.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/right_finger.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/right_inner_knuckle.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/right_outer_knuckle.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/scene_xarm6.xml +24 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/xarm6.urdf +293 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/xarm6.xml +170 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/base_link.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/end_tool.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/left_finger.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/left_inner_knuckle.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/left_outer_knuckle.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link1.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link2.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link3.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link4.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link5.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link6.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link7.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link_base.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/right_finger.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/right_inner_knuckle.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/right_outer_knuckle.stl +0 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/hand.xml +124 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/scene_xarm7.xml +58 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/xarm7.urdf +219 -0
- orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/xarm7.xml +202 -0
- orion_bridge-2.0.0/orion_bridge/transport.py +327 -0
- orion_bridge-2.0.0/orion_bridge/ui/__init__.py +153 -0
- orion_bridge-2.0.0/orion_bridge/ui/banner.py +138 -0
- orion_bridge-2.0.0/orion_bridge/ui/logs.py +19 -0
- orion_bridge-2.0.0/orion_bridge/ui/menus.py +151 -0
- orion_bridge-2.0.0/orion_bridge/ui/status.py +101 -0
- orion_bridge-2.0.0/orion_bridge/ui/tables.py +108 -0
- orion_bridge-2.0.0/orion_bridge/ui/theme.py +64 -0
- orion_bridge-2.0.0/orion_bridge.egg-info/PKG-INFO +204 -0
- orion_bridge-2.0.0/orion_bridge.egg-info/SOURCES.txt +75 -0
- orion_bridge-2.0.0/orion_bridge.egg-info/dependency_links.txt +1 -0
- orion_bridge-2.0.0/orion_bridge.egg-info/entry_points.txt +2 -0
- orion_bridge-2.0.0/orion_bridge.egg-info/requires.txt +30 -0
- orion_bridge-2.0.0/orion_bridge.egg-info/top_level.txt +1 -0
- orion_bridge-2.0.0/pyproject.toml +79 -0
- orion_bridge-2.0.0/setup.cfg +4 -0
- orion_bridge-2.0.0/tests/test_collision.py +412 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: orion-bridge
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: ORION Lab Bridge — Modular device bridge for industrial robotics
|
|
5
|
+
Author-email: Leonardo <a01174639@tec.mx>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/FILL_USER/orion-bridge
|
|
8
|
+
Project-URL: Documentation, https://github.com/FILL_USER/orion-bridge#readme
|
|
9
|
+
Project-URL: Issues, https://github.com/FILL_USER/orion-bridge/issues
|
|
10
|
+
Project-URL: Source, https://github.com/FILL_USER/orion-bridge
|
|
11
|
+
Keywords: robotics,industrial,automation,xarm,abb,plc,ros2,mujoco,industry-4.0
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Classifier: Topic :: System :: Hardware
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
Requires-Dist: websockets>=12.0
|
|
28
|
+
Requires-Dist: rich>=13.0
|
|
29
|
+
Provides-Extra: xarm
|
|
30
|
+
Requires-Dist: xarm-python-sdk; extra == "xarm"
|
|
31
|
+
Provides-Extra: abb
|
|
32
|
+
Provides-Extra: plc
|
|
33
|
+
Requires-Dist: python-snap7; extra == "plc"
|
|
34
|
+
Provides-Extra: gazebo
|
|
35
|
+
Requires-Dist: roslibpy; extra == "gazebo"
|
|
36
|
+
Provides-Extra: mujoco
|
|
37
|
+
Requires-Dist: mujoco>=3.5.0; extra == "mujoco"
|
|
38
|
+
Provides-Extra: supabase
|
|
39
|
+
Requires-Dist: supabase; extra == "supabase"
|
|
40
|
+
Provides-Extra: http
|
|
41
|
+
Requires-Dist: flask; extra == "http"
|
|
42
|
+
Provides-Extra: all
|
|
43
|
+
Requires-Dist: xarm-python-sdk; extra == "all"
|
|
44
|
+
Requires-Dist: python-snap7; extra == "all"
|
|
45
|
+
Requires-Dist: roslibpy; extra == "all"
|
|
46
|
+
Requires-Dist: mujoco>=3.5.0; extra == "all"
|
|
47
|
+
Requires-Dist: supabase; extra == "all"
|
|
48
|
+
Requires-Dist: flask; extra == "all"
|
|
49
|
+
|
|
50
|
+
# ORION Bridge
|
|
51
|
+
|
|
52
|
+
**Modular device bridge connecting industrial robotics hardware to the ORION AI agent platform.**
|
|
53
|
+
|
|
54
|
+
[](https://pypi.org/project/orion-bridge/)
|
|
55
|
+
[](https://opensource.org/licenses/MIT)
|
|
56
|
+
[](https://www.python.org/downloads/)
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## What is ORION Bridge?
|
|
61
|
+
|
|
62
|
+
ORION Bridge is a lightweight Python daemon that sits between physical (or simulated) lab equipment and the [ORION agent platform](https://orion-learning.com). It speaks the hardware protocols — xArm SDK, Snap7 for Siemens S7-1200 PLCs, ABB Robot Web Services — and exposes a unified WebSocket interface that the ORION AI understands.
|
|
63
|
+
|
|
64
|
+
The bridge runs on any machine with network access to your devices: a lab PC next to the robot arm, a Raspberry Pi on the factory floor, or your laptop for simulation. Once running, ORION can read live telemetry, execute actions, and run multi-step routines through natural language — without any changes to your hardware configuration.
|
|
65
|
+
|
|
66
|
+
Supported hardware backends out of the box: **UFACTORY xArm 6/7** (physical + MuJoCo simulation), **ABB robots** (via Robot Web Services), **Siemens S7-1200 PLCs** (via python-snap7), and a **local shell** for scripting and debugging. A ROS/Gazebo adapter is also available for simulation-heavy workflows.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install orion-bridge[all]
|
|
74
|
+
orion
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The interactive menu guides you through device setup and server connection. No config file needed for the first run — it generates one for you.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Installation Options
|
|
82
|
+
|
|
83
|
+
Install only the drivers you need:
|
|
84
|
+
|
|
85
|
+
| Command | Installs |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `pip install orion-bridge` | Core only (WebSocket transport, CLI) |
|
|
88
|
+
| `pip install orion-bridge[xarm]` | + UFACTORY xArm SDK |
|
|
89
|
+
| `pip install orion-bridge[plc]` | + Siemens S7-1200 (python-snap7) |
|
|
90
|
+
| `pip install orion-bridge[mujoco]` | + MuJoCo simulation engine |
|
|
91
|
+
| `pip install orion-bridge[gazebo]` | + ROS/Gazebo (roslibpy) |
|
|
92
|
+
| `pip install orion-bridge[http]` | + PLC HTTP sidecar API (Flask) |
|
|
93
|
+
| `pip install orion-bridge[all]` | Everything above |
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## First Run
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
$ orion
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The CLI opens an interactive menu:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
[1] Add device — register an xArm, ABB, PLC, or shell device
|
|
107
|
+
[2] Remove device — remove a device from the list
|
|
108
|
+
[3] Configure server — pick a lab preset or enter a custom server URL + token
|
|
109
|
+
[4] Show status — display current devices and connection info
|
|
110
|
+
[5] Save config — write lab_config.json to disk
|
|
111
|
+
[6] Save & Start — save and launch the bridge
|
|
112
|
+
[0] Exit
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Typical first-run flow:**
|
|
116
|
+
|
|
117
|
+
1. Choose **3** to configure the server — pick a preset or enter your ORION server URL and bridge token.
|
|
118
|
+
2. Choose **1** to add devices — select the device type (xArm, ABB, PLC, shell) and provide the IP address.
|
|
119
|
+
3. Choose **6** to save the configuration and start the bridge.
|
|
120
|
+
|
|
121
|
+
The bridge connects to all registered devices, then opens a WebSocket to the ORION server. The ORION AI can now see your devices and send commands.
|
|
122
|
+
|
|
123
|
+
**Skip the menu with a saved config:**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
orion --config lab_config.json
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Run without a server (local testing):**
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
orion --local
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Configuration
|
|
138
|
+
|
|
139
|
+
After the first run, configuration is saved to `lab_config.json` in your working directory. You can edit this file directly or regenerate it through the menu.
|
|
140
|
+
|
|
141
|
+
Example `lab_config.json`:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"server": "wss://your-orion-server/ws/robot",
|
|
146
|
+
"token": "your-bridge-token",
|
|
147
|
+
"lab_name": "My Lab",
|
|
148
|
+
"devices": [
|
|
149
|
+
{ "type": "xarm", "id": "xarm-lab1", "ip": "192.168.1.185" },
|
|
150
|
+
{ "type": "plc", "id": "plc-station1", "ips": ["192.168.1.10"] }
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Environment variables override the config file:
|
|
156
|
+
|
|
157
|
+
| Variable | Purpose |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `WS_SERVER_URL` | Override server WebSocket URL |
|
|
160
|
+
| `WS_TOKEN` | Override bridge authentication token |
|
|
161
|
+
| `BRIDGE_ID` | Override bridge identifier (default: `lab-bridge-01`) |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## MuJoCo Simulation
|
|
166
|
+
|
|
167
|
+
Install with `pip install orion-bridge[mujoco]` to use the built-in MuJoCo physics simulation. xArm 6 and xArm 7 MJCF models are included in the package.
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"type": "xarm",
|
|
172
|
+
"id": "xarm7-sim",
|
|
173
|
+
"handler": "mujoco",
|
|
174
|
+
"viewer": true,
|
|
175
|
+
"mjcf_path": "sim/models/xarm7/scene_xarm7.xml"
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Set `"viewer": true` to open the MuJoCo interactive viewer window.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Requirements
|
|
184
|
+
|
|
185
|
+
- **Python**: 3.10 or higher
|
|
186
|
+
- **OS**: Windows 10/11, macOS 12+, Linux (Ubuntu 20.04+)
|
|
187
|
+
- **Network**: Access to device IPs and the ORION server endpoint
|
|
188
|
+
|
|
189
|
+
Hardware-specific requirements are installed via optional extras (see Installation Options above).
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Links
|
|
200
|
+
|
|
201
|
+
- **ORION Platform**: [orion-learning.com](https://orion-learning.com)
|
|
202
|
+
- **PyPI**: [pypi.org/project/orion-bridge](https://pypi.org/project/orion-bridge/)
|
|
203
|
+
- **Issues**: [github.com/FILL_USER/orion-bridge/issues](https://github.com/FILL_USER/orion-bridge/issues)
|
|
204
|
+
- **Source**: [github.com/FILL_USER/orion-bridge](https://github.com/FILL_USER/orion-bridge)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# ORION Bridge
|
|
2
|
+
|
|
3
|
+
**Modular device bridge connecting industrial robotics hardware to the ORION AI agent platform.**
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/orion-bridge/)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What is ORION Bridge?
|
|
12
|
+
|
|
13
|
+
ORION Bridge is a lightweight Python daemon that sits between physical (or simulated) lab equipment and the [ORION agent platform](https://orion-learning.com). It speaks the hardware protocols — xArm SDK, Snap7 for Siemens S7-1200 PLCs, ABB Robot Web Services — and exposes a unified WebSocket interface that the ORION AI understands.
|
|
14
|
+
|
|
15
|
+
The bridge runs on any machine with network access to your devices: a lab PC next to the robot arm, a Raspberry Pi on the factory floor, or your laptop for simulation. Once running, ORION can read live telemetry, execute actions, and run multi-step routines through natural language — without any changes to your hardware configuration.
|
|
16
|
+
|
|
17
|
+
Supported hardware backends out of the box: **UFACTORY xArm 6/7** (physical + MuJoCo simulation), **ABB robots** (via Robot Web Services), **Siemens S7-1200 PLCs** (via python-snap7), and a **local shell** for scripting and debugging. A ROS/Gazebo adapter is also available for simulation-heavy workflows.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install orion-bridge[all]
|
|
25
|
+
orion
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The interactive menu guides you through device setup and server connection. No config file needed for the first run — it generates one for you.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Installation Options
|
|
33
|
+
|
|
34
|
+
Install only the drivers you need:
|
|
35
|
+
|
|
36
|
+
| Command | Installs |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `pip install orion-bridge` | Core only (WebSocket transport, CLI) |
|
|
39
|
+
| `pip install orion-bridge[xarm]` | + UFACTORY xArm SDK |
|
|
40
|
+
| `pip install orion-bridge[plc]` | + Siemens S7-1200 (python-snap7) |
|
|
41
|
+
| `pip install orion-bridge[mujoco]` | + MuJoCo simulation engine |
|
|
42
|
+
| `pip install orion-bridge[gazebo]` | + ROS/Gazebo (roslibpy) |
|
|
43
|
+
| `pip install orion-bridge[http]` | + PLC HTTP sidecar API (Flask) |
|
|
44
|
+
| `pip install orion-bridge[all]` | Everything above |
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## First Run
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
$ orion
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The CLI opens an interactive menu:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
[1] Add device — register an xArm, ABB, PLC, or shell device
|
|
58
|
+
[2] Remove device — remove a device from the list
|
|
59
|
+
[3] Configure server — pick a lab preset or enter a custom server URL + token
|
|
60
|
+
[4] Show status — display current devices and connection info
|
|
61
|
+
[5] Save config — write lab_config.json to disk
|
|
62
|
+
[6] Save & Start — save and launch the bridge
|
|
63
|
+
[0] Exit
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Typical first-run flow:**
|
|
67
|
+
|
|
68
|
+
1. Choose **3** to configure the server — pick a preset or enter your ORION server URL and bridge token.
|
|
69
|
+
2. Choose **1** to add devices — select the device type (xArm, ABB, PLC, shell) and provide the IP address.
|
|
70
|
+
3. Choose **6** to save the configuration and start the bridge.
|
|
71
|
+
|
|
72
|
+
The bridge connects to all registered devices, then opens a WebSocket to the ORION server. The ORION AI can now see your devices and send commands.
|
|
73
|
+
|
|
74
|
+
**Skip the menu with a saved config:**
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
orion --config lab_config.json
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Run without a server (local testing):**
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
orion --local
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Configuration
|
|
89
|
+
|
|
90
|
+
After the first run, configuration is saved to `lab_config.json` in your working directory. You can edit this file directly or regenerate it through the menu.
|
|
91
|
+
|
|
92
|
+
Example `lab_config.json`:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"server": "wss://your-orion-server/ws/robot",
|
|
97
|
+
"token": "your-bridge-token",
|
|
98
|
+
"lab_name": "My Lab",
|
|
99
|
+
"devices": [
|
|
100
|
+
{ "type": "xarm", "id": "xarm-lab1", "ip": "192.168.1.185" },
|
|
101
|
+
{ "type": "plc", "id": "plc-station1", "ips": ["192.168.1.10"] }
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Environment variables override the config file:
|
|
107
|
+
|
|
108
|
+
| Variable | Purpose |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `WS_SERVER_URL` | Override server WebSocket URL |
|
|
111
|
+
| `WS_TOKEN` | Override bridge authentication token |
|
|
112
|
+
| `BRIDGE_ID` | Override bridge identifier (default: `lab-bridge-01`) |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## MuJoCo Simulation
|
|
117
|
+
|
|
118
|
+
Install with `pip install orion-bridge[mujoco]` to use the built-in MuJoCo physics simulation. xArm 6 and xArm 7 MJCF models are included in the package.
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"type": "xarm",
|
|
123
|
+
"id": "xarm7-sim",
|
|
124
|
+
"handler": "mujoco",
|
|
125
|
+
"viewer": true,
|
|
126
|
+
"mjcf_path": "sim/models/xarm7/scene_xarm7.xml"
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Set `"viewer": true` to open the MuJoCo interactive viewer window.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Requirements
|
|
135
|
+
|
|
136
|
+
- **Python**: 3.10 or higher
|
|
137
|
+
- **OS**: Windows 10/11, macOS 12+, Linux (Ubuntu 20.04+)
|
|
138
|
+
- **Network**: Access to device IPs and the ORION server endpoint
|
|
139
|
+
|
|
140
|
+
Hardware-specific requirements are installed via optional extras (see Installation Options above).
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Links
|
|
151
|
+
|
|
152
|
+
- **ORION Platform**: [orion-learning.com](https://orion-learning.com)
|
|
153
|
+
- **PyPI**: [pypi.org/project/orion-bridge](https://pypi.org/project/orion-bridge/)
|
|
154
|
+
- **Issues**: [github.com/FILL_USER/orion-bridge/issues](https://github.com/FILL_USER/orion-bridge/issues)
|
|
155
|
+
- **Source**: [github.com/FILL_USER/orion-bridge](https://github.com/FILL_USER/orion-bridge)
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""
|
|
2
|
+
orion_bridge/cli.py — Interactive CLI and bridge entry point.
|
|
3
|
+
|
|
4
|
+
Extracted from bridge.py (Phase 3, Step 7).
|
|
5
|
+
Connects all modules: config, ui, dispatcher, transport, logger, http.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
orion # interactive menu
|
|
9
|
+
orion --local # skip server, local only
|
|
10
|
+
orion --config lab.json # autostart from saved config
|
|
11
|
+
python -m orion_bridge # same as 'orion'
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
import time
|
|
17
|
+
import asyncio
|
|
18
|
+
import argparse
|
|
19
|
+
import logging
|
|
20
|
+
import threading
|
|
21
|
+
|
|
22
|
+
from orion_bridge import __version__, __codename__
|
|
23
|
+
from orion_bridge.config import (
|
|
24
|
+
LABS, DEVICE_TYPES, CONFIG_FILE,
|
|
25
|
+
load_config, save_config,
|
|
26
|
+
)
|
|
27
|
+
from orion_bridge.ui import (
|
|
28
|
+
RICH_UI,
|
|
29
|
+
print_header, print_devices, print_connection_panel,
|
|
30
|
+
print_menu, print_device_types, pick_lab, prompt,
|
|
31
|
+
msg_success, msg_error, msg_warning, msg_info,
|
|
32
|
+
print_bridge_active,
|
|
33
|
+
)
|
|
34
|
+
from orion_bridge.dispatcher import build_dispatcher
|
|
35
|
+
from orion_bridge.logger import sb_logger
|
|
36
|
+
|
|
37
|
+
logging.basicConfig(
|
|
38
|
+
level=logging.INFO,
|
|
39
|
+
format="%(asctime)s [%(levelname)s] %(name)s — %(message)s",
|
|
40
|
+
datefmt="%H:%M:%S",
|
|
41
|
+
)
|
|
42
|
+
log = logging.getLogger("orion_bridge")
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
import roslibpy
|
|
46
|
+
except ImportError:
|
|
47
|
+
roslibpy = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ── Back exception for menu navigation ────────────────────────
|
|
51
|
+
|
|
52
|
+
class _BackException(Exception):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _prompt(label="", default=""):
|
|
57
|
+
"""Prompt with 'back' support."""
|
|
58
|
+
value = prompt(label, default)
|
|
59
|
+
if value.lower() in ("back", "b"):
|
|
60
|
+
raise _BackException()
|
|
61
|
+
return value
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ── PLC HTTP sidecar ──────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
PLC_HTTP_PORT = int(os.getenv("PLC_HTTP_PORT", "5000"))
|
|
67
|
+
PLC_HTTP_ENABLED = os.getenv("PLC_HTTP_ENABLED", "true").lower() == "true"
|
|
68
|
+
_plc_ref = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _start_plc_http(dispatcher):
|
|
72
|
+
global _plc_ref
|
|
73
|
+
plc_handlers = dispatcher.handlers.get("plc", {})
|
|
74
|
+
if not plc_handlers:
|
|
75
|
+
log.info("PLC HTTP API: no PLCs, skipping")
|
|
76
|
+
return
|
|
77
|
+
_plc_ref = next(iter(plc_handlers.values()))
|
|
78
|
+
try:
|
|
79
|
+
from orion_bridge.http.plc_api import make_plc_app
|
|
80
|
+
app = make_plc_app(_plc_ref)
|
|
81
|
+
t = threading.Thread(
|
|
82
|
+
target=lambda: app.run(
|
|
83
|
+
host="0.0.0.0", port=PLC_HTTP_PORT,
|
|
84
|
+
debug=False, use_reloader=False,
|
|
85
|
+
),
|
|
86
|
+
daemon=True,
|
|
87
|
+
)
|
|
88
|
+
t.start()
|
|
89
|
+
log.info(f"PLC HTTP API on :{PLC_HTTP_PORT}")
|
|
90
|
+
except ImportError:
|
|
91
|
+
log.warning("Flask not installed — PLC HTTP API off")
|
|
92
|
+
except Exception as e:
|
|
93
|
+
log.error(f"PLC HTTP API failed: {e}")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# ── Menu actions ──────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
def _add_device(config):
|
|
99
|
+
try:
|
|
100
|
+
print_device_types(DEVICE_TYPES)
|
|
101
|
+
choice = _prompt("Select type")
|
|
102
|
+
dt = DEVICE_TYPES.get(choice)
|
|
103
|
+
if not dt:
|
|
104
|
+
msg_error("Invalid type.")
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
dtype = dt["type"]
|
|
108
|
+
name = _prompt("Device name/ID (e.g. 'xarm-lab1')")
|
|
109
|
+
if not name:
|
|
110
|
+
msg_error("Name required.")
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
existing = [d.get("id") for d in config.get("devices", [])]
|
|
114
|
+
if name in existing:
|
|
115
|
+
msg_error(f"'{name}' already exists.")
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
dev = {"type": dtype, "id": name}
|
|
119
|
+
|
|
120
|
+
if dtype == "xarm":
|
|
121
|
+
dev["ip"] = _prompt("xArm IP", "192.168.1.185")
|
|
122
|
+
elif dtype == "abb":
|
|
123
|
+
dev["ip"] = _prompt("ABB IP", "192.168.125.1")
|
|
124
|
+
dev["port"] = int(_prompt("Port", "1025"))
|
|
125
|
+
elif dtype == "plc":
|
|
126
|
+
msg_info("Enter PLC IPs one by one (empty to finish):")
|
|
127
|
+
ips = []
|
|
128
|
+
while True:
|
|
129
|
+
ip = _prompt("PLC IP")
|
|
130
|
+
if not ip:
|
|
131
|
+
break
|
|
132
|
+
ips.append(ip)
|
|
133
|
+
if not ips:
|
|
134
|
+
msg_error("Need at least one IP.")
|
|
135
|
+
return
|
|
136
|
+
dev["ips"] = ips
|
|
137
|
+
elif dtype == "shell":
|
|
138
|
+
dev["ip"] = "localhost"
|
|
139
|
+
|
|
140
|
+
config.setdefault("devices", []).append(dev)
|
|
141
|
+
msg_success(f"Added {dtype} '{name}'")
|
|
142
|
+
except _BackException:
|
|
143
|
+
msg_info("Cancelled.")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _remove_device(config):
|
|
147
|
+
devices = config.get("devices", [])
|
|
148
|
+
if not devices:
|
|
149
|
+
msg_info("Nothing to remove.")
|
|
150
|
+
return
|
|
151
|
+
try:
|
|
152
|
+
print_devices(config)
|
|
153
|
+
idx = int(_prompt("Device # to remove (0=cancel)")) - 1
|
|
154
|
+
if idx < 0 or idx >= len(devices):
|
|
155
|
+
msg_info("Cancelled.")
|
|
156
|
+
return
|
|
157
|
+
removed = devices.pop(idx)
|
|
158
|
+
msg_success(f"Removed {removed['type']}/{removed['id']}")
|
|
159
|
+
except _BackException:
|
|
160
|
+
msg_info("Cancelled.")
|
|
161
|
+
except ValueError:
|
|
162
|
+
msg_error("Invalid number.")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _configure_server(config):
|
|
166
|
+
try:
|
|
167
|
+
lab = pick_lab(LABS)
|
|
168
|
+
|
|
169
|
+
if lab is None:
|
|
170
|
+
config.update({"server": None, "token": None, "lab_name": "Local"})
|
|
171
|
+
msg_success("Local mode")
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
config["server"] = lab["server"]
|
|
175
|
+
config["token"] = lab.get("token", "")
|
|
176
|
+
config["lab_name"] = lab.get("lab_name", lab.get("name", "Custom"))
|
|
177
|
+
|
|
178
|
+
if "devices" in lab:
|
|
179
|
+
config["devices"] = lab["devices"]
|
|
180
|
+
device_summary = ", ".join(
|
|
181
|
+
d.get("id", d.get("type", "?")) for d in lab["devices"]
|
|
182
|
+
)
|
|
183
|
+
msg_success(f"Lab: {config['lab_name']}")
|
|
184
|
+
msg_info(f"Devices: {device_summary}")
|
|
185
|
+
else:
|
|
186
|
+
msg_success(f"Lab: {config['lab_name']}")
|
|
187
|
+
|
|
188
|
+
except _BackException:
|
|
189
|
+
msg_info("Cancelled.")
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _save(config):
|
|
193
|
+
save_config(config)
|
|
194
|
+
msg_success(f"Config saved → {CONFIG_FILE}")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# ── Interactive menu ──────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
def interactive_cli():
|
|
200
|
+
config = load_config()
|
|
201
|
+
print_header()
|
|
202
|
+
print_connection_panel(config)
|
|
203
|
+
print_devices(config)
|
|
204
|
+
|
|
205
|
+
while True:
|
|
206
|
+
print_menu()
|
|
207
|
+
choice = prompt()
|
|
208
|
+
|
|
209
|
+
if choice == "1":
|
|
210
|
+
_add_device(config)
|
|
211
|
+
elif choice == "2":
|
|
212
|
+
_remove_device(config)
|
|
213
|
+
elif choice == "3":
|
|
214
|
+
_configure_server(config)
|
|
215
|
+
elif choice == "4":
|
|
216
|
+
print_devices(config)
|
|
217
|
+
print_connection_panel(config)
|
|
218
|
+
elif choice == "5":
|
|
219
|
+
_save(config)
|
|
220
|
+
elif choice == "6":
|
|
221
|
+
_save(config)
|
|
222
|
+
return config
|
|
223
|
+
elif choice == "0":
|
|
224
|
+
save_q = prompt("Save before exit? (y/n)", "y")
|
|
225
|
+
if save_q != "n":
|
|
226
|
+
_save(config)
|
|
227
|
+
return None
|
|
228
|
+
elif choice.lower() in ("back", "b"):
|
|
229
|
+
msg_info("Already at main menu.")
|
|
230
|
+
else:
|
|
231
|
+
msg_error("Invalid option.")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ── Bridge startup ────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
def start_bridge(config, local=False):
|
|
237
|
+
if local:
|
|
238
|
+
config["server"] = None
|
|
239
|
+
|
|
240
|
+
dispatcher = build_dispatcher(config.get("devices", []), sb_logger=sb_logger)
|
|
241
|
+
dispatcher.connect_all()
|
|
242
|
+
|
|
243
|
+
if PLC_HTTP_ENABLED:
|
|
244
|
+
_start_plc_http(dispatcher)
|
|
245
|
+
|
|
246
|
+
status = dispatcher.get_status()
|
|
247
|
+
print_bridge_active(
|
|
248
|
+
config, status,
|
|
249
|
+
plc_http_port=PLC_HTTP_PORT if PLC_HTTP_ENABLED and _plc_ref else None,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
if config.get("server") and config.get("token"):
|
|
254
|
+
from orion_bridge.transport import ws_all_devices
|
|
255
|
+
asyncio.run(
|
|
256
|
+
ws_all_devices(
|
|
257
|
+
dispatcher,
|
|
258
|
+
config["server"],
|
|
259
|
+
config["token"],
|
|
260
|
+
config.get("devices", []),
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
else:
|
|
264
|
+
print(" Running local. Ctrl+C to stop.\n")
|
|
265
|
+
while True:
|
|
266
|
+
time.sleep(1)
|
|
267
|
+
except KeyboardInterrupt:
|
|
268
|
+
print("\n\nStopping...")
|
|
269
|
+
finally:
|
|
270
|
+
dispatcher.disconnect_all()
|
|
271
|
+
print("Done.")
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# ── Entry point ───────────────────────────────────────────────
|
|
275
|
+
|
|
276
|
+
def main():
|
|
277
|
+
parser = argparse.ArgumentParser(description="ORION Lab Bridge")
|
|
278
|
+
parser.add_argument("--local", action="store_true", help="No server, local only")
|
|
279
|
+
parser.add_argument("--config", type=str, help="Config file (skip interactive menu)")
|
|
280
|
+
args = parser.parse_args()
|
|
281
|
+
|
|
282
|
+
if args.config:
|
|
283
|
+
import json
|
|
284
|
+
with open(args.config) as f:
|
|
285
|
+
config = json.load(f)
|
|
286
|
+
start_bridge(config, local=args.local)
|
|
287
|
+
else:
|
|
288
|
+
config = interactive_cli()
|
|
289
|
+
if config:
|
|
290
|
+
start_bridge(config, local=args.local)
|
|
291
|
+
else:
|
|
292
|
+
print(" Bye.")
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
if __name__ == "__main__":
|
|
296
|
+
main()
|