Metadata-Version: 2.4
Name: orion-bridge
Version: 3.9.0
Summary: ORION Bridge — Modular device bridge for industrial robotics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: websockets>=12.0
Requires-Dist: rich>=13.0
Requires-Dist: tomli-w>=1.0
Requires-Dist: tomli>=2.0; python_version < "3.11"
Requires-Dist: python-dotenv>=1.0
Requires-Dist: customtkinter>=5.2
Requires-Dist: supabase>=2.4.0
Requires-Dist: pyjwt[crypto]>=2.8.0
Requires-Dist: xarm-python-sdk
Provides-Extra: cloud
Requires-Dist: supabase>=2.4.0; extra == "cloud"
Requires-Dist: pyjwt[crypto]>=2.8.0; extra == "cloud"
Provides-Extra: gui
Requires-Dist: customtkinter>=5.2; extra == "gui"
Provides-Extra: xarm
Requires-Dist: xarm-python-sdk; extra == "xarm"
Provides-Extra: abb
Provides-Extra: plc
Requires-Dist: python-snap7; extra == "plc"
Provides-Extra: gazebo
Requires-Dist: roslibpy; extra == "gazebo"
Provides-Extra: mujoco
Requires-Dist: mujoco>=3.5.0; extra == "mujoco"
Requires-Dist: numpy>=1.24; extra == "mujoco"
Provides-Extra: sim-viewer
Requires-Dist: mujoco>=3.5.0; extra == "sim-viewer"
Requires-Dist: numpy>=1.24; extra == "sim-viewer"
Requires-Dist: imgui-bundle>=1.3; extra == "sim-viewer"
Requires-Dist: PyOpenGL>=3.1; extra == "sim-viewer"
Requires-Dist: glfw>=2.5; extra == "sim-viewer"
Provides-Extra: http
Requires-Dist: flask; extra == "http"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: jsonschema>=4.0; extra == "dev"
Provides-Extra: all
Requires-Dist: supabase>=2.4.0; extra == "all"
Requires-Dist: pyjwt[crypto]>=2.8.0; extra == "all"
Requires-Dist: customtkinter>=5.2; extra == "all"
Requires-Dist: xarm-python-sdk; extra == "all"
Requires-Dist: python-snap7; extra == "all"
Requires-Dist: roslibpy; extra == "all"
Requires-Dist: mujoco>=3.5.0; extra == "all"
Requires-Dist: numpy>=1.24; extra == "all"
Requires-Dist: flask; extra == "all"

# ORION Bridge

ORION Bridge connects lab equipment — xArm cobots, ABB robots,
Siemens S7 PLCs, MuJoCo/Gazebo simulations, and local shell — to the ORION
cloud platform. The bridge runs next to the hardware and opens a single
outbound WebSocket to the ORION backend, so no inbound ports need to be
exposed in the lab network. New device types plug in as handler packages
via Python entry points; a cookiecutter template and a step-by-step guide
are included.

Further reading: [HANDLERS.md](HANDLERS.md) (handler plugin contract) and
[TUTORIAL_PLUGINS.md](TUTORIAL_PLUGINS.md) (write your first plugin).

## Quick start

### Option A — one line, no Python required

