|
Some checks are pending
CI / test (push) Waiting to run
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .github/workflows | ||
| docs | ||
| src/rung_reader | ||
| tests | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
Rung Reader
AI-powered Allen-Bradley PLC ladder logic troubleshooter. Deterministic L5X trace + LLM explanation + RAG SOP correlator + proactive anomaly detection.
Status: pre-release · v0.5.0 (M5 of 14 milestones complete) · 1140 tests green · demo target v0.13.0
What it does
When a PLC faults on a production line, an operator typically reads ladder logic in Studio 5000, traces back from a failed output through every condition until they find the false rung, then walks the floor checking sensors and wires. Rung Reader automates the trace: given an L5X export and live tag values, it produces the same root-cause analysis a senior maintenance tech would, narrates it in plain English, surfaces relevant SOPs from the plant document corpus, and (optionally) proactively flags anomalies before an operator notices.
The core trace is deterministic — pure-function rung walker on the L5X AST. The LLM only narrates what the walker found; it never reasons about ladder semantics from scratch. This is by design: during the M0 spike, llama3.1:8b claimed XIC means "not in contact" (it actually means "examine if closed"). LLMs hallucinate domain knowledge; the walker doesn't.
Non-negotiable constraints
- Read-only PLC access. Never writes to the PLC. Approval interrupts gate the operator's physical action ("go replace the wire"), not PLC writes.
- On-prem only. Local LLM (Ollama), local RAG (FAISS), local everything. No cloud dependency.
- Graceful degradation. Works without LLM (raw trace). Works without PLC (stale-cached values with timestamps). Works without Redis (single-turn, no resume).
- Mock everything in CI. No real hardware or Ollama required for
pytest.
Architecture
L5X file ──► Parser ──► Walker ──┐
├──► LangGraph ──► Explanation
Live PLC ──► Tag Resolver ───────┘ + SOP refs
▲
RAG ────────────┘ (FAISS + MiniLM-L6-v2)
Anomaly Detector ─► Proactive alerts (arq worker)
| Layer | Tech | Module |
|---|---|---|
| L5X parsing | lxml + pydantic | parser/ |
| Rung walker | pure Python | walker/ |
| Live tag I/O | pycomm3 (mock-swappable) | tags/ |
| Multi-agent orchestration | LangGraph | ai/ |
| RAG | LlamaIndex + FAISS + sentence-transformers | rag/ |
| API | FastAPI (SSE) | api/ |
| Observability | Langfuse (fail-silent) | api/deps.py |
| Async worker (anomaly) | arq + Redis | (M10) anomaly/ |
| UI | Streamlit + streamlit-agraph | streamlit/ |
Quickstart
Prerequisites: Python 3.12+ (3.14 tested), uv, a CIP-capable PLC or Studio 5000 Logix Emulate (M4+).
git clone <this-repo> && cd rung-reader
uv sync --dev
# All tests, no real hardware required
uv run pytest -m "not integration"
# Lint + type check
uv run ruff check src/ tests/
uv run mypy -p rung_reader
End-to-end demo (Streamlit UI) lands at M11. Single-trace CLI landed at M4; LLM explainer landed at M5.
CLI quick reference
# Deterministic trace — no LLM
uv run python -m rung_reader trace \
--tag Motor_01_Running \
--l5x tests/fixtures/handcrafted/simple_rung.L5X \
--plc mock \
--scenario tests/fixtures/mock_plc_scenarios/simple_rung.yaml
# Trace + LLM narration via on-prem Ollama (requires `ollama pull qwen2.5:3b`)
LLM_PROVIDER=ollama uv run python -m rung_reader explain \
--tag Motor_01_Running \
--l5x tests/fixtures/handcrafted/simple_rung.L5X \
--plc mock \
--scenario tests/fixtures/mock_plc_scenarios/simple_rung.yaml
# Quality mode (llama3.1:8b — gaming-box only, ~20s on 1050 Ti)
uv run python -m rung_reader explain ... --quality
# JSON output for downstream consumers (M7 FastAPI will return this same shape)
uv run python -m rung_reader explain ... --json
# Mock LLM for CI / local smoke (no Ollama required)
LLM_PROVIDER=mock uv run python -m rung_reader explain ...
Environment variables (M5):
| Var | Default | Purpose |
|---|---|---|
LLM_PROVIDER |
ollama |
mock | ollama — on-prem only, cloud branches are not accepted |
OLLAMA_MODEL |
qwen2.5:3b |
Overridden to llama3.1:8b by --quality |
OLLAMA_BASE_URL |
http://localhost:11434 |
Override for LAN-hosted Ollama |
RUNG_READER_OLLAMA_HOST |
unset | Enables the opt-in integration test suite |
Exit codes: 0 success, 2 argparse usage, 3 tag not found, 4 driver / config failure, 5 LLM error (empty response, unreachable Ollama, timeout).
Project layout
rung-reader/
├── docs/
│ ├── design.md # Approved design spec (213 lines)
│ ├── ROADMAP.md # Live milestone status
│ ├── SESSION_NOTES.md # Cross-session handoff log
│ └── spikes/ # Spike notes (pycomm3, ollama, sibling port map)
├── src/rung_reader/
│ ├── parser/ # M1, M2 — L5X → AST + AOI/alias/metadata resolution
│ ├── walker/ # M3 — deterministic rung trace
│ ├── tags/ # M4 — live PLC reads, mock driver
│ ├── ai/ # M5, M9 — LangGraph nodes
│ ├── api/ # M7, M9 — FastAPI gateway
│ ├── rag/ # M8 — FAISS retriever + teach-mode
│ └── (anomaly/) # M10 — arq watcher (planned)
├── tests/
│ ├── fixtures/
│ │ ├── public/ # MIT-licensed L5X samples + Rockwell V35 schema
│ │ └── handcrafted/ # Edge-case fixtures (malformed/circular/missing)
│ └── eval/ # M6 + M12 — ground-truth eval cases
└── streamlit/ # M11 — UI app
Roadmap
14 milestones (M0–M13), versioned v0.N.0 per milestone. v0.13.0 is the final demo. Live status: docs/ROADMAP.md. Canonical plan: /mnt/TempNVME/projects/docs/superpowers/plans/2026-04-16-rung-reader-roadmap.md.
Fixtures + Attribution
tests/fixtures/public/logix-libraries/— Studio 5000 AOIs/UDTs from JeremyMedders/LogixLibraries (MIT)tests/fixtures/public/l5x2c/— single ladder example from alairjunior/l5x2c (MIT)tests/fixtures/handcrafted/— minimal edge-case fixtures (this project, GPL-3.0)
Each upstream LICENSE is preserved alongside its files.
License
GPL-3.0-or-later. The deterministic core (parser/walker/tags) is intentionally GPL so any production fork must publish parser-rule fixes back to the community.
Sibling project
Architecture patterns (LangGraph state, graph builder, FastAPI graph singleton, RAG ingest+retriever, MockChatModel) are ported from /mnt/TempNVME/projects/supply-chain-agents/. See docs/spikes/sibling-port-map.md for the verified port table.