Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions inorbit_edge/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
from typing import Optional

# Third-party
from pydantic import BaseModel, AnyUrl, field_validator, HttpUrl
from pydantic import AnyUrl, BaseModel, HttpUrl, field_validator

# InOrbit
from inorbit_edge.robot import INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL, INORBIT_REST_API_URL
from inorbit_edge.robot import (
INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL,
INORBIT_DEFAULT_API_URL,
)


class CameraConfig(BaseModel):
Expand Down Expand Up @@ -85,10 +88,10 @@ class RobotSessionModel(BaseModel):

* INORBIT_API_KEY (required): The InOrbit API key
* INORBIT_USE_SSL: If SSL should be used (default is true)
* INORBIT_API_URL: The URL of the API (default is
inorbit_edge.robot.INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL)
* INORBIT_REST_API_URL: The URL of the InOrbit REST API (default is
inorbit_edge.robot.INORBIT_REST_API_URL)
* INORBIT_CONNECTION_CONFIG_URL: The URL of the MQTT configuration API
(default is inorbit_edge.robot.INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL)
* INORBIT_API_URL: The URL of the InOrbit REST API (default is
inorbit_edge.robot.INORBIT_DEFAULT_API_URL)

Attributes:
robot_id (str): The unique ID of the robot
Expand All @@ -99,7 +102,7 @@ class RobotSessionModel(BaseModel):
endpoint (HttpUrl, optional): The URL of the API or inorbit_edge's
INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL by default
rest_api_endpoint (HttpUrl, optional): The URL of the InOrbit REST API.
INORBIT_REST_API_URL by default.
INORBIT_DEFAULT_API_URL by default.
"""

robot_id: str
Expand All @@ -108,10 +111,10 @@ class RobotSessionModel(BaseModel):
api_key: Optional[str] = os.getenv("INORBIT_API_KEY")
use_ssl: bool = os.environ.get("INORBIT_USE_SSL", "true").lower() == "true"
endpoint: HttpUrl = os.environ.get(
"INORBIT_API_URL", INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL
"INORBIT_CONNECTION_CONFIG_URL", INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL
)
rest_api_endpoint: Optional[HttpUrl] = os.environ.get(
"INORBIT_REST_API_URL", INORBIT_REST_API_URL
"INORBIT_API_URL", INORBIT_DEFAULT_API_URL
)

# noinspection PyMethodParameters
Expand Down
103 changes: 51 additions & 52 deletions inorbit_edge/robot.py
Original file line number Diff line number Diff line change
@@ -1,76 +1,76 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
import zlib
from dataclasses import dataclass, field, asdict
import json
from typing import Tuple, Optional, List, Dict

from inorbit_edge import __version__ as inorbit_edge_version
from inorbit_edge.types import Pose, SpatialTolerance
from inorbit_edge.metrics import (
attrs_from_self,
with_counter_metric,
publish_map_counter,
publish_camera_frame_counter,
publish_pose_counter,
publish_key_values_counter,
publish_system_stats_counter,
publish_odometry_counter,
publish_laser_counter,
publish_path_counter,
)
import os
import logging
import paho.mqtt.client as mqtt
from PIL import Image
from urllib.parse import urlsplit
import socks
import math
import os
import re
import ssl
import subprocess
import threading
import time
import zlib
from dataclasses import asdict, dataclass, field
from typing import Dict, List, Optional, Tuple
from urllib.parse import urlsplit

import certifi
import paho.mqtt.client as mqtt
import requests
import socks
import yaml
from deprecated import deprecated
from PIL import Image

from inorbit_edge import __version__ as inorbit_edge_version
from inorbit_edge.commands import (
COMMAND_CUSTOM_COMMAND,
COMMAND_INITIAL_POSE,
COMMAND_MESSAGE,
COMMAND_NAV_GOAL,
)
from inorbit_edge.inorbit_pb2 import (
CameraMessage,
CustomCommandRosMessage,
CustomDataMessage,
CustomScriptCommandMessage,
CustomScriptStatusMessage,
Echo,
KeyValueCustomElement,
LaserMessage,
LocationAndPoseMessage,
MapMessage,
MapRequest,
OdometryDataMessage,
LaserMessage,
PathDataMessage,
PathPoint,
RobotPath,
PathDataMessage,
Echo,
CustomScriptCommandMessage,
CustomScriptStatusMessage,
CustomCommandRosMessage,
CameraMessage,
SystemStatsMessage,
MapMessage,
MapRequest,
)
from inorbit_edge.video import CameraStreamer, Camera
from inorbit_edge.missions import MissionsModule
from inorbit_edge.commands import (
COMMAND_INITIAL_POSE,
COMMAND_NAV_GOAL,
COMMAND_CUSTOM_COMMAND,
COMMAND_MESSAGE,
from inorbit_edge.metrics import (
attrs_from_self,
publish_camera_frame_counter,
publish_key_values_counter,
publish_laser_counter,
publish_map_counter,
publish_odometry_counter,
publish_path_counter,
publish_pose_counter,
publish_system_stats_counter,
with_counter_metric,
)
import time
import requests
import math
from inorbit_edge.missions import MissionsModule
from inorbit_edge.types import Pose, SpatialTolerance
from inorbit_edge.utils import (
calculate_pose_delta,
encode_floating_point_list,
reduce_path,
calculate_pose_delta,
)
import certifi
import subprocess
import re
from deprecated import deprecated
from inorbit_edge.video import Camera, CameraStreamer

INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL = "https://control.inorbit.ai/cloud_sdk_robot_config"
INORBIT_REST_API_URL = "https://api.inorbit.ai"
INORBIT_DEFAULT_API_URL = "https://api.inorbit.ai"

MQTT_SUBTOPIC_POSE = "ros/loc/data2"
MQTT_SUBTOPIC_PATH = "ros/loc/path"
Expand Down Expand Up @@ -299,7 +299,6 @@ def discard_next_delta(self):


class RobotSession:

def __init__(self, robot_id, robot_name, api_key=None, **kwargs) -> None:
"""Initialize a robot session.

