Skip to content

Commit 545cf14

Browse files
ORTODOX1ORTODOX1
authored andcommitted
Add tests, CI/CD, CLI entry point, docker-compose, example config
1 parent fec46bd commit 545cf14

11 files changed

Lines changed: 613 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
lint-test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.11"]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Python ${{ matrix.python-version }}
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install -e ".[dev]"
28+
29+
- name: Lint with ruff
30+
run: ruff check argos/ tests/
31+
32+
- name: Type check with mypy
33+
run: mypy argos/ --ignore-missing-imports
34+
35+
- name: Run tests
36+
run: pytest tests/ -v --tb=short

.gitignore

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
7+
# Distribution / packaging
8+
build/
9+
dist/
10+
*.egg-info/
11+
*.egg
12+
.eggs/
13+
14+
# Virtual environments
15+
venv/
16+
.venv/
17+
env/
18+
19+
# Environment variables
20+
.env
21+
.env.*
22+
23+
# ONNX and PyTorch model weights
24+
*.onnx
25+
*.pt
26+
*.pth
27+
*.tflite
28+
*.engine
29+
30+
# IDE
31+
.vscode/
32+
.idea/
33+
*.swp
34+
*.swo
35+
36+
# Testing / coverage
37+
.pytest_cache/
38+
htmlcov/
39+
.coverage
40+
coverage.xml
41+
42+
# mypy
43+
.mypy_cache/
44+
45+
# ruff
46+
.ruff_cache/
47+
48+
# OS
49+
.DS_Store
50+
Thumbs.db
51+
52+
# Docker
53+
docker-compose.override.yml
54+
55+
# Logs
56+
*.log

