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.
Files changed (77) hide show
  1. orion_bridge-2.0.0/PKG-INFO +204 -0
  2. orion_bridge-2.0.0/README.md +155 -0
  3. orion_bridge-2.0.0/orion_bridge/__init__.py +4 -0
  4. orion_bridge-2.0.0/orion_bridge/__main__.py +3 -0
  5. orion_bridge-2.0.0/orion_bridge/cli.py +296 -0
  6. orion_bridge-2.0.0/orion_bridge/config.py +166 -0
  7. orion_bridge-2.0.0/orion_bridge/dispatcher.py +186 -0
  8. orion_bridge-2.0.0/orion_bridge/handlers/__init__.py +24 -0
  9. orion_bridge-2.0.0/orion_bridge/handlers/abb.py +202 -0
  10. orion_bridge-2.0.0/orion_bridge/handlers/base.py +784 -0
  11. orion_bridge-2.0.0/orion_bridge/handlers/plc.py +158 -0
  12. orion_bridge-2.0.0/orion_bridge/handlers/shell.py +183 -0
  13. orion_bridge-2.0.0/orion_bridge/handlers/xarm_gazebo.py +253 -0
  14. orion_bridge-2.0.0/orion_bridge/handlers/xarm_mujoco.py +246 -0
  15. orion_bridge-2.0.0/orion_bridge/handlers/xarm_physical.py +251 -0
  16. orion_bridge-2.0.0/orion_bridge/http/plc_api.py +89 -0
  17. orion_bridge-2.0.0/orion_bridge/logger.py +80 -0
  18. orion_bridge-2.0.0/orion_bridge/reactive.py +351 -0
  19. orion_bridge-2.0.0/orion_bridge/sim/__init__.py +7 -0
  20. orion_bridge-2.0.0/orion_bridge/sim/collision.py +334 -0
  21. orion_bridge-2.0.0/orion_bridge/sim/engine.py +241 -0
  22. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/base_link.stl +0 -0
  23. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/end_tool.stl +0 -0
  24. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/left_finger.stl +0 -0
  25. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/left_inner_knuckle.stl +0 -0
  26. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/left_outer_knuckle.stl +0 -0
  27. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link1.stl +0 -0
  28. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link2.stl +0 -0
  29. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link3.stl +0 -0
  30. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link4.stl +0 -0
  31. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link5.stl +0 -0
  32. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link6.stl +0 -0
  33. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link7.stl +0 -0
  34. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/link_base.stl +0 -0
  35. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/right_finger.stl +0 -0
  36. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/right_inner_knuckle.stl +0 -0
  37. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/assets/right_outer_knuckle.stl +0 -0
  38. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/scene_xarm6.xml +24 -0
  39. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/xarm6.urdf +293 -0
  40. orion_bridge-2.0.0/orion_bridge/sim/models/xarm6/xarm6.xml +170 -0
  41. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/base_link.stl +0 -0
  42. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/end_tool.stl +0 -0
  43. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/left_finger.stl +0 -0
  44. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/left_inner_knuckle.stl +0 -0
  45. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/left_outer_knuckle.stl +0 -0
  46. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link1.stl +0 -0
  47. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link2.stl +0 -0
  48. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link3.stl +0 -0
  49. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link4.stl +0 -0
  50. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link5.stl +0 -0
  51. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link6.stl +0 -0
  52. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link7.stl +0 -0
  53. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/link_base.stl +0 -0
  54. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/right_finger.stl +0 -0
  55. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/right_inner_knuckle.stl +0 -0
  56. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/assets/right_outer_knuckle.stl +0 -0
  57. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/hand.xml +124 -0
  58. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/scene_xarm7.xml +58 -0
  59. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/xarm7.urdf +219 -0
  60. orion_bridge-2.0.0/orion_bridge/sim/models/xarm7/xarm7.xml +202 -0
  61. orion_bridge-2.0.0/orion_bridge/transport.py +327 -0
  62. orion_bridge-2.0.0/orion_bridge/ui/__init__.py +153 -0
  63. orion_bridge-2.0.0/orion_bridge/ui/banner.py +138 -0
  64. orion_bridge-2.0.0/orion_bridge/ui/logs.py +19 -0
  65. orion_bridge-2.0.0/orion_bridge/ui/menus.py +151 -0
  66. orion_bridge-2.0.0/orion_bridge/ui/status.py +101 -0
  67. orion_bridge-2.0.0/orion_bridge/ui/tables.py +108 -0
  68. orion_bridge-2.0.0/orion_bridge/ui/theme.py +64 -0
  69. orion_bridge-2.0.0/orion_bridge.egg-info/PKG-INFO +204 -0
  70. orion_bridge-2.0.0/orion_bridge.egg-info/SOURCES.txt +75 -0
  71. orion_bridge-2.0.0/orion_bridge.egg-info/dependency_links.txt +1 -0
  72. orion_bridge-2.0.0/orion_bridge.egg-info/entry_points.txt +2 -0
  73. orion_bridge-2.0.0/orion_bridge.egg-info/requires.txt +30 -0
  74. orion_bridge-2.0.0/orion_bridge.egg-info/top_level.txt +1 -0
  75. orion_bridge-2.0.0/pyproject.toml +79 -0
  76. orion_bridge-2.0.0/setup.cfg +4 -0
  77. 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
+ [![PyPI version](https://badge.fury.io/py/orion-bridge.svg)](https://pypi.org/project/orion-bridge/)
55
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
56
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](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
+ [![PyPI version](https://badge.fury.io/py/orion-bridge.svg)](https://pypi.org/project/orion-bridge/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](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,4 @@
1
+ """ORION Lab Bridge — Modular device bridge for industrial robotics."""
2
+
3
+ __version__ = "2.0.0"
4
+ __codename__ = "unified"
@@ -0,0 +1,3 @@
1
+ """Allow running as: python -m orion_bridge"""
2
+ from orion_bridge.cli import main
3
+ main()
@@ -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()