Expand All @@ -315,7 +314,7 @@ def __init__(self, robot_id, robot_name, api_key=None, **kwargs) -> None:
endpoint (str): InOrbit URL. Defaults: INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL.
use_ssl (bool): Configures MQTT client to use SSL. Defaults: True.
rest_api_endpoint (str): The URL of the InOrbit REST API.
Defaults: INORBIT_REST_API_URL.
Defaults: INORBIT_DEFAULT_API_URL.
keepalive_secs (int): Keepalive for MQTT connection (seconds). Default: 10.
estimate_distance_linear (bool): Whether to publish an estimate value for
linear_distance based on poses when the value is not provided on a
Expand Down Expand Up @@ -348,7 +347,7 @@ def __init__(self, robot_id, robot_name, api_key=None, **kwargs) -> None:
self.use_ssl = kwargs.get("use_ssl", True)
# InOrbit REST API endpoint
self.inorbit_rest_api_endpoint = kwargs.get(
"rest_api_endpoint", INORBIT_REST_API_URL
"rest_api_endpoint", INORBIT_DEFAULT_API_URL
)
# Account ID, lazily fetched from REST API via get_account_id()
self._account_id: str | None = None
Expand Down Expand Up @@ -1669,7 +1668,7 @@ def get_account_id(self) -> str:
account_ids = response.json().get("accountIds") or []
if not isinstance(account_ids, list):
raise ValueError(
"Unexpected response from the REST API: " "accountIds is not a list"
"Unexpected response from the REST API: accountIds is not a list"
)
if len(account_ids) == 0:
raise ValueError("No account IDs found for the authenticated API key")
Expand Down
4 changes: 2 additions & 2 deletions inorbit_edge/tests/demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ cd /path/to/edge-sdk-python
pip install -e '.[video,telemetry]'
cd inorbit_edge/tests/demo

