Update README to reflect completed Phase 3 infrastructure layer: - Documented ModbusRelayController, MockRelayController, SqliteRelayLabelRepository, and HealthMonitor implementations - Added testing coverage details (20+ tests across infrastructure components) - Updated architecture diagrams and project structure - Changed task reference to tasks.org format - Updated dependency list with production infrastructure dependencies Ref: Phase 3 tasks in specs/001-modbus-relay-control/tasks.org
402 lines
16 KiB
Markdown
402 lines
16 KiB
Markdown
<h1 align="center">STA</h1>
|
|
<div align="center">
|
|
<strong>
|
|
Smart Temperature & Appliance Control
|
|
</strong>
|
|
</div>
|
|
<br/>
|
|
|
|
<div align="center">
|
|
<!-- Wakapi -->
|
|
<img alt="Coding Time Badge" src="https://clock.phundrak.com/api/badge/phundrak/interval:any/project:sta">
|
|
<!-- Emacs -->
|
|
<a href="https://www.gnu.org/software/emacs/"><img src="https://img.shields.io/badge/Emacs-29.1-blueviolet.svg?style=flat-square&logo=GNU%20Emacs&logoColor=white" /></a>
|
|
</div>
|
|
|
|
> **🤖 AI-Assisted Development Notice**: This project uses Claude Code as a development assistant for task planning, code organization, and workflow management. However, all code is human-written, reviewed, and validated by the project maintainer. AI is used as a productivity tool, not as the author of the implementation.
|
|
|
|
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
|
|
|
|
### 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
|
|
- **API**: RESTful HTTP with OpenAPI documentation
|
|
- **CORS**: Production-ready configurable middleware with security validation
|
|
- **Middleware Chain**: Rate Limiting -> CORS -> Data injection
|
|
- **Modbus Integration**: tokio-modbus for Modbus TCP communication
|
|
- **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
|
|
|
|
## Quick Start
|
|
|
|
### Prerequisites
|
|
|
|
- Rust 1.83+ (edition 2024)
|
|
- Just command runner
|
|
|
|
### Development
|
|
|
|
```bash
|
|
# Run development server
|
|
just run
|
|
|
|
# Run tests
|
|
just test
|
|
|
|
# Run linter
|
|
just lint
|
|
|
|
# Format code
|
|
just format
|
|
|
|
# Watch mode with bacon
|
|
bacon # clippy-all (default)
|
|
bacon test # test watcher
|
|
```
|
|
|
|
### Configuration
|
|
|
|
Edit `backend/settings/base.yaml` for Modbus device settings:
|
|
|
|
```yaml
|
|
modbus:
|
|
host: "192.168.0.200"
|
|
port: 502
|
|
slave_id: 0
|
|
timeout_secs: 5
|
|
|
|
relay:
|
|
label_max_length: 50
|
|
```
|
|
|
|
Override with environment variables:
|
|
```bash
|
|
APP__MODBUS__HOST=192.168.1.100 cargo run
|
|
```
|
|
|
|
#### CORS Configuration
|
|
|
|
**Development Mode** (frontend on `localhost:5173`):
|
|
|
|
```yaml
|
|
# backend/settings/development.yaml
|
|
cors:
|
|
allowed_origins:
|
|
- "*" # Permissive for local development
|
|
allow_credentials: false # MUST be false with wildcard
|
|
max_age_secs: 3600
|
|
```
|
|
|
|
**Production Mode** (frontend on Cloudflare Pages):
|
|
|
|
```yaml
|
|
# backend/settings/production.yaml
|
|
cors:
|
|
allowed_origins:
|
|
- "https://sta.yourdomain.com" # Specific origin only
|
|
allow_credentials: true # Required for Authelia authentication
|
|
max_age_secs: 3600
|
|
```
|
|
|
|
**Security Notes:**
|
|
- Wildcard `"*"` origin is **only allowed with `allow_credentials: false`**
|
|
- Production **must** use specific origins (e.g., `https://sta.example.com`)
|
|
- Multiple origins are supported as a list
|
|
- Credentials must be enabled for Authelia authentication to work
|
|
|
|
**Hardcoded Security Defaults:**
|
|
- **Methods**: GET, POST, PUT, PATCH, DELETE, OPTIONS (all required methods)
|
|
- **Headers**: content-type, authorization (minimum for API + auth)
|
|
|
|
**Fail-Safe Defaults:**
|
|
- `allowed_origins: []` (restrictive - no origins allowed)
|
|
- `allow_credentials: false`
|
|
- `max_age_secs: 3600` (1 hour)
|
|
|
|
See [CORS Configuration Guide](docs/cors-configuration.md) for complete documentation.
|
|
|
|
## API Documentation
|
|
|
|
The server provides OpenAPI documentation via Swagger UI:
|
|
- Swagger UI: `http://localhost:3100/`
|
|
- OpenAPI Spec: `http://localhost:3100/specs`
|
|
|
|
**Current Endpoints:**
|
|
- `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
|
|
|
|
## Project Structure
|
|
|
|
**Monorepo Layout:**
|
|
```
|
|
sta/ # Repository root
|
|
├── backend/ # Rust backend workspace member
|
|
│ ├── src/
|
|
│ │ ├── lib.rs - Library entry point
|
|
│ │ ├── main.rs - Binary entry point
|
|
│ │ ├── startup.rs - Application builder and server config
|
|
│ │ ├── 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
|
|
│ │ │
|
|
│ │ ├── application/ - Use cases and orchestration (Phase 3)
|
|
│ │ │ └── health/ - Health monitoring service
|
|
│ │ │ └── health_monitor.rs - HealthMonitor with state tracking
|
|
│ │ │
|
|
│ │ ├── 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
|
|
│ │ │
|
|
│ │ ├── 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
|
|
│ │
|
|
│ ├── 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
|
|
│
|
|
├── 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
|
|
│ └── 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
|
|
```
|
|
|
|
## Technology Stack
|
|
|
|
**Currently Used:**
|
|
- Rust 2024 edition
|
|
- Poem 3.1 (web framework with OpenAPI support)
|
|
- Tokio 1.48 (async runtime)
|
|
- tokio-modbus (Modbus TCP client for relay hardware)
|
|
- SQLx 0.8 (async SQLite with compile-time SQL verification)
|
|
- async-trait (async methods in traits)
|
|
- config (YAML configuration)
|
|
- tracing + tracing-subscriber (structured logging)
|
|
- governor (rate limiting)
|
|
- thiserror (error handling)
|
|
- serde + serde_yaml (configuration deserialization)
|
|
|
|
**Frontend** (scaffolding complete):
|
|
- Vue 3 + TypeScript
|
|
- Vite build tool
|
|
- openapi-typescript (type-safe API client generation)
|
|
|
|
## Testing Strategy
|
|
|
|
**Phase 0.5 CORS Testing:**
|
|
- **Unit Tests**: 11 tests in `backend/src/settings/cors.rs`
|
|
- CorsSettings deserialization (5 tests)
|
|
- From<CorsSettings> for Cors trait (6 tests)
|
|
- Security validation (wildcard + credentials check)
|
|
- **Integration Tests**: 9 tests in `backend/tests/cors_test.rs`
|
|
- Preflight OPTIONS requests
|
|
- Actual request CORS headers
|
|
- Max-age configuration
|
|
- Credentials handling
|
|
- Allowed methods verification
|
|
- Wildcard origin behavior
|
|
- Multiple origins support
|
|
- Unauthorized origin rejection
|
|
|
|
**Test Coverage Achieved**: 15 comprehensive tests covering all CORS scenarios
|
|
|
|
**Phase 2 Domain Layer Testing:**
|
|
- **Unit Tests**: 50+ tests embedded in domain modules
|
|
- RelayId validation (5 tests)
|
|
- RelayState serialization (3 tests)
|
|
- RelayLabel validation (5 tests)
|
|
- Relay aggregate behavior (8 tests)
|
|
- ModbusAddress conversion (3 tests)
|
|
- HealthStatus state transitions (15 tests)
|
|
- **TDD Approach**: Red-Green-Refactor for all implementations
|
|
- **Coverage**: 100% for domain layer (zero external dependencies)
|
|
|
|
**Test Coverage Achieved**: 100% domain layer coverage with comprehensive test suites
|
|
|
|
**Phase 3 Infrastructure Testing:**
|
|
- **MockRelayController Tests**: 6 tests in `mock_controller.rs`
|
|
- Read/write state operations
|
|
- Read/write all relay states
|
|
- Invalid relay ID handling
|
|
- Thread-safe concurrent access
|
|
- **ModbusRelayController Tests**: Hardware integration tests (#[ignore])
|
|
- Real hardware communication tests
|
|
- Connection timeout handling
|
|
- **SqliteRelayLabelRepository Tests**: Database layer tests
|
|
- CRUD operations on relay labels
|
|
- In-memory database for fast tests
|
|
- Compile-time SQL verification
|
|
- **HealthMonitor Tests**: 15+ tests in `health_monitor.rs`
|
|
- State transitions (Healthy -> Degraded -> Unhealthy)
|
|
- Recovery from failure states
|
|
- Concurrent access safety
|
|
|
|
**Test Coverage Achieved**: Comprehensive coverage across all layers with TDD approach
|
|
|
|
## Documentation
|
|
|
|
### Configuration Guides
|
|
- [CORS Configuration](docs/cors-configuration.md) - Cross-origin setup for frontend-backend communication
|
|
- [Modbus Hardware Documentation](docs/Modbus_POE_ETH_Relay.md) - 8-channel relay device documentation
|
|
|
|
### Architecture Documentation
|
|
- [Domain Layer Architecture](docs/domain-layer.md) - Type-driven domain design and implementation
|
|
- [Domain Layer Details](specs/001-modbus-relay-control/domain-layer-architecture.md) - Comprehensive domain layer documentation
|
|
- [Phase 2 Lessons Learned](specs/001-modbus-relay-control/lessons-learned.md) - Implementation insights and best practices
|
|
|
|
### Development Guides
|
|
- [Project Constitution](specs/constitution.md) - Architectural principles and development guidelines
|
|
- [Modbus Relay Control Spec](specs/001-modbus-relay-control/spec.md) - Feature specification
|
|
- [CLAUDE.md](CLAUDE.md) - Developer guide and code style rules
|
|
|
|
## License
|
|
|
|
This project is under the AGPL-3.0 license. You can find it in the [LICENSE.md](LICENSE.md) file.
|