caasi 0.1.0__py3-none-any.whl

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 (71) hide show
  1. caasi/__init__.py +7 -0
  2. caasi/checks/__init__.py +96 -0
  3. caasi/checks/containers.py +35 -0
  4. caasi/checks/graphics.py +50 -0
  5. caasi/checks/hardware.py +47 -0
  6. caasi/checks/isaac.py +99 -0
  7. caasi/checks/ml.py +42 -0
  8. caasi/checks/nvidia.py +70 -0
  9. caasi/checks/python_env.py +51 -0
  10. caasi/checks/robotics.py +54 -0
  11. caasi/checks/ros.py +63 -0
  12. caasi/checks/simulators.py +41 -0
  13. caasi/checks/storage.py +39 -0
  14. caasi/checks/system.py +40 -0
  15. caasi/checks/vision.py +41 -0
  16. caasi/cli/__init__.py +1 -0
  17. caasi/cli/benchmark_cmd.py +111 -0
  18. caasi/cli/config_cmd.py +126 -0
  19. caasi/cli/container_cmd.py +107 -0
  20. caasi/cli/control_cmd.py +132 -0
  21. caasi/cli/dataset_cmd.py +195 -0
  22. caasi/cli/definitions.py +114 -0
  23. caasi/cli/doctor.py +98 -0
  24. caasi/cli/gpu.py +165 -0
  25. caasi/cli/init_cmd.py +37 -0
  26. caasi/cli/lab.py +47 -0
  27. caasi/cli/main.py +126 -0
  28. caasi/cli/misc.py +120 -0
  29. caasi/cli/moveit_cmd.py +151 -0
  30. caasi/cli/native_cmd.py +119 -0
  31. caasi/cli/nav_cmd.py +177 -0
  32. caasi/cli/project_cmd.py +67 -0
  33. caasi/cli/remote_cmd.py +114 -0
  34. caasi/cli/replay_cmd.py +75 -0
  35. caasi/cli/ros_cmd.py +217 -0
  36. caasi/cli/run_cmd.py +207 -0
  37. caasi/cli/sensor_cmd.py +105 -0
  38. caasi/cli/setup_cmd.py +103 -0
  39. caasi/cli/shell_cmd.py +53 -0
  40. caasi/cli/sim.py +167 -0
  41. caasi/cli/system.py +148 -0
  42. caasi/cli/train_cmd.py +94 -0
  43. caasi/cli/view_cmd.py +93 -0
  44. caasi/cli/vision_cmd.py +121 -0
  45. caasi/core/__init__.py +1 -0
  46. caasi/core/benchmark.py +46 -0
  47. caasi/core/config.py +240 -0
  48. caasi/core/containers.py +56 -0
  49. caasi/core/dataset.py +196 -0
  50. caasi/core/experiment.py +178 -0
  51. caasi/core/nvidia.py +133 -0
  52. caasi/core/project.py +187 -0
  53. caasi/core/remotes.py +89 -0
  54. caasi/core/ros.py +89 -0
  55. caasi/core/runs.py +369 -0
  56. caasi/core/sensors.py +130 -0
  57. caasi/core/viewers.py +99 -0
  58. caasi/core/vision.py +58 -0
  59. caasi/i18n/__init__.py +78 -0
  60. caasi/i18n/en.py +678 -0
  61. caasi/state.py +69 -0
  62. caasi/utils/__init__.py +1 -0
  63. caasi/utils/output.py +64 -0
  64. caasi/utils/pydist.py +18 -0
  65. caasi/utils/shell.py +57 -0
  66. caasi/utils/sysinfo.py +120 -0
  67. caasi-0.1.0.dist-info/METADATA +178 -0
  68. caasi-0.1.0.dist-info/RECORD +71 -0
  69. caasi-0.1.0.dist-info/WHEEL +4 -0
  70. caasi-0.1.0.dist-info/entry_points.txt +2 -0
  71. caasi-0.1.0.dist-info/licenses/LICENSE +21 -0
