Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8f552e4
checkpoint towards faster more robust kinematics
Jepson2k Oct 1, 2025
1d4cc9f
buttery smooth moves
Jepson2k Oct 2, 2025
e1541e8
Add robotics-toolbox-python as a proper submodule
Jepson2k Oct 2, 2025
5441189
towards faster kinematics
Jepson2k Oct 14, 2025
9c26697
remove submodule and put prebuilt binary urls in pyproject
Jepson2k Oct 15, 2025
a65f967
some more optimizations
Jepson2k Oct 15, 2025
4c0c560
using modified urdf for kinematics
Jepson2k Oct 19, 2025
73b022b
swappable tools
Jepson2k Oct 19, 2025
3442468
forgot some files
Jepson2k Oct 19, 2025
8c2fe93
run tests on push
Jepson2k Oct 19, 2025
5756b23
add action tracking
Jepson2k Nov 10, 2025
0890d5b
mypy type fixes and ruff formatting fixes
Jepson2k Nov 11, 2025
3fba08e
added context management
Jepson2k Nov 14, 2025
ad45a15
added mypy to pre-commit
Jepson2k Nov 14, 2025
b6bef07
make host and port immutable
Jepson2k Nov 14, 2025
eef3f52
fix x86_64 linux urls
Jepson2k Nov 14, 2025
1caa458
making more test-able
Jepson2k Nov 19, 2025
d36c1cd
fallback to unicast when multicast unavailable
Jepson2k Nov 19, 2025
7fb8672
multicast fail to unicast potential fix
Jepson2k Nov 20, 2025
81886c5
increase timeout for slow ci runners
Jepson2k Nov 20, 2025
4142cc1
skip accuracy tests which perform flaky on macos ci runners
Jepson2k Nov 20, 2025
e088c2a
add lint check
Jepson2k Nov 20, 2025
5a59a33
ruff, mypy, and timeout fixes
Jepson2k Nov 20, 2025
ea9537e
ruff format
Jepson2k Nov 20, 2025
7cec2f1
potential python 3.10 timeout fix
Jepson2k Nov 20, 2025
0c1c7c7
compute joint and cart enablement and send via status
Jepson2k Nov 21, 2025
f951112
updated readme and examples
Jepson2k Nov 24, 2025
6cf0f41
ruff fixes
Jepson2k Nov 24, 2025
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
70 changes: 70 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: tests

on:
push:
paths:
- '**'
pull_request:
paths:
- '**'

jobs:
lint:
name: Lint (ruff + mypy)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: pip
cache-dependency-path: pyproject.toml
- name: Install dev dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: Ruff (lint)
run: ruff check parol6
- name: Ruff (format check)
run: ruff format --check parol6
- name: MyPy
run: mypy parol6
test:
name: ${{ matrix.os }} / Python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.10', '3.11']

steps:
- name: Checkout repository (with submodules)
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: pyproject.toml

- name: Upgrade pip and install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]" pytest-timeout

- name: Show environment
run: |
python -V
pip list

- name: Run tests (skip hardware)
env:
PYTHONUNBUFFERED: '1'
PYTHONUTF8: '1'
run: |
pytest
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,4 @@ cython_debug/
# VS Code
.vscode/

serial_port.txt
serial_port.txt
33 changes: 33 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
default_language_version:
python: python3.11

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-added-large-files

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff
args: ["--fix"]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
hooks:
- id: mypy
name: mypy (parol6)
files: ^parol6/
pass_filenames: false
args: ["parol6"]
stages: [commit, push]
additional_dependencies:
- numpy==1.26.4
- spatialmath-python==1.1.14
- scipy==1.11.4
1,124 changes: 226 additions & 898 deletions README.md

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions examples/async_client_quickstart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
Async client quickstart for PAROL6.
- Starts a local headless controller at 127.0.0.1:5001
- Enables simulator mode for safety
- Shows basic queries and status streaming

Run from the repository root:
python external/PAROL6-python-API/examples/async_client_quickstart.py
"""

import asyncio
from parol6 import AsyncRobotClient
from parol6.client.manager import managed_server

HOST = "127.0.0.1"
PORT = 5001


async def run_client() -> int:
async with AsyncRobotClient(host=HOST, port=PORT, timeout=2.0) as client:
ready = await client.wait_for_server_ready(timeout=5.0)
print(f"server ready: {ready}")
if not ready:
return 1

# Safety: enable simulator for this demo
ok = await client.simulator_on()
print(f"simulator_on: {ok}")

print("ping:", await client.ping())
pose_xyz = await client.get_pose_xyz()
print("pose xyz:", pose_xyz)

# Consume one status broadcast
print("one status frame speeds:")
async for status in client.status_stream():
print(status.get("speeds"))
break

# Small relative TRF move (safe in simulator)
# Move +5mm in Z over 1.0s
moved = await client.move_cartesian_rel_trf([0, 0, 5, 0, 0, 0], duration=1.0)
print("move_cartesian_rel_trf ->", moved)

return 0


def main() -> None:
# Auto-start and stop controller for this example
with managed_server(host=HOST, port=PORT, normalize_logs=True):
code = asyncio.run(run_client())
raise SystemExit(code)


if __name__ == "__main__":
main()
48 changes: 48 additions & 0 deletions examples/manage_server_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
Manage server lifecycle demonstration for PAROL6.
- Starts a local headless controller at 127.0.0.1:5001
- Waits until ready, then connects with RobotClient
- Toggles simulator ON for a safe demo motion, then OFF
- Stops the controller on exit

Run from the repository root:
python external/PAROL6-python-API/examples/manage_server_demo.py
"""

from parol6 import manage_server, RobotClient

HOST = "127.0.0.1"
PORT = 5001


def main() -> None:
mgr = manage_server(host=HOST, port=PORT, normalize_logs=True)
try:
with RobotClient(host=HOST, port=PORT, timeout=2.0) as client:
ready = client.wait_for_server_ready(timeout=5.0)
print(f"server ready: {ready}")
if not ready:
raise SystemExit(1)

print("ping:", client.ping())

# Enable simulator for a safe motion
sim_on = client.simulator_on()
print("simulator_on:", sim_on)

if sim_on:
# Small relative TRF move: +3mm in Z over 0.8s
moved = client.move_cartesian_rel_trf([0, 0, 3, 0, 0, 0], duration=0.8)
print("move_cartesian_rel_trf ->", moved)

# Demonstrate toggling simulator off again (no motion follows)
sim_off = client.simulator_off()
print("simulator_off:", sim_off)

raise SystemExit(0)
finally:
mgr.stop_controller()


if __name__ == "__main__":
main()
29 changes: 29 additions & 0 deletions examples/sync_client_quickstart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
Sync client quickstart for PAROL6.
- Assumes a controller is already running at 127.0.0.1:5001
- Does not enable simulator in this example
- Performs ping and basic queries

Run from the repository root:
python external/PAROL6-python-API/examples/sync_client_quickstart.py
"""

from parol6 import RobotClient

HOST = "127.0.0.1"
PORT = 5001


def main() -> None:
with RobotClient(host=HOST, port=PORT, timeout=2.0) as client:
ready = client.wait_for_server_ready(timeout=3.0)
print(f"server ready: {ready}")
print("ping:", client.ping())
print("pose xyz:", client.get_pose_xyz())
print("angles:", client.get_angles())
code = 0 if ready else 1
raise SystemExit(code)


if __name__ == "__main__":
main()
Loading