This installs [uv](https://docs.astral.sh/uv/), downloads Python 3.10 if
needed, and puts the `orion` command on your PATH. Nothing to preinstall.

**Windows (PowerShell):**

```powershell
irm https://raw.githubusercontent.com/Starman26/orion-bridge-v2/main/install.ps1 | iex
```

**Linux / macOS:**

```bash
curl -LsSf https://raw.githubusercontent.com/Starman26/orion-bridge-v2/main/install.sh | sh
```

Then:

```bash
orion login   # authenticate (first time)
orion         # opens the GUI (use `orion --cli` for the terminal menu)
```

To include optional hardware drivers (`mujoco`, `plc`, `gazebo`, `http`),
set `ORION_EXTRAS` before running the one-liner, e.g.
`$env:ORION_EXTRAS = "mujoco,plc"` (PowerShell) or `ORION_EXTRAS="mujoco,plc"`
(bash).

### Option B — already have Python? Just pip install

```bash
pip install --upgrade "orion-bridge[all]"   # core + GUI + all device drivers
orion                                        # opens the GUI (use `orion --cli` for the terminal menu)
```

The base package already includes the GUI, the cloud/login stack, and the
xArm SDK, so a plain `pip install orion-bridge` gives a working bridge for
physical xArms. On Windows the first run registers the Python Scripts
directory in your user PATH automatically — if `orion` is not recognized
right after installing, open a new terminal. If another program named
`orion` shadows the command (e.g. Oracle's ORION disk benchmark), use the
`orion-bridge` alias or `python -m orion_bridge`.

For a lighter install pick only the device drivers you need:

```bash
pip install "orion-bridge[mujoco]"      # MuJoCo simulation
pip install "orion-bridge[gazebo]"      # Gazebo via rosbridge
pip install "orion-bridge[plc]"         # Siemens S7 snap7
pip install "orion-bridge[http]"        # PLC REST sidecar (Flask)
pip install "orion-bridge[sim-viewer]"  # MuJoCo + in-process viewer (imgui)
```

Available extras: `xarm`, `abb`, `plc`, `gazebo`, `mujoco`, `sim-viewer`,
`http`, `cloud`, `gui`, `all`. (`xarm`, `cloud`, and `gui` now ship in the
base install; they remain as extras so existing install commands keep
resolving.)

## Configuration

Profiles are stored in `~/.orion/connections.toml`.
The wizard creates this file on first run. You can also edit it directly.

### connections.toml format

```toml
# Which profile to use when no --connection flag is given
[default]
profile = "mezzanine"

# One [connections.<name>] section per lab
[connections.mezzanine]
server    = "wss://your-server.example.com/ws/robot"
lab_id    = "mezzanine"
bridge_id = "hostname-mezzanine"   # unique per bridge process
token_env = "ORION_TOKEN_MEZZANINE"  # real token read from this env-var

[[connections.mezzanine.devices]]
type = "xarm"
id   = "xarm-lab1"
ip   = "192.168.1.185"

[[connections.mezzanine.devices]]
type = "shell"
id   = "shell-local"

# A second profile (e.g. local dev)
[connections.dev]
server    = "ws://localhost:8000/ws/robot"
lab_id    = "dev"
bridge_id = "localhost-dev"
token_env = "ORION_TOKEN_DEV"

[[connections.dev.devices]]
type    = "xarm"
id      = "xarm6-mujoco"
handler = "mujoco"
viewer  = true
mjcf_path = "sim/models/xarm6/scene_xarm6.xml"
```

### Token management

The token is **never stored in the TOML file**. Set the env-var named in
`token_env` before starting:

```bash
export ORION_TOKEN_MEZZANINE="your-secret-token"
orion --connection mezzanine
```

Or use a `.env` file with `python-dotenv` / `direnv`. If `token_env` is
left empty, the bridge authenticates with your `orion login` session
instead.

### CLI flags

| Flag | Description |
|------|-------------|
| `orion` | Open the GUI (default) |
| `orion --cli` | Interactive terminal menu instead of the GUI |
| `orion --start` | Start the bridge directly, skip the menu (headless / service mode) |
| `orion --connection NAME` | Use a specific profile (`-c NAME` also works) |
| `orion --list` | Print all saved profiles and exit |
| `orion --local` | Run without a server (local dispatch only) |
| `orion --portable` | USB/portable mode: config and credentials in `./orion_data/` |
| `orion --config FILE` | Legacy: load a JSON config file and start |
| `orion --version` | Print version and exit |

Subcommands:

| Command | Description |
|---------|-------------|
| `orion login` | Sign in via the ORION web platform (browser) |
| `orion login --otp` | Sign in with an email OTP code instead |
| `orion logout` | Clear stored credentials |
| `orion repl` | Interactive REPL (requires prior login) |
| `orion handlers` | List all registered device handlers (built-in + plugins) |
| `orion conformance` | Check whether handlers do what their capability cards declare |
| `orion create-plugin` | Wizard to scaffold a custom handler plugin |

### Migrating from lab_config.json

If you have a legacy `lab_config.json` from an older installation, run `orion`
once — on first run (no `connections.toml` yet) the bridge detects the file
and migrates it to `connections.toml`. The JSON file is left untouched.

You can also migrate manually from Python:

```python
from orion_bridge.connections import migrate_from_json
name = migrate_from_json("lab_config.json")
print(f"Migrated to profile '{name}'")
```

## Supported devices

| Type | Handler | Notes |
|------|---------|-------|
| `xarm` | `physical` (default) | UFACTORY SDK over IP (xArm 6/7, Lite 6) |
| `xarm` | `mujoco` | MuJoCo simulation (`pip install "orion-bridge[mujoco]"`) |
| `xarm` | `gazebo` | Gazebo via rosbridge (`pip install "orion-bridge[gazebo]"`) |
| `abb`  | TCP socket | RAPID SocketServer |
| `plc`  | snap7 | Siemens S7-1200 (`pip install "orion-bridge[plc]"`) |
| `shell` | subprocess | Local commands (whitelisted) |
| `failure` | simulated | Fault injection module for training scenarios |

Additional device types (e.g. UR, Schneider Lexium, Cognex cameras) ship as
separate handler plugins. Run `orion handlers` to list everything registered
on your machine, and see [HANDLERS.md](HANDLERS.md) to write your own.

## Environment variables

| Variable | Required | Description |
|----------|----------|-------------|
| `ORION_TOKEN_<NAME>` | Per profile | Bridge token; exact name set by the profile's `token_env`. Optional when using an `orion login` session |
| `SUPABASE_URL` | No | Override the built-in Supabase project URL |
| `SUPABASE_ANON_KEY` | No | Override the built-in anon key — NOT service_role |
| `ORION_PLATFORM_URL` | No | Web platform URL used for browser login |
| `PLC_HTTP_PORT` | No | Port for the PLC REST sidecar (default `5000`) |
| `PLC_HTTP_ENABLED` | No | Set to `false` to disable the PLC REST sidecar |
| `SHELL_WHITELIST` | No | Comma-separated commands the shell handler may run |
| `SHELL_TIMEOUT` | No | Shell command timeout in seconds (default `30`) |
| `ORION_LOG_LEVEL` | No | Log level for the running bridge (default `WARNING`). Set to `INFO` to see connect / reconnect / registration lines — the record of how the bridge behaves across a dropped connection |
| `ORION_PORTABLE` | No | Same effect as `--portable` |
| `ORION_TEST_PLUGINS` | No | Pipe-separated plugin zips to hot-load at startup |
| `ORION_EXTRAS` | No | Extras installed by the one-line installer |

## Authentication

Bridges have two authentication layers:

1. **User identity**: `orion login` opens the ORION web platform in your
   browser (or use `orion login --otp` for an email OTP code). The session
   is stored in `~/.orion/credentials.json` and is required for the GUI,
   the interactive menu, and `orion repl`.
2. **Bridge identity**: each profile carries a `bridge_id` plus a token,
   read from the env-var named in `token_env` — or taken from the login
   session when `token_env` is empty.

```bash
orion login     # browser sign-in, stores JWT in ~/.orion/credentials.json
orion logout    # removes stored credentials
orion repl      # interactive REPL — requires prior login
```

Headless invocations (`orion --start`, `orion --list`, `orion --config`)
work without login, e.g. for lab PCs running the bridge as a service.

## Writing a device plugin

Third-party handlers live in their own pip package and register through the
`orion_bridge.handlers` entry-point group — no changes to the bridge core.
Scaffold one with `orion create-plugin` or from the cookiecutter template in
`cookiecutter-orion-handler/`, then verify it loads with `orion handlers`.
The full contract is documented in [HANDLERS.md](HANDLERS.md); a guided
walkthrough is in [TUTORIAL_PLUGINS.md](TUTORIAL_PLUGINS.md).

## Development

```bash
pip install -e ".[all]"   # editable install with all extras
```

### Tests and CI

Every push and pull request runs the suite on Python 3.10–3.12 (Linux, plus one
Windows leg), a clean-wheel install smoke test, and a package metadata check.
The same checks run locally with:

```bash
pytest --cov=orion_bridge.dispatcher --cov=orion_bridge.capability_cards --cov=orion_bridge.handlers.base --cov-fail-under=70
```

The MuJoCo extra is the only one CI installs — the Gazebo and PLC drivers are
guarded by optional imports, so the suite runs without `roslibpy` or the native
snap7 library.

The frontend's xArm kinematic chain is a build artifact, not a hand-written
file. If `tests/test_kinematics_export.py` reports it stale, regenerate it:

```bash
python scripts/export_kinematics.py
```
