docs: update documentation of STA
This commit is contained in:
306
README.md
306
README.md
@@ -18,107 +18,20 @@
|
||||
|
||||
Web-based Modbus relay control system for managing 8-channel relay modules over TCP.
|
||||
|
||||
> **⚠️ Development Status**: This project is in early development. Core features are currently being implemented following a specification-driven approach.
|
||||
|
||||
## Overview
|
||||
|
||||
STA will provide a modern web interface for controlling Modbus-compatible relay devices, eliminating the need for specialized industrial software. The goal is to enable browser-based relay control with real-time status updates.
|
||||
|
||||
## Current Status
|
||||
|
||||
### Phase 1 Complete - Foundation
|
||||
- ✅ Monorepo structure (backend + frontend at root)
|
||||
- ✅ Rust web server with Poem 3.1 framework
|
||||
- ✅ Configuration system (YAML + environment variables)
|
||||
- ✅ Modbus TCP and relay settings structures
|
||||
- ✅ Health check and metadata API endpoints
|
||||
- ✅ OpenAPI documentation with Swagger UI
|
||||
- ✅ Rate limiting middleware
|
||||
- ✅ SQLite schema and repository for relay labels
|
||||
- ✅ Vue 3 + TypeScript frontend scaffolding with Vite
|
||||
- ✅ Type-safe API client generation from OpenAPI specs
|
||||
**US1 (MVP) — Complete** — Users can view all 8 relay states and toggle individual relays on/off via the web UI, backed by a Rust API with Modbus TCP control. Phases 1–4 complete: domain layer with type-driven development, infrastructure with mock/real Modbus controllers and SQLite persistence, application use cases, REST API with OpenAPI docs, and Vue 3 frontend with real-time polling.
|
||||
|
||||
### Phase 0.5 Complete - CORS Configuration & Production Security
|
||||
- ✅ T009: CorsSettings struct with comprehensive unit tests (5 tests)
|
||||
- ✅ T010: CorsSettings implementation with restrictive fail-safe defaults
|
||||
- ✅ T011: Development YAML configuration with permissive CORS
|
||||
- ✅ T012: Production YAML configuration with restrictive CORS
|
||||
- ✅ T013: From<CorsSettings> for Cors trait unit tests (6 tests)
|
||||
- ✅ T014: From<CorsSettings> for Cors implementation with security validation
|
||||
- ✅ T015: Middleware chain integration using From trait
|
||||
- ✅ T016: Integration tests for CORS headers (9 comprehensive tests)
|
||||
|
||||
#### Key CORS Features Implemented
|
||||
- Environment-specific CORS configuration (development vs production)
|
||||
- Wildcard origin support for development (`allowed_origins: ["*"]`)
|
||||
- Multiple specific origins for production
|
||||
- Credentials support for Authelia authentication
|
||||
- Security validation (prevents wildcard + credentials)
|
||||
- Configurable preflight cache duration
|
||||
- Hardcoded secure methods and headers
|
||||
- Structured logging for CORS configuration
|
||||
- Comprehensive test coverage (15 tests total)
|
||||
|
||||
### Phase 2 Complete - Domain Layer (Type-Driven Development)
|
||||
- ✅ T017-T018: RelayId newtype with 1-8 validation and zero-cost abstraction
|
||||
- ✅ T019-T020: RelayState enum (On/Off) with serialization support
|
||||
- ✅ T021-T022: Relay aggregate with state control methods (toggle, turn_on, turn_off)
|
||||
- ✅ T023-T024: RelayLabel newtype with 1-50 character validation
|
||||
- ✅ T025-T026: ModbusAddress type with From<RelayId> trait (1-8 → 0-7 offset mapping)
|
||||
- ✅ T027: HealthStatus enum with state machine (Healthy/Degraded/Unhealthy)
|
||||
|
||||
#### Key Domain Layer Features Implemented
|
||||
- 100% test coverage for domain layer (50+ comprehensive tests)
|
||||
- Zero external dependencies (pure business logic)
|
||||
- All newtypes use `#[repr(transparent)]` for zero-cost abstractions
|
||||
- Smart constructors with `Result<T, E>` for type-safe validation
|
||||
- TDD workflow (red-green-refactor) for all implementations
|
||||
- RelayController and RelayLabelRepository trait definitions
|
||||
- Complete separation from infrastructure concerns (hexagonal architecture)
|
||||
|
||||
### Phase 3 Complete - Infrastructure Layer
|
||||
- ✅ T028-T029: MockRelayController tests and implementation
|
||||
- ✅ T030: RelayController trait with async methods (read_state, write_state, read_all, write_all)
|
||||
- ✅ T031: ControllerError enum (ConnectionError, Timeout, ModbusException, InvalidRelayId)
|
||||
- ✅ T032: MockRelayController comprehensive tests (6 tests)
|
||||
- ✅ T025a-f: ModbusRelayController implementation (decomposed):
|
||||
- Connection setup with tokio-modbus
|
||||
- Timeout-wrapped read_coils and write_single_coil helpers
|
||||
- RelayController trait implementation
|
||||
- ✅ T034: Integration test with real hardware (uses #[ignore] attribute)
|
||||
- ✅ T035-T036: RelayLabelRepository trait and SQLite implementation
|
||||
- ✅ T037-T038: MockRelayLabelRepository for testing
|
||||
- ✅ T039-T040: HealthMonitor service with state tracking
|
||||
|
||||
#### Key Infrastructure Features Implemented
|
||||
- **ModbusRelayController**: Thread-safe Modbus TCP client with timeout handling
|
||||
- Uses `Arc<Mutex<Context>>` for concurrent access
|
||||
- Native Modbus TCP protocol (MBAP header, no CRC16)
|
||||
- Configurable timeout with `tokio::time::timeout`
|
||||
- **MockRelayController**: In-memory testing without hardware
|
||||
- Uses `Arc<Mutex<HashMap<RelayId, RelayState>>>` for state
|
||||
- Optional timeout simulation for error handling tests
|
||||
- **SqliteRelayLabelRepository**: Compile-time verified SQL queries
|
||||
- Automatic migrations via SQLx
|
||||
- In-memory mode for testing
|
||||
- **HealthMonitor**: State machine for health tracking
|
||||
- Healthy -> Degraded -> Unhealthy transitions
|
||||
- Recovery on successful operations
|
||||
|
||||
### Planned - Phases 4-8
|
||||
- 📋 US1: Monitor & toggle relay states - MVP (Phase 4)
|
||||
- 📋 US2: Bulk relay controls (Phase 5)
|
||||
- 📋 US3: Health status display (Phase 6)
|
||||
- 📋 US4: Relay labeling (Phase 7)
|
||||
- 📋 Production deployment (Phase 8)
|
||||
|
||||
See [tasks.org](specs/001-modbus-relay-control/tasks.org) for detailed implementation roadmap.
|
||||
|
||||
## Architecture
|
||||
|
||||
**Current:**
|
||||
- **Backend**: Rust 2024 with Poem web framework (hexagonal architecture)
|
||||
- **Configuration**: YAML-based with environment variable overrides
|
||||
- **Frontend**: Vue 3 + TypeScript with real-time polling (2s interval)
|
||||
- **API**: RESTful HTTP with OpenAPI documentation
|
||||
- **CORS**: Production-ready configurable middleware with security validation
|
||||
- **Middleware Chain**: Rate Limiting -> CORS -> Data injection
|
||||
@@ -126,7 +39,6 @@ See [tasks.org](specs/001-modbus-relay-control/tasks.org) for detailed implement
|
||||
- **Persistence**: SQLite for relay labels with compile-time SQL verification
|
||||
|
||||
**Planned:**
|
||||
- **Frontend**: Vue 3 with TypeScript
|
||||
- **Deployment**: Backend on Raspberry Pi, frontend on Cloudflare Pages
|
||||
- **Access**: Traefik reverse proxy with Authelia authentication
|
||||
|
||||
@@ -140,17 +52,20 @@ See [tasks.org](specs/001-modbus-relay-control/tasks.org) for detailed implement
|
||||
### Development
|
||||
|
||||
```bash
|
||||
# Run development server
|
||||
just run
|
||||
# Run backend development server
|
||||
just backend run
|
||||
|
||||
# Run tests
|
||||
just test
|
||||
# Run frontend
|
||||
just frontend run
|
||||
|
||||
# Run linter
|
||||
just lint
|
||||
# Run backend tests
|
||||
just backend test
|
||||
|
||||
# Format code
|
||||
just format
|
||||
# Run backend linter
|
||||
just backend lint
|
||||
|
||||
# Format backend code
|
||||
just backend format
|
||||
|
||||
# Watch mode with bacon
|
||||
bacon # clippy-all (default)
|
||||
@@ -163,9 +78,9 @@ Edit `backend/settings/base.yaml` for Modbus device settings:
|
||||
|
||||
```yaml
|
||||
modbus:
|
||||
host: "192.168.0.200"
|
||||
host: "192.168.1.200"
|
||||
port: 502
|
||||
slave_id: 0
|
||||
slave_id: 1
|
||||
timeout_secs: 5
|
||||
|
||||
relay:
|
||||
@@ -184,8 +99,7 @@ APP__MODBUS__HOST=192.168.1.100 cargo run
|
||||
```yaml
|
||||
# backend/settings/development.yaml
|
||||
cors:
|
||||
allowed_origins:
|
||||
- "*" # Permissive for local development
|
||||
allowed_origins: ["*"] # Permissive for local development
|
||||
allow_credentials: false # MUST be false with wildcard
|
||||
max_age_secs: 3600
|
||||
```
|
||||
@@ -225,12 +139,12 @@ The server provides OpenAPI documentation via Swagger UI:
|
||||
- OpenAPI Spec: `http://localhost:3100/specs`
|
||||
|
||||
**Current Endpoints:**
|
||||
- `GET /api/relays` - List all relay states
|
||||
- `POST /api/relays/{id}/toggle` - Toggle individual relay state
|
||||
- `GET /api/health` - Health check endpoint
|
||||
- `GET /api/meta` - Application metadata
|
||||
|
||||
**Planned Endpoints (see spec):**
|
||||
- `GET /api/relays` - List all relay states
|
||||
- `POST /api/relays/{id}/toggle` - Toggle relay state
|
||||
- `POST /api/relays/all/on` - Turn all relays on
|
||||
- `POST /api/relays/all/off` - Turn all relays off
|
||||
- `PUT /api/relays/{id}/label` - Set relay label
|
||||
@@ -239,74 +153,133 @@ The server provides OpenAPI documentation via Swagger UI:
|
||||
|
||||
**Monorepo Layout:**
|
||||
```
|
||||
sta/ # Repository root
|
||||
├── backend/ # Rust backend workspace member
|
||||
sta/
|
||||
├── backend/ # Rust backend
|
||||
│ ├── src/
|
||||
│ │ ├── lib.rs - Library entry point
|
||||
│ │ ├── main.rs - Binary entry point
|
||||
│ │ ├── startup.rs - Application builder and server config
|
||||
│ │ ├── telemetry.rs - Logging and tracing setup
|
||||
│ │ ├── main.rs - Binary entry point
|
||||
│ │ ├── lib.rs - Library entry point
|
||||
│ │ ├── startup.rs - Application builder and server wiring
|
||||
│ │ ├── telemetry.rs - Logging and tracing setup
|
||||
│ │ │
|
||||
│ │ ├── domain/ - Business logic layer (Phase 2)
|
||||
│ │ │ ├── relay/ - Relay domain aggregate
|
||||
│ │ │ │ ├── types/ - RelayId, RelayState, RelayLabel newtypes
|
||||
│ │ │ │ ├── entity.rs - Relay aggregate with state control
|
||||
│ │ │ │ ├── controller.rs - RelayController trait & ControllerError
|
||||
│ │ │ │ └── repository/ - RelayLabelRepository trait
|
||||
│ │ │ ├── modbus.rs - ModbusAddress type with conversion
|
||||
│ │ │ └── health.rs - HealthStatus state machine
|
||||
│ │ ├── domain/ - Business logic
|
||||
│ │ │ ├── health.rs - HealthStatus state machine
|
||||
│ │ │ ├── modbus.rs - ModbusAddress type
|
||||
│ │ │ └── relay/
|
||||
│ │ │ ├── entity.rs - Relay aggregate (state control)
|
||||
│ │ │ ├── controller.rs - RelayController trait
|
||||
│ │ │ ├── types/
|
||||
│ │ │ │ ├── relayid.rs - RelayId newtype (1..=8)
|
||||
│ │ │ │ ├── relaylabel.rs - RelayLabel newtype
|
||||
│ │ │ │ └── relaystate.rs - RelayState enum (On/Off)
|
||||
│ │ │ └── repository/
|
||||
│ │ │ └── label.rs - RelayLabelRepository trait
|
||||
│ │ │
|
||||
│ │ ├── application/ - Use cases and orchestration (Phase 3)
|
||||
│ │ │ └── health/ - Health monitoring service
|
||||
│ │ │ └── health_monitor.rs - HealthMonitor with state tracking
|
||||
│ │ ├── application/ - Use cases
|
||||
│ │ │ ├── health/
|
||||
│ │ │ │ └── health_monitor.rs - Health monitoring
|
||||
│ │ │ └── use_cases/
|
||||
│ │ │ ├── get_all_relays.rs - List all relays
|
||||
│ │ │ └── toggle_relay.rs - Toggle single relay
|
||||
│ │ │
|
||||
│ │ ├── infrastructure/ - External integrations (Phase 3)
|
||||
│ │ │ ├── modbus/ - Modbus TCP communication
|
||||
│ │ │ │ ├── client.rs - ModbusRelayController (real hardware)
|
||||
│ │ │ │ ├── client_test.rs - Hardware integration tests
|
||||
│ │ │ │ └── mock_controller.rs - MockRelayController for testing
|
||||
│ │ │ └── persistence/ - Database layer
|
||||
│ │ │ ├── entities/ - Database record types
|
||||
│ │ │ ├── sqlite_repository.rs - SqliteRelayLabelRepository
|
||||
│ │ │ └── label_repository.rs - MockRelayLabelRepository
|
||||
│ │ ├── infrastructure/ - External integrations
|
||||
│ │ │ ├── modbus/
|
||||
│ │ │ │ ├── client.rs - ModbusRelayController
|
||||
│ │ │ │ ├── client_test.rs - Unit tests
|
||||
│ │ │ │ ├── factory.rs - Controller factory (retry, fallback)
|
||||
│ │ │ │ └── mock_controller.rs - MockRelayController
|
||||
│ │ │ └── persistence/
|
||||
│ │ │ ├── factory.rs - Repository factory
|
||||
│ │ │ ├── label_repository.rs - SQL implementation
|
||||
│ │ │ ├── label_repository_tests.rs - Unit tests
|
||||
│ │ │ ├── sqlite_repository.rs - SQLite implementation
|
||||
│ │ │ └── entities/
|
||||
│ │ │ └── relay_label_record.rs - DB row struct
|
||||
│ │ │
|
||||
│ │ ├── presentation/ - API layer (planned Phase 4)
|
||||
│ │ ├── settings/ - Configuration module
|
||||
│ │ │ ├── mod.rs - Settings aggregation
|
||||
│ │ │ └── cors.rs - CORS configuration
|
||||
│ │ ├── route/ - HTTP endpoint handlers
|
||||
│ │ │ ├── health.rs - Health check endpoints
|
||||
│ │ │ └── meta.rs - Application metadata
|
||||
│ │ └── middleware/ - Custom middleware
|
||||
│ │ └── rate_limit.rs
|
||||
│ │ ├── presentation/ - API handlers and DTOs
|
||||
│ │ │ ├── error.rs - API error types
|
||||
│ │ │ ├── api/
|
||||
│ │ │ │ └── relay_api.rs - Relay HTTP handlers
|
||||
│ │ │ └── dto/
|
||||
│ │ │ └── relay_dto.rs - Relay DTOs
|
||||
│ │ │
|
||||
│ │ ├── route/ - Route definitions
|
||||
│ │ │ ├── health.rs - Health check
|
||||
│ │ │ └── meta.rs - App metadata
|
||||
│ │ │
|
||||
│ │ ├── middleware/
|
||||
│ │ │ └── rate_limit.rs - Rate limiting
|
||||
│ │ │
|
||||
│ │ └── settings/ - Configuration
|
||||
│ │ ├── application.rs - App-wide settings
|
||||
│ │ ├── cors.rs - CORS settings
|
||||
│ │ ├── database.rs - Database settings
|
||||
│ │ ├── environment.rs - Environment enum
|
||||
│ │ ├── modbus.rs - Modbus settings
|
||||
│ │ ├── rate_limiting.rs - Rate limit config
|
||||
│ │ └── relay.rs - Relay settings
|
||||
│ │
|
||||
│ ├── settings/ - YAML configuration files
|
||||
│ │ ├── base.yaml - Base configuration
|
||||
│ │ ├── development.yaml - Development overrides
|
||||
│ │ └── production.yaml - Production overrides
|
||||
│ └── tests/ - Integration tests
|
||||
│ └── cors_test.rs - CORS integration tests
|
||||
│ ├── settings/ - YAML config files
|
||||
│ │ ├── base.yaml
|
||||
│ │ ├── development.yaml
|
||||
│ │ └── production.yaml
|
||||
│ │
|
||||
│ └── tests/ - Integration/contract tests
|
||||
│ ├── contract/
|
||||
│ │ └── test_relay_api.rs - Relay API contract tests
|
||||
│ ├── cors_test.rs - CORS integration tests
|
||||
│ ├── modbus_hardware_test.rs - Hardware tests (#[ignore])
|
||||
│ ├── sqlite_repository_test.rs - SQLite integration tests
|
||||
│ └── sqlite_repository_functional_test.rs - Functional tests
|
||||
│
|
||||
├── migrations/ - SQLx database migrations
|
||||
├── src/ # Frontend source (Vue/TypeScript)
|
||||
│ └── api/ - Type-safe API client
|
||||
├── docs/ # Project documentation
|
||||
│ ├── cors-configuration.md - CORS setup guide
|
||||
│ ├── domain-layer.md - Domain layer architecture
|
||||
│ └── Modbus_POE_ETH_Relay.md - Hardware documentation
|
||||
├── specs/ # Feature specifications
|
||||
│ ├── constitution.md - Architectural principles
|
||||
├── src/ # Frontend (Vue 3 + TypeScript)
|
||||
│ ├── main.ts - App entry point
|
||||
│ ├── App.vue - Root component
|
||||
│ ├── style.css / style.less - Global styles
|
||||
│ ├── api/
|
||||
│ │ ├── client.ts - HTTP client
|
||||
│ │ └── schema.ts - API types
|
||||
│ ├── components/
|
||||
│ │ ├── RelayCard.vue - Relay card
|
||||
│ │ ├── StaFooter.vue - Footer
|
||||
│ │ └── StaHeader.vue - Header
|
||||
│ ├── composables/
|
||||
│ │ ├── useMeta.ts - Page metadata
|
||||
│ │ ├── useRelay.ts - Relay state management
|
||||
│ │ └── useRelayPolling.ts - Real-time polling (2s)
|
||||
│ ├── pages/
|
||||
│ │ └── RelaysView.vue - Main relay view
|
||||
│ ├── types/
|
||||
│ │ ├── relay.ts - Relay type definitions
|
||||
│ │ └── mappers/
|
||||
│ │ └── relayDtoMapper.ts
|
||||
│ └── utils/
|
||||
│ └── isNil.ts
|
||||
│
|
||||
├── migrations/ - SQLx migrations
|
||||
│ ├── 0001_relay-labels.up.sql
|
||||
│ └── 0001_relay-labels.down.sql
|
||||
│
|
||||
├── docs/ - Documentation
|
||||
│ ├── cors-configuration.md
|
||||
│ ├── domain-layer.md
|
||||
│ └── Modbus_POE_ETH_Relay.md
|
||||
│
|
||||
├── specs/ - Specifications
|
||||
│ ├── constitution.md
|
||||
│ └── 001-modbus-relay-control/
|
||||
│ ├── spec.md - Feature specification
|
||||
│ ├── plan.md - Implementation plan
|
||||
│ ├── tasks.org - Task breakdown (org-mode format)
|
||||
│ ├── data-model.md - Data model specification
|
||||
│ ├── types-design.md - Domain types design
|
||||
│ ├── domain-layer-architecture.md - Domain layer docs
|
||||
│ └── lessons-learned.md - Phase 2/3 insights
|
||||
├── package.json - Frontend dependencies
|
||||
├── vite.config.ts - Vite build configuration
|
||||
└── justfile - Build commands
|
||||
│ ├── spec.md, plan.md, tasks.org
|
||||
│ ├── data-model.md, types-design.md
|
||||
│ ├── domain-layer-architecture.md
|
||||
│ ├── lessons-learned.md
|
||||
│ └── ...
|
||||
│
|
||||
├── nix/ - Nix flake configs
|
||||
├── public/ - Static assets
|
||||
├── justfile - Build commands
|
||||
├── package.json
|
||||
├── vite.config.ts
|
||||
├── Cargo.toml
|
||||
└── flake.nix
|
||||
```
|
||||
|
||||
## Technology Stack
|
||||
@@ -324,9 +297,10 @@ sta/ # Repository root
|
||||
- thiserror (error handling)
|
||||
- serde + serde_yaml (configuration deserialization)
|
||||
|
||||
**Frontend** (scaffolding complete):
|
||||
- Vue 3 + TypeScript
|
||||
**Frontend** (US1 complete):
|
||||
- Vue 3 + TypeScript with composables (useRelayPolling)
|
||||
- Vite build tool
|
||||
- RelayCard and RelayGrid components with real-time polling
|
||||
- openapi-typescript (type-safe API client generation)
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
Reference in New Issue
Block a user