argos/__main__.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"""
2+
argos.__main__ -- CLI entry point for the ARGOS inspection system.
3+
4+
Usage:
5+
python -m argos --config config.yaml --mode simulation
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import argparse
11+
import asyncio
12+
import logging
13+
import sys
14+
from pathlib import Path
15+
16+
import yaml
17+
18+
from argos.config import ArgosSettings
19+
from argos.inspector import InspectionEngine
20+
21+
logger = logging.getLogger("argos")
22+
23+
24+
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
25+
"""Parse command-line arguments."""
26+
parser = argparse.ArgumentParser(
27+
prog="argos",
28+
description="ARGOS -- Autonomous Robot for General Onboard Surveillance",
29+
)
30+
parser.add_argument(
31+
"--config", type=Path, default=Path("config.yaml"),
32+
help="Path to YAML configuration file (default: config.yaml)",
33+
)
34+
parser.add_argument(
35+
"--mode", choices=["simulation", "live", "replay"],
36+
default="simulation",
37+
help="Inspection mode: simulation (fake frames), live (camera), replay (video file)",
38+
)
39+
parser.add_argument(
40+
"--replay-file", type=Path, default=None,
41+
help="Path to video file for replay mode",
42+
)
43+
parser.add_argument(
44+
"--interval", type=float, default=5.0,
45+
help="Seconds between inspection cycles (default: 5.0)",
46+
)
47+
parser.add_argument(
48+
"--log-level", default="INFO",
49+
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
50+
help="Logging verbosity (default: INFO)",
51+
)
52+
return parser.parse_args(argv)
53+
54+
55+
def load_config(path: Path) -> ArgosSettings:
56+
"""Load settings from a YAML file, falling back to env/defaults."""
57+
if path.exists():
58+
with open(path) as fh:
59+
raw = yaml.safe_load(fh) or {}
60+
logger.info("Loaded config from %s", path)
61+
return ArgosSettings(**raw)
62+
logger.warning("Config file %s not found, using defaults", path)
63+
return ArgosSettings()
64+
65+
66+
async def run(args: argparse.Namespace) -> None:
67+
"""Initialise and run the inspection engine."""
68+
settings = load_config(args.config)
69+
engine = InspectionEngine(settings=settings)
70+
71+
if args.mode == "simulation":
72+
logger.info("Starting in SIMULATION mode (interval=%.1fs)", args.interval)
73+
elif args.mode == "live":
74+
logger.info("Starting in LIVE mode with camera device")
75+
elif args.mode == "replay":
76+
if not args.replay_file or not args.replay_file.exists():
77+
logger.error("Replay mode requires --replay-file with a valid path")
78+
sys.exit(1)
79+
logger.info("Starting in REPLAY mode from %s", args.replay_file)
80+
81+
await engine.run_continuous(
82+
gps_lat=45.4315, gps_lon=12.3456,
83+
interval_s=args.interval,
84+
)
85+
86+
87+
def main(argv: list[str] | None = None) -> None:
88+
"""Entry point for ``python -m argos``."""
89+
args = parse_args(argv)
90+
logging.basicConfig(
91+
level=getattr(logging, args.log_level),
92+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
93+
)
94+
try:
95+
asyncio.run(run(args))
96+
except KeyboardInterrupt:
97+
logger.info("Interrupted by user")
98+
99+
100+
if __name__ == "__main__":
101+
main()

config.example.yaml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# ARGOS Configuration
2+
# Copy to config.yaml and adjust for your environment.
3+
4+
# --- Camera settings ---
5+
camera:
6+
device_id: 0 # V4L2 device index
7+
width: 1920
8+
height: 1080
9+
fps: 30
10+
exposure_auto: true
11+
white_balance_k: 5500 # Colour temperature in Kelvin
12+
13+
# --- Edge processor (ONNX Runtime) ---
14+
edge:
15+
detector_model_path: "models/defect_detector_v3.onnx"
16+
classifier_model_path: "models/severity_classifier_v2.onnx"
17+
execution_provider: "CUDAExecutionProvider" # or CPUExecutionProvider
18+
confidence_threshold: 0.45
19+
nms_iou_threshold: 0.50
20+
input_size: [640, 640]
21+
22+
# --- SYNIZ TRIZ engine ---
23+
syniz:
24+
ws_endpoint: "wss://syniz.fincantieri.internal/v1/ws"
25+
api_key: "" # Set via ARGOS_SYNIZ__API_KEY env var
26+
timeout_s: 30.0
27+
max_reconnect_attempts: 5
28+
29+
# --- POSEIDON-DIAG CAN bus ---
30+
poseidon:
31+
can_interface: "can0"
32+
can_bustype: "socketcan"
33+
can_bitrate: 250000
34+
j1939_source_address: 0xFE
35+
36+
# --- ROS 2 topics ---
37+
ros:
38+
image_topic: "/argos/camera/image_raw"
39+
defect_topic: "/argos/defects"
40+
odometry_topic: "/argos/odom"
41+
health_topic: "/argos/health"
42+
43+
# --- External services ---
44+
neo4j_uri: "bolt://localhost:7687"
45+
neo4j_user: "neo4j"
46+
neo4j_password: "" # Set via ARGOS_NEO4J_PASSWORD env var
47+
aegis_dashboard_url: "https://aegis.fincantieri.internal/api/v2"

docker-compose.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
version: "3.9"
2+
3+
services:
4+
argos:
5+
build:
6+
context: .
7+
dockerfile: Dockerfile
8+
container_name: argos-core
9+
environment:
10+
ARGOS_NEO4J_URI: bolt://neo4j:7687
11+
ARGOS_NEO4J_USER: neo4j
12+
ARGOS_NEO4J_PASSWORD: ${NEO4J_PASSWORD:-changeme}
13+
ARGOS_SYNIZ__WS_ENDPOINT: wss://syniz-backend:8443/v1/ws
14+
ARGOS_EDGE__EXECUTION_PROVIDER: CPUExecutionProvider
15+
volumes:
16+
- ./models:/app/models:ro
17+
- ./config.yaml:/app/config.yaml:ro
18+
ports:
19+
- "8000:8000"
20+
depends_on:
21+
neo4j:
22+
condition: service_healthy
23+
networks:
24+
- argos-net
25+
restart: unless-stopped
26+
27+
neo4j:
28+
image: neo4j:5-community
29+
container_name: argos-neo4j
30+
environment:
31+
NEO4J_AUTH: neo4j/${NEO4J_PASSWORD:-changeme}
32+
NEO4J_PLUGINS: '["apoc"]'
33+
ports:
34+
- "7474:7474"
35+
- "7687:7687"
36+
volumes:
37+
- neo4j-data:/data
38+
healthcheck:
39+
test: ["CMD-SHELL", "cypher-shell -u neo4j -p ${NEO4J_PASSWORD:-changeme} 'RETURN 1'"]
40+
interval: 10s
41+
timeout: 5s
42+
retries: 5
43+
networks:
44+
- argos-net
45+
restart: unless-stopped
46+
47+
syniz-backend:
48+
image: ghcr.io/ortodox1/syniz:latest
49+
container_name: argos-syniz
50+
environment:
51+
SYNIZ_PORT: "8443"
52+
SYNIZ_NEO4J_URI: bolt://neo4j:7687
53+
ports:
54+
- "8443:8443"
55+
depends_on:
56+
neo4j:
57+
condition: service_healthy
58+
networks:
59+
- argos-net
60+
restart: unless-stopped
61+
62+
volumes:
63+
neo4j-data:
64+
65+
networks:
66+
argos-net:
67+
driver: bridge

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ dependencies = [
3131
"numpy>=1.26.0",
3232
"httpx>=0.27.0",
3333
"uvicorn>=0.29.0",
34+
"pyyaml>=6.0",
3435
]
3536

3637
[project.optional-dependencies]

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ pydantic>=2.6.0
88
numpy>=1.26.0
99
httpx>=0.27.0
1010
uvicorn>=0.29.0
11+
pyyaml>=6.0

tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)