robotrt-sdk 0.1.0b1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. robotrt_sdk-0.1.0b1/PKG-INFO +112 -0
  2. robotrt_sdk-0.1.0b1/README.md +99 -0
  3. robotrt_sdk-0.1.0b1/pyproject.toml +25 -0
  4. robotrt_sdk-0.1.0b1/robotrt_sdk/__init__.py +67 -0
  5. robotrt_sdk-0.1.0b1/robotrt_sdk/_ffi.py +20 -0
  6. robotrt_sdk-0.1.0b1/robotrt_sdk/_ffi_loader.py +82 -0
  7. robotrt_sdk-0.1.0b1/robotrt_sdk/_ffi_signatures.py +359 -0
  8. robotrt_sdk-0.1.0b1/robotrt_sdk/_ffi_types.py +57 -0
  9. robotrt_sdk-0.1.0b1/robotrt_sdk/_version.py +1 -0
  10. robotrt_sdk-0.1.0b1/robotrt_sdk/action.py +250 -0
  11. robotrt_sdk-0.1.0b1/robotrt_sdk/buffer.py +116 -0
  12. robotrt_sdk-0.1.0b1/robotrt_sdk/core.py +45 -0
  13. robotrt_sdk-0.1.0b1/robotrt_sdk/errors.py +20 -0
  14. robotrt_sdk-0.1.0b1/robotrt_sdk/executor.py +610 -0
  15. robotrt_sdk-0.1.0b1/robotrt_sdk/mission.py +102 -0
  16. robotrt_sdk-0.1.0b1/robotrt_sdk/models.py +39 -0
  17. robotrt_sdk-0.1.0b1/robotrt_sdk/node.py +206 -0
  18. robotrt_sdk-0.1.0b1/robotrt_sdk/pubsub.py +159 -0
  19. robotrt_sdk-0.1.0b1/robotrt_sdk/service.py +139 -0
  20. robotrt_sdk-0.1.0b1/robotrt_sdk/status.py +509 -0
  21. robotrt_sdk-0.1.0b1/robotrt_sdk/utils.py +36 -0
  22. robotrt_sdk-0.1.0b1/robotrt_sdk.egg-info/PKG-INFO +112 -0
  23. robotrt_sdk-0.1.0b1/robotrt_sdk.egg-info/SOURCES.txt +29 -0
  24. robotrt_sdk-0.1.0b1/robotrt_sdk.egg-info/dependency_links.txt +1 -0
  25. robotrt_sdk-0.1.0b1/robotrt_sdk.egg-info/top_level.txt +1 -0
  26. robotrt_sdk-0.1.0b1/setup.cfg +4 -0
  27. robotrt_sdk-0.1.0b1/tests/test_data_plane_batch.py +109 -0
  28. robotrt_sdk-0.1.0b1/tests/test_embedded_zero_copy.py +90 -0
  29. robotrt_sdk-0.1.0b1/tests/test_executor.py +166 -0
  30. robotrt_sdk-0.1.0b1/tests/test_full_comms.py +136 -0
  31. robotrt_sdk-0.1.0b1/tests/test_status_mode_parity.py +123 -0
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: robotrt-sdk
3
+ Version: 0.1.0b1
4
+ Summary: RobotRT Python SDK (FFI thin wrapper)
5
+ Author: RobotRT Maintainers
6
+ License-Expression: Apache-2.0
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+
14
+ # RobotRT Python SDK (Embedded + Daemon)
15
+
16
+ This directory contains the Python-first SDK implementation on top of the frozen Core FFI ABI.
17
+
18
+ ## Scope
19
+
20
+ - Embedded mode SDK surface for node and buffer lease lifecycle.
21
+ - Embedded mode full communication APIs: pub/sub, service, action, mission.
22
+ - Embedded mode executor APIs: SimpleExecutor, CallbackExecutor, and MultiThreadExecutor.
23
+ - Daemon mode status SDK surface for snapshot/list/info/watch query.
24
+ - Unified error mapping from C ABI status and error payload.
25
+ - Zero-copy readable view via borrowed lease pointer to Python `memoryview`.
26
+
27
+ ## Embedded Communication API (MVP)
28
+
29
+ ```python
30
+ import robotrt_sdk as sdk
31
+
32
+ with sdk.Node.create() as node:
33
+ pub = node.create_publisher("/demo/topic")
34
+ sub = node.create_subscriber("/demo/topic")
35
+ pub.publish_batch([b"hello", b"robotrt", b"batch"])
36
+ print(sub.recv_batch(8))
37
+ print("backpressure:", sub.backpressure_signal().value)
38
+
39
+ server = node.create_service_server("/demo/service")
40
+ client = node.create_service_client("/demo/service")
41
+ request_id = client.call(b"ping")
42
+ rid, req = server.recv_request()
43
+ server.reply(rid, b"pong")
44
+ print(client.poll_response(request_id))
45
+
46
+ action_server = node.create_action_server("/demo/action")
47
+ action_client = node.create_action_client("/demo/action")
48
+ goal_id, accepted = action_client.send_goal(b"goal")
49
+ gid, goal = action_server.recv_goal()
50
+ action_server.accept_goal(gid)
51
+ action_server.publish_feedback(gid, b"50%")
52
+ action_server.succeed(gid, b"done")
53
+ print(action_client.poll_result(goal_id))
54
+
55
+ mission = node.create_mission_session(42)
56
+ mission.open("demo_mission")
57
+ mission.send_command("start:demo")
58
+ mission.reconcile("cp-1", 2)
59
+ ```
60
+
61
+ ## Quick Start
62
+
63
+ 1. Build the shared library:
64
+
65
+ ```bash
66
+ cargo build -p core-ffi --lib
67
+ ```
68
+
69
+ 2. Run tests:
70
+
71
+ ```bash
72
+ PYTHONPATH=bindings/python python3 -m unittest discover -s bindings/python/tests -p 'test_*.py'
73
+ ```
74
+
75
+ 3. Run the embedded zero-copy demo:
76
+
77
+ ```bash
78
+ PYTHONPATH=bindings/python python3 bindings/python/examples/embedded_zero_copy_demo.py
79
+ ```
80
+
81
+ 4. Run the daemon status demo:
82
+
83
+ ```bash
84
+ PYTHONPATH=bindings/python python3 bindings/python/examples/daemon_status_demo.py
85
+ ```
86
+
87
+ 5. Run additional examples:
88
+
89
+ ```bash
90
+ PYTHONPATH=bindings/python python3 bindings/python/examples/full_comms_demo.py
91
+ PYTHONPATH=bindings/python python3 bindings/python/examples/executor_demo.py
92
+ PYTHONPATH=bindings/python python3 bindings/python/examples/status_watch_demo.py
93
+ ```
94
+
95
+ ## Mode Switch API
96
+
97
+ ```python
98
+ import robotrt_sdk as sdk
99
+
100
+ # Embedded mode: read local status report.
101
+ embedded = sdk.StatusClient(mode="embedded", report_path="artifacts/introspection/local-loop-status.json")
102
+ print(embedded.node_list())
103
+
104
+ # Daemon mode: query robotrt-host status service.
105
+ daemon = sdk.StatusClient(mode="daemon", endpoint="127.0.0.1:7588", timeout_ms=1000)
106
+ print(daemon.service_list())
107
+ ```
108
+
109
+ ## Environment Variables
110
+
111
+ - `ROBOTRT_CORE_FFI_LIB`: optional absolute path to the `core-ffi` dynamic library.
112
+ - If not set, SDK auto-discovers under `target/debug` and `target/debug/deps`.
@@ -0,0 +1,99 @@
1
+ # RobotRT Python SDK (Embedded + Daemon)
2
+
3
+ This directory contains the Python-first SDK implementation on top of the frozen Core FFI ABI.
4
+
5
+ ## Scope
6
+
7
+ - Embedded mode SDK surface for node and buffer lease lifecycle.
8
+ - Embedded mode full communication APIs: pub/sub, service, action, mission.
9
+ - Embedded mode executor APIs: SimpleExecutor, CallbackExecutor, and MultiThreadExecutor.
10
+ - Daemon mode status SDK surface for snapshot/list/info/watch query.
11
+ - Unified error mapping from C ABI status and error payload.
12
+ - Zero-copy readable view via borrowed lease pointer to Python `memoryview`.
13
+
14
+ ## Embedded Communication API (MVP)
15
+
16
+ ```python
17
+ import robotrt_sdk as sdk
18
+
19
+ with sdk.Node.create() as node:
20
+ pub = node.create_publisher("/demo/topic")
21
+ sub = node.create_subscriber("/demo/topic")
22
+ pub.publish_batch([b"hello", b"robotrt", b"batch"])
23
+ print(sub.recv_batch(8))
24
+ print("backpressure:", sub.backpressure_signal().value)
25
+
26
+ server = node.create_service_server("/demo/service")
27
+ client = node.create_service_client("/demo/service")
28
+ request_id = client.call(b"ping")
29
+ rid, req = server.recv_request()
30
+ server.reply(rid, b"pong")
31
+ print(client.poll_response(request_id))
32
+
33
+ action_server = node.create_action_server("/demo/action")
34
+ action_client = node.create_action_client("/demo/action")
35
+ goal_id, accepted = action_client.send_goal(b"goal")
36
+ gid, goal = action_server.recv_goal()
37
+ action_server.accept_goal(gid)
38
+ action_server.publish_feedback(gid, b"50%")
39
+ action_server.succeed(gid, b"done")
40
+ print(action_client.poll_result(goal_id))
41
+
42
+ mission = node.create_mission_session(42)
43
+ mission.open("demo_mission")
44
+ mission.send_command("start:demo")
45
+ mission.reconcile("cp-1", 2)
46
+ ```
47
+
48
+ ## Quick Start
49
+
50
+ 1. Build the shared library:
51
+
52
+ ```bash
53
+ cargo build -p core-ffi --lib
54
+ ```
55
+
56
+ 2. Run tests:
57
+
58
+ ```bash
59
+ PYTHONPATH=bindings/python python3 -m unittest discover -s bindings/python/tests -p 'test_*.py'
60
+ ```
61
+
62
+ 3. Run the embedded zero-copy demo:
63
+
64
+ ```bash
65
+ PYTHONPATH=bindings/python python3 bindings/python/examples/embedded_zero_copy_demo.py
66
+ ```
67
+
68
+ 4. Run the daemon status demo:
69
+
70
+ ```bash
71
+ PYTHONPATH=bindings/python python3 bindings/python/examples/daemon_status_demo.py
72
+ ```
73
+
74
+ 5. Run additional examples:
75
+
76
+ ```bash
77
+ PYTHONPATH=bindings/python python3 bindings/python/examples/full_comms_demo.py
78
+ PYTHONPATH=bindings/python python3 bindings/python/examples/executor_demo.py
79
+ PYTHONPATH=bindings/python python3 bindings/python/examples/status_watch_demo.py
80
+ ```
81
+
82
+ ## Mode Switch API
83
+
84
+ ```python
85
+ import robotrt_sdk as sdk
86
+
87
+ # Embedded mode: read local status report.
88
+ embedded = sdk.StatusClient(mode="embedded", report_path="artifacts/introspection/local-loop-status.json")
89
+ print(embedded.node_list())
90
+
91
+ # Daemon mode: query robotrt-host status service.
92
+ daemon = sdk.StatusClient(mode="daemon", endpoint="127.0.0.1:7588", timeout_ms=1000)
93
+ print(daemon.service_list())
94
+ ```
95
+
96
+ ## Environment Variables
97
+
98
+ - `ROBOTRT_CORE_FFI_LIB`: optional absolute path to the `core-ffi` dynamic library.
99
+ - If not set, SDK auto-discovers under `target/debug` and `target/debug/deps`.
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "robotrt-sdk"
7
+ version = "0.1.0b1"
8
+ description = "RobotRT Python SDK (FFI thin wrapper)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ authors = [{ name = "RobotRT Maintainers" }]
12
+ license = "Apache-2.0"
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+
20
+ [tool.setuptools]
21
+ include-package-data = true
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["."]
25
+ include = ["robotrt_sdk", "robotrt_sdk.*"]
@@ -0,0 +1,67 @@
1
+ from ._ffi import _CORE
2
+ from ._version import __version__
3
+ from .core import (
4
+ ActionClient,
5
+ ActionGoalHealth,
6
+ ActionServer,
7
+ BackpressureSignal,
8
+ CallbackGroupType,
9
+ BufferDescriptor,
10
+ BufferLease,
11
+ ExternalBufferRef,
12
+ LeaseView,
13
+ MissionSession,
14
+ Node,
15
+ Publisher,
16
+ CallbackExecutor,
17
+ MultiThreadExecutor,
18
+ MultiThreadExecutorStats,
19
+ RegisteredWork,
20
+ RegisteredWorkKind,
21
+ ServiceClient,
22
+ ServiceServer,
23
+ SimpleExecutor,
24
+ Subscriber,
25
+ TimerRegistration,
26
+ )
27
+ from .errors import ErrorInfo, RobotRtError
28
+ from .status import StatusClient
29
+
30
+ __all__ = [
31
+ "BufferDescriptor",
32
+ "BufferLease",
33
+ "ErrorInfo",
34
+ "LeaseView",
35
+ "Node",
36
+ "Publisher",
37
+ "Subscriber",
38
+ "ServiceClient",
39
+ "ServiceServer",
40
+ "ActionClient",
41
+ "ActionGoalHealth",
42
+ "ActionServer",
43
+ "BackpressureSignal",
44
+ "CallbackGroupType",
45
+ "CallbackExecutor",
46
+ "ExternalBufferRef",
47
+ "MissionSession",
48
+ "MultiThreadExecutor",
49
+ "MultiThreadExecutorStats",
50
+ "RobotRtError",
51
+ "RegisteredWork",
52
+ "RegisteredWorkKind",
53
+ "StatusClient",
54
+ "SimpleExecutor",
55
+ "TimerRegistration",
56
+ "__version__",
57
+ "abi_version",
58
+ "protocol_version",
59
+ ]
60
+
61
+
62
+ def abi_version() -> int:
63
+ return int(_CORE.lib.robotrt_core_abi_version())
64
+
65
+
66
+ def protocol_version() -> int:
67
+ return int(_CORE.lib.robotrt_core_protocol_version())
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from ._ffi_loader import CoreFfi, _CORE
4
+ from ._ffi_types import (
5
+ RT_OK,
6
+ RtActionGoalHealth,
7
+ RtBufferDescriptor,
8
+ RtErrorInfo,
9
+ RtExternalBufferRef,
10
+ )
11
+
12
+ __all__ = [
13
+ "RT_OK",
14
+ "CoreFfi",
15
+ "RtActionGoalHealth",
16
+ "RtBufferDescriptor",
17
+ "RtErrorInfo",
18
+ "RtExternalBufferRef",
19
+ "_CORE",
20
+ ]
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+
3
+ import ctypes
4
+ import os
5
+ import platform
6
+ from pathlib import Path
7
+
8
+ from ._ffi_signatures import declare_signatures
9
+ from ._ffi_types import RT_OK, RtErrorInfo
10
+ from .errors import ErrorInfo, RobotRtError
11
+
12
+
13
+ def _workspace_root() -> Path:
14
+ return Path(__file__).resolve().parents[3]
15
+
16
+
17
+ def _dylib_name() -> str:
18
+ name = platform.system().lower()
19
+ if name == "darwin":
20
+ return "librobotrt_core_ffi.dylib"
21
+ if name == "windows":
22
+ return "robotrt_core_ffi.dll"
23
+ return "librobotrt_core_ffi.so"
24
+
25
+
26
+ def _resolve_library_path() -> Path:
27
+ env = os.environ.get("ROBOTRT_CORE_FFI_LIB")
28
+ if env:
29
+ p = Path(env).expanduser().resolve()
30
+ if p.exists():
31
+ return p
32
+ raise FileNotFoundError(f"ROBOTRT_CORE_FFI_LIB does not exist: {p}")
33
+
34
+ root = _workspace_root()
35
+ direct = root / "target" / "debug" / _dylib_name()
36
+ if direct.exists():
37
+ return direct
38
+
39
+ deps = root / "target" / "debug" / "deps"
40
+ if deps.exists():
41
+ suffixes = {".dylib", ".so", ".dll"}
42
+ for candidate in sorted(deps.iterdir()):
43
+ if candidate.suffix not in suffixes:
44
+ continue
45
+ if candidate.name.startswith("librobotrt_core_ffi") or candidate.name == _dylib_name():
46
+ return candidate
47
+
48
+ raise FileNotFoundError("robotrt core ffi dynamic library not found; run cargo build -p core-ffi --lib")
49
+
50
+
51
+ class CoreFfi:
52
+ def __init__(self) -> None:
53
+ self.library_path = _resolve_library_path()
54
+ self.lib = ctypes.CDLL(str(self.library_path))
55
+ declare_signatures(self.lib)
56
+
57
+ def last_error(self) -> ErrorInfo:
58
+ info = RtErrorInfo()
59
+ self.lib.robotrt_error_last(ctypes.byref(info))
60
+
61
+ needed = ctypes.c_uint32(0)
62
+ self.lib.robotrt_error_message_copy(None, 0, ctypes.byref(needed))
63
+ message = ""
64
+ if needed.value > 0:
65
+ buf = ctypes.create_string_buffer(needed.value)
66
+ self.lib.robotrt_error_message_copy(buf, needed.value, ctypes.byref(needed))
67
+ message = buf.value.decode("utf-8", errors="replace")
68
+
69
+ return ErrorInfo(
70
+ code=int(info.code),
71
+ domain=int(info.domain),
72
+ retryable=bool(info.retryable),
73
+ message=message,
74
+ )
75
+
76
+ def check(self, status: int, context: str) -> None:
77
+ if status == RT_OK:
78
+ return
79
+ raise RobotRtError(self.last_error(), context)
80
+
81
+
82
+ _CORE = CoreFfi()