caasi/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Caasi — a lightweight, CLI-first orchestration layer for the robotics ecosystem.
2
+
3
+ Headless-first, CLI-first, process-oriented, and visualization-independent.
4
+ Independent open-source project; not affiliated with or endorsed by NVIDIA.
5
+ """
6
+
7
+ __version__ = "0.1.0"
@@ -0,0 +1,96 @@
1
+ """Doctor check engine.
2
+
3
+ Checks are grouped into sections. Each check module registers callables that
4
+ receive a :class:`CheckContext` and return :class:`CheckResult` objects. All
5
+ external probes are subprocess/metadata based — the CLI never imports heavy
6
+ robotics libraries.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import asdict, dataclass
12
+ from typing import TYPE_CHECKING, Any, Callable, Literal
13
+
14
+ if TYPE_CHECKING: # pragma: no cover
15
+ from ..core.config import Config
16
+
17
+ Status = Literal["ok", "warn", "fail", "skip"]
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class CheckResult:
22
+ section: str
23
+ name: str
24
+ status: Status
25
+ detail: str = ""
26
+ hint: str = ""
27
+
28
+ def to_dict(self) -> dict[str, Any]:
29
+ return asdict(self)
30
+
31
+
32
+ @dataclass
33
+ class CheckContext:
34
+ config: "Config"
35
+
36
+
37
+ CheckFn = Callable[[CheckContext], list[CheckResult]]
38
+
39
+ # Ordered section keys; titles are resolved through i18n at render time.
40
+ SECTION_KEYS: list[str] = [
41
+ "system",
42
+ "hardware",
43
+ "nvidia",
44
+ "graphics",
45
+ "python",
46
+ "isaac",
47
+ "ros",
48
+ "robotics",
49
+ "ml",
50
+ "vision",
51
+ "simulators",
52
+ "containers",
53
+ "storage",
54
+ ]
55
+
56
+ _REGISTRY: dict[str, list[CheckFn]] = {}
57
+
58
+
59
+ def register(section: str) -> Callable[[CheckFn], CheckFn]:
60
+ def decorator(fn: CheckFn) -> CheckFn:
61
+ _REGISTRY.setdefault(section, []).append(fn)
62
+ return fn
63
+
64
+ return decorator
65
+
66
+
67
+ def run_checks(
68
+ sections: list[str] | None = None, config: "Config | None" = None
69
+ ) -> list[CheckResult]:
70
+ import_all_check_modules()
71
+ from ..state import cfg as state_cfg
72
+
73
+ ctx = CheckContext(config=config or state_cfg())
74
+ results: list[CheckResult] = []
75
+ for key in sections or SECTION_KEYS:
76
+ for fn in _REGISTRY.get(key, []):
77
+ results.extend(fn(ctx))
78
+ return results
79
+
80
+
81
+ def import_all_check_modules() -> None:
82
+ from . import ( # noqa: F401
83
+ containers,
84
+ graphics,
85
+ hardware,
86
+ isaac,
87
+ ml,
88
+ nvidia,
89
+ python_env,
90
+ robotics,
91
+ ros,
92
+ simulators,
93
+ storage,
94
+ system,
95
+ vision,
96
+ )
@@ -0,0 +1,35 @@
1
+ """Container runtime checks: docker, podman."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..i18n import _
6
+ from ..utils import shell
7
+ from . import CheckResult, register
8
+
9
+
10
+ def _probe(name: str, binary: str, name_key: str) -> CheckResult:
11
+ path = shell.which(binary)
12
+ if not path:
13
+ return CheckResult(name, _(name_key), "skip", _("doctor.containers.not_installed"))
14
+ result = shell.run_cmd([binary, "--version"], timeout=5)
15
+ if result.ok:
16
+ return CheckResult(name, _(name_key), "ok", result.stdout.strip().splitlines()[0])
17
+ return CheckResult(name, _(name_key), "warn", result.stderr.strip() or _("doctor.containers.error"))
18
+
19
+
20
+ @register("containers")
21
+ def check_containers(ctx) -> list[CheckResult]:
22
+ docker = _probe("containers", "docker", "doctor.containers.docker")
23
+ podman = _probe("containers", "podman", "doctor.containers.podman")
24
+ results = [docker, podman]
25
+ if docker.status == "skip" and podman.status == "skip":
26
+ results.append(
27
+ CheckResult(
28
+ "containers",
29
+ _("doctor.containers.title"),
30
+ "warn",
31
+ _("doctor.containers.none"),
32
+ _("doctor.containers.hint"),
33
+ )
34
+ )
35
+ return results
@@ -0,0 +1,50 @@
1
+ """Graphics checks: Vulkan availability, display server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+ from ..i18n import _
8
+ from ..utils import shell
9
+ from . import CheckResult, register
10
+
11
+
12
+ @register("graphics")
13
+ def check_graphics(ctx) -> list[CheckResult]:
14
+ results: list[CheckResult] = []
15
+
16
+ if shell.which("vulkaninfo"):
17
+ results.append(CheckResult("graphics", _("doctor.graphics.vulkan"), "ok", "vulkaninfo"))
18
+ else:
19
+ ld = shell.run_cmd(["ldconfig", "-p"], timeout=5)
20
+ if ld.ok and "libvulkan" in ld.stdout:
21
+ results.append(
22
+ CheckResult(
23
+ "graphics", _("doctor.graphics.vulkan"), "ok", _("doctor.graphics.vulkan_lib")
24
+ )
25
+ )
26
+ else:
27
+ results.append(
28
+ CheckResult(
29
+ "graphics",
30
+ _("doctor.graphics.vulkan"),
31
+ "warn",
32
+ _("doctor.graphics.vulkan_missing"),
33
+ _("doctor.graphics.vulkan_hint"),
34
+ )
35
+ )
36
+
37
+ if os.environ.get("WAYLAND_DISPLAY") or os.environ.get("DISPLAY"):
38
+ results.append(
39
+ CheckResult("graphics", _("doctor.graphics.display"), "ok", _("doctor.graphics.display_on"))
40
+ )
41
+ else:
42
+ results.append(
43
+ CheckResult(
44
+ "graphics",
45
+ _("doctor.graphics.display"),
46
+ "skip",
47
+ _("doctor.graphics.display_off"),
48
+ )
49
+ )
50
+ return results
@@ -0,0 +1,47 @@
1
+ """Hardware checks: CPU, RAM."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+ from ..i18n import _
8
+ from ..utils import sysinfo
9
+ from . import CheckResult, register
10
+
11
+ RAM_WARN_KIB = 16 * 1024 * 1024 # below 16 GiB is tight for Isaac workflows
12
+
13
+
14
+ @register("hardware")
15
+ def check_hardware(ctx) -> list[CheckResult]:
16
+ results: list[CheckResult] = []
17
+
18
+ model = sysinfo.cpu_model()
19
+ cores = os.cpu_count()
20
+ if model or cores:
21
+ detail = ", ".join(part for part in (model, f"{cores} cores") if part)
22
+ results.append(CheckResult("hardware", _("doctor.hardware.cpu"), "ok", detail))
23
+ else:
24
+ results.append(
25
+ CheckResult(
26
+ "hardware", _("doctor.hardware.cpu"), "warn", "", _("doctor.hardware.cpu_hint")
27
+ )
28
+ )
29
+
30
+ meminfo = sysinfo.read_meminfo()
31
+ total_kib = meminfo.get("MemTotal")
32
+ available_kib = meminfo.get("MemAvailable")
33
+ if total_kib:
34
+ detail = _("doctor.hardware.ram_detail").format(
35
+ total=total_kib / (1024 * 1024),
36
+ available=(available_kib or 0) / (1024 * 1024),
37
+ )
38
+ status = "ok" if total_kib >= RAM_WARN_KIB else "warn"
39
+ hint = "" if status == "ok" else _("doctor.hardware.ram_hint")
40
+ results.append(CheckResult("hardware", _("doctor.hardware.ram"), status, detail, hint))
41
+ else:
42
+ results.append(
43
+ CheckResult(
44
+ "hardware", _("doctor.hardware.ram"), "warn", "", _("doctor.hardware.ram_hint")
45
+ )
46
+ )
47
+ return results
caasi/checks/isaac.py ADDED
@@ -0,0 +1,99 @@
1
+ """Isaac Sim / Isaac Lab detection.
2
+
3
+ Detection order per tool: configured tool registry -> environment variables
4
+ -> common install locations -> pip metadata. Reused by `caasi info`.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import glob
10
+ import os
11
+ from pathlib import Path
12
+
13
+ from ..core.config import Config
14
+ from ..i18n import _
15
+ from ..utils import pydist
16
+ from . import CheckResult, register
17
+
18
+ Detection = tuple[str, str, str] # (status, detail, hint)
19
+
20
+
21
+ def _glob_first(patterns: list[str]) -> str | None:
22
+ for pattern in patterns:
23
+ matches = sorted(glob.glob(os.path.expanduser(pattern)))
24
+ if matches:
25
+ return matches[-1]
26
+ return None
27
+
28
+
29
+ def detect_isaac_sim(config: Config) -> Detection:
30
+ tool = config.resolve_tool("isaacsim")
31
+ if tool is not None:
32
+ path = tool.expanded_path
33
+ if path is not None and path.exists():
34
+ label = f" {tool.version}" if tool.version else ""
35
+ return "ok", _("doctor.isaac.sim_found").format(version=label, path=str(path)), ""
36
+ return (
37
+ "warn",
38
+ _("doctor.isaac.sim_configured_missing").format(path=tool.path or "?"),
39
+ _("doctor.isaac.config_hint"),
40
+ )
41
+
42
+ env_path = os.environ.get("ISAACSIM_PATH")
43
+ if env_path and Path(env_path).expanduser().exists():
44
+ return "ok", _("doctor.isaac.sim_found").format(version="", path=env_path), ""
45
+
46
+ common = _glob_first(
47
+ [
48
+ "~/isaacsim",
49
+ "~/.local/share/ov/pkg/isaac-sim-*",
50
+ "~/.local/share/ov/pkg/isaac_sim-*",
51
+ "/opt/isaac-sim*",
52
+ "/opt/isaacsim*",
53
+ ]
54
+ )
55
+ if common:
56
+ return "ok", _("doctor.isaac.sim_found").format(version="", path=common), ""
57
+
58
+ version = pydist.pip_version("isaacsim")
59
+ if version:
60
+ return "ok", _("doctor.isaac.sim_pip").format(version=version), ""
61
+
62
+ return "fail", _("doctor.isaac.sim_missing"), _("doctor.isaac.sim_hint")
63
+
64
+
65
+ def detect_isaac_lab(config: Config) -> Detection:
66
+ tool = config.resolve_tool("isaaclab")
67
+ if tool is not None:
68
+ path = tool.expanded_path
69
+ if path is not None and path.exists():
70
+ label = f" {tool.version}" if tool.version else ""
71
+ return "ok", _("doctor.isaac.lab_found").format(version=label, path=str(path)), ""
72
+ return (
73
+ "warn",
74
+ _("doctor.isaac.lab_configured_missing").format(path=tool.path or "?"),
75
+ _("doctor.isaac.config_hint"),
76
+ )
77
+
78
+ env_path = os.environ.get("ISAACLAB_PATH")
79
+ if env_path and Path(env_path).expanduser().exists():
80
+ return "ok", _("doctor.isaac.lab_found").format(version="", path=env_path), ""
81
+
82
+ common = _glob_first(["~/isaaclab", "~/IsaacLab", "~/workspace/isaaclab", "~/workspace/IsaacLab"])
83
+ if common:
84
+ return "ok", _("doctor.isaac.lab_found").format(version="", path=common), ""
85
+
86
+ version = pydist.pip_version("isaaclab")
87
+ if version:
88
+ return "ok", _("doctor.isaac.lab_pip").format(version=version), ""
89
+
90
+ return "fail", _("doctor.isaac.lab_missing"), _("doctor.isaac.lab_hint")
91
+
92
+
93
+ @register("isaac")
94
+ def check_isaac(ctx) -> list[CheckResult]:
95
+ status, detail, hint = detect_isaac_sim(ctx.config)
96
+ results = [CheckResult("isaac", _("doctor.isaac.sim"), status, detail, hint)]
97
+ status, detail, hint = detect_isaac_lab(ctx.config)
98
+ results.append(CheckResult("isaac", _("doctor.isaac.lab"), status, detail, hint))
99
+ return results
caasi/checks/ml.py ADDED
@@ -0,0 +1,42 @@
1
+ """ML stack checks: PyTorch, TensorRT, ONNX Runtime, cuDNN (metadata only)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..i18n import _
6
+ from ..utils import pydist, shell
7
+ from . import CheckResult, register
8
+
9
+
10
+ def _pip_check(section: str, name_key: str, dists: tuple[str, ...], hint_key: str) -> CheckResult:
11
+ version = pydist.pip_version(*dists)
12
+ if version:
13
+ return CheckResult(section, _(name_key), "ok", version)
14
+ return CheckResult(section, _(name_key), "fail", _("doctor.ml.not_installed"), _(hint_key))
15
+
16
+
17
+ @register("ml")
18
+ def check_ml(ctx) -> list[CheckResult]:
19
+ results = [
20
+ _pip_check("ml", "doctor.ml.torch", ("torch",), "doctor.ml.torch_hint"),
21
+ _pip_check("ml", "doctor.ml.tensorrt", ("tensorrt", "tensorrt_libs"), "doctor.ml.tensorrt_hint"),
22
+ _pip_check(
23
+ "ml", "doctor.ml.onnx", ("onnxruntime-gpu", "onnxruntime"), "doctor.ml.onnx_hint"
24
+ ),
25
+ ]
26
+
27
+ cudnn = pydist.pip_version("nvidia-cudnn-cu12", "nvidia-cudnn-cu11")
28
+ if cudnn:
29
+ results.append(CheckResult("ml", _("doctor.ml.cudnn"), "ok", cudnn))
30
+ else:
31
+ ld = shell.run_cmd(["ldconfig", "-p"], timeout=5)
32
+ if ld.ok and "libcudnn" in ld.stdout:
33
+ results.append(
34
+ CheckResult("ml", _("doctor.ml.cudnn"), "ok", _("doctor.ml.cudnn_system"))
35
+ )
36
+ else:
37
+ results.append(
38
+ CheckResult(
39
+ "ml", _("doctor.ml.cudnn"), "warn", _("doctor.ml.not_installed"), _("doctor.ml.cudnn_hint")
40
+ )
41
+ )
42
+ return results
caasi/checks/nvidia.py ADDED
@@ -0,0 +1,70 @@
1
+ """NVIDIA checks: driver, GPUs, CUDA (via nvidia-smi)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..core import nvidia
6
+ from ..i18n import _
7
+ from . import CheckResult, register
8
+
9
+
10
+ @register("nvidia")
11
+ def check_nvidia(ctx) -> list[CheckResult]:
12
+ if not nvidia.available():
13
+ return [
14
+ CheckResult(
15
+ "nvidia",
16
+ _("doctor.nvidia.smi"),
17
+ "fail",
18
+ _("doctor.nvidia.smi_missing_detail"),
19
+ _("doctor.nvidia.smi_hint"),
20
+ )
21
+ ]
22
+
23
+ results: list[CheckResult] = []
24
+ try:
25
+ gpus = nvidia.query(["index", "name", "memory.total", "driver_version"])
26
+ except nvidia.NvidiaSmiError as exc:
27
+ return [
28
+ CheckResult(
29
+ "nvidia",
30
+ _("doctor.nvidia.smi"),
31
+ "fail",
32
+ str(exc),
33
+ _("doctor.nvidia.smi_hint"),
34
+ )
35
+ ]
36
+
37
+ driver = gpus[0].get("driver_version") if gpus else None
38
+ results.append(
39
+ CheckResult(
40
+ "nvidia",
41
+ _("doctor.nvidia.driver"),
42
+ "ok",
43
+ _("doctor.nvidia.driver_detail").format(driver=driver or "?"),
44
+ )
45
+ )
46
+ for gpu in gpus:
47
+ vram = gpu.get("memory.total")
48
+ detail = _("doctor.nvidia.gpu_detail").format(
49
+ name=gpu.get("name", "?"),
50
+ vram=f"{vram / 1024:.1f} GiB" if isinstance(vram, (int, float)) else "?",
51
+ )
52
+ results.append(CheckResult("nvidia", _("doctor.nvidia.gpu"), "ok", detail))
53
+
54
+ cuda = nvidia.cuda_version()
55
+ if cuda:
56
+ results.append(
57
+ CheckResult(
58
+ "nvidia",
59
+ _("doctor.nvidia.cuda"),
60
+ "ok",
61
+ _("doctor.nvidia.cuda_detail").format(version=cuda),
62
+ )
63
+ )
64
+ else:
65
+ results.append(
66
+ CheckResult(
67
+ "nvidia", _("doctor.nvidia.cuda"), "warn", "", _("doctor.nvidia.cuda_hint")
68
+ )
69
+ )
70
+ return results
@@ -0,0 +1,51 @@
1
+ """Python environment checks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import platform
7
+ import sys
8
+
9
+ from ..i18n import _
10
+ from . import CheckResult, register
11
+
12
+ MINIMUM = (3, 10)
13
+
14
+
15
+ @register("python")
16
+ def check_python(ctx) -> list[CheckResult]:
17
+ results: list[CheckResult] = []
18
+ version = platform.python_version()
19
+ if sys.version_info[:2] >= MINIMUM:
20
+ results.append(
21
+ CheckResult(
22
+ "python", _("doctor.python.version"), "ok", f"Python {version} ({sys.executable})"
23
+ )
24
+ )
25
+ else:
26
+ results.append(
27
+ CheckResult(
28
+ "python",
29
+ _("doctor.python.version"),
30
+ "fail",
31
+ f"Python {version}",
32
+ _("doctor.python.version_hint"),
33
+ )
34
+ )
35
+
36
+ conda_env = os.environ.get("CONDA_DEFAULT_ENV")
37
+ if conda_env:
38
+ results.append(
39
+ CheckResult(
40
+ "python", _("doctor.python.env"), "ok", _("doctor.python.conda").format(env=conda_env)
41
+ )
42
+ )
43
+ elif sys.prefix != sys.base_prefix:
44
+ results.append(
45
+ CheckResult("python", _("doctor.python.env"), "ok", _("doctor.python.venv"))
46
+ )
47
+ else:
48
+ results.append(
49
+ CheckResult("python", _("doctor.python.env"), "skip", _("doctor.python.system_env"))
50
+ )
51
+ return results
@@ -0,0 +1,54 @@
1
+ """Robotics stack checks: Nav2, MoveIt 2, ros2_control (best-effort via ros2 CLI)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..core import ros as ros_core
6
+ from ..i18n import _
7
+ from ..utils import shell
8
+ from . import CheckResult, register
9
+
10
+ STACKS = [
11
+ ("nav2", "nav2_bringup", "doctor.robotics.nav2"),
12
+ ("moveit2", "moveit_core", "doctor.robotics.moveit"),
13
+ ("ros2_control", "controller_manager", "doctor.robotics.control"),
14
+ ]
15
+
16
+ PKG_TIMEOUT = 8.0
17
+
18
+
19
+ @register("robotics")
20
+ def check_robotics(ctx) -> list[CheckResult]:
21
+ ros2 = ros_core.find_ros2_binary()
22
+ if not ros2:
23
+ return [
24
+ CheckResult(
25
+ "robotics",
26
+ _("doctor.robotics.title"),
27
+ "skip",
28
+ _("doctor.robotics.no_ros2"),
29
+ _("doctor.robotics.no_ros2_hint"),
30
+ )
31
+ ]
32
+
33
+ results: list[CheckResult] = []
34
+ for stack, package, name_key in STACKS:
35
+ probe = shell.run_cmd([ros2, "pkg", "prefix", package], timeout=PKG_TIMEOUT)
36
+ if probe.ok:
37
+ results.append(CheckResult("robotics", _(name_key), "ok", package))
38
+ elif probe.returncode == -1:
39
+ results.append(
40
+ CheckResult(
41
+ "robotics", _(name_key), "warn", _("doctor.robotics.timeout").format(package=package)
42
+ )
43
+ )
44
+ else:
45
+ results.append(
46
+ CheckResult(
47
+ "robotics",
48
+ _(name_key),
49
+ "fail",
50
+ _("doctor.robotics.not_installed").format(package=package),
51
+ _("doctor.robotics.hint").format(stack=stack),
52
+ )
53
+ )
54
+ return results
caasi/checks/ros.py ADDED
@@ -0,0 +1,63 @@
1
+ """ROS 2 checks: distro, setup file, ros2 CLI, middleware."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+ from ..core import ros as ros_core
8
+ from ..i18n import _
9
+ from . import CheckResult, register
10
+
11
+
12
+ @register("ros")
13
+ def check_ros(ctx) -> list[CheckResult]:
14
+ distro, root = ros_core.find_distro()
15
+ if distro is None:
16
+ return [
17
+ CheckResult(
18
+ "ros",
19
+ _("doctor.ros.distro"),
20
+ "fail",
21
+ _("doctor.ros.missing"),
22
+ _("doctor.ros.hint"),
23
+ )
24
+ ]
25
+
26
+ results: list[CheckResult] = []
27
+ results.append(CheckResult("ros", _("doctor.ros.distro"), "ok", distro))
28
+
29
+ if root is not None and (root / "setup.bash").exists():
30
+ results.append(
31
+ CheckResult(
32
+ "ros", _("doctor.ros.setup"), "ok", str(root / "setup.bash")
33
+ )
34
+ )
35
+ else:
36
+ results.append(
37
+ CheckResult(
38
+ "ros",
39
+ _("doctor.ros.setup"),
40
+ "warn",
41
+ _("doctor.ros.setup_missing").format(distro=distro),
42
+ _("doctor.ros.setup_hint"),
43
+ )
44
+ )
45
+
46
+ ros2 = ros_core.find_ros2_binary()
47
+ if ros2:
48
+ results.append(CheckResult("ros", _("doctor.ros.cli"), "ok", ros2))
49
+ else:
50
+ results.append(
51
+ CheckResult(
52
+ "ros", _("doctor.ros.cli"), "warn", _("doctor.ros.cli_missing"), _("doctor.ros.hint")
53
+ )
54
+ )
55
+
56
+ rmw = os.environ.get("RMW_IMPLEMENTATION")
57
+ if rmw:
58
+ results.append(CheckResult("ros", _("doctor.ros.rmw"), "ok", rmw))
59
+ else:
60
+ results.append(
61
+ CheckResult("ros", _("doctor.ros.rmw"), "skip", _("doctor.ros.rmw_default"))
62
+ )
63
+ return results
@@ -0,0 +1,41 @@
1
+ """Alternative simulator checks: Gazebo, MuJoCo."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..i18n import _
6
+ from ..utils import pydist, shell
7
+ from . import CheckResult, register
8
+
9
+
10
+ @register("simulators")
11
+ def check_simulators(ctx) -> list[CheckResult]:
12
+ results: list[CheckResult] = []
13
+
14
+ gz = shell.which("gz") or shell.which("gazebo")
15
+ if gz:
16
+ results.append(CheckResult("simulators", _("doctor.sims.gazebo"), "ok", gz))
17
+ else:
18
+ results.append(
19
+ CheckResult(
20
+ "simulators",
21
+ _("doctor.sims.gazebo"),
22
+ "skip",
23
+ _("doctor.sims.not_installed"),
24
+ _("doctor.sims.gazebo_hint"),
25
+ )
26
+ )
27
+
28
+ mujoco = pydist.pip_version("mujoco")
29
+ if mujoco:
30
+ results.append(CheckResult("simulators", _("doctor.sims.mujoco"), "ok", mujoco))
31
+ else:
32
+ results.append(
33
+ CheckResult(
34
+ "simulators",
35
+ _("doctor.sims.mujoco"),
36
+ "skip",
37
+ _("doctor.sims.not_installed"),
38
+ _("doctor.sims.mujoco_hint"),
39
+ )
40
+ )
41
+ return results