From c293004cc2125e2ad0d396da0b69866fbb228e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Badenes?= Date: Thu, 18 Dec 2025 17:50:06 -0300 Subject: [PATCH 1/8] feat: add Connector Framework documentation and CI workflow - Updated docs.json to include a new "Connector Framework" section with detailed pages on usage, configuration, and specifications. - Introduced a GitHub Actions workflow for syncing Connector Framework documentation from the inorbit-connector-python repository. - Modified connectors.mdx to link to the new Connector Framework documentation, enhancing accessibility for users. --- .../multirepo-connector-framework.yml | 60 +++++++++++++++++++ docs.json | 29 +++++++++ .../robot-integration/connectors.mdx | 4 +- 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/multirepo-connector-framework.yml diff --git a/.github/workflows/multirepo-connector-framework.yml b/.github/workflows/multirepo-connector-framework.yml new file mode 100644 index 0000000..f9f2f86 --- /dev/null +++ b/.github/workflows/multirepo-connector-framework.yml @@ -0,0 +1,60 @@ +name: Sync Connector Framework Docs + +on: + # Manual trigger + workflow_dispatch: + # Nightly sync + schedule: + - cron: "17 3 * * *" + +permissions: + contents: write + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - name: Checkout docs repo + uses: actions/checkout@v4 + with: + path: docs-repo + + - name: Checkout connector repo + uses: actions/checkout@v4 + with: + repository: inorbit-ai/inorbit-connector-python + ref: main + path: connector-repo + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Build Mintlify-compatible docs + run: | + cd connector-repo/docs/mintlify + make build + + - name: Sync to docs repo + run: | + # Remove old connector framework docs + rm -rf docs-repo/ground-control/robot-integration/connector-framework + + # Copy built docs + mkdir -p docs-repo/ground-control/robot-integration/connector-framework + cp -r connector-repo/docs/mintlify/_build/* docs-repo/ground-control/robot-integration/connector-framework/ + + - name: Commit and push + run: | + cd docs-repo + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git add -A + if git diff --staged --quiet; then + echo "No changes to commit" + else + git commit -m "Sync connector framework docs from inorbit-connector-python" + git push + fi diff --git a/docs.json b/docs.json index 8e8bdeb..f738935 100644 --- a/docs.json +++ b/docs.json @@ -78,6 +78,35 @@ "ground-control/robot-integration/overview", "ground-control/robot-integration/agent", "ground-control/robot-integration/connectors", + { + "group": "Connector Framework", + "pages": [ + "ground-control/robot-integration/connector-framework/index", + "ground-control/robot-integration/connector-framework/getting-started", + { + "group": "Usage", + "pages": [ + "ground-control/robot-integration/connector-framework/usage/index", + "ground-control/robot-integration/connector-framework/usage/single-robot", + "ground-control/robot-integration/connector-framework/usage/fleet", + "ground-control/robot-integration/connector-framework/usage/commands-handling" + ] + }, + "ground-control/robot-integration/connector-framework/configuration", + "ground-control/robot-integration/connector-framework/publishing", + { + "group": "Specification", + "pages": [ + "ground-control/robot-integration/connector-framework/specification/index", + "ground-control/robot-integration/connector-framework/specification/connector", + "ground-control/robot-integration/connector-framework/specification/models", + "ground-control/robot-integration/connector-framework/specification/commands", + "ground-control/robot-integration/connector-framework/specification/utils", + "ground-control/robot-integration/connector-framework/specification/logging" + ] + } + ] + }, "ground-control/robot-integration/robot-sdk", "ground-control/robot-integration/edge-sdk", "ground-control/robot-integration/interoperability" diff --git a/ground-control/robot-integration/connectors.mdx b/ground-control/robot-integration/connectors.mdx index f0f1fe8..dd412c5 100644 --- a/ground-control/robot-integration/connectors.mdx +++ b/ground-control/robot-integration/connectors.mdx @@ -108,8 +108,8 @@ class MyConnector(InOrbitConnector): pass ``` - - View documentation and examples on GitHub + + Learn how to build your own connectors with the Python framework (API spec, configuration, and examples) ## Connector Configuration From 17ecabf075961f0e5ca23fb6d3474a3a3d63e685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Badenes?= Date: Thu, 18 Dec 2025 17:56:40 -0300 Subject: [PATCH 2/8] Fix broken links by adding placeholder pages --- docs.json | 30 +------------------ .../connector-framework/configuration.md | 7 +++++ .../connector-framework/getting-started.md | 7 +++++ .../connector-framework/index.md | 9 ++++++ .../connector-framework/publishing.md | 7 +++++ .../specification/commands.md | 7 +++++ .../specification/connector.md | 7 +++++ .../specification/index.md | 7 +++++ .../specification/logging.md | 7 +++++ .../specification/models.md | 7 +++++ .../specification/utils.md | 7 +++++ .../usage/commands-handling.md | 7 +++++ .../connector-framework/usage/fleet.md | 7 +++++ .../connector-framework/usage/index.md | 7 +++++ .../connector-framework/usage/single-robot.md | 7 +++++ 15 files changed, 101 insertions(+), 29 deletions(-) create mode 100644 ground-control/robot-integration/connector-framework/configuration.md create mode 100644 ground-control/robot-integration/connector-framework/getting-started.md create mode 100644 ground-control/robot-integration/connector-framework/index.md create mode 100644 ground-control/robot-integration/connector-framework/publishing.md create mode 100644 ground-control/robot-integration/connector-framework/specification/commands.md create mode 100644 ground-control/robot-integration/connector-framework/specification/connector.md create mode 100644 ground-control/robot-integration/connector-framework/specification/index.md create mode 100644 ground-control/robot-integration/connector-framework/specification/logging.md create mode 100644 ground-control/robot-integration/connector-framework/specification/models.md create mode 100644 ground-control/robot-integration/connector-framework/specification/utils.md create mode 100644 ground-control/robot-integration/connector-framework/usage/commands-handling.md create mode 100644 ground-control/robot-integration/connector-framework/usage/fleet.md create mode 100644 ground-control/robot-integration/connector-framework/usage/index.md create mode 100644 ground-control/robot-integration/connector-framework/usage/single-robot.md diff --git a/docs.json b/docs.json index f738935..f6f5940 100644 --- a/docs.json +++ b/docs.json @@ -78,35 +78,7 @@ "ground-control/robot-integration/overview", "ground-control/robot-integration/agent", "ground-control/robot-integration/connectors", - { - "group": "Connector Framework", - "pages": [ - "ground-control/robot-integration/connector-framework/index", - "ground-control/robot-integration/connector-framework/getting-started", - { - "group": "Usage", - "pages": [ - "ground-control/robot-integration/connector-framework/usage/index", - "ground-control/robot-integration/connector-framework/usage/single-robot", - "ground-control/robot-integration/connector-framework/usage/fleet", - "ground-control/robot-integration/connector-framework/usage/commands-handling" - ] - }, - "ground-control/robot-integration/connector-framework/configuration", - "ground-control/robot-integration/connector-framework/publishing", - { - "group": "Specification", - "pages": [ - "ground-control/robot-integration/connector-framework/specification/index", - "ground-control/robot-integration/connector-framework/specification/connector", - "ground-control/robot-integration/connector-framework/specification/models", - "ground-control/robot-integration/connector-framework/specification/commands", - "ground-control/robot-integration/connector-framework/specification/utils", - "ground-control/robot-integration/connector-framework/specification/logging" - ] - } - ] - }, + "ground-control/robot-integration/connector-framework/index", "ground-control/robot-integration/robot-sdk", "ground-control/robot-integration/edge-sdk", "ground-control/robot-integration/interoperability" diff --git a/ground-control/robot-integration/connector-framework/configuration.md b/ground-control/robot-integration/connector-framework/configuration.md new file mode 100644 index 0000000..50ddfa8 --- /dev/null +++ b/ground-control/robot-integration/connector-framework/configuration.md @@ -0,0 +1,7 @@ +--- +title: "Configuration" +description: "Configuration models and file formats for connectors" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/getting-started.md b/ground-control/robot-integration/connector-framework/getting-started.md new file mode 100644 index 0000000..47b5fd0 --- /dev/null +++ b/ground-control/robot-integration/connector-framework/getting-started.md @@ -0,0 +1,7 @@ +--- +title: "Getting Started" +description: "Installation and setup for the InOrbit Connector Framework" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/index.md b/ground-control/robot-integration/connector-framework/index.md new file mode 100644 index 0000000..53b7711 --- /dev/null +++ b/ground-control/robot-integration/connector-framework/index.md @@ -0,0 +1,9 @@ +--- +title: "Connector Framework" +description: "Python framework for developing InOrbit robot connectors" +--- + +This page will be automatically populated by the [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python) repository. + +See the [GitHub repository](https://github.com/inorbit-ai/inorbit-connector-python) for the latest documentation. + diff --git a/ground-control/robot-integration/connector-framework/publishing.md b/ground-control/robot-integration/connector-framework/publishing.md new file mode 100644 index 0000000..ca99305 --- /dev/null +++ b/ground-control/robot-integration/connector-framework/publishing.md @@ -0,0 +1,7 @@ +--- +title: "Publishing Data" +description: "How to publish robot data to InOrbit" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/specification/commands.md b/ground-control/robot-integration/connector-framework/specification/commands.md new file mode 100644 index 0000000..7da2d56 --- /dev/null +++ b/ground-control/robot-integration/connector-framework/specification/commands.md @@ -0,0 +1,7 @@ +--- +title: "Commands Utilities" +description: "Command handling utilities specification" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/specification/connector.md b/ground-control/robot-integration/connector-framework/specification/connector.md new file mode 100644 index 0000000..5698eab --- /dev/null +++ b/ground-control/robot-integration/connector-framework/specification/connector.md @@ -0,0 +1,7 @@ +--- +title: "Connector API" +description: "Connector and FleetConnector class specifications" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/specification/index.md b/ground-control/robot-integration/connector-framework/specification/index.md new file mode 100644 index 0000000..3562ad6 --- /dev/null +++ b/ground-control/robot-integration/connector-framework/specification/index.md @@ -0,0 +1,7 @@ +--- +title: "Specification" +description: "Public API specification for the inorbit-connector package" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/specification/logging.md b/ground-control/robot-integration/connector-framework/specification/logging.md new file mode 100644 index 0000000..1ca6d9e --- /dev/null +++ b/ground-control/robot-integration/connector-framework/specification/logging.md @@ -0,0 +1,7 @@ +--- +title: "Logging" +description: "Logging configuration specification" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/specification/models.md b/ground-control/robot-integration/connector-framework/specification/models.md new file mode 100644 index 0000000..21dee7e --- /dev/null +++ b/ground-control/robot-integration/connector-framework/specification/models.md @@ -0,0 +1,7 @@ +--- +title: "Models" +description: "Configuration models specification" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/specification/utils.md b/ground-control/robot-integration/connector-framework/specification/utils.md new file mode 100644 index 0000000..18877bc --- /dev/null +++ b/ground-control/robot-integration/connector-framework/specification/utils.md @@ -0,0 +1,7 @@ +--- +title: "Utilities" +description: "Utility functions specification" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/usage/commands-handling.md b/ground-control/robot-integration/connector-framework/usage/commands-handling.md new file mode 100644 index 0000000..f0e85cd --- /dev/null +++ b/ground-control/robot-integration/connector-framework/usage/commands-handling.md @@ -0,0 +1,7 @@ +--- +title: "Commands Handling" +description: "How to handle commands from InOrbit" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/usage/fleet.md b/ground-control/robot-integration/connector-framework/usage/fleet.md new file mode 100644 index 0000000..dce0c20 --- /dev/null +++ b/ground-control/robot-integration/connector-framework/usage/fleet.md @@ -0,0 +1,7 @@ +--- +title: "Fleet Connector" +description: "Guide for implementing a fleet connector" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/usage/index.md b/ground-control/robot-integration/connector-framework/usage/index.md new file mode 100644 index 0000000..629e190 --- /dev/null +++ b/ground-control/robot-integration/connector-framework/usage/index.md @@ -0,0 +1,7 @@ +--- +title: "Usage" +description: "Guides for implementing connectors" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + diff --git a/ground-control/robot-integration/connector-framework/usage/single-robot.md b/ground-control/robot-integration/connector-framework/usage/single-robot.md new file mode 100644 index 0000000..3bc4cf4 --- /dev/null +++ b/ground-control/robot-integration/connector-framework/usage/single-robot.md @@ -0,0 +1,7 @@ +--- +title: "Single-Robot Connector" +description: "Guide for implementing a single-robot connector" +--- + +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). + From 1d04f04e30c21712b0bf737fef81ca0d7001bb61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Badenes?= Date: Fri, 19 Dec 2025 10:13:24 -0300 Subject: [PATCH 3/8] Use a temporary branch --- .github/workflows/multirepo-connector-framework.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/multirepo-connector-framework.yml b/.github/workflows/multirepo-connector-framework.yml index f9f2f86..10ceafb 100644 --- a/.github/workflows/multirepo-connector-framework.yml +++ b/.github/workflows/multirepo-connector-framework.yml @@ -23,7 +23,8 @@ jobs: uses: actions/checkout@v4 with: repository: inorbit-ai/inorbit-connector-python - ref: main + # ref: main + ref: b-Tomas/mintlify-docs path: connector-repo - name: Set up Python From e6e8657575250ab3aaa6cd7d45f2e97b340d4082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Badenes?= Date: Mon, 22 Dec 2025 10:33:07 -0300 Subject: [PATCH 4/8] Test trigger --- .github/workflows/multirepo-connector-framework.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/multirepo-connector-framework.yml b/.github/workflows/multirepo-connector-framework.yml index 10ceafb..9587b22 100644 --- a/.github/workflows/multirepo-connector-framework.yml +++ b/.github/workflows/multirepo-connector-framework.yml @@ -3,6 +3,8 @@ name: Sync Connector Framework Docs on: # Manual trigger workflow_dispatch: + # Test trigger + push: # Nightly sync schedule: - cron: "17 3 * * *" From e14333d9fdcca0b47f0bd6f4c9e5c21d6ed14dd7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 22 Dec 2025 13:33:28 +0000 Subject: [PATCH 5/8] Sync connector framework docs from inorbit-connector-python --- .../connector-framework/_static/favicon.ico | Bin 0 -> 247166 bytes .../_static/inorbit-logo-black.svg | 33 ++ .../_static/inorbit-logo-white.svg | 33 ++ .../_static/mark-github.svg | 4 + .../connector-framework/configuration.md | 99 ++++- .../connector-framework/docs.json | 48 +++ .../connector-framework/getting-started.md | 78 +++- .../connector-framework/index.md | 32 +- .../connector-framework/publishing.md | 158 +++++++- .../specification/commands.md | 53 ++- .../specification/connector.md | 183 +++++++++- .../specification/index.md | 83 ++++- .../specification/logging.md | 31 +- .../specification/models.md | 65 +++- .../specification/utils.md | 24 +- .../usage/commands-handling.md | 342 +++++++++++++++++- .../connector-framework/usage/fleet.md | 176 ++++++++- .../connector-framework/usage/index.md | 5 +- .../connector-framework/usage/single-robot.md | 192 +++++++++- 19 files changed, 1623 insertions(+), 16 deletions(-) create mode 100644 ground-control/robot-integration/connector-framework/_static/favicon.ico create mode 100644 ground-control/robot-integration/connector-framework/_static/inorbit-logo-black.svg create mode 100644 ground-control/robot-integration/connector-framework/_static/inorbit-logo-white.svg create mode 100644 ground-control/robot-integration/connector-framework/_static/mark-github.svg create mode 100644 ground-control/robot-integration/connector-framework/docs.json diff --git a/ground-control/robot-integration/connector-framework/_static/favicon.ico b/ground-control/robot-integration/connector-framework/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..54299a599ec737f7525028f11c5fa8bff6436e15 GIT binary patch literal 247166 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ground-control/robot-integration/connector-framework/_static/inorbit-logo-white.svg b/ground-control/robot-integration/connector-framework/_static/inorbit-logo-white.svg new file mode 100644 index 0000000..96ca845 --- /dev/null +++ b/ground-control/robot-integration/connector-framework/_static/inorbit-logo-white.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ground-control/robot-integration/connector-framework/_static/mark-github.svg b/ground-control/robot-integration/connector-framework/_static/mark-github.svg new file mode 100644 index 0000000..e486a0d --- /dev/null +++ b/ground-control/robot-integration/connector-framework/_static/mark-github.svg @@ -0,0 +1,4 @@ + + diff --git a/ground-control/robot-integration/connector-framework/configuration.md b/ground-control/robot-integration/connector-framework/configuration.md index 50ddfa8..2f689cf 100644 --- a/ground-control/robot-integration/connector-framework/configuration.md +++ b/ground-control/robot-integration/connector-framework/configuration.md @@ -3,5 +3,102 @@ title: "Configuration" description: "Configuration models and file formats for connectors" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +The `inorbit-connector` framework uses Pydantic models for configuration, providing validation and type safety. + +## ConnectorConfig + +The main configuration class is `ConnectorConfig`, which contains all settings for your connector. It includes a `fleet` field containing a list of `RobotConfig` entries. + +Connectors should subclass `inorbit_connector.models.ConnectorConfig` and define a `connector_config` field that contains the configuration for the connector. For more details see the [Creating a Custom Configuration](#creating-a-custom-configuration) section below. + +### Key Fields + +- **`api_key`** (str | None): The InOrbit API key. Can be set via environment variable `INORBIT_API_KEY` +- **`api_url`** (HttpUrl): The URL of the InOrbit API endpoint. Defaults to InOrbit Cloud SDK URL. Can be set via environment variable `INORBIT_API_URL` +- **`connector_type`** (str): A string identifier for your connector type (e.g., "example_bot") +- **`connector_config`** (BaseModel): Your custom configuration model that inherits from Pydantic's `BaseModel`. This is where you define connector-specific fields +- **`update_freq`** (float): Update frequency in Hz for the execution loop. Default is 1.0 +- **`location_tz`** (str): The timezone of the robot location (e.g., "America/Los_Angeles", "UTC"). Must be a valid pytz timezone +- **`logging`** (LoggingConfig): Logging configuration (see below) +- **`maps`** (dict[str, MapConfig]): Dictionary mapping frame_id to map configuration (see below) +- **`env_vars`** (dict[str, str]): Environment variables to be set in the connector or user scripts +- **`fleet`** (list[RobotConfig]): List of robot configurations (see below) +- **`user_scripts_dir`** (DirectoryPath | None): Path to directory containing user scripts for command execution +- **`account_id`** (str | None): InOrbit account ID, required for publishing footprints +- **`inorbit_robot_key`** (str | None): Robot key for InOrbit Connect robots. See [API documentation](https://api.inorbit.ai/docs/index.html#operation/generateRobotKey) + +### Environment Variables + +The following environment variables are automatically read during configuration: + +- **`INORBIT_API_KEY`** (required): The InOrbit API key +- **`INORBIT_API_URL`** (optional): The InOrbit API endpoint URL + +## RobotConfig + +Represents configuration for a single robot in the fleet: + +- **`robot_id`** (str): The InOrbit robot ID +- **`cameras`** (list[CameraConfig]): List of camera configurations for this robot + +## MapConfig + +Configuration for a map that can be associated with a frame_id: + +- **`file`** (FilePath): Path to the PNG map file +- **`map_id`** (str): The map identifier +- **`map_label`** (str, optional): Human-readable map label +- **`origin_x`** (float): X coordinate of the map origin +- **`origin_y`** (float): Y coordinate of the map origin +- **`resolution`** (float): Map resolution in meters per pixel +- **`format_version`** (int, optional): Default is 2. A value of 1 indicates the Y axis is inverted while a value of 2 indicates natural map rendering. See https://developer.inorbit.ai/docs#maps + +## LoggingConfig + +Configuration for logging: + +- **`config_file`** (FilePath | None): Path to logging configuration file. If not set, uses default configuration +- **`log_level`** (LogLevels | None): Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL). Overrides the level set in the config file +- **`defaults`** (dict[str, str]): Default values to pass to the logging configuration file (e.g., log file path) + +(creating-a-custom-configuration)= +## Creating a Custom Configuration + +To create a connector-specific configuration, subclass `ConnectorConfig`: + +```python +from pydantic import BaseModel +from inorbit_connector.models import ConnectorConfig, RobotConfig + +class MyRobotConfig(BaseModel): + """Custom fields for your robot.""" + api_version: str + hardware_revision: str + custom_setting: str + +class MyConnectorConfig(ConnectorConfig): + """Configuration for your connector.""" + connector_config: MyRobotConfig + + @field_validator("connector_type") + def check_connector_type(cls, connector_type: str) -> str: + if connector_type != "my_connector": + raise ValueError(f"Expected connector type 'my_connector'") + return connector_type +``` + +## Configuration Files + +Configuration is typically loaded from YAML files. See: +- Single robot: [`examples/example.yaml`](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/example.yaml) +- Fleet: [`examples/example.fleet.yaml`](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/example.fleet.yaml) + +Use `inorbit_connector.utils.read_yaml()` to load configuration from YAML files: + +```python +from inorbit_connector.utils import read_yaml + +yaml_data = read_yaml("config.yaml") +config = MyConnectorConfig(**yaml_data) +``` diff --git a/ground-control/robot-integration/connector-framework/docs.json b/ground-control/robot-integration/connector-framework/docs.json new file mode 100644 index 0000000..d89530f --- /dev/null +++ b/ground-control/robot-integration/connector-framework/docs.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://mintlify.com/docs.json", + "navigation": { + "products": [ + { + "product": "Ground Control", + "tabs": [ + { + "tab": "Connecting Devices", + "pages": [ + { + "group": "Connector Framework", + "pages": [ + "ground-control/robot-integration/connector-framework/index", + "ground-control/robot-integration/connector-framework/getting-started", + { + "group": "Usage", + "pages": [ + "ground-control/robot-integration/connector-framework/usage/index", + "ground-control/robot-integration/connector-framework/usage/single-robot", + "ground-control/robot-integration/connector-framework/usage/fleet", + "ground-control/robot-integration/connector-framework/usage/commands-handling" + ] + }, + "ground-control/robot-integration/connector-framework/configuration", + "ground-control/robot-integration/connector-framework/publishing", + { + "group": "Specification", + "pages": [ + "ground-control/robot-integration/connector-framework/specification/index", + "ground-control/robot-integration/connector-framework/specification/connector", + "ground-control/robot-integration/connector-framework/specification/models", + "ground-control/robot-integration/connector-framework/specification/commands", + "ground-control/robot-integration/connector-framework/specification/utils", + "ground-control/robot-integration/connector-framework/specification/logging" + ] + } + ] + } + ] + } + ] + } + ] + } +} + + diff --git a/ground-control/robot-integration/connector-framework/getting-started.md b/ground-control/robot-integration/connector-framework/getting-started.md index 47b5fd0..4c318e1 100644 --- a/ground-control/robot-integration/connector-framework/getting-started.md +++ b/ground-control/robot-integration/connector-framework/getting-started.md @@ -3,5 +3,81 @@ title: "Getting Started" description: "Installation and setup for the InOrbit Connector Framework" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +## Installation +There are two ways of installing the `inorbit-connector` Python package. + +### From PyPI + +Install the package from [PyPI](https://pypi.org/project/inorbit-connector/): + +```bash +pip install inorbit-connector +``` + +### From Source + +Clone the repository and install the dependencies: + +```bash +git clone https://github.com/inorbit-ai/inorbit-connector-python.git +cd inorbit-connector-python +virtualenv venv +. venv/bin/activate +pip install . +``` + +Refer to the Github repository at [github.com/inorbit-ai/inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python) details. + +## Requirements + +- **Python 3.10 or later** +- **InOrbit account**: [Sign up for free](https://control.inorbit.ai) +- **InOrbit API key**: Export as `INORBIT_API_KEY` environment variable + +```bash +export INORBIT_API_KEY="" +``` + +## Run the Examples + +The [`examples`](https://github.com/inorbit-ai/inorbit-connector-python/tree/main/examples) directory contains usage examples of the connector. See [examples/README](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/README.md) for more information. + +### Simple Connector Example + +The simplest example demonstrates basic connector functionality: + +```bash +cd examples +source example.env +python simple-connector/connector.py +``` + +To stop the connector, press `Ctrl+C` in the terminal. + +### Robot Connector Example + +A more comprehensive example with command-line interface: + +```bash +cd examples +source example.env +python robot-connector/main.py --config example.yaml --robot_id my-example-robot +``` + +### Fleet Connector Example + +For managing multiple robots: + +```bash +cd examples +source example.env +python fleet-connector/main.py --config example.fleet.yaml +``` + +## Next Steps + +- Review the [Specification](/ground-control/robot-integration/connector-framework/specification/index) to understand the public API of the package +- Read the [Usage Guide](/ground-control/robot-integration/connector-framework/usage/index) to learn how to implement your own connector +- Review the [Configuration Guide](/ground-control/robot-integration/connector-framework/configuration) to understand connector configuration +- Check the [Publishing Guide](/ground-control/robot-integration/connector-framework/publishing) to learn how to publish data to InOrbit diff --git a/ground-control/robot-integration/connector-framework/index.md b/ground-control/robot-integration/connector-framework/index.md index 53b7711..ebabf6f 100644 --- a/ground-control/robot-integration/connector-framework/index.md +++ b/ground-control/robot-integration/connector-framework/index.md @@ -3,7 +3,35 @@ title: "Connector Framework" description: "Python framework for developing InOrbit robot connectors" --- -This page will be automatically populated by the [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python) repository. +A Python framework for developing *connectors* for the [InOrbit](https://inorbit.ai/) RobOps ecosystem. -See the [GitHub repository](https://github.com/inorbit-ai/inorbit-connector-python) for the latest documentation. +## Overview +This framework provides a base structure for developing [InOrbit](https://inorbit.ai/) robot connectors. Making use of InOrbit's [Edge SDK](https://developer.inorbit.ai/docs#edge-sdk), `inorbit-connector` provides a starting point for the integration of a fleet of robots in InOrbit, unlocking interoperability. + +The framework supports both single-robot and fleet connectors: + +- **Single-robot connectors**: Subclass `Connector` to manage one robot at a time +- **Fleet connectors**: Subclass `FleetConnector` to manage multiple robots simultaneously, ideal for managing robots through a fleet manager + +Both connector types provide: +- Automatic InOrbit robot provisioning +- Built-in publishing methods for pose, odometry, key-values, and system stats +- Command handling infrastructure for receiving commands from InOrbit +- Map management with automatic updates +- Camera feed registration +- User scripts support for custom command execution +- Configurable logging + +## Requirements + +- Python 3.10 or later +- InOrbit account ([it's free to sign up!](https://control.inorbit.ai)) + +## Documentation Sections + +- **Getting Started**: Installation, environment setup, and running a connector ([Getting Started](/ground-control/robot-integration/connector-framework/getting-started)) +- **Specification**: Package-level specification of the connector API ([Specification](/ground-control/robot-integration/connector-framework/specification/index)) +- **Usage**: Detailed guides for implementing single-robot and fleet connectors ([Usage](/ground-control/robot-integration/connector-framework/usage/index)) +- **Configuration**: Connector configuration models and file formats ([Configuration](/ground-control/robot-integration/connector-framework/configuration)) +- **Publishing**: How to publish data to InOrbit ([Publishing](/ground-control/robot-integration/connector-framework/publishing)) diff --git a/ground-control/robot-integration/connector-framework/publishing.md b/ground-control/robot-integration/connector-framework/publishing.md index ca99305..09189aa 100644 --- a/ground-control/robot-integration/connector-framework/publishing.md +++ b/ground-control/robot-integration/connector-framework/publishing.md @@ -3,5 +3,161 @@ title: "Publishing Data" description: "How to publish robot data to InOrbit" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +The connector framework provides methods to publish various types of data to InOrbit. Both single-robot (`Connector`) and fleet (`FleetConnector`) connectors support publishing, with fleet connectors using methods prefixed with `publish_robot_*` that require a `robot_id` parameter. +Publishing methods are mostly thin wrappers around `RobotSession` methods from the [InOrbit Edge SDK](https://github.com/inorbit-ai/inorbit-edge-sdk-python), except for the `publish_pose()` and `publish_map()` methods which include additional map handling logic. + +## Pose and Map + +### Single-Robot Connectors + +```python +publish_pose(x: float, y: float, yaw: float, frame_id: str, **kwargs) -> None +``` + +Publishes a pose to InOrbit. If the `frame_id` is different from the last published frame_id, the map metadata is automatically sent via `publish_map()`. + +**Parameters:** +- `x` (float): X coordinate +- `y` (float): Y coordinate +- `yaw` (float): Yaw angle in radians +- `frame_id` (str): Frame ID for the pose. If this frame_id exists in the maps configuration, the map will be automatically uploaded +- `**kwargs`: Additional arguments for pose publishing + +```python +publish_map(frame_id: str, is_update: bool = False) -> None +``` + +Manually publish map metadata. Usually called automatically when `frame_id` changes in `publish_pose()`. + +**Parameters:** +- `frame_id` (str): The frame ID of the map (must exist in configuration maps) +- `is_update` (bool): Whether this is an update to an existing map + +### Fleet Connectors + +```python +publish_robot_pose(robot_id: str, x: float, y: float, yaw: float, frame_id: str = None, **kwargs) -> None +``` + +Publishes a pose for a specific robot. Automatically updates map when `frame_id` changes. + +```python +publish_robot_map(robot_id: str, frame_id: str, is_update: bool = False) -> None +``` + +Manually publish map metadata for a specific robot. + +## Telemetry + +### Odometry + +**Single-Robot:** +```python +publish_odometry(**kwargs) -> None +``` + +**Fleet:** +```python +publish_robot_odometry(robot_id: str, **kwargs) -> None +``` + +Publishes odometry data to InOrbit. Common fields include: +- `linear_speed`: Linear velocity +- `angular_speed`: Angular velocity +- `linear_acceleration`: Linear acceleration +- `angular_acceleration`: Angular acceleration + +### Key-Value Pairs + +**Single-Robot:** +```python +publish_key_values(**kwargs) -> None +``` + +**Fleet:** +```python +publish_robot_key_values(robot_id: str, **kwargs) -> None +``` + +Publishes key-value pairs as telemetry data. These can represent any custom robot state or metrics. + +**Example:** +```python +self.publish_key_values( + battery_level=85.5, + status="idle", + current_mission="delivery_1", + temperature=23.4 +) +``` + +### System Statistics + +**Single-Robot:** +```python +publish_system_stats(**kwargs) -> None +``` + +**Fleet:** +```python +publish_robot_system_stats(robot_id: str, **kwargs) -> None +``` + +Publishes system statistics such as CPU, RAM, and disk usage. + +**Example:** +```python +self.publish_system_stats( + cpu_load_percentage=45.2, + ram_usage_percentage=62.8, + hdd_usage_percentage=34.1 +) +``` + +## Cameras + +Cameras are configured in the `RobotConfig.cameras` field and are automatically registered when the connector connects. + +### Camera Configuration + +Cameras are configured using `CameraConfig` from the InOrbit Edge SDK. Each camera in the `RobotConfig.cameras` list is automatically registered with InOrbit. + +**Example configuration:** +```yaml +fleet: + - robot_id: my-robot + cameras: + - video_url: "rtsp://camera.example.com/stream" + camera_id: "front_camera" + - video_url: "http://camera.example.com/feed" + camera_id: "back_camera" +``` + +Camera feeds are registered automatically during the `_connect()` phase, so no additional code is needed in your connector implementation. + +## Publishing Best Practices + +1. **Pose Updates**: Publish pose updates regularly in your `_execution_loop()` method +2. **Map Updates**: Maps are automatically updated when the `frame_id` changes. Ensure all frame_ids used in poses are defined in your configuration +3. **Telemetry Frequency**: Use the `update_freq` configuration to control how often your execution loop runs +4. **Error Handling**: Wrap publishing calls in try-except blocks to handle network errors gracefully +5. **Async Operations**: Use `asyncio.gather()` to publish multiple data types concurrently for better performance + +**Example:** +```python +async def _execution_loop(self) -> None: + # Fetch data from robot API + pose_data = await self._get_robot_pose() + telemetry_data = await self._get_robot_telemetry() + + # Publish data + self.publish_pose( + pose_data['x'], + pose_data['y'], + pose_data['yaw'], + pose_data['frame_id'] + ) + + self.publish_key_values(**telemetry_data) +``` diff --git a/ground-control/robot-integration/connector-framework/specification/commands.md b/ground-control/robot-integration/connector-framework/specification/commands.md index 7da2d56..982e3e5 100644 --- a/ground-control/robot-integration/connector-framework/specification/commands.md +++ b/ground-control/robot-integration/connector-framework/specification/commands.md @@ -3,5 +3,56 @@ title: "Commands Utilities" description: "Command handling utilities specification" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +This page specifies command handling helpers from `inorbit_connector.commands`. + +(spec-commands-commandfailure)= +## `CommandResultCode` / `CommandFailure` + +- `CommandResultCode` is an enum with `SUCCESS` and `FAILURE`. +- `CommandFailure` is an exception that carries: + - `execution_status_details`: a user-visible failure summary + - `stderr`: a more detailed error payload + +When `CommandFailure` is raised inside a command handler, the framework converts it into a failure result via the provided `options["result_function"]`. + +(spec-commands-parse-custom-command-args)= +## `parse_custom_command_args(custom_command_args) -> (script_name, params)` + +Parses arguments for a `COMMAND_CUSTOM_COMMAND`/RunScript-style payload. + +Input assumptions: + +- `custom_command_args[0]` is a script name (string). +- `custom_command_args[1]` is a list-like container with alternating `key, value, key, value, ...`. + +Output: + +- `script_name`: string +- `params`: dictionary of parsed key/value pairs (last value wins on duplicate keys) + +Raises: + +- `ValueError` when the outer container does not match the expected types/shapes. +- `CommandFailure` when the arguments list is not pairs (odd length). + +(spec-commands-commandmodel)= +## `CommandModel` / `ExcludeUnsetMixin` + +These classes support type-safe parsing and validation of structured command parameters. + +- `CommandModel` is a Pydantic `BaseModel` configured with `extra="forbid"`, and converts `ValidationError` into `CommandFailure`. +- `ExcludeUnsetMixin` changes `model_dump()` default behavior to `exclude_unset=True` (useful when you want to emit only explicitly-provided fields). + +See [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) for end-to-end usage patterns. + +## Note: re-exports via `inorbit_connector.connector` + +For backwards compatibility, the connector module re-exports: + +- `CommandFailure` +- `CommandResultCode` +- `parse_custom_command_args` + +New code may import from either module, but the canonical definitions live in `inorbit_connector.commands`. + diff --git a/ground-control/robot-integration/connector-framework/specification/connector.md b/ground-control/robot-integration/connector-framework/specification/connector.md index 5698eab..c4b4965 100644 --- a/ground-control/robot-integration/connector-framework/specification/connector.md +++ b/ground-control/robot-integration/connector-framework/specification/connector.md @@ -3,5 +3,186 @@ title: "Connector API" description: "Connector and FleetConnector class specifications" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +This page specifies the connector base classes you subclass to build connectors. + +## `FleetConnector` + +`inorbit_connector.connector.FleetConnector` is the base class for connectors that manage multiple robots. + + +### `_connect()` + +**Override.** + +Called once on startup (inside the connector’s background thread / event loop), before robot sessions are initialized. + +Typical responsibilities: + +- Connect to your fleet manager / backend services. +- Optionally fetch the fleet membership and call `update_fleet()` **before** sessions are created. +- Start background polling tasks that keep fresh state for all robots. + + +### `_execution_loop()` + +**Override.** + +Called repeatedly until stopped. The loop is rate-limited by `config.update_freq`, but it will never run faster than the body can execute. + +Typical responsibilities: + +- For each robot in `self.robot_ids`, fetch the latest state (often from background polling state) and publish data via the `publish_robot_*` methods. +- Handle exceptions inside the loop when possible (the framework logs and continues on exceptions). + + +### `_disconnect()` + +**Override.** + +Called once during shutdown, after Edge SDK sessions are disconnected. Use this to stop polling tasks, close sockets, and release resources. + + +### `_inorbit_robot_command_handler(robot_id, command_name, args, options)` + +**Override.** + +Called when a command arrives from InOrbit for a specific robot in the fleet. + +Contract: + +- `options["result_function"](/ground-control/robot-integration/connector-framework/specification/...)` must be called to report success/failure, or you may raise `CommandFailure` for structured failure reporting. + +See [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) for the full command result contract. + + +### `fetch_robot_map(robot_id, frame_id) -> MapConfigTemp | None` + +**Optional override.** + +If `publish_robot_pose()` references a `frame_id` that is not present in `config.maps`, the framework schedules an async fetch: + +- Calls your `fetch_robot_map()` coroutine. +- If you return a `MapConfigTemp` containing `image` bytes and metadata, the framework writes the image to a temporary file and inserts a corresponding `MapConfig` into `config.maps`. +- Then it publishes the map via `publish_robot_map()`. + +Return `None` if the map can’t be fetched. + + +### `_is_fleet_robot_online(robot_id) -> bool` + +**Optional override.** + +The Edge SDK uses this callback to determine if a robot should be considered online. Default implementation returns `True`. + + +### `start()` / `join()` / `stop()` + +**Callable.** + +- `start()` creates a background thread and runs the connector lifecycle in an asyncio event loop. +- `join()` blocks until the background thread exits. +- `stop()` signals the event loop to stop and waits briefly for shutdown. + + +### `robot_ids` / `update_fleet(fleet)` + +**Callable.** + +- `robot_ids` is a cached list of robot IDs from `config.fleet`. +- `update_fleet()` updates `config.fleet` and refreshes `robot_ids`. + +This is designed to be used during `_connect()` when your fleet membership comes from an external system. + + +### `publish_robot_pose(robot_id, x, y, yaw, frame_id=None, **kwargs)` + +**Callable.** + +Publishes pose for one robot. If the `frame_id` differs from the last published `frame_id` for that robot, the framework triggers `publish_robot_map(..., is_update=True)`. + + +### `publish_robot_map(robot_id, frame_id, is_update=False)` + +**Callable.** + +- If `frame_id` exists in `config.maps`, publishes the configured map to InOrbit. +- Otherwise schedules an async fetch via `fetch_robot_map()` (see above). + + +### `publish_robot_odometry(robot_id, **kwargs)` + +**Callable.** Publishes odometry data for one robot. + + +### `publish_robot_key_values(robot_id, **kwargs)` + +**Callable.** Publishes key-value telemetry for one robot. + + +### `publish_robot_system_stats(robot_id, **kwargs)` + +**Callable.** Publishes system stats for one robot. + + +### `_get_robot_session(robot_id) -> RobotSession` + +**Callable (advanced).** + +Returns the underlying Edge SDK session for `robot_id`. Use this if you need Edge SDK functionality that is not wrapped by this package. + +## `Connector` + +`inorbit_connector.connector.Connector` is a single-robot specialization of `FleetConnector`. It selects a single robot out of the fleet config and provides convenience wrappers that omit `robot_id`. + + +### Lifecycle hooks + +**Override.** Same intent as fleet: + +- `_connect()` +- `_execution_loop()` +- `_disconnect()` + + +### `_inorbit_command_handler(command_name, args, options)` + +**Override.** + +Single-robot command handler. It is called through the fleet-level handler internally, but without requiring you to accept `robot_id`. + + +### `fetch_map(frame_id) -> MapConfigTemp | None` + +**Optional override.** + +Single-robot convenience for map fetching. The framework uses it by delegating `fetch_robot_map()` to `fetch_map()`. + + +### `_is_robot_online() -> bool` + +**Optional override.** + +Single-robot convenience for online status. The fleet-level online check delegates to this method. + + +### Publishing wrappers + +**Callable.** Single-robot wrappers over the fleet publishing methods: + +- `publish_pose(...)` / `publish_map(...)` +- `publish_odometry(...)` +- `publish_key_values(...)` +- `publish_system_stats(...)` + + +### `_get_session() -> RobotSession` + +**Callable (advanced).** + +Returns the underlying Edge SDK session for the current robot. + +### Deprecated: `_robot_session` + +`Connector._robot_session` is deprecated; use `_get_session()` instead. + diff --git a/ground-control/robot-integration/connector-framework/specification/index.md b/ground-control/robot-integration/connector-framework/specification/index.md index 3562ad6..f362def 100644 --- a/ground-control/robot-integration/connector-framework/specification/index.md +++ b/ground-control/robot-integration/connector-framework/specification/index.md @@ -3,5 +3,86 @@ title: "Specification" description: "Public API specification for the inorbit-connector package" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +This section specifies the public surface of the `inorbit-connector` package: what you are expected to **override** when implementing a connector, and what you can **call** at runtime. +## Intended usage + +An InOrbit connector is an application that: + +- Connects to your robot (or fleet manager) and to InOrbit via the InOrbit Edge SDK. +- Runs a periodic execution loop to publish telemetry (pose, odometry, key-values, system stats). +- Optionally handles commands coming from InOrbit. + +You typically: + +- Implement a **single-robot** connector by subclassing `inorbit_connector.connector.Connector`. +- Implement a **fleet** connector by subclassing `inorbit_connector.connector.FleetConnector`. + +At runtime, you call: + +- `start()` to spawn the connector thread and begin the async lifecycle. +- `join()` to block the main thread until shutdown. +- `stop()` to request shutdown. + +During the lifecycle, the framework calls your overrides: + +- `_connect()` once at startup (before sessions are initialized). +- `_execution_loop()` repeatedly at approximately `config.update_freq` Hz. +- `_disconnect()` once at shutdown. +- A command handler (`_inorbit_command_handler()` for single-robot; `_inorbit_robot_command_handler()` for fleet) when commands arrive. + +For map handling, `publish_pose()` / `publish_robot_pose()` automatically trigger map publication when the `frame_id` changes. If a map is not configured, the connector can fetch it by overriding `fetch_map()` / `fetch_robot_map()`. + +For narrative guides, see: + +- Single robot: [Single-Robot Connector](/ground-control/robot-integration/connector-framework/usage/single-robot) +- Fleet: [Fleet Connector](/ground-control/robot-integration/connector-framework/usage/fleet) +- Commands: [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) +- Publishing: [Publishing Data](/ground-control/robot-integration/connector-framework/publishing) +- Configuration: [Configuration](/ground-control/robot-integration/connector-framework/configuration) + +## API surface (callable + overridable) + +The table below lists package-defined symbols meant for direct use (call) or extension (override). Each row links to a longer specification page. + +| Kind | Symbol | Purpose | Details | +| --- | --- | --- | --- | +| Override | `FleetConnector._connect()` | Connect to external services (fleet manager, robot backends) before sessions initialize. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-connect) | +| Override | `FleetConnector._execution_loop()` | Periodic loop; publish telemetry for each robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-execution-loop) | +| Override | `FleetConnector._disconnect()` | Shutdown external services and release resources. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-disconnect) | +| Override | `FleetConnector._inorbit_robot_command_handler()` | Handle commands for a specific `robot_id`. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-command-handler) | +| Override (optional) | `FleetConnector.fetch_robot_map()` | Fetch a missing map (bytes + metadata) when publishing pose refers to an unknown `frame_id`. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-fetch-robot-map) | +| Override (optional) | `FleetConnector._is_fleet_robot_online()` | Provide robot online status; used by Edge SDK callback. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-is-online) | +| Call | `FleetConnector.start()` / `join()` / `stop()` | Run and control the connector lifecycle. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-lifecycle) | +| Call | `FleetConnector.update_fleet()` / `FleetConnector.robot_ids` | Update fleet configuration (typically during `_connect()`) and access robot IDs. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-fleet-management) | +| Call | `FleetConnector.publish_robot_pose()` | Publish pose; triggers map publish when `frame_id` changes. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-publish-robot-pose) | +| Call | `FleetConnector.publish_robot_map()` | Publish map metadata/image from configured maps (or after fetch). | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-publish-robot-map) | +| Call | `FleetConnector.publish_robot_odometry()` | Publish odometry. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-publish-robot-odometry) | +| Call | `FleetConnector.publish_robot_key_values()` | Publish key-value telemetry. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-publish-robot-key-values) | +| Call | `FleetConnector.publish_robot_system_stats()` | Publish system stats telemetry. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-publish-robot-system-stats) | +| Call (advanced) | `FleetConnector._get_robot_session()` | Access the underlying Edge SDK `RobotSession` for a specific robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-get-robot-session) | +| Override | `Connector._connect()` / `_execution_loop()` / `_disconnect()` | Same lifecycle hooks as fleet, for a single robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-lifecycle-hooks) | +| Override | `Connector._inorbit_command_handler()` | Handle commands for the single robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-command-handler) | +| Override (optional) | `Connector.fetch_map()` | Fetch a missing map for the current robot when pose references an unknown `frame_id`. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-fetch-map) | +| Override (optional) | `Connector._is_robot_online()` | Provide online status for the current robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-is-online) | +| Call | `Connector.publish_pose()` / `publish_map()` | Publish pose/map for the current robot (map handling included). | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-publishing) | +| Call | `Connector.publish_odometry()` / `publish_key_values()` / `publish_system_stats()` | Publish telemetry for the current robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-publishing) | +| Call (advanced) | `Connector._get_session()` | Access the underlying Edge SDK `RobotSession` for the current robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-get-session) | +| Type | `ConnectorConfig` | Base configuration model for connectors. | [Details](/ground-control/robot-integration/connector-framework/specification/models#spec-models-connectorconfig) | +| Type | `RobotConfig` | Per-robot configuration (robot_id + cameras). | [Details](/ground-control/robot-integration/connector-framework/specification/models#spec-models-robotconfig) | +| Type | `MapConfig` / `MapConfigTemp` | Map configuration (file-backed vs in-memory bytes) used by map publishing/fetching. | [Details](/ground-control/robot-integration/connector-framework/specification/models#spec-models-mapconfig) | +| Type | `LoggingConfig` / `LogLevels` | Logging configuration and log-level enum. | [Details](/ground-control/robot-integration/connector-framework/specification/logging#spec-logging-loggingconfig) | +| Call | `read_yaml()` | Load YAML configuration data (with deprecated `robot_id` selection support). | [Details](/ground-control/robot-integration/connector-framework/specification/utils#spec-utils-readyaml) | +| Type / Call | `CommandResultCode` / `CommandFailure` | Standard command result code enum and structured failure exception. | [Details](/ground-control/robot-integration/connector-framework/specification/commands#spec-commands-commandfailure) | +| Call | `parse_custom_command_args()` | Parse custom-command args (RunScript action payload) into `(script_name, params)`. | [Details](/ground-control/robot-integration/connector-framework/specification/commands#spec-commands-parse-custom-command-args) | +| Type | `CommandModel` / `ExcludeUnsetMixin` | Pydantic-based command argument validation utilities. | [Details](/ground-control/robot-integration/connector-framework/specification/commands#spec-commands-commandmodel) | +| Call | `setup_logger()` | Configure logging from `LoggingConfig`. | [Details](/ground-control/robot-integration/connector-framework/specification/logging#spec-logging-setup-logger) | +| Type | `ConditionalColoredFormatter` | Optional `colorlog`-backed formatter used by the default logging config. | [Details](/ground-control/robot-integration/connector-framework/specification/logging#spec-logging-conditional-colored-formatter) | + +## Pages + +- [Connector API](/ground-control/robot-integration/connector-framework/specification/connector) +- [Models (configuration)](/ground-control/robot-integration/connector-framework/specification/models) +- [Commands utilities](/ground-control/robot-integration/connector-framework/specification/commands) +- [Utilities](/ground-control/robot-integration/connector-framework/specification/utils) +- [Logging](/ground-control/robot-integration/connector-framework/specification/logging) diff --git a/ground-control/robot-integration/connector-framework/specification/logging.md b/ground-control/robot-integration/connector-framework/specification/logging.md index 1ca6d9e..b6d3ae4 100644 --- a/ground-control/robot-integration/connector-framework/specification/logging.md +++ b/ground-control/robot-integration/connector-framework/specification/logging.md @@ -3,5 +3,34 @@ title: "Logging" description: "Logging configuration specification" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +This page specifies logging-related helpers from `inorbit_connector.logging`. + +(spec-logging-setup-logger)= +## `setup_logger(config: LoggingConfig) -> None` + +Configures Python logging using the standard library `logging.config.fileConfig`. + +Behavior: + +- If `config.config_file` is set, it is loaded via `logging.config.fileConfig(..., disable_existing_loggers=False, defaults=config.defaults)`. +- If `config.log_level` is set, the root logger level is overridden to that value after loading the config file. + +(spec-logging-loggingconfig)= +## `LoggingConfig` / `LogLevels` + +- `LoggingConfig` is the configuration model used by `setup_logger()`. +- `LogLevels` is an enum of `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`. + +(spec-logging-conditional-colored-formatter)= +## `ConditionalColoredFormatter` + +Formatter used by the package default logging configuration. + +Behavior: + +- If `colorlog` is installed, it uses `colorlog.ColoredFormatter`. +- Otherwise it falls back to `logging.Formatter` and removes `%(log_color)s` / `%(reset)s` tokens from the format string. + +The default logging config (`inorbit_connector/logging/logging.default.conf`) references this formatter by class name. + diff --git a/ground-control/robot-integration/connector-framework/specification/models.md b/ground-control/robot-integration/connector-framework/specification/models.md index 21dee7e..8d550bc 100644 --- a/ground-control/robot-integration/connector-framework/specification/models.md +++ b/ground-control/robot-integration/connector-framework/specification/models.md @@ -1,7 +1,68 @@ --- title: "Models" description: "Configuration models specification" ---- +--- (configuration) + +This page specifies the configuration models defined by `inorbit_connector.models`. + +(spec-models-connectorconfig)= +## `ConnectorConfig` + +Base configuration model for connectors. + +Key points: + +- You typically **subclass** this to define your connector-specific `connector_config` model. +- The base model reads `INORBIT_API_KEY` and `INORBIT_API_URL` from environment variables by default. +- `fleet` must contain at least one `RobotConfig`, and robot IDs must be unique. + +### `to_singular_config(robot_id) -> ConnectorConfig` + +Returns a config instance of the same subclass type, with `fleet` filtered down to exactly the requested robot. + +### Deprecated: `log_level` + +`ConnectorConfig.log_level` is deprecated in favor of `ConnectorConfig.logging.log_level`. + +(spec-models-robotconfig)= +## `RobotConfig` + +Per-robot configuration: + +- `robot_id`: the InOrbit robot ID. +- `cameras`: list of Edge SDK `CameraConfig` objects. Camera registration is performed automatically during connector startup. + +(spec-models-mapconfig)= +## `MapConfig` / `MapConfigTemp` + +These models describe map metadata and image source. + +- `MapConfig`: file-backed map with `file: FilePath` pointing to a `.png`. +- `MapConfigTemp`: in-memory map payload with `image: bytes`. + +Both carry metadata via `MapConfigBase`: + +- `map_id`, optional `map_label` +- `origin_x`, `origin_y`, `resolution` +- `format_version` (must be 1 or 2) + +(spec-models-loggingconfig)= +## `LoggingConfig` + +Logging configuration used by the connector at startup: + +- `config_file`: path to a logging config file (defaults to the package’s `logging.default.conf`). +- `log_level`: optional override for the root logger level. +- `defaults`: dictionary passed to the logging config (e.g. `log_file`). + +See [setup_logger()](/ground-control/robot-integration/connector-framework/specification/logging#spec-logging-setup-logger) for how it is applied. + +## Deprecated: `InorbitConnectorConfig` + +`InorbitConnectorConfig` is a deprecated single-robot configuration format. It can be converted to a fleet config via: + +- `to_fleet_config(robot_id) -> ConnectorConfig` + +New implementations should use `ConnectorConfig` directly. -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). diff --git a/ground-control/robot-integration/connector-framework/specification/utils.md b/ground-control/robot-integration/connector-framework/specification/utils.md index 18877bc..5680f25 100644 --- a/ground-control/robot-integration/connector-framework/specification/utils.md +++ b/ground-control/robot-integration/connector-framework/specification/utils.md @@ -3,5 +3,27 @@ title: "Utilities" description: "Utility functions specification" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +This page specifies utilities from `inorbit_connector.utils`. + +(spec-utils-readyaml)= +## `read_yaml(fname, robot_id=None) -> dict` + +Reads a YAML file and returns a dictionary. + +Behavior: + +- If the file is empty (YAML `null` / no content), returns `{}`. +- If `robot_id` is **not** provided, returns the entire YAML object. +- If `robot_id` **is** provided and is a top-level key in the YAML object, returns `data[robot_id]` and emits a **DeprecationWarning** (this selection format is deprecated). +- If `robot_id` is provided but not found, raises `IndexError`. + +Notes: + +- New configurations should follow the `ConnectorConfig` schema described in [Configuration](/ground-control/robot-integration/connector-framework/configuration). + +## Constants + +- `DEFAULT_TIMEZONE`: default timezone string (`"UTC"`). +- `DEFAULT_LOGGING_CONFIG`: path to the package default logging config file. + diff --git a/ground-control/robot-integration/connector-framework/usage/commands-handling.md b/ground-control/robot-integration/connector-framework/usage/commands-handling.md index f0e85cd..0a5f133 100644 --- a/ground-control/robot-integration/connector-framework/usage/commands-handling.md +++ b/ground-control/robot-integration/connector-framework/usage/commands-handling.md @@ -3,5 +3,345 @@ title: "Commands Handling" description: "How to handle commands from InOrbit" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +Commands from InOrbit are automatically routed to your `_inorbit_robot_command_handler()` method in the case of a fleet connector, or to your `_inorbit_command_handler()` method in the case of a single-robot connector. +The fleet handler `_inorbit_robot_command_handler()` receives the following parameters: +- `robot_id` (str): The ID of the robot receiving the command +- `command_name` (str): The name of the command +- `args` (list): Command arguments +- `options` (dict): Options including `result_function` to report results + +While the single-robot handler `_inorbit_command_handler()` omits the `robot_id` parameter. + +```python +@override +async def _inorbit_robot_command_handler( + self, robot_id: str, command_name: str, args: list, options: dict +) -> None: + """Handle InOrbit commands for a specific robot.""" + if command_name == "start_mission": + result = await self._fleet_manager.send_command( + robot_id, "start_mission", args[0] + ) +``` + +A more complete example including all provided utilities is available [at the bottom](#example-usage) of this page. + +## Reporting Command Results + +The `options` dictionary contains a `result_function` with the following signature: +```python +options['result_function'](/ground-control/robot-integration/connector-framework/usage/ + result_code: CommandResultCode, + execution_status_details: str | None = None, + stdout: str | None = None, + stderr: str | None = None, +) -> None +``` +- The `result_code` parameter must be set to `CommandResultCode.SUCCESS` or `CommandResultCode.FAILURE` to report the command result. +- The `execution_status_details` and `stderr` parameters are optional and can be used to provide specific error details. +- The `stdout` parameter is optional and can be used to provide specific output details. + +### Reporting Success + +The commands handler can call the `result_function` with a `CommandResultCode.SUCCESS` to report a successful command execution before returning. The `stdout` parameter is optional and can be used to provide specific output details. + +```python +options["result_function"](/ground-control/robot-integration/connector-framework/usage/ + CommandResultCode.SUCCESS, + stdout="Mission dispatched with ID 123456", +) +``` + +(reporting-failure)= +### Reporting Failure + +There are three ways to report a failed command execution: + +1. (Recommended) Raise a `CommandFailure` exception. +2. Raise any other exception. +3. Call the `result_function` with a `CommandResultCode.FAILURE` and provide the `execution_status_details` and `stderr` parameters. + +Unhandled exceptions raised during the execution of the command handler will be caught and reported with a generic error message in its execution details, and the exception message attached to the `stderr` field. All exceptions are logged. + +The `CommandFailure` exception can be used to intentionally indicate a failure: +- Error details are automatically passed to InOrbit's result function +- `execution_status_details` is displayed in alert messages when commands are dispatched from the actions UI +- Both `execution_status_details` and `stderr` are available in audit logs and through the [action execution details API](https://api.inorbit.ai/docs/index.html#operation/getActionExecutionStatus) + +Example usage: + +```python +from inorbit_connector.connector import CommandResultCode, CommandFailure + +@override +async def _inorbit_robot_command_handler( + self, robot_id: str, command_name: str, args: list, options: dict +) -> None: + """Handle InOrbit commands for a specific robot.""" + if command_name == "start_mission": + try: + if robot_id not in self.robot_ids: + raise CommandFailure( + execution_status_details=f"Robot {robot_id} not found in fleet", + stderr="Invalid robot ID" + ) + + result = await self._fleet_manager.send_command( + robot_id, "start_mission", args[0] + ) + if not result: + raise CommandFailure( + execution_status_details=f"Failed to start mission for {robot_id}", + stderr="Fleet manager returned error" + ) + options["result_function"](/ground-control/robot-integration/connector-framework/usage/CommandResultCode.SUCCESS) + except ValueError as e: + raise CommandFailure( + execution_status_details=f"Invalid parameters for {robot_id}", + stderr=str(e) + ) +``` + +## Parsing Script Arguments + +When handling custom script commands, the Edge SDK delivers arguments in the form: + +- `args[0]`: script file name (e.g., `"script.sh"`) +- `args[1]`: a flat list of alternating keys and values (e.g., `["x", "1.0", "y", "2.0"]`) + +In the case of InOrbit actions of `RunScript` type (identified with the `command_name` `COMMAND_CUSTOM_COMMAND`), +the script name corresponds to the `filename` field of the action definition, +and the arguments key-value pairs. + +For details on how to configure actions with arguments, refer to the +[InOrbit Actions Definitions documentation](https://developer.inorbit.ai/docs#configuring-action-definitions). + +Use the helper `parse_custom_command_args()` to turn these into a script name and a parameters dictionary: + +```python +from inorbit_connector.connector import ( + parse_custom_command_args, + CommandResultCode, + CommandFailure, +) +from inorbit_edge.commands import COMMAND_CUSTOM_COMMAND + +@override +async def _inorbit_robot_command_handler( + self, robot_id: str, command_name: str, args: list, options: dict +) -> None: + if command_name == COMMAND_CUSTOM_COMMAND: + # args format: [file_name, [k1, v1, k2, v2, ...]] + script, params = parse_custom_command_args(args) + # Example: script == "script.sh", params == {"x": "1.0", "y": "2.0"} + # Use script and params as needed + options["result_function"](/ground-control/robot-integration/connector-framework/usage/ + CommandResultCode.SUCCESS, + stdout=f"Ran {script} with params {params}", + ) +``` + +It is recommended to complement its use with the `CommandModel` class (see [Using CommandModel for Type-Safe Argument Parsing](#using-commandmodel-for-type-safe-argument-parsing)) for safe type validation and parsing. + +(using-commandmodel-for-type-safe-argument-parsing)= +## Using `CommandModel` for Type-Safe Argument Parsing + +For structured command arguments that require validation and type safety, use the `CommandModel` base class. This is particularly useful when commands have multiple parameters with specific types and validation rules. + +`CommandModel` provides: +- Automatic type validation and conversion using Pydantic +- Conversion of validation errors to [`CommandFailure`](#reporting-failure) exceptions +- Protection against extra fields (forbids unknown parameters) + +Optionally, you can combine `CommandModel` with `ExcludeUnsetMixin` to exclude unset fields from model dumps, which is useful for API calls where you only want to send non-default values. + +### Basic Usage + +Define a command model by subclassing `CommandModel`: + +```python +from inorbit_connector.commands import CommandModel, parse_custom_command_args +from inorbit_connector.connector import CommandResultCode, CommandFailure + +class CommandQueueMission(CommandModel): + """Command model for queue_mission command.""" + mission_id: str + robot_id: int | None = None + priority: int | None = None + description: str | None = None +``` + +### Using with `ExcludeUnsetMixin` + +To exclude unset fields from model dumps (useful for API calls), inherit from both `ExcludeUnsetMixin` and `CommandModel`. The mixin must come first in the inheritance list: + +```python +from inorbit_connector.commands import CommandModel, ExcludeUnsetMixin + +class CommandQueueMission(ExcludeUnsetMixin, CommandModel): + """Command model for queue_mission command.""" + mission_id: str + robot_id: int | None = None + priority: int | None = None + description: str | None = None +``` + +### Using with `parse_custom_command_args` + +`CommandModel` works seamlessly with `parse_custom_command_args()`: + +```python +from inorbit_edge.commands import COMMAND_CUSTOM_COMMAND + +@override +async def _inorbit_robot_command_handler( + self, robot_id: str, command_name: str, args: list, options: dict +) -> None: + if command_name == COMMAND_CUSTOM_COMMAND: + script_name, script_args = parse_custom_command_args(args) + + if script_name == "queue_mission": + # Validation happens automatically - raises CommandFailure on error + command = CommandQueueMission(**script_args) + + # If using ExcludeUnsetMixin, model_dump() excludes unset fields + # Useful for API calls where you only want to send non-default values + await self._fleet_client.schedule_mission(**command.model_dump()) + + options["result_function"](/ground-control/robot-integration/connector-framework/usage/CommandResultCode.SUCCESS) +``` + +### Automatic Error Handling + +If validation fails, `CommandModel` automatically raises a `CommandFailure` with appropriate error details: + +```python +# If script_args contains invalid data: +# script_args = {"mission_id": "test", "priority": "not_an_int"} +command = CommandQueueMission(**script_args) +# Raises CommandFailure with execution_status_details="Bad arguments" +# and stderr containing the validation error details +``` + +The exception is automatically handled by the connector's command execution framework, so you don't need to catch `ValidationError` exceptions. +See the [Reporting Failure](#reporting-failure) section for more details. + +### Excluding Unset Fields + +When using `ExcludeUnsetMixin`, `model_dump()` excludes fields that weren't explicitly set, which is useful when making API calls where you only want to send non-default values: + +```python +# With ExcludeUnsetMixin +command = CommandQueueMission(mission_id="test123", priority=5) +command.model_dump() +# Returns: {"mission_id": "test123", "priority": 5} +# Note: robot_id and description are excluded since they weren't set + +# Without ExcludeUnsetMixin +class SimpleCommand(CommandModel): + mission_id: str + priority: int | None = None + +command = SimpleCommand(mission_id="test123") +command.model_dump() +# Returns: {"mission_id": "test123", "priority": None} +# All fields are included, even if they have default values +``` + +(example-usage)= +## Example Usage + +Here's a concrete example of using `CommandModel` with `ExcludeUnsetMixin` to handle a custom command. + +The command is a RunScript action, whose filename is `schedule_mission`. + +```yaml +# ActionDefinition.yaml +apiVersion: v0.1 +kind: ActionDefinition +metadata: + id: dock + scope: tag/my-account-id/my-collection-id # Or any applicable scope +spec: + type: RunScript + arguments: + - name: filename + type: string + value: schedule_mission + - name: mission_id + type: string + value: 4eaa3a62-7a17-11ed-9f3c-0001299981c4 + description: Sends robot to charging dock + label: Dock +``` + +Once applied using the [InOrbit CLI](https://developer.inorbit.ai/docs#using-the-inorbit-cli) or [REST APIs](https://api.inorbit.ai/docs/index.html#tag/configAPI), the action can be executed through the InOrbit UI or through the [REST APIs](https://api.inorbit.ai/docs/index.html#tag/actions). + +```shell +# Apply the action definition +inorbit apply -f ActionDefinition.yaml + +# Execute the action +curl --location 'https://api.inorbit.ai/robots//actions' \ +--header 'Content-Type: application/json' \ +--header 'Accept: application/json' \ +--header 'x-auth-inorbit-app-key: ' \ +--data ' +{ + "actionId": "dock" +}' +``` + +The connector implements the command handler for the `schedule_mission` command and passes the arguments to an API client to schedule the mission. + +```python +from enum import StrEnum +from inorbit_connector.commands import CommandModel, ExcludeUnsetMixin, parse_custom_command_args +from inorbit_connector.connector import CommandResultCode +from inorbit_edge.commands import COMMAND_CUSTOM_COMMAND + +class CommandScheduleMission(ExcludeUnsetMixin, CommandModel): + """Command model for scheduling a mission.""" + mission_id: str + robot_id: int | None = None + priority: int | None = None + +class CustomScripts(StrEnum): + """Custom scripts supported by the connector.""" + SCHEDULE_MISSION = "schedule_mission" + # Add other custom scripts here + +class ExampleConnector(Connector): + ... # other methods + + @override + async def _inorbit_command_handler( + self, command_name: str, args: list, options: dict + ) -> None: + if command_name == COMMAND_CUSTOM_COMMAND: + script_name, script_args = parse_custom_command_args(args) + # script_name = "schedule_mission" + # script_args = {"mission_id": "4eaa3a62-7a17-11ed-9f3c-0001299981c4"} + + if script_name == CustomScripts.SCHEDULE_MISSION: + # Validation and type conversion happen automatically + command = CommandScheduleMission(**script_args) + + # Only explicitly set fields are included in the dump + await self._api_client.schedule_mission(**command.model_dump()) + + else: + raise CommandFailure( + execution_status_details=f"Command not implemented", + stderr=f"Command '{script_name}' not yet implemented", + ) + + # Call the result function to indicate success + options["result_function"](/ground-control/robot-integration/connector-framework/usage/CommandResultCode.SUCCESS) + else: + raise CommandFailure( + execution_status_details=f"Command not implemented", + stderr=f"Command '{command_name}' not yet implemented", + ) +``` diff --git a/ground-control/robot-integration/connector-framework/usage/fleet.md b/ground-control/robot-integration/connector-framework/usage/fleet.md index dce0c20..d66d53e 100644 --- a/ground-control/robot-integration/connector-framework/usage/fleet.md +++ b/ground-control/robot-integration/connector-framework/usage/fleet.md @@ -3,5 +3,179 @@ title: "Fleet Connector" description: "Guide for implementing a fleet connector" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +Subclass `inorbit_connector.connector.FleetConnector` to manage multiple robots simultaneously. + +## Constructor + +```python +def __init__(self, config: ConnectorConfig, **kwargs) -> None +``` + +**Parameters:** +- `config` (ConnectorConfig): The connector configuration containing the fleet + +**Keyword Arguments:** +- `register_user_scripts` (bool): Automatically register user scripts. Default: `False` +- `default_user_scripts_dir` (str): Default directory for user scripts. Default: `~/.inorbit_connectors/connector-{class_name}/local/` +- `create_user_scripts_dir` (bool): Create the user scripts directory if it doesn't exist. Default: `False` +- `register_custom_command_handler` (bool): Automatically register the command handler. Default: `True` + +## Required Methods + +Subclasses must implement the same abstract methods as single-robot connectors, with one difference: + +### `_inorbit_robot_command_handler()` + +:::{hint} +See the [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) chapter for more details. +::: + +Handle commands for a specific robot. This method is automatically registered if `register_custom_command_handler` is True (default). + +```python +from inorbit_connector.connector import CommandResultCode, CommandFailure + +@override +async def _inorbit_robot_command_handler( + self, robot_id: str, command_name: str, args: list, options: dict +) -> None: + """Handle InOrbit commands for a specific robot.""" + if command_name == "start_mission": + result = await self._fleet_manager.send_command( + robot_id, "start_mission", args[0] + ) + if result: + options["result_function"](/ground-control/robot-integration/connector-framework/usage/CommandResultCode.SUCCESS) + else: + raise CommandFailure( + execution_status_details=f"Failed to start mission for {robot_id}", + stderr="Fleet manager returned error" + ) +``` + +Exceptions raised in the command handler are automatically caught and reported. Use `CommandFailure` to provide specific error details that will be displayed in InOrbit's audit logs and action execution details. + +## Fleet Management + +### Accessing Robot IDs + +Access the list of robot IDs in the fleet: + +```python +for robot_id in self.robot_ids: + # Process each robot + pass +``` + +### Updating the Fleet + +You can update the fleet configuration during `_connect()` by calling the `update_fleet()` method. This is useful for dynamically setting the robots list before the connector starts, for example, when provisioning robots from fleet manager data instead of hardcoded values in the config files: + +```python +@override +async def _connect(self) -> None: + """Connect to fleet manager and update fleet.""" + # Fetch robot list from fleet manager API + robots = await self._fleet_manager.get_robots() + + # Update fleet configuration + fleet_config = [ + RobotConfig(robot_id=robot.id, cameras=robot.cameras) + for robot in robots + ] + self.update_fleet(fleet_config) +``` + +The `update_fleet()` method updates the fleet configuration and initializes sessions for all robots. + +## Publishing Methods + +All publishing methods require a `robot_id` parameter. See the [Publishing Guide](/ground-control/robot-integration/connector-framework/publishing) for detailed information. + +- `publish_robot_pose(robot_id, x, y, yaw, frame_id)`: Publish pose for a specific robot +- `publish_robot_odometry(robot_id, **kwargs)`: Publish odometry for a specific robot +- `publish_robot_key_values(robot_id, **kwargs)`: Publish key-values for a specific robot +- `publish_robot_system_stats(robot_id, **kwargs)`: Publish system stats for a specific robot +- `publish_robot_map(robot_id, frame_id, is_update=False)`: Publish map for a specific robot + +## Advanced Methods + +### `_get_robot_session()` + +Access the underlying `RobotSession` from the InOrbit Edge SDK for a specific robot. Use this for advanced use cases not covered by the connector API. + +```python +def _get_robot_session(self, robot_id: str) -> RobotSession: + """Get a robot session for a specific robot ID. + + Args: + robot_id (str): The robot ID to get the session for + + Returns: + RobotSession: The robot session for the specified robot + """ +``` + +**Example:** +```python +async def _execution_loop(self) -> None: + for robot_id in self.robot_ids: + # Access the session directly for advanced features + session = self._get_robot_session(robot_id) + # Use Edge SDK methods directly + ... +``` + +## Robot Online Status + +Override `_is_fleet_robot_online()` to provide custom online status checks: + +```python +@override +def _is_fleet_robot_online(self, robot_id: str) -> bool: + """Check if a robot is online.""" + # Check robot status via fleet manager API + return self._fleet_manager.is_robot_online(robot_id) +``` + +This is used by InOrbit to determine robot availability. + +## Example Execution Loop + +```python +@override +async def _execution_loop(self) -> None: + """Main execution loop for fleet.""" + for robot_id in self.robot_ids: + try: + # Fetch robot data from fleet manager + robot_data = await self._fleet_manager.get_robot_data(robot_id) + + # Publish pose + self.publish_robot_pose( + robot_id, + robot_data.x, + robot_data.y, + robot_data.yaw, + robot_data.frame_id + ) + + # Publish telemetry + self.publish_robot_key_values(robot_id, **robot_data.telemetry) + except Exception as e: + self._logger.error(f"Error processing robot {robot_id}: {e}") +``` + +## Lifecycle + +The lifecycle methods are the same as single-robot connectors: +- `start()`: Start the connector +- `join()`: Block until stopped +- `stop()`: Stop the connector + +## Examples + +- **Simple fleet connector**: [examples/simple-fleet-connector/connector.py](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/simple-fleet-connector/connector.py) +- **Fleet connector (CLI)**: [examples/fleet-connector/](https://github.com/inorbit-ai/inorbit-connector-python/tree/main/examples/fleet-connector) +- **Examples index**: [examples/README.md](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/README.md) diff --git a/ground-control/robot-integration/connector-framework/usage/index.md b/ground-control/robot-integration/connector-framework/usage/index.md index 629e190..359ce6c 100644 --- a/ground-control/robot-integration/connector-framework/usage/index.md +++ b/ground-control/robot-integration/connector-framework/usage/index.md @@ -3,5 +3,8 @@ title: "Usage" description: "Guides for implementing connectors" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +## Guides +- [Single-Robot Connector](/ground-control/robot-integration/connector-framework/usage/single-robot) +- [Fleet Connector](/ground-control/robot-integration/connector-framework/usage/fleet) +- [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) diff --git a/ground-control/robot-integration/connector-framework/usage/single-robot.md b/ground-control/robot-integration/connector-framework/usage/single-robot.md index 3bc4cf4..0766371 100644 --- a/ground-control/robot-integration/connector-framework/usage/single-robot.md +++ b/ground-control/robot-integration/connector-framework/usage/single-robot.md @@ -3,5 +3,195 @@ title: "Single-Robot Connector" description: "Guide for implementing a single-robot connector" --- -Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). +Subclass `inorbit_connector.connector.Connector` to create a connector for a single robot. + +## Constructor + +```python +def __init__(self, robot_id: str, config: ConnectorConfig, **kwargs) -> None +``` + +**Parameters:** +- `robot_id` (str): The InOrbit robot ID +- `config` (ConnectorConfig): The connector configuration + +**Keyword Arguments:** +- `register_user_scripts` (bool): Automatically register user scripts. Default: `False` +- `default_user_scripts_dir` (str): Default directory for user scripts. Default: `~/.inorbit_connectors/connector-{robot_id}/local/` +- `create_user_scripts_dir` (bool): Create the user scripts directory if it doesn't exist. Default: `False` +- `register_custom_command_handler` (bool): Automatically register the command handler. Default: `True` + +## Required Methods + +Subclasses must implement the following abstract methods: + +### `_connect()` + +Set up external services and connections. This is called once when the connector starts, before the execution loop begins. + +```python +@override +async def _connect(self) -> None: + """Connect to robot services.""" + # Initialize robot API client and/or other related services + # e.g. initialize a REST API client and start a polling loop + pass +``` + +### `_execution_loop()` + +The main execution loop that runs periodically. This is where you fetch robot data and publish it to InOrbit. + +Refer to the [publishing guide](/ground-control/robot-integration/connector-framework/publishing) for more details on publishing data to InOrbit. + +With polling-based connectors, it is advisable to run polling loops concurrently with the execution loop to avoid long running `_execution_loop` calls. See the [robot-connector](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/robot-connector/connector.py) and [fleet-connector](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/simple-fleet-connector/connector.py) examples for more details. + +```python +@override +async def _execution_loop(self) -> None: + """Main execution loop.""" + # Fetch robot pose + pose = await self._get_robot_pose() + self.publish_pose(pose.x, pose.y, pose.yaw, pose.frame_id) + + # Fetch and publish telemetry + telemetry = await self._get_robot_telemetry() + self.publish_key_values(**telemetry) +``` + +The loop runs at the frequency specified by `config.update_freq` (default: 1.0 Hz). + +### `_disconnect()` + +Clean up resources and disconnect from external services. This is called when the connector stops. + +```python +@override +async def _disconnect(self) -> None: + """Disconnect from robot services.""" + # Close robot API connections + # Clean up resources + pass +``` + +### `_inorbit_command_handler()` + +:::{hint} +See the [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) chapter for more details. +::: + +Handle commands received from InOrbit. This method is automatically registered if `register_custom_command_handler` is True (default). + +```python +from inorbit_connector.connector import CommandResultCode, CommandFailure + +@override +async def _inorbit_command_handler( + self, command_name: str, args: list, options: dict +) -> None: + """Handle InOrbit commands.""" + if command_name == "start_mission": + result = await self._robot.start_mission(args[0]) + if result: + options["result_function"](/ground-control/robot-integration/connector-framework/usage/CommandResultCode.SUCCESS) + else: + raise CommandFailure( + execution_status_details="Mission start failed", + stderr="Robot returned error code" + ) +``` + +Exceptions raised in the command handler are automatically caught and reported. Use `CommandFailure` to provide specific error details that will be displayed in InOrbit's audit logs and action execution details. + +## Lifecycle Methods + +### `start()` + +Starts the connector in a background thread. Creates an async event loop and begins the connection process. + +```python +connector = MyConnector(robot_id, config) +connector.start() +``` + +### `join()` + +Blocks until the connector thread finishes. Use this to keep your main thread alive. + +```python +connector.join() +``` + +### `stop()` + +Signals the connector to stop and waits for shutdown. This calls `_disconnect()` and cleans up resources. + +```python +connector.stop() +``` + +## Publishing Methods + +See the [Publishing Guide](/ground-control/robot-integration/connector-framework/publishing) for detailed information on publishing methods. + +## Advanced Methods + +### `_get_session()` + +Access the underlying `RobotSession` from the InOrbit Edge SDK for advanced use cases not covered by the connector API. + +```python +def _get_session(self) -> RobotSession: + """Get the edge-sdk robot session for the current robot.""" +``` + +**Example:** +```python +async def _execution_loop(self) -> None: + # Access the session directly for advanced features + session = self._get_session() + # Use Edge SDK methods directly + ... +``` + +### `_is_robot_online()` + +Override this method to provide custom robot health checks. The default implementation assumes the robot is online if the connector is running. + +```python +def _is_robot_online(self) -> bool: + """Check if the robot is online. + + Returns: + bool: True if robot is online, False otherwise. + """ +``` + +**Example:** +```python +@override +def _is_robot_online(self) -> bool: + """Check robot connectivity via API.""" + try: + return self._robot_api.is_connected() + except Exception: + return False +``` + + +## User Scripts + +User scripts allow executing custom shell scripts from InOrbit. To enable: + +1. Set `user_scripts_dir` in your configuration +2. Pass `register_user_scripts=True` to the constructor +3. Place `.sh` scripts in the user scripts directory + +Scripts are automatically registered and can be executed from InOrbit. + +## Examples + +- **Simple connector**: [examples/simple-connector/connector.py](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/simple-connector/connector.py) +- **Robot connector (CLI)**: [examples/robot-connector/](https://github.com/inorbit-ai/inorbit-connector-python/tree/main/examples/robot-connector) +- **Examples index**: [examples/README.md](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/README.md) From b3057b4de03bda1f15ae44fbf97fbd98dd923b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Badenes?= Date: Mon, 22 Dec 2025 11:21:20 -0300 Subject: [PATCH 6/8] Revert "Sync connector framework docs from inorbit-connector-python" This reverts commit e14333d9fdcca0b47f0bd6f4c9e5c21d6ed14dd7. --- .../connector-framework/_static/favicon.ico | Bin 247166 -> 0 bytes .../_static/inorbit-logo-black.svg | 33 -- .../_static/inorbit-logo-white.svg | 33 -- .../_static/mark-github.svg | 4 - .../connector-framework/configuration.md | 99 +---- .../connector-framework/docs.json | 48 --- .../connector-framework/getting-started.md | 78 +--- .../connector-framework/index.md | 32 +- .../connector-framework/publishing.md | 158 +------- .../specification/commands.md | 53 +-- .../specification/connector.md | 183 +--------- .../specification/index.md | 83 +---- .../specification/logging.md | 31 +- .../specification/models.md | 65 +--- .../specification/utils.md | 24 +- .../usage/commands-handling.md | 342 +----------------- .../connector-framework/usage/fleet.md | 176 +-------- .../connector-framework/usage/index.md | 5 +- .../connector-framework/usage/single-robot.md | 192 +--------- 19 files changed, 16 insertions(+), 1623 deletions(-) delete mode 100644 ground-control/robot-integration/connector-framework/_static/favicon.ico delete mode 100644 ground-control/robot-integration/connector-framework/_static/inorbit-logo-black.svg delete mode 100644 ground-control/robot-integration/connector-framework/_static/inorbit-logo-white.svg delete mode 100644 ground-control/robot-integration/connector-framework/_static/mark-github.svg delete mode 100644 ground-control/robot-integration/connector-framework/docs.json diff --git a/ground-control/robot-integration/connector-framework/_static/favicon.ico b/ground-control/robot-integration/connector-framework/_static/favicon.ico deleted file mode 100644 index 54299a599ec737f7525028f11c5fa8bff6436e15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 247166 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ground-control/robot-integration/connector-framework/_static/inorbit-logo-white.svg b/ground-control/robot-integration/connector-framework/_static/inorbit-logo-white.svg deleted file mode 100644 index 96ca845..0000000 --- a/ground-control/robot-integration/connector-framework/_static/inorbit-logo-white.svg +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ground-control/robot-integration/connector-framework/_static/mark-github.svg b/ground-control/robot-integration/connector-framework/_static/mark-github.svg deleted file mode 100644 index e486a0d..0000000 --- a/ground-control/robot-integration/connector-framework/_static/mark-github.svg +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/ground-control/robot-integration/connector-framework/configuration.md b/ground-control/robot-integration/connector-framework/configuration.md index 2f689cf..50ddfa8 100644 --- a/ground-control/robot-integration/connector-framework/configuration.md +++ b/ground-control/robot-integration/connector-framework/configuration.md @@ -3,102 +3,5 @@ title: "Configuration" description: "Configuration models and file formats for connectors" --- -The `inorbit-connector` framework uses Pydantic models for configuration, providing validation and type safety. - -## ConnectorConfig - -The main configuration class is `ConnectorConfig`, which contains all settings for your connector. It includes a `fleet` field containing a list of `RobotConfig` entries. - -Connectors should subclass `inorbit_connector.models.ConnectorConfig` and define a `connector_config` field that contains the configuration for the connector. For more details see the [Creating a Custom Configuration](#creating-a-custom-configuration) section below. - -### Key Fields - -- **`api_key`** (str | None): The InOrbit API key. Can be set via environment variable `INORBIT_API_KEY` -- **`api_url`** (HttpUrl): The URL of the InOrbit API endpoint. Defaults to InOrbit Cloud SDK URL. Can be set via environment variable `INORBIT_API_URL` -- **`connector_type`** (str): A string identifier for your connector type (e.g., "example_bot") -- **`connector_config`** (BaseModel): Your custom configuration model that inherits from Pydantic's `BaseModel`. This is where you define connector-specific fields -- **`update_freq`** (float): Update frequency in Hz for the execution loop. Default is 1.0 -- **`location_tz`** (str): The timezone of the robot location (e.g., "America/Los_Angeles", "UTC"). Must be a valid pytz timezone -- **`logging`** (LoggingConfig): Logging configuration (see below) -- **`maps`** (dict[str, MapConfig]): Dictionary mapping frame_id to map configuration (see below) -- **`env_vars`** (dict[str, str]): Environment variables to be set in the connector or user scripts -- **`fleet`** (list[RobotConfig]): List of robot configurations (see below) -- **`user_scripts_dir`** (DirectoryPath | None): Path to directory containing user scripts for command execution -- **`account_id`** (str | None): InOrbit account ID, required for publishing footprints -- **`inorbit_robot_key`** (str | None): Robot key for InOrbit Connect robots. See [API documentation](https://api.inorbit.ai/docs/index.html#operation/generateRobotKey) - -### Environment Variables - -The following environment variables are automatically read during configuration: - -- **`INORBIT_API_KEY`** (required): The InOrbit API key -- **`INORBIT_API_URL`** (optional): The InOrbit API endpoint URL - -## RobotConfig - -Represents configuration for a single robot in the fleet: - -- **`robot_id`** (str): The InOrbit robot ID -- **`cameras`** (list[CameraConfig]): List of camera configurations for this robot - -## MapConfig - -Configuration for a map that can be associated with a frame_id: - -- **`file`** (FilePath): Path to the PNG map file -- **`map_id`** (str): The map identifier -- **`map_label`** (str, optional): Human-readable map label -- **`origin_x`** (float): X coordinate of the map origin -- **`origin_y`** (float): Y coordinate of the map origin -- **`resolution`** (float): Map resolution in meters per pixel -- **`format_version`** (int, optional): Default is 2. A value of 1 indicates the Y axis is inverted while a value of 2 indicates natural map rendering. See https://developer.inorbit.ai/docs#maps - -## LoggingConfig - -Configuration for logging: - -- **`config_file`** (FilePath | None): Path to logging configuration file. If not set, uses default configuration -- **`log_level`** (LogLevels | None): Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL). Overrides the level set in the config file -- **`defaults`** (dict[str, str]): Default values to pass to the logging configuration file (e.g., log file path) - -(creating-a-custom-configuration)= -## Creating a Custom Configuration - -To create a connector-specific configuration, subclass `ConnectorConfig`: - -```python -from pydantic import BaseModel -from inorbit_connector.models import ConnectorConfig, RobotConfig - -class MyRobotConfig(BaseModel): - """Custom fields for your robot.""" - api_version: str - hardware_revision: str - custom_setting: str - -class MyConnectorConfig(ConnectorConfig): - """Configuration for your connector.""" - connector_config: MyRobotConfig - - @field_validator("connector_type") - def check_connector_type(cls, connector_type: str) -> str: - if connector_type != "my_connector": - raise ValueError(f"Expected connector type 'my_connector'") - return connector_type -``` - -## Configuration Files - -Configuration is typically loaded from YAML files. See: -- Single robot: [`examples/example.yaml`](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/example.yaml) -- Fleet: [`examples/example.fleet.yaml`](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/example.fleet.yaml) - -Use `inorbit_connector.utils.read_yaml()` to load configuration from YAML files: - -```python -from inorbit_connector.utils import read_yaml - -yaml_data = read_yaml("config.yaml") -config = MyConnectorConfig(**yaml_data) -``` +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). diff --git a/ground-control/robot-integration/connector-framework/docs.json b/ground-control/robot-integration/connector-framework/docs.json deleted file mode 100644 index d89530f..0000000 --- a/ground-control/robot-integration/connector-framework/docs.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "$schema": "https://mintlify.com/docs.json", - "navigation": { - "products": [ - { - "product": "Ground Control", - "tabs": [ - { - "tab": "Connecting Devices", - "pages": [ - { - "group": "Connector Framework", - "pages": [ - "ground-control/robot-integration/connector-framework/index", - "ground-control/robot-integration/connector-framework/getting-started", - { - "group": "Usage", - "pages": [ - "ground-control/robot-integration/connector-framework/usage/index", - "ground-control/robot-integration/connector-framework/usage/single-robot", - "ground-control/robot-integration/connector-framework/usage/fleet", - "ground-control/robot-integration/connector-framework/usage/commands-handling" - ] - }, - "ground-control/robot-integration/connector-framework/configuration", - "ground-control/robot-integration/connector-framework/publishing", - { - "group": "Specification", - "pages": [ - "ground-control/robot-integration/connector-framework/specification/index", - "ground-control/robot-integration/connector-framework/specification/connector", - "ground-control/robot-integration/connector-framework/specification/models", - "ground-control/robot-integration/connector-framework/specification/commands", - "ground-control/robot-integration/connector-framework/specification/utils", - "ground-control/robot-integration/connector-framework/specification/logging" - ] - } - ] - } - ] - } - ] - } - ] - } -} - - diff --git a/ground-control/robot-integration/connector-framework/getting-started.md b/ground-control/robot-integration/connector-framework/getting-started.md index 4c318e1..47b5fd0 100644 --- a/ground-control/robot-integration/connector-framework/getting-started.md +++ b/ground-control/robot-integration/connector-framework/getting-started.md @@ -3,81 +3,5 @@ title: "Getting Started" description: "Installation and setup for the InOrbit Connector Framework" --- -## Installation +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). -There are two ways of installing the `inorbit-connector` Python package. - -### From PyPI - -Install the package from [PyPI](https://pypi.org/project/inorbit-connector/): - -```bash -pip install inorbit-connector -``` - -### From Source - -Clone the repository and install the dependencies: - -```bash -git clone https://github.com/inorbit-ai/inorbit-connector-python.git -cd inorbit-connector-python -virtualenv venv -. venv/bin/activate -pip install . -``` - -Refer to the Github repository at [github.com/inorbit-ai/inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python) details. - -## Requirements - -- **Python 3.10 or later** -- **InOrbit account**: [Sign up for free](https://control.inorbit.ai) -- **InOrbit API key**: Export as `INORBIT_API_KEY` environment variable - -```bash -export INORBIT_API_KEY="" -``` - -## Run the Examples - -The [`examples`](https://github.com/inorbit-ai/inorbit-connector-python/tree/main/examples) directory contains usage examples of the connector. See [examples/README](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/README.md) for more information. - -### Simple Connector Example - -The simplest example demonstrates basic connector functionality: - -```bash -cd examples -source example.env -python simple-connector/connector.py -``` - -To stop the connector, press `Ctrl+C` in the terminal. - -### Robot Connector Example - -A more comprehensive example with command-line interface: - -```bash -cd examples -source example.env -python robot-connector/main.py --config example.yaml --robot_id my-example-robot -``` - -### Fleet Connector Example - -For managing multiple robots: - -```bash -cd examples -source example.env -python fleet-connector/main.py --config example.fleet.yaml -``` - -## Next Steps - -- Review the [Specification](/ground-control/robot-integration/connector-framework/specification/index) to understand the public API of the package -- Read the [Usage Guide](/ground-control/robot-integration/connector-framework/usage/index) to learn how to implement your own connector -- Review the [Configuration Guide](/ground-control/robot-integration/connector-framework/configuration) to understand connector configuration -- Check the [Publishing Guide](/ground-control/robot-integration/connector-framework/publishing) to learn how to publish data to InOrbit diff --git a/ground-control/robot-integration/connector-framework/index.md b/ground-control/robot-integration/connector-framework/index.md index ebabf6f..53b7711 100644 --- a/ground-control/robot-integration/connector-framework/index.md +++ b/ground-control/robot-integration/connector-framework/index.md @@ -3,35 +3,7 @@ title: "Connector Framework" description: "Python framework for developing InOrbit robot connectors" --- -A Python framework for developing *connectors* for the [InOrbit](https://inorbit.ai/) RobOps ecosystem. +This page will be automatically populated by the [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python) repository. -## Overview +See the [GitHub repository](https://github.com/inorbit-ai/inorbit-connector-python) for the latest documentation. -This framework provides a base structure for developing [InOrbit](https://inorbit.ai/) robot connectors. Making use of InOrbit's [Edge SDK](https://developer.inorbit.ai/docs#edge-sdk), `inorbit-connector` provides a starting point for the integration of a fleet of robots in InOrbit, unlocking interoperability. - -The framework supports both single-robot and fleet connectors: - -- **Single-robot connectors**: Subclass `Connector` to manage one robot at a time -- **Fleet connectors**: Subclass `FleetConnector` to manage multiple robots simultaneously, ideal for managing robots through a fleet manager - -Both connector types provide: -- Automatic InOrbit robot provisioning -- Built-in publishing methods for pose, odometry, key-values, and system stats -- Command handling infrastructure for receiving commands from InOrbit -- Map management with automatic updates -- Camera feed registration -- User scripts support for custom command execution -- Configurable logging - -## Requirements - -- Python 3.10 or later -- InOrbit account ([it's free to sign up!](https://control.inorbit.ai)) - -## Documentation Sections - -- **Getting Started**: Installation, environment setup, and running a connector ([Getting Started](/ground-control/robot-integration/connector-framework/getting-started)) -- **Specification**: Package-level specification of the connector API ([Specification](/ground-control/robot-integration/connector-framework/specification/index)) -- **Usage**: Detailed guides for implementing single-robot and fleet connectors ([Usage](/ground-control/robot-integration/connector-framework/usage/index)) -- **Configuration**: Connector configuration models and file formats ([Configuration](/ground-control/robot-integration/connector-framework/configuration)) -- **Publishing**: How to publish data to InOrbit ([Publishing](/ground-control/robot-integration/connector-framework/publishing)) diff --git a/ground-control/robot-integration/connector-framework/publishing.md b/ground-control/robot-integration/connector-framework/publishing.md index 09189aa..ca99305 100644 --- a/ground-control/robot-integration/connector-framework/publishing.md +++ b/ground-control/robot-integration/connector-framework/publishing.md @@ -3,161 +3,5 @@ title: "Publishing Data" description: "How to publish robot data to InOrbit" --- -The connector framework provides methods to publish various types of data to InOrbit. Both single-robot (`Connector`) and fleet (`FleetConnector`) connectors support publishing, with fleet connectors using methods prefixed with `publish_robot_*` that require a `robot_id` parameter. +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). -Publishing methods are mostly thin wrappers around `RobotSession` methods from the [InOrbit Edge SDK](https://github.com/inorbit-ai/inorbit-edge-sdk-python), except for the `publish_pose()` and `publish_map()` methods which include additional map handling logic. - -## Pose and Map - -### Single-Robot Connectors - -```python -publish_pose(x: float, y: float, yaw: float, frame_id: str, **kwargs) -> None -``` - -Publishes a pose to InOrbit. If the `frame_id` is different from the last published frame_id, the map metadata is automatically sent via `publish_map()`. - -**Parameters:** -- `x` (float): X coordinate -- `y` (float): Y coordinate -- `yaw` (float): Yaw angle in radians -- `frame_id` (str): Frame ID for the pose. If this frame_id exists in the maps configuration, the map will be automatically uploaded -- `**kwargs`: Additional arguments for pose publishing - -```python -publish_map(frame_id: str, is_update: bool = False) -> None -``` - -Manually publish map metadata. Usually called automatically when `frame_id` changes in `publish_pose()`. - -**Parameters:** -- `frame_id` (str): The frame ID of the map (must exist in configuration maps) -- `is_update` (bool): Whether this is an update to an existing map - -### Fleet Connectors - -```python -publish_robot_pose(robot_id: str, x: float, y: float, yaw: float, frame_id: str = None, **kwargs) -> None -``` - -Publishes a pose for a specific robot. Automatically updates map when `frame_id` changes. - -```python -publish_robot_map(robot_id: str, frame_id: str, is_update: bool = False) -> None -``` - -Manually publish map metadata for a specific robot. - -## Telemetry - -### Odometry - -**Single-Robot:** -```python -publish_odometry(**kwargs) -> None -``` - -**Fleet:** -```python -publish_robot_odometry(robot_id: str, **kwargs) -> None -``` - -Publishes odometry data to InOrbit. Common fields include: -- `linear_speed`: Linear velocity -- `angular_speed`: Angular velocity -- `linear_acceleration`: Linear acceleration -- `angular_acceleration`: Angular acceleration - -### Key-Value Pairs - -**Single-Robot:** -```python -publish_key_values(**kwargs) -> None -``` - -**Fleet:** -```python -publish_robot_key_values(robot_id: str, **kwargs) -> None -``` - -Publishes key-value pairs as telemetry data. These can represent any custom robot state or metrics. - -**Example:** -```python -self.publish_key_values( - battery_level=85.5, - status="idle", - current_mission="delivery_1", - temperature=23.4 -) -``` - -### System Statistics - -**Single-Robot:** -```python -publish_system_stats(**kwargs) -> None -``` - -**Fleet:** -```python -publish_robot_system_stats(robot_id: str, **kwargs) -> None -``` - -Publishes system statistics such as CPU, RAM, and disk usage. - -**Example:** -```python -self.publish_system_stats( - cpu_load_percentage=45.2, - ram_usage_percentage=62.8, - hdd_usage_percentage=34.1 -) -``` - -## Cameras - -Cameras are configured in the `RobotConfig.cameras` field and are automatically registered when the connector connects. - -### Camera Configuration - -Cameras are configured using `CameraConfig` from the InOrbit Edge SDK. Each camera in the `RobotConfig.cameras` list is automatically registered with InOrbit. - -**Example configuration:** -```yaml -fleet: - - robot_id: my-robot - cameras: - - video_url: "rtsp://camera.example.com/stream" - camera_id: "front_camera" - - video_url: "http://camera.example.com/feed" - camera_id: "back_camera" -``` - -Camera feeds are registered automatically during the `_connect()` phase, so no additional code is needed in your connector implementation. - -## Publishing Best Practices - -1. **Pose Updates**: Publish pose updates regularly in your `_execution_loop()` method -2. **Map Updates**: Maps are automatically updated when the `frame_id` changes. Ensure all frame_ids used in poses are defined in your configuration -3. **Telemetry Frequency**: Use the `update_freq` configuration to control how often your execution loop runs -4. **Error Handling**: Wrap publishing calls in try-except blocks to handle network errors gracefully -5. **Async Operations**: Use `asyncio.gather()` to publish multiple data types concurrently for better performance - -**Example:** -```python -async def _execution_loop(self) -> None: - # Fetch data from robot API - pose_data = await self._get_robot_pose() - telemetry_data = await self._get_robot_telemetry() - - # Publish data - self.publish_pose( - pose_data['x'], - pose_data['y'], - pose_data['yaw'], - pose_data['frame_id'] - ) - - self.publish_key_values(**telemetry_data) -``` diff --git a/ground-control/robot-integration/connector-framework/specification/commands.md b/ground-control/robot-integration/connector-framework/specification/commands.md index 982e3e5..7da2d56 100644 --- a/ground-control/robot-integration/connector-framework/specification/commands.md +++ b/ground-control/robot-integration/connector-framework/specification/commands.md @@ -3,56 +3,5 @@ title: "Commands Utilities" description: "Command handling utilities specification" --- -This page specifies command handling helpers from `inorbit_connector.commands`. - -(spec-commands-commandfailure)= -## `CommandResultCode` / `CommandFailure` - -- `CommandResultCode` is an enum with `SUCCESS` and `FAILURE`. -- `CommandFailure` is an exception that carries: - - `execution_status_details`: a user-visible failure summary - - `stderr`: a more detailed error payload - -When `CommandFailure` is raised inside a command handler, the framework converts it into a failure result via the provided `options["result_function"]`. - -(spec-commands-parse-custom-command-args)= -## `parse_custom_command_args(custom_command_args) -> (script_name, params)` - -Parses arguments for a `COMMAND_CUSTOM_COMMAND`/RunScript-style payload. - -Input assumptions: - -- `custom_command_args[0]` is a script name (string). -- `custom_command_args[1]` is a list-like container with alternating `key, value, key, value, ...`. - -Output: - -- `script_name`: string -- `params`: dictionary of parsed key/value pairs (last value wins on duplicate keys) - -Raises: - -- `ValueError` when the outer container does not match the expected types/shapes. -- `CommandFailure` when the arguments list is not pairs (odd length). - -(spec-commands-commandmodel)= -## `CommandModel` / `ExcludeUnsetMixin` - -These classes support type-safe parsing and validation of structured command parameters. - -- `CommandModel` is a Pydantic `BaseModel` configured with `extra="forbid"`, and converts `ValidationError` into `CommandFailure`. -- `ExcludeUnsetMixin` changes `model_dump()` default behavior to `exclude_unset=True` (useful when you want to emit only explicitly-provided fields). - -See [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) for end-to-end usage patterns. - -## Note: re-exports via `inorbit_connector.connector` - -For backwards compatibility, the connector module re-exports: - -- `CommandFailure` -- `CommandResultCode` -- `parse_custom_command_args` - -New code may import from either module, but the canonical definitions live in `inorbit_connector.commands`. - +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). diff --git a/ground-control/robot-integration/connector-framework/specification/connector.md b/ground-control/robot-integration/connector-framework/specification/connector.md index c4b4965..5698eab 100644 --- a/ground-control/robot-integration/connector-framework/specification/connector.md +++ b/ground-control/robot-integration/connector-framework/specification/connector.md @@ -3,186 +3,5 @@ title: "Connector API" description: "Connector and FleetConnector class specifications" --- -This page specifies the connector base classes you subclass to build connectors. - -## `FleetConnector` - -`inorbit_connector.connector.FleetConnector` is the base class for connectors that manage multiple robots. - - -### `_connect()` - -**Override.** - -Called once on startup (inside the connector’s background thread / event loop), before robot sessions are initialized. - -Typical responsibilities: - -- Connect to your fleet manager / backend services. -- Optionally fetch the fleet membership and call `update_fleet()` **before** sessions are created. -- Start background polling tasks that keep fresh state for all robots. - - -### `_execution_loop()` - -**Override.** - -Called repeatedly until stopped. The loop is rate-limited by `config.update_freq`, but it will never run faster than the body can execute. - -Typical responsibilities: - -- For each robot in `self.robot_ids`, fetch the latest state (often from background polling state) and publish data via the `publish_robot_*` methods. -- Handle exceptions inside the loop when possible (the framework logs and continues on exceptions). - - -### `_disconnect()` - -**Override.** - -Called once during shutdown, after Edge SDK sessions are disconnected. Use this to stop polling tasks, close sockets, and release resources. - - -### `_inorbit_robot_command_handler(robot_id, command_name, args, options)` - -**Override.** - -Called when a command arrives from InOrbit for a specific robot in the fleet. - -Contract: - -- `options["result_function"](/ground-control/robot-integration/connector-framework/specification/...)` must be called to report success/failure, or you may raise `CommandFailure` for structured failure reporting. - -See [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) for the full command result contract. - - -### `fetch_robot_map(robot_id, frame_id) -> MapConfigTemp | None` - -**Optional override.** - -If `publish_robot_pose()` references a `frame_id` that is not present in `config.maps`, the framework schedules an async fetch: - -- Calls your `fetch_robot_map()` coroutine. -- If you return a `MapConfigTemp` containing `image` bytes and metadata, the framework writes the image to a temporary file and inserts a corresponding `MapConfig` into `config.maps`. -- Then it publishes the map via `publish_robot_map()`. - -Return `None` if the map can’t be fetched. - - -### `_is_fleet_robot_online(robot_id) -> bool` - -**Optional override.** - -The Edge SDK uses this callback to determine if a robot should be considered online. Default implementation returns `True`. - - -### `start()` / `join()` / `stop()` - -**Callable.** - -- `start()` creates a background thread and runs the connector lifecycle in an asyncio event loop. -- `join()` blocks until the background thread exits. -- `stop()` signals the event loop to stop and waits briefly for shutdown. - - -### `robot_ids` / `update_fleet(fleet)` - -**Callable.** - -- `robot_ids` is a cached list of robot IDs from `config.fleet`. -- `update_fleet()` updates `config.fleet` and refreshes `robot_ids`. - -This is designed to be used during `_connect()` when your fleet membership comes from an external system. - - -### `publish_robot_pose(robot_id, x, y, yaw, frame_id=None, **kwargs)` - -**Callable.** - -Publishes pose for one robot. If the `frame_id` differs from the last published `frame_id` for that robot, the framework triggers `publish_robot_map(..., is_update=True)`. - - -### `publish_robot_map(robot_id, frame_id, is_update=False)` - -**Callable.** - -- If `frame_id` exists in `config.maps`, publishes the configured map to InOrbit. -- Otherwise schedules an async fetch via `fetch_robot_map()` (see above). - - -### `publish_robot_odometry(robot_id, **kwargs)` - -**Callable.** Publishes odometry data for one robot. - - -### `publish_robot_key_values(robot_id, **kwargs)` - -**Callable.** Publishes key-value telemetry for one robot. - - -### `publish_robot_system_stats(robot_id, **kwargs)` - -**Callable.** Publishes system stats for one robot. - - -### `_get_robot_session(robot_id) -> RobotSession` - -**Callable (advanced).** - -Returns the underlying Edge SDK session for `robot_id`. Use this if you need Edge SDK functionality that is not wrapped by this package. - -## `Connector` - -`inorbit_connector.connector.Connector` is a single-robot specialization of `FleetConnector`. It selects a single robot out of the fleet config and provides convenience wrappers that omit `robot_id`. - - -### Lifecycle hooks - -**Override.** Same intent as fleet: - -- `_connect()` -- `_execution_loop()` -- `_disconnect()` - - -### `_inorbit_command_handler(command_name, args, options)` - -**Override.** - -Single-robot command handler. It is called through the fleet-level handler internally, but without requiring you to accept `robot_id`. - - -### `fetch_map(frame_id) -> MapConfigTemp | None` - -**Optional override.** - -Single-robot convenience for map fetching. The framework uses it by delegating `fetch_robot_map()` to `fetch_map()`. - - -### `_is_robot_online() -> bool` - -**Optional override.** - -Single-robot convenience for online status. The fleet-level online check delegates to this method. - - -### Publishing wrappers - -**Callable.** Single-robot wrappers over the fleet publishing methods: - -- `publish_pose(...)` / `publish_map(...)` -- `publish_odometry(...)` -- `publish_key_values(...)` -- `publish_system_stats(...)` - - -### `_get_session() -> RobotSession` - -**Callable (advanced).** - -Returns the underlying Edge SDK session for the current robot. - -### Deprecated: `_robot_session` - -`Connector._robot_session` is deprecated; use `_get_session()` instead. - +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). diff --git a/ground-control/robot-integration/connector-framework/specification/index.md b/ground-control/robot-integration/connector-framework/specification/index.md index f362def..3562ad6 100644 --- a/ground-control/robot-integration/connector-framework/specification/index.md +++ b/ground-control/robot-integration/connector-framework/specification/index.md @@ -3,86 +3,5 @@ title: "Specification" description: "Public API specification for the inorbit-connector package" --- -This section specifies the public surface of the `inorbit-connector` package: what you are expected to **override** when implementing a connector, and what you can **call** at runtime. +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). -## Intended usage - -An InOrbit connector is an application that: - -- Connects to your robot (or fleet manager) and to InOrbit via the InOrbit Edge SDK. -- Runs a periodic execution loop to publish telemetry (pose, odometry, key-values, system stats). -- Optionally handles commands coming from InOrbit. - -You typically: - -- Implement a **single-robot** connector by subclassing `inorbit_connector.connector.Connector`. -- Implement a **fleet** connector by subclassing `inorbit_connector.connector.FleetConnector`. - -At runtime, you call: - -- `start()` to spawn the connector thread and begin the async lifecycle. -- `join()` to block the main thread until shutdown. -- `stop()` to request shutdown. - -During the lifecycle, the framework calls your overrides: - -- `_connect()` once at startup (before sessions are initialized). -- `_execution_loop()` repeatedly at approximately `config.update_freq` Hz. -- `_disconnect()` once at shutdown. -- A command handler (`_inorbit_command_handler()` for single-robot; `_inorbit_robot_command_handler()` for fleet) when commands arrive. - -For map handling, `publish_pose()` / `publish_robot_pose()` automatically trigger map publication when the `frame_id` changes. If a map is not configured, the connector can fetch it by overriding `fetch_map()` / `fetch_robot_map()`. - -For narrative guides, see: - -- Single robot: [Single-Robot Connector](/ground-control/robot-integration/connector-framework/usage/single-robot) -- Fleet: [Fleet Connector](/ground-control/robot-integration/connector-framework/usage/fleet) -- Commands: [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) -- Publishing: [Publishing Data](/ground-control/robot-integration/connector-framework/publishing) -- Configuration: [Configuration](/ground-control/robot-integration/connector-framework/configuration) - -## API surface (callable + overridable) - -The table below lists package-defined symbols meant for direct use (call) or extension (override). Each row links to a longer specification page. - -| Kind | Symbol | Purpose | Details | -| --- | --- | --- | --- | -| Override | `FleetConnector._connect()` | Connect to external services (fleet manager, robot backends) before sessions initialize. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-connect) | -| Override | `FleetConnector._execution_loop()` | Periodic loop; publish telemetry for each robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-execution-loop) | -| Override | `FleetConnector._disconnect()` | Shutdown external services and release resources. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-disconnect) | -| Override | `FleetConnector._inorbit_robot_command_handler()` | Handle commands for a specific `robot_id`. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-command-handler) | -| Override (optional) | `FleetConnector.fetch_robot_map()` | Fetch a missing map (bytes + metadata) when publishing pose refers to an unknown `frame_id`. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-fetch-robot-map) | -| Override (optional) | `FleetConnector._is_fleet_robot_online()` | Provide robot online status; used by Edge SDK callback. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-is-online) | -| Call | `FleetConnector.start()` / `join()` / `stop()` | Run and control the connector lifecycle. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-lifecycle) | -| Call | `FleetConnector.update_fleet()` / `FleetConnector.robot_ids` | Update fleet configuration (typically during `_connect()`) and access robot IDs. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-fleet-management) | -| Call | `FleetConnector.publish_robot_pose()` | Publish pose; triggers map publish when `frame_id` changes. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-publish-robot-pose) | -| Call | `FleetConnector.publish_robot_map()` | Publish map metadata/image from configured maps (or after fetch). | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-publish-robot-map) | -| Call | `FleetConnector.publish_robot_odometry()` | Publish odometry. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-publish-robot-odometry) | -| Call | `FleetConnector.publish_robot_key_values()` | Publish key-value telemetry. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-publish-robot-key-values) | -| Call | `FleetConnector.publish_robot_system_stats()` | Publish system stats telemetry. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-publish-robot-system-stats) | -| Call (advanced) | `FleetConnector._get_robot_session()` | Access the underlying Edge SDK `RobotSession` for a specific robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-fleetconnector-get-robot-session) | -| Override | `Connector._connect()` / `_execution_loop()` / `_disconnect()` | Same lifecycle hooks as fleet, for a single robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-lifecycle-hooks) | -| Override | `Connector._inorbit_command_handler()` | Handle commands for the single robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-command-handler) | -| Override (optional) | `Connector.fetch_map()` | Fetch a missing map for the current robot when pose references an unknown `frame_id`. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-fetch-map) | -| Override (optional) | `Connector._is_robot_online()` | Provide online status for the current robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-is-online) | -| Call | `Connector.publish_pose()` / `publish_map()` | Publish pose/map for the current robot (map handling included). | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-publishing) | -| Call | `Connector.publish_odometry()` / `publish_key_values()` / `publish_system_stats()` | Publish telemetry for the current robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-publishing) | -| Call (advanced) | `Connector._get_session()` | Access the underlying Edge SDK `RobotSession` for the current robot. | [Details](/ground-control/robot-integration/connector-framework/specification/connector#spec-connector-connector-get-session) | -| Type | `ConnectorConfig` | Base configuration model for connectors. | [Details](/ground-control/robot-integration/connector-framework/specification/models#spec-models-connectorconfig) | -| Type | `RobotConfig` | Per-robot configuration (robot_id + cameras). | [Details](/ground-control/robot-integration/connector-framework/specification/models#spec-models-robotconfig) | -| Type | `MapConfig` / `MapConfigTemp` | Map configuration (file-backed vs in-memory bytes) used by map publishing/fetching. | [Details](/ground-control/robot-integration/connector-framework/specification/models#spec-models-mapconfig) | -| Type | `LoggingConfig` / `LogLevels` | Logging configuration and log-level enum. | [Details](/ground-control/robot-integration/connector-framework/specification/logging#spec-logging-loggingconfig) | -| Call | `read_yaml()` | Load YAML configuration data (with deprecated `robot_id` selection support). | [Details](/ground-control/robot-integration/connector-framework/specification/utils#spec-utils-readyaml) | -| Type / Call | `CommandResultCode` / `CommandFailure` | Standard command result code enum and structured failure exception. | [Details](/ground-control/robot-integration/connector-framework/specification/commands#spec-commands-commandfailure) | -| Call | `parse_custom_command_args()` | Parse custom-command args (RunScript action payload) into `(script_name, params)`. | [Details](/ground-control/robot-integration/connector-framework/specification/commands#spec-commands-parse-custom-command-args) | -| Type | `CommandModel` / `ExcludeUnsetMixin` | Pydantic-based command argument validation utilities. | [Details](/ground-control/robot-integration/connector-framework/specification/commands#spec-commands-commandmodel) | -| Call | `setup_logger()` | Configure logging from `LoggingConfig`. | [Details](/ground-control/robot-integration/connector-framework/specification/logging#spec-logging-setup-logger) | -| Type | `ConditionalColoredFormatter` | Optional `colorlog`-backed formatter used by the default logging config. | [Details](/ground-control/robot-integration/connector-framework/specification/logging#spec-logging-conditional-colored-formatter) | - -## Pages - -- [Connector API](/ground-control/robot-integration/connector-framework/specification/connector) -- [Models (configuration)](/ground-control/robot-integration/connector-framework/specification/models) -- [Commands utilities](/ground-control/robot-integration/connector-framework/specification/commands) -- [Utilities](/ground-control/robot-integration/connector-framework/specification/utils) -- [Logging](/ground-control/robot-integration/connector-framework/specification/logging) diff --git a/ground-control/robot-integration/connector-framework/specification/logging.md b/ground-control/robot-integration/connector-framework/specification/logging.md index b6d3ae4..1ca6d9e 100644 --- a/ground-control/robot-integration/connector-framework/specification/logging.md +++ b/ground-control/robot-integration/connector-framework/specification/logging.md @@ -3,34 +3,5 @@ title: "Logging" description: "Logging configuration specification" --- -This page specifies logging-related helpers from `inorbit_connector.logging`. - -(spec-logging-setup-logger)= -## `setup_logger(config: LoggingConfig) -> None` - -Configures Python logging using the standard library `logging.config.fileConfig`. - -Behavior: - -- If `config.config_file` is set, it is loaded via `logging.config.fileConfig(..., disable_existing_loggers=False, defaults=config.defaults)`. -- If `config.log_level` is set, the root logger level is overridden to that value after loading the config file. - -(spec-logging-loggingconfig)= -## `LoggingConfig` / `LogLevels` - -- `LoggingConfig` is the configuration model used by `setup_logger()`. -- `LogLevels` is an enum of `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`. - -(spec-logging-conditional-colored-formatter)= -## `ConditionalColoredFormatter` - -Formatter used by the package default logging configuration. - -Behavior: - -- If `colorlog` is installed, it uses `colorlog.ColoredFormatter`. -- Otherwise it falls back to `logging.Formatter` and removes `%(log_color)s` / `%(reset)s` tokens from the format string. - -The default logging config (`inorbit_connector/logging/logging.default.conf`) references this formatter by class name. - +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). diff --git a/ground-control/robot-integration/connector-framework/specification/models.md b/ground-control/robot-integration/connector-framework/specification/models.md index 8d550bc..21dee7e 100644 --- a/ground-control/robot-integration/connector-framework/specification/models.md +++ b/ground-control/robot-integration/connector-framework/specification/models.md @@ -1,68 +1,7 @@ --- title: "Models" description: "Configuration models specification" ---- (configuration) - -This page specifies the configuration models defined by `inorbit_connector.models`. - -(spec-models-connectorconfig)= -## `ConnectorConfig` - -Base configuration model for connectors. - -Key points: - -- You typically **subclass** this to define your connector-specific `connector_config` model. -- The base model reads `INORBIT_API_KEY` and `INORBIT_API_URL` from environment variables by default. -- `fleet` must contain at least one `RobotConfig`, and robot IDs must be unique. - -### `to_singular_config(robot_id) -> ConnectorConfig` - -Returns a config instance of the same subclass type, with `fleet` filtered down to exactly the requested robot. - -### Deprecated: `log_level` - -`ConnectorConfig.log_level` is deprecated in favor of `ConnectorConfig.logging.log_level`. - -(spec-models-robotconfig)= -## `RobotConfig` - -Per-robot configuration: - -- `robot_id`: the InOrbit robot ID. -- `cameras`: list of Edge SDK `CameraConfig` objects. Camera registration is performed automatically during connector startup. - -(spec-models-mapconfig)= -## `MapConfig` / `MapConfigTemp` - -These models describe map metadata and image source. - -- `MapConfig`: file-backed map with `file: FilePath` pointing to a `.png`. -- `MapConfigTemp`: in-memory map payload with `image: bytes`. - -Both carry metadata via `MapConfigBase`: - -- `map_id`, optional `map_label` -- `origin_x`, `origin_y`, `resolution` -- `format_version` (must be 1 or 2) - -(spec-models-loggingconfig)= -## `LoggingConfig` - -Logging configuration used by the connector at startup: - -- `config_file`: path to a logging config file (defaults to the package’s `logging.default.conf`). -- `log_level`: optional override for the root logger level. -- `defaults`: dictionary passed to the logging config (e.g. `log_file`). - -See [setup_logger()](/ground-control/robot-integration/connector-framework/specification/logging#spec-logging-setup-logger) for how it is applied. - -## Deprecated: `InorbitConnectorConfig` - -`InorbitConnectorConfig` is a deprecated single-robot configuration format. It can be converted to a fleet config via: - -- `to_fleet_config(robot_id) -> ConnectorConfig` - -New implementations should use `ConnectorConfig` directly. +--- +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). diff --git a/ground-control/robot-integration/connector-framework/specification/utils.md b/ground-control/robot-integration/connector-framework/specification/utils.md index 5680f25..18877bc 100644 --- a/ground-control/robot-integration/connector-framework/specification/utils.md +++ b/ground-control/robot-integration/connector-framework/specification/utils.md @@ -3,27 +3,5 @@ title: "Utilities" description: "Utility functions specification" --- -This page specifies utilities from `inorbit_connector.utils`. - -(spec-utils-readyaml)= -## `read_yaml(fname, robot_id=None) -> dict` - -Reads a YAML file and returns a dictionary. - -Behavior: - -- If the file is empty (YAML `null` / no content), returns `{}`. -- If `robot_id` is **not** provided, returns the entire YAML object. -- If `robot_id` **is** provided and is a top-level key in the YAML object, returns `data[robot_id]` and emits a **DeprecationWarning** (this selection format is deprecated). -- If `robot_id` is provided but not found, raises `IndexError`. - -Notes: - -- New configurations should follow the `ConnectorConfig` schema described in [Configuration](/ground-control/robot-integration/connector-framework/configuration). - -## Constants - -- `DEFAULT_TIMEZONE`: default timezone string (`"UTC"`). -- `DEFAULT_LOGGING_CONFIG`: path to the package default logging config file. - +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). diff --git a/ground-control/robot-integration/connector-framework/usage/commands-handling.md b/ground-control/robot-integration/connector-framework/usage/commands-handling.md index 0a5f133..f0e85cd 100644 --- a/ground-control/robot-integration/connector-framework/usage/commands-handling.md +++ b/ground-control/robot-integration/connector-framework/usage/commands-handling.md @@ -3,345 +3,5 @@ title: "Commands Handling" description: "How to handle commands from InOrbit" --- -Commands from InOrbit are automatically routed to your `_inorbit_robot_command_handler()` method in the case of a fleet connector, or to your `_inorbit_command_handler()` method in the case of a single-robot connector. +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). -The fleet handler `_inorbit_robot_command_handler()` receives the following parameters: -- `robot_id` (str): The ID of the robot receiving the command -- `command_name` (str): The name of the command -- `args` (list): Command arguments -- `options` (dict): Options including `result_function` to report results - -While the single-robot handler `_inorbit_command_handler()` omits the `robot_id` parameter. - -```python -@override -async def _inorbit_robot_command_handler( - self, robot_id: str, command_name: str, args: list, options: dict -) -> None: - """Handle InOrbit commands for a specific robot.""" - if command_name == "start_mission": - result = await self._fleet_manager.send_command( - robot_id, "start_mission", args[0] - ) -``` - -A more complete example including all provided utilities is available [at the bottom](#example-usage) of this page. - -## Reporting Command Results - -The `options` dictionary contains a `result_function` with the following signature: -```python -options['result_function'](/ground-control/robot-integration/connector-framework/usage/ - result_code: CommandResultCode, - execution_status_details: str | None = None, - stdout: str | None = None, - stderr: str | None = None, -) -> None -``` -- The `result_code` parameter must be set to `CommandResultCode.SUCCESS` or `CommandResultCode.FAILURE` to report the command result. -- The `execution_status_details` and `stderr` parameters are optional and can be used to provide specific error details. -- The `stdout` parameter is optional and can be used to provide specific output details. - -### Reporting Success - -The commands handler can call the `result_function` with a `CommandResultCode.SUCCESS` to report a successful command execution before returning. The `stdout` parameter is optional and can be used to provide specific output details. - -```python -options["result_function"](/ground-control/robot-integration/connector-framework/usage/ - CommandResultCode.SUCCESS, - stdout="Mission dispatched with ID 123456", -) -``` - -(reporting-failure)= -### Reporting Failure - -There are three ways to report a failed command execution: - -1. (Recommended) Raise a `CommandFailure` exception. -2. Raise any other exception. -3. Call the `result_function` with a `CommandResultCode.FAILURE` and provide the `execution_status_details` and `stderr` parameters. - -Unhandled exceptions raised during the execution of the command handler will be caught and reported with a generic error message in its execution details, and the exception message attached to the `stderr` field. All exceptions are logged. - -The `CommandFailure` exception can be used to intentionally indicate a failure: -- Error details are automatically passed to InOrbit's result function -- `execution_status_details` is displayed in alert messages when commands are dispatched from the actions UI -- Both `execution_status_details` and `stderr` are available in audit logs and through the [action execution details API](https://api.inorbit.ai/docs/index.html#operation/getActionExecutionStatus) - -Example usage: - -```python -from inorbit_connector.connector import CommandResultCode, CommandFailure - -@override -async def _inorbit_robot_command_handler( - self, robot_id: str, command_name: str, args: list, options: dict -) -> None: - """Handle InOrbit commands for a specific robot.""" - if command_name == "start_mission": - try: - if robot_id not in self.robot_ids: - raise CommandFailure( - execution_status_details=f"Robot {robot_id} not found in fleet", - stderr="Invalid robot ID" - ) - - result = await self._fleet_manager.send_command( - robot_id, "start_mission", args[0] - ) - if not result: - raise CommandFailure( - execution_status_details=f"Failed to start mission for {robot_id}", - stderr="Fleet manager returned error" - ) - options["result_function"](/ground-control/robot-integration/connector-framework/usage/CommandResultCode.SUCCESS) - except ValueError as e: - raise CommandFailure( - execution_status_details=f"Invalid parameters for {robot_id}", - stderr=str(e) - ) -``` - -## Parsing Script Arguments - -When handling custom script commands, the Edge SDK delivers arguments in the form: - -- `args[0]`: script file name (e.g., `"script.sh"`) -- `args[1]`: a flat list of alternating keys and values (e.g., `["x", "1.0", "y", "2.0"]`) - -In the case of InOrbit actions of `RunScript` type (identified with the `command_name` `COMMAND_CUSTOM_COMMAND`), -the script name corresponds to the `filename` field of the action definition, -and the arguments key-value pairs. - -For details on how to configure actions with arguments, refer to the -[InOrbit Actions Definitions documentation](https://developer.inorbit.ai/docs#configuring-action-definitions). - -Use the helper `parse_custom_command_args()` to turn these into a script name and a parameters dictionary: - -```python -from inorbit_connector.connector import ( - parse_custom_command_args, - CommandResultCode, - CommandFailure, -) -from inorbit_edge.commands import COMMAND_CUSTOM_COMMAND - -@override -async def _inorbit_robot_command_handler( - self, robot_id: str, command_name: str, args: list, options: dict -) -> None: - if command_name == COMMAND_CUSTOM_COMMAND: - # args format: [file_name, [k1, v1, k2, v2, ...]] - script, params = parse_custom_command_args(args) - # Example: script == "script.sh", params == {"x": "1.0", "y": "2.0"} - # Use script and params as needed - options["result_function"](/ground-control/robot-integration/connector-framework/usage/ - CommandResultCode.SUCCESS, - stdout=f"Ran {script} with params {params}", - ) -``` - -It is recommended to complement its use with the `CommandModel` class (see [Using CommandModel for Type-Safe Argument Parsing](#using-commandmodel-for-type-safe-argument-parsing)) for safe type validation and parsing. - -(using-commandmodel-for-type-safe-argument-parsing)= -## Using `CommandModel` for Type-Safe Argument Parsing - -For structured command arguments that require validation and type safety, use the `CommandModel` base class. This is particularly useful when commands have multiple parameters with specific types and validation rules. - -`CommandModel` provides: -- Automatic type validation and conversion using Pydantic -- Conversion of validation errors to [`CommandFailure`](#reporting-failure) exceptions -- Protection against extra fields (forbids unknown parameters) - -Optionally, you can combine `CommandModel` with `ExcludeUnsetMixin` to exclude unset fields from model dumps, which is useful for API calls where you only want to send non-default values. - -### Basic Usage - -Define a command model by subclassing `CommandModel`: - -```python -from inorbit_connector.commands import CommandModel, parse_custom_command_args -from inorbit_connector.connector import CommandResultCode, CommandFailure - -class CommandQueueMission(CommandModel): - """Command model for queue_mission command.""" - mission_id: str - robot_id: int | None = None - priority: int | None = None - description: str | None = None -``` - -### Using with `ExcludeUnsetMixin` - -To exclude unset fields from model dumps (useful for API calls), inherit from both `ExcludeUnsetMixin` and `CommandModel`. The mixin must come first in the inheritance list: - -```python -from inorbit_connector.commands import CommandModel, ExcludeUnsetMixin - -class CommandQueueMission(ExcludeUnsetMixin, CommandModel): - """Command model for queue_mission command.""" - mission_id: str - robot_id: int | None = None - priority: int | None = None - description: str | None = None -``` - -### Using with `parse_custom_command_args` - -`CommandModel` works seamlessly with `parse_custom_command_args()`: - -```python -from inorbit_edge.commands import COMMAND_CUSTOM_COMMAND - -@override -async def _inorbit_robot_command_handler( - self, robot_id: str, command_name: str, args: list, options: dict -) -> None: - if command_name == COMMAND_CUSTOM_COMMAND: - script_name, script_args = parse_custom_command_args(args) - - if script_name == "queue_mission": - # Validation happens automatically - raises CommandFailure on error - command = CommandQueueMission(**script_args) - - # If using ExcludeUnsetMixin, model_dump() excludes unset fields - # Useful for API calls where you only want to send non-default values - await self._fleet_client.schedule_mission(**command.model_dump()) - - options["result_function"](/ground-control/robot-integration/connector-framework/usage/CommandResultCode.SUCCESS) -``` - -### Automatic Error Handling - -If validation fails, `CommandModel` automatically raises a `CommandFailure` with appropriate error details: - -```python -# If script_args contains invalid data: -# script_args = {"mission_id": "test", "priority": "not_an_int"} -command = CommandQueueMission(**script_args) -# Raises CommandFailure with execution_status_details="Bad arguments" -# and stderr containing the validation error details -``` - -The exception is automatically handled by the connector's command execution framework, so you don't need to catch `ValidationError` exceptions. -See the [Reporting Failure](#reporting-failure) section for more details. - -### Excluding Unset Fields - -When using `ExcludeUnsetMixin`, `model_dump()` excludes fields that weren't explicitly set, which is useful when making API calls where you only want to send non-default values: - -```python -# With ExcludeUnsetMixin -command = CommandQueueMission(mission_id="test123", priority=5) -command.model_dump() -# Returns: {"mission_id": "test123", "priority": 5} -# Note: robot_id and description are excluded since they weren't set - -# Without ExcludeUnsetMixin -class SimpleCommand(CommandModel): - mission_id: str - priority: int | None = None - -command = SimpleCommand(mission_id="test123") -command.model_dump() -# Returns: {"mission_id": "test123", "priority": None} -# All fields are included, even if they have default values -``` - -(example-usage)= -## Example Usage - -Here's a concrete example of using `CommandModel` with `ExcludeUnsetMixin` to handle a custom command. - -The command is a RunScript action, whose filename is `schedule_mission`. - -```yaml -# ActionDefinition.yaml -apiVersion: v0.1 -kind: ActionDefinition -metadata: - id: dock - scope: tag/my-account-id/my-collection-id # Or any applicable scope -spec: - type: RunScript - arguments: - - name: filename - type: string - value: schedule_mission - - name: mission_id - type: string - value: 4eaa3a62-7a17-11ed-9f3c-0001299981c4 - description: Sends robot to charging dock - label: Dock -``` - -Once applied using the [InOrbit CLI](https://developer.inorbit.ai/docs#using-the-inorbit-cli) or [REST APIs](https://api.inorbit.ai/docs/index.html#tag/configAPI), the action can be executed through the InOrbit UI or through the [REST APIs](https://api.inorbit.ai/docs/index.html#tag/actions). - -```shell -# Apply the action definition -inorbit apply -f ActionDefinition.yaml - -# Execute the action -curl --location 'https://api.inorbit.ai/robots//actions' \ ---header 'Content-Type: application/json' \ ---header 'Accept: application/json' \ ---header 'x-auth-inorbit-app-key: ' \ ---data ' -{ - "actionId": "dock" -}' -``` - -The connector implements the command handler for the `schedule_mission` command and passes the arguments to an API client to schedule the mission. - -```python -from enum import StrEnum -from inorbit_connector.commands import CommandModel, ExcludeUnsetMixin, parse_custom_command_args -from inorbit_connector.connector import CommandResultCode -from inorbit_edge.commands import COMMAND_CUSTOM_COMMAND - -class CommandScheduleMission(ExcludeUnsetMixin, CommandModel): - """Command model for scheduling a mission.""" - mission_id: str - robot_id: int | None = None - priority: int | None = None - -class CustomScripts(StrEnum): - """Custom scripts supported by the connector.""" - SCHEDULE_MISSION = "schedule_mission" - # Add other custom scripts here - -class ExampleConnector(Connector): - ... # other methods - - @override - async def _inorbit_command_handler( - self, command_name: str, args: list, options: dict - ) -> None: - if command_name == COMMAND_CUSTOM_COMMAND: - script_name, script_args = parse_custom_command_args(args) - # script_name = "schedule_mission" - # script_args = {"mission_id": "4eaa3a62-7a17-11ed-9f3c-0001299981c4"} - - if script_name == CustomScripts.SCHEDULE_MISSION: - # Validation and type conversion happen automatically - command = CommandScheduleMission(**script_args) - - # Only explicitly set fields are included in the dump - await self._api_client.schedule_mission(**command.model_dump()) - - else: - raise CommandFailure( - execution_status_details=f"Command not implemented", - stderr=f"Command '{script_name}' not yet implemented", - ) - - # Call the result function to indicate success - options["result_function"](/ground-control/robot-integration/connector-framework/usage/CommandResultCode.SUCCESS) - else: - raise CommandFailure( - execution_status_details=f"Command not implemented", - stderr=f"Command '{command_name}' not yet implemented", - ) -``` diff --git a/ground-control/robot-integration/connector-framework/usage/fleet.md b/ground-control/robot-integration/connector-framework/usage/fleet.md index d66d53e..dce0c20 100644 --- a/ground-control/robot-integration/connector-framework/usage/fleet.md +++ b/ground-control/robot-integration/connector-framework/usage/fleet.md @@ -3,179 +3,5 @@ title: "Fleet Connector" description: "Guide for implementing a fleet connector" --- -Subclass `inorbit_connector.connector.FleetConnector` to manage multiple robots simultaneously. - -## Constructor - -```python -def __init__(self, config: ConnectorConfig, **kwargs) -> None -``` - -**Parameters:** -- `config` (ConnectorConfig): The connector configuration containing the fleet - -**Keyword Arguments:** -- `register_user_scripts` (bool): Automatically register user scripts. Default: `False` -- `default_user_scripts_dir` (str): Default directory for user scripts. Default: `~/.inorbit_connectors/connector-{class_name}/local/` -- `create_user_scripts_dir` (bool): Create the user scripts directory if it doesn't exist. Default: `False` -- `register_custom_command_handler` (bool): Automatically register the command handler. Default: `True` - -## Required Methods - -Subclasses must implement the same abstract methods as single-robot connectors, with one difference: - -### `_inorbit_robot_command_handler()` - -:::{hint} -See the [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) chapter for more details. -::: - -Handle commands for a specific robot. This method is automatically registered if `register_custom_command_handler` is True (default). - -```python -from inorbit_connector.connector import CommandResultCode, CommandFailure - -@override -async def _inorbit_robot_command_handler( - self, robot_id: str, command_name: str, args: list, options: dict -) -> None: - """Handle InOrbit commands for a specific robot.""" - if command_name == "start_mission": - result = await self._fleet_manager.send_command( - robot_id, "start_mission", args[0] - ) - if result: - options["result_function"](/ground-control/robot-integration/connector-framework/usage/CommandResultCode.SUCCESS) - else: - raise CommandFailure( - execution_status_details=f"Failed to start mission for {robot_id}", - stderr="Fleet manager returned error" - ) -``` - -Exceptions raised in the command handler are automatically caught and reported. Use `CommandFailure` to provide specific error details that will be displayed in InOrbit's audit logs and action execution details. - -## Fleet Management - -### Accessing Robot IDs - -Access the list of robot IDs in the fleet: - -```python -for robot_id in self.robot_ids: - # Process each robot - pass -``` - -### Updating the Fleet - -You can update the fleet configuration during `_connect()` by calling the `update_fleet()` method. This is useful for dynamically setting the robots list before the connector starts, for example, when provisioning robots from fleet manager data instead of hardcoded values in the config files: - -```python -@override -async def _connect(self) -> None: - """Connect to fleet manager and update fleet.""" - # Fetch robot list from fleet manager API - robots = await self._fleet_manager.get_robots() - - # Update fleet configuration - fleet_config = [ - RobotConfig(robot_id=robot.id, cameras=robot.cameras) - for robot in robots - ] - self.update_fleet(fleet_config) -``` - -The `update_fleet()` method updates the fleet configuration and initializes sessions for all robots. - -## Publishing Methods - -All publishing methods require a `robot_id` parameter. See the [Publishing Guide](/ground-control/robot-integration/connector-framework/publishing) for detailed information. - -- `publish_robot_pose(robot_id, x, y, yaw, frame_id)`: Publish pose for a specific robot -- `publish_robot_odometry(robot_id, **kwargs)`: Publish odometry for a specific robot -- `publish_robot_key_values(robot_id, **kwargs)`: Publish key-values for a specific robot -- `publish_robot_system_stats(robot_id, **kwargs)`: Publish system stats for a specific robot -- `publish_robot_map(robot_id, frame_id, is_update=False)`: Publish map for a specific robot - -## Advanced Methods - -### `_get_robot_session()` - -Access the underlying `RobotSession` from the InOrbit Edge SDK for a specific robot. Use this for advanced use cases not covered by the connector API. - -```python -def _get_robot_session(self, robot_id: str) -> RobotSession: - """Get a robot session for a specific robot ID. - - Args: - robot_id (str): The robot ID to get the session for - - Returns: - RobotSession: The robot session for the specified robot - """ -``` - -**Example:** -```python -async def _execution_loop(self) -> None: - for robot_id in self.robot_ids: - # Access the session directly for advanced features - session = self._get_robot_session(robot_id) - # Use Edge SDK methods directly - ... -``` - -## Robot Online Status - -Override `_is_fleet_robot_online()` to provide custom online status checks: - -```python -@override -def _is_fleet_robot_online(self, robot_id: str) -> bool: - """Check if a robot is online.""" - # Check robot status via fleet manager API - return self._fleet_manager.is_robot_online(robot_id) -``` - -This is used by InOrbit to determine robot availability. - -## Example Execution Loop - -```python -@override -async def _execution_loop(self) -> None: - """Main execution loop for fleet.""" - for robot_id in self.robot_ids: - try: - # Fetch robot data from fleet manager - robot_data = await self._fleet_manager.get_robot_data(robot_id) - - # Publish pose - self.publish_robot_pose( - robot_id, - robot_data.x, - robot_data.y, - robot_data.yaw, - robot_data.frame_id - ) - - # Publish telemetry - self.publish_robot_key_values(robot_id, **robot_data.telemetry) - except Exception as e: - self._logger.error(f"Error processing robot {robot_id}: {e}") -``` - -## Lifecycle - -The lifecycle methods are the same as single-robot connectors: -- `start()`: Start the connector -- `join()`: Block until stopped -- `stop()`: Stop the connector - -## Examples - -- **Simple fleet connector**: [examples/simple-fleet-connector/connector.py](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/simple-fleet-connector/connector.py) -- **Fleet connector (CLI)**: [examples/fleet-connector/](https://github.com/inorbit-ai/inorbit-connector-python/tree/main/examples/fleet-connector) -- **Examples index**: [examples/README.md](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/README.md) +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). diff --git a/ground-control/robot-integration/connector-framework/usage/index.md b/ground-control/robot-integration/connector-framework/usage/index.md index 359ce6c..629e190 100644 --- a/ground-control/robot-integration/connector-framework/usage/index.md +++ b/ground-control/robot-integration/connector-framework/usage/index.md @@ -3,8 +3,5 @@ title: "Usage" description: "Guides for implementing connectors" --- -## Guides +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). -- [Single-Robot Connector](/ground-control/robot-integration/connector-framework/usage/single-robot) -- [Fleet Connector](/ground-control/robot-integration/connector-framework/usage/fleet) -- [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) diff --git a/ground-control/robot-integration/connector-framework/usage/single-robot.md b/ground-control/robot-integration/connector-framework/usage/single-robot.md index 0766371..3bc4cf4 100644 --- a/ground-control/robot-integration/connector-framework/usage/single-robot.md +++ b/ground-control/robot-integration/connector-framework/usage/single-robot.md @@ -3,195 +3,5 @@ title: "Single-Robot Connector" description: "Guide for implementing a single-robot connector" --- -Subclass `inorbit_connector.connector.Connector` to create a connector for a single robot. - -## Constructor - -```python -def __init__(self, robot_id: str, config: ConnectorConfig, **kwargs) -> None -``` - -**Parameters:** -- `robot_id` (str): The InOrbit robot ID -- `config` (ConnectorConfig): The connector configuration - -**Keyword Arguments:** -- `register_user_scripts` (bool): Automatically register user scripts. Default: `False` -- `default_user_scripts_dir` (str): Default directory for user scripts. Default: `~/.inorbit_connectors/connector-{robot_id}/local/` -- `create_user_scripts_dir` (bool): Create the user scripts directory if it doesn't exist. Default: `False` -- `register_custom_command_handler` (bool): Automatically register the command handler. Default: `True` - -## Required Methods - -Subclasses must implement the following abstract methods: - -### `_connect()` - -Set up external services and connections. This is called once when the connector starts, before the execution loop begins. - -```python -@override -async def _connect(self) -> None: - """Connect to robot services.""" - # Initialize robot API client and/or other related services - # e.g. initialize a REST API client and start a polling loop - pass -``` - -### `_execution_loop()` - -The main execution loop that runs periodically. This is where you fetch robot data and publish it to InOrbit. - -Refer to the [publishing guide](/ground-control/robot-integration/connector-framework/publishing) for more details on publishing data to InOrbit. - -With polling-based connectors, it is advisable to run polling loops concurrently with the execution loop to avoid long running `_execution_loop` calls. See the [robot-connector](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/robot-connector/connector.py) and [fleet-connector](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/simple-fleet-connector/connector.py) examples for more details. - -```python -@override -async def _execution_loop(self) -> None: - """Main execution loop.""" - # Fetch robot pose - pose = await self._get_robot_pose() - self.publish_pose(pose.x, pose.y, pose.yaw, pose.frame_id) - - # Fetch and publish telemetry - telemetry = await self._get_robot_telemetry() - self.publish_key_values(**telemetry) -``` - -The loop runs at the frequency specified by `config.update_freq` (default: 1.0 Hz). - -### `_disconnect()` - -Clean up resources and disconnect from external services. This is called when the connector stops. - -```python -@override -async def _disconnect(self) -> None: - """Disconnect from robot services.""" - # Close robot API connections - # Clean up resources - pass -``` - -### `_inorbit_command_handler()` - -:::{hint} -See the [Commands Handling](/ground-control/robot-integration/connector-framework/usage/commands-handling) chapter for more details. -::: - -Handle commands received from InOrbit. This method is automatically registered if `register_custom_command_handler` is True (default). - -```python -from inorbit_connector.connector import CommandResultCode, CommandFailure - -@override -async def _inorbit_command_handler( - self, command_name: str, args: list, options: dict -) -> None: - """Handle InOrbit commands.""" - if command_name == "start_mission": - result = await self._robot.start_mission(args[0]) - if result: - options["result_function"](/ground-control/robot-integration/connector-framework/usage/CommandResultCode.SUCCESS) - else: - raise CommandFailure( - execution_status_details="Mission start failed", - stderr="Robot returned error code" - ) -``` - -Exceptions raised in the command handler are automatically caught and reported. Use `CommandFailure` to provide specific error details that will be displayed in InOrbit's audit logs and action execution details. - -## Lifecycle Methods - -### `start()` - -Starts the connector in a background thread. Creates an async event loop and begins the connection process. - -```python -connector = MyConnector(robot_id, config) -connector.start() -``` - -### `join()` - -Blocks until the connector thread finishes. Use this to keep your main thread alive. - -```python -connector.join() -``` - -### `stop()` - -Signals the connector to stop and waits for shutdown. This calls `_disconnect()` and cleans up resources. - -```python -connector.stop() -``` - -## Publishing Methods - -See the [Publishing Guide](/ground-control/robot-integration/connector-framework/publishing) for detailed information on publishing methods. - -## Advanced Methods - -### `_get_session()` - -Access the underlying `RobotSession` from the InOrbit Edge SDK for advanced use cases not covered by the connector API. - -```python -def _get_session(self) -> RobotSession: - """Get the edge-sdk robot session for the current robot.""" -``` - -**Example:** -```python -async def _execution_loop(self) -> None: - # Access the session directly for advanced features - session = self._get_session() - # Use Edge SDK methods directly - ... -``` - -### `_is_robot_online()` - -Override this method to provide custom robot health checks. The default implementation assumes the robot is online if the connector is running. - -```python -def _is_robot_online(self) -> bool: - """Check if the robot is online. - - Returns: - bool: True if robot is online, False otherwise. - """ -``` - -**Example:** -```python -@override -def _is_robot_online(self) -> bool: - """Check robot connectivity via API.""" - try: - return self._robot_api.is_connected() - except Exception: - return False -``` - - -## User Scripts - -User scripts allow executing custom shell scripts from InOrbit. To enable: - -1. Set `user_scripts_dir` in your configuration -2. Pass `register_user_scripts=True` to the constructor -3. Place `.sh` scripts in the user scripts directory - -Scripts are automatically registered and can be executed from InOrbit. - -## Examples - -- **Simple connector**: [examples/simple-connector/connector.py](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/simple-connector/connector.py) -- **Robot connector (CLI)**: [examples/robot-connector/](https://github.com/inorbit-ai/inorbit-connector-python/tree/main/examples/robot-connector) -- **Examples index**: [examples/README.md](https://github.com/inorbit-ai/inorbit-connector-python/blob/main/examples/README.md) +Content synced from [inorbit-connector-python](https://github.com/inorbit-ai/inorbit-connector-python). From 33accf9d1e70b3772be6b6471a5d2ca4db912e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Badenes?= Date: Mon, 22 Dec 2025 11:21:22 -0300 Subject: [PATCH 7/8] Revert "Test trigger" This reverts commit e6e8657575250ab3aaa6cd7d45f2e97b340d4082. --- .github/workflows/multirepo-connector-framework.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/multirepo-connector-framework.yml b/.github/workflows/multirepo-connector-framework.yml index 9587b22..10ceafb 100644 --- a/.github/workflows/multirepo-connector-framework.yml +++ b/.github/workflows/multirepo-connector-framework.yml @@ -3,8 +3,6 @@ name: Sync Connector Framework Docs on: # Manual trigger workflow_dispatch: - # Test trigger - push: # Nightly sync schedule: - cron: "17 3 * * *" From c311f7ad1807ffe8722fa6d7db033578300fb88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Badenes?= Date: Mon, 22 Dec 2025 11:21:25 -0300 Subject: [PATCH 8/8] Revert "Use a temporary branch" This reverts commit 1d04f04e30c21712b0bf737fef81ca0d7001bb61. --- .github/workflows/multirepo-connector-framework.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/multirepo-connector-framework.yml b/.github/workflows/multirepo-connector-framework.yml index 10ceafb..f9f2f86 100644 --- a/.github/workflows/multirepo-connector-framework.yml +++ b/.github/workflows/multirepo-connector-framework.yml @@ -23,8 +23,7 @@ jobs: uses: actions/checkout@v4 with: repository: inorbit-ai/inorbit-connector-python - # ref: main - ref: b-Tomas/mintlify-docs + ref: main path: connector-repo - name: Set up Python