chore: scaffold project skeleton (pyproject, Docker, systemd, README)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
148
README.md
148
README.md
@@ -1,3 +1,149 @@
|
||||
# cosma-log-analyzer
|
||||
|
||||
Détecteur anomalies logs AUV COSMA (règles déterministes + NATS)
|
||||
Deterministic anomaly detection service for COSMA AUV logs. Ingests MCAP
|
||||
files produced by the AUV/USV pipeline, evaluates a set of rules against
|
||||
IMU / USBL / battery topics, and publishes each detection as a JSON event
|
||||
on NATS (or stdout in dev).
|
||||
|
||||
## Context
|
||||
|
||||
COSMA (Flag) operates an AUV that streams telemetry to a surface USV. All
|
||||
telemetry is persisted as MCAP (ROS2-native container). This service is
|
||||
livrable #3: the first-pass observability layer before any statistical or
|
||||
ML detection is added.
|
||||
|
||||
```
|
||||
┌──────┐ MCAP ┌──────┐ MCAP ┌───────────────────┐ NATS ┌────────────────┐
|
||||
│ AUV │────────▶│ USV │────────▶│ cosma-log-analyzer│──────────▶│ cosma-monitor │
|
||||
└──────┘ └──────┘ │ (this repo) │ events │ UI │
|
||||
└───────────────────┘ └────────────────┘
|
||||
```
|
||||
|
||||
## Rules (v0)
|
||||
|
||||
| Rule | Threshold (default) | Severity | Topic |
|
||||
|-----------------------|-------------------------------------------|----------|----------------------------------|
|
||||
| `imu_outliers` | rolling 10 s window, \|z\| > 3 | warn | `/mavros/imu/data` |
|
||||
| `watchdog_imu` | gap > 2 s between two IMU msgs | critical | `/mavros/imu/data` |
|
||||
| `usbl_snr_low` | SNR < 5 dB for 3 consecutive samples | warn | `/usbl_reading/usbl_solution` |
|
||||
| `usbl_distance_spike` | \|Δdistance\| > 50 m in less than 1 s | warn | `/usbl_reading/usbl_solution` |
|
||||
| `battery_low` | voltage < 13.5 V for more than 5 s | critical | `/mavros/battery` |
|
||||
|
||||
All thresholds are tunable via environment variables (`BATTERY_LOW_V`,
|
||||
`USBL_SNR_LOW`, `USBL_DIST_SPIKE_M`, `WATCHDOG_IMU_S`) or rule
|
||||
constructor arguments.
|
||||
|
||||
## NATS subject
|
||||
|
||||
```
|
||||
cosma.auv.{subject}.anomaly.{rule}
|
||||
# ex: cosma.auv.AUV206.anomaly.battery_low
|
||||
```
|
||||
|
||||
If `NATS_URL` is empty, events are written as JSON Lines to stdout —
|
||||
useful in dev and CI.
|
||||
|
||||
## Example anomaly payload
|
||||
|
||||
```json
|
||||
{
|
||||
"rule": "battery_low",
|
||||
"severity": "critical",
|
||||
"timestamp": 1700000055.0,
|
||||
"subject": "AUV206",
|
||||
"topic": "/mavros/battery",
|
||||
"value": 13.26,
|
||||
"context": {
|
||||
"min_voltage_v": 13.5,
|
||||
"min_duration_s": 5.0,
|
||||
"run_start_ts": 1700000051.0,
|
||||
"below_duration_s": 9.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
Python 3.11+ recommended. Works on 3.10.
|
||||
|
||||
```bash
|
||||
pip install -e .[dev]
|
||||
```
|
||||
|
||||
## CLI
|
||||
|
||||
```bash
|
||||
# One-shot on a single MCAP file
|
||||
cosma-log-analyzer ingest path/to/log.mcap --subject AUV206
|
||||
|
||||
# Dry-run: force stdout even if NATS_URL is set
|
||||
cosma-log-analyzer ingest path/to/log.mcap --dry-run
|
||||
|
||||
# Service mode: watch a directory for new MCAP files
|
||||
cosma-log-analyzer serve --mcap-dir /data/mcap
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
# drop MCAP files into ./data/mcap and watch NATS on :4222
|
||||
```
|
||||
|
||||
## systemd
|
||||
|
||||
```bash
|
||||
sudo cp systemd/cosma-log-analyzer.service /etc/systemd/system/
|
||||
sudo systemctl enable --now cosma-log-analyzer
|
||||
journalctl -u cosma-log-analyzer -f
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
pytest -v # 32 tests, runs the e2e against a fake MCAP
|
||||
pytest --cov --cov-report=term # coverage (rules/ > 95%)
|
||||
```
|
||||
|
||||
The fake MCAP generator (`tests/fixtures/generate_fake_mcap.py`) produces
|
||||
a synthetic 60 s trace with one instance of each rule's trigger
|
||||
condition — the e2e test asserts we detect exactly those.
|
||||
|
||||
## Adding a rule
|
||||
|
||||
1. Subclass `Rule` in `src/cosma_log_analyzer/rules/<name>.py`:
|
||||
|
||||
```python
|
||||
class MyRule(Rule):
|
||||
name = "my_rule"
|
||||
topic = "/my/topic"
|
||||
severity = "warn"
|
||||
|
||||
def detect(self, df: pd.DataFrame) -> list[Anomaly]:
|
||||
...
|
||||
```
|
||||
|
||||
2. Register it in `rules/__init__.py::all_rules()`.
|
||||
3. Add a test in `tests/test_rules.py`.
|
||||
|
||||
## Roadmap v1
|
||||
|
||||
- Rolling-stats rules (heading drift, GPS dropout correlated with USBL).
|
||||
- Time alignment between MCAP IMU and CSV USV nav.
|
||||
- ML anomaly layer (Isolation Forest) once we have > 50 h of nominal
|
||||
dive datasets to train against.
|
||||
- Backpressure + JetStream persistence for the NATS publisher.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
src/cosma_log_analyzer/ # package code
|
||||
rules/ # one file per rule
|
||||
main.py # Click CLI: `ingest` + `serve`
|
||||
ingest.py # MCAP + CSV readers -> pandas
|
||||
bus.py # NATS publisher + stdout fallback
|
||||
models.py # Anomaly dataclass
|
||||
tests/ # pytest suite + fake MCAP fixture
|
||||
examples/run_on_fake.sh # end-to-end demo
|
||||
systemd/ # unit file for on-prem deployment
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user