export INORBIT_URL="https://control.inorbit.ai"
export INORBIT_CONNECTION_CONFIG_URL="https://control.inorbit.ai"
export INORBIT_API_URL="https://api.inorbit.ai"
export INORBIT_API_KEY="foobar123"
export INORBIT_ROBOT_ID_PREFIX="$(hostname)"
Expand Down Expand Up @@ -51,7 +51,7 @@ metrics on the host:
```bash
docker run --rm -p 9464:9464 \
-v "$PWD/inorbit_edge/tests/demo:/demo:ro" \
-e INORBIT_URL=... \
-e INORBIT_CONNECTION_CONFIG_URL=... \
-e INORBIT_API_URL=... \
-e INORBIT_API_KEY=... \
-e INORBIT_ROBOT_ID_PREFIX=$(hostname) \
Expand Down
2 changes: 1 addition & 1 deletion inorbit_edge/tests/demo/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def main():
radius=0.2,
)

inorbit_api_endpoint = _required_env("INORBIT_URL")
inorbit_api_endpoint = _required_env("INORBIT_CONNECTION_CONFIG_URL")
inorbit_api_url = _required_env("INORBIT_API_URL")
inorbit_api_key = _required_env("INORBIT_API_KEY")

Expand Down
28 changes: 18 additions & 10 deletions inorbit_edge/tests/test_robot_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
import math
import os
from unittest.mock import MagicMock

import pytest
from requests import HTTPError

from inorbit_edge.robot import RobotSession, RobotFootprintSpec, RobotMap
from inorbit_edge.robot import INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL, INORBIT_REST_API_URL
from inorbit_edge import get_module_version
from inorbit_edge.inorbit_pb2 import MapMessage, RobotPath, PathDataMessage, PathPoint
from inorbit_edge.inorbit_pb2 import MapMessage, PathDataMessage, PathPoint, RobotPath
from inorbit_edge.robot import (
INORBIT_CLOUD_SDK_ROBOT_CONFIG_URL,
INORBIT_DEFAULT_API_URL,
RobotFootprintSpec,
RobotMap,
RobotSession,
)


def test_robot_session_init(monkeypatch, mock_sleep):
Expand Down Expand Up @@ -125,11 +131,11 @@ def test_method_throttling(mock_sleep):

def test_apply_footprint(requests_mock, mock_sleep):
requests_mock.get(
f"{INORBIT_REST_API_URL}/user",
f"{INORBIT_DEFAULT_API_URL}/user",
json={"userId": "user_abc", "name": "Test User", "accountIds": ["account_123"]},
)
adapter = requests_mock.post(
f"{INORBIT_REST_API_URL}/configuration/apply",
f"{INORBIT_DEFAULT_API_URL}/configuration/apply",
json={"operationStatus": "SUCCESS"},
)
footprint = RobotFootprintSpec(
Expand Down Expand Up @@ -168,14 +174,16 @@ def test_apply_footprint(requests_mock, mock_sleep):
}

# HTTP error on apply
requests_mock.post(f"{INORBIT_REST_API_URL}/configuration/apply", status_code=400)
requests_mock.post(
f"{INORBIT_DEFAULT_API_URL}/configuration/apply", status_code=400
)
with pytest.raises(HTTPError):
robot_session.apply_footprint(footprint)


def test_get_account_id(requests_mock, mock_sleep):
requests_mock.get(
f"{INORBIT_REST_API_URL}/user",
f"{INORBIT_DEFAULT_API_URL}/user",
json={"userId": "user_abc", "name": "Test User", "accountIds": ["account_123"]},
)
robot_session = RobotSession(
Expand All @@ -202,7 +210,7 @@ def test_get_account_id_no_api_key(mock_sleep):

def test_get_account_id_empty_accounts(requests_mock, mock_sleep):
requests_mock.get(
f"{INORBIT_REST_API_URL}/user",
f"{INORBIT_DEFAULT_API_URL}/user",
json={"userId": "user_abc", "name": "Test User", "accountIds": []},
)
robot_session = RobotSession(
Expand All @@ -216,7 +224,7 @@ def test_get_account_id_empty_accounts(requests_mock, mock_sleep):

def test_get_account_id_multiple_accounts(requests_mock, mock_sleep):
requests_mock.get(
f"{INORBIT_REST_API_URL}/user",
f"{INORBIT_DEFAULT_API_URL}/user",
json={"userId": "user_abc", "name": "Test", "accountIds": ["a1", "a2"]},
)
robot_session = RobotSession(
Expand All @@ -229,7 +237,7 @@ def test_get_account_id_multiple_accounts(requests_mock, mock_sleep):


def test_get_account_id_api_error(requests_mock, mock_sleep):
requests_mock.get(f"{INORBIT_REST_API_URL}/user", status_code=401)
requests_mock.get(f"{INORBIT_DEFAULT_API_URL}/user", status_code=401)
robot_session = RobotSession(
robot_id="id_123",
robot_name="name_123",
Expand Down
Loading