477 lines
26 KiB
Markdown
477 lines
26 KiB
Markdown
# Architecture Research: RTU Web Interface
|
|
|
|
**Domain:** Embedded IoT/SCADA Web Interface for Rainfall Monitoring
|
|
**Researched:** 2026-03-13
|
|
**Confidence:** HIGH
|
|
|
|
## System Overview
|
|
|
|
Modern RTU (Remote Terminal Unit) web interfaces are embedded applications that bridge physical sensors with human operators. For a Raspberry Pi-based rainfall monitoring system, the architecture must balance real-time data visualization, configuration management, and remote accessibility while operating on constrained hardware.
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ PRESENTATION LAYER │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
|
│ │ Dashboard UI │ │ Settings UI │ │ Calibration UI │ │
|
|
│ │ (Home Screen) │ │ (11 Submenus) │ │ (ADC/Levels) │ │
|
|
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
|
|
│ │ │ │ │
|
|
│ └────────────────────┼────────────────────┘ │
|
|
│ ↓ │
|
|
│ ┌─────────────────────┐ │
|
|
│ │ Navigation/Router │ │
|
|
│ └─────────────────────┘ │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ STATE MANAGEMENT LAYER │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ Sensor Data Store │ │
|
|
│ │ (Rainfall, Voltage, ADC, Status) │ │
|
|
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ Configuration Store │ │
|
|
│ │ (Settings, Calibration, Network, Station Info) │ │
|
|
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ DATA ACCESS LAYER │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
│ │ Sensor API │ │ Config API │ │ File Manager │ │ Network API │ │
|
|
│ │ (Real-time) │ │ (CRUD) │ │ (CSV/Flash) │ │ (FTP/SCP) │ │
|
|
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
│ │ │ │ │ │
|
|
├───────────┴──────────────┴──────────────┴──────────────┴───────────────────┤
|
|
│ HARDWARE INTERFACE LAYER │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
│ │ Rainfall │ │ ADC Inputs │ │ GPIO/Power │ │ Network │ │
|
|
│ │ Sensor │ │ (4-20mA) │ │ Monitoring │ │ Interface │ │
|
|
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Component Responsibilities
|
|
|
|
| Component | Responsibility | Implementation Notes |
|
|
|-----------|----------------|---------------------|
|
|
| **Dashboard UI** | Real-time sensor visualization, status indicators, quick actions | Optimized for 1024x600, refresh every 1-5s |
|
|
| **Settings UI** | 11 configuration views (Utility, Calibration, Flash, Mobile, ADC, Rain, EVAP, GPRS, Level, Siren, Network) | Form-based, validation, persistence |
|
|
| **Calibration UI** | ADC channel calibration, level sensor calibration | Wizard-style, step-by-step |
|
|
| **Navigation** | Route management between views, breadcrumb trails | React Router, conditional rendering |
|
|
| **Sensor Data Store** | Real-time sensor readings, historical data buffering | In-memory + localStorage for resilience |
|
|
| **Config Store** | Application settings, station configuration, calibration values | localStorage/IndexedDB |
|
|
| **API Layer** | Abstract hardware communication, RESTful interface to backend | Fetch API, error handling, retries |
|
|
| **Hardware Interface** | GPIO access, serial communication, ADC reading | Python backend or Node.js native addons |
|
|
|
|
## Recommended Project Structure
|
|
|
|
```
|
|
src/
|
|
├── components/
|
|
│ ├── ui/ # shadcn/ui base components
|
|
│ │ ├── button.tsx
|
|
│ │ ├── card.tsx
|
|
│ │ ├── input.tsx
|
|
│ │ ├── select.tsx
|
|
│ │ └── ...
|
|
│ ├── dashboard/ # Dashboard-specific components
|
|
│ │ ├── SensorCard.tsx
|
|
│ │ ├── RainfallDisplay.tsx
|
|
│ │ ├── VoltageGauge.tsx
|
|
│ │ └── StatusBar.tsx
|
|
│ ├── settings/ # Settings view components
|
|
│ │ ├── SettingsLayout.tsx
|
|
│ │ ├── SettingsNav.tsx
|
|
│ │ └── forms/
|
|
│ │ ├── AdcSettingsForm.tsx
|
|
│ │ ├── NetworkSettingsForm.tsx
|
|
│ │ └── ...
|
|
│ └── layout/ # App shell components
|
|
│ ├── AppLayout.tsx
|
|
│ ├── Header.tsx
|
|
│ ├── Sidebar.tsx
|
|
│ └── KioskWrapper.tsx
|
|
├── hooks/
|
|
│ ├── useSensorData.ts # Real-time sensor data hook
|
|
│ ├── useConfig.ts # Configuration management hook
|
|
│ ├── useHardware.ts # Hardware status hook
|
|
│ └── useNetwork.ts # Network status hook
|
|
├── stores/
|
|
│ ├── sensorStore.ts # Sensor data state management
|
|
│ ├── configStore.ts # Configuration state management
|
|
│ └── uiStore.ts # UI state (theme, layout mode)
|
|
├── services/
|
|
│ ├── api.ts # API client configuration
|
|
│ ├── sensorService.ts # Sensor data API calls
|
|
│ ├── configService.ts # Configuration API calls
|
|
│ └── fileService.ts # File management API calls
|
|
├── types/
|
|
│ ├── sensor.ts # Sensor data types
|
|
│ ├── config.ts # Configuration types
|
|
│ └── api.ts # API response types
|
|
├── utils/
|
|
│ ├── formatters.ts # Data formatting utilities
|
|
│ ├── validators.ts # Form validation
|
|
│ └── constants.ts # App constants
|
|
├── views/
|
|
│ ├── Dashboard.tsx # Main dashboard view
|
|
│ ├── Settings/ # Settings views
|
|
│ │ ├── index.tsx
|
|
│ │ ├── AdcSettings.tsx
|
|
│ │ ├── NetworkSettings.tsx
|
|
│ │ └── ...
|
|
│ ├── Calibration.tsx # Calibration view
|
|
│ └── FlashMemory.tsx # File manager view
|
|
├── App.tsx # Root component
|
|
├── main.tsx # Entry point
|
|
└── index.css # Global styles + Tailwind
|
|
```
|
|
|
|
### Structure Rationale
|
|
|
|
- **components/ui/:** shadcn/ui components are copy-paste ready, open for modification
|
|
- **components/dashboard/:** Dashboard-specific visualization components, optimized for 7" display
|
|
- **components/settings/:** Modular settings forms, one per configuration category
|
|
- **hooks/:** Custom React hooks for data fetching and hardware interaction
|
|
- **stores/:** State management (Zustand recommended for minimal overhead)
|
|
- **services/:** API abstraction layer for hardware communication
|
|
- **views/:** Page-level components corresponding to routes
|
|
|
|
## Architectural Patterns
|
|
|
|
### Pattern 1: Dual-Mode Display Architecture
|
|
|
|
**What:** Single codebase supporting two display modes:
|
|
- **Kiosk Mode (1024x600):** Fixed layout, touch-optimized, no browser chrome
|
|
- **Remote Mode (Full HD):** Responsive layout, desktop-friendly, expanded details
|
|
|
|
**When to use:** When RTU has both local touchscreen display and remote web access requirements.
|
|
|
|
**Trade-offs:**
|
|
- **Pros:** Single codebase, consistent UX, easier maintenance
|
|
- **Cons:** Conditional complexity, testing matrix doubles
|
|
|
|
**Implementation:**
|
|
```typescript
|
|
// Detect display mode based on port or viewport
|
|
const useDisplayMode = () => {
|
|
const [mode, setMode] = useState<'kiosk' | 'remote'>('kiosk');
|
|
|
|
useEffect(() => {
|
|
// Detect from window location (port 8080 = kiosk, 9090 = remote)
|
|
const port = window.location.port;
|
|
setMode(port === '9090' ? 'remote' : 'kiosk');
|
|
}, []);
|
|
|
|
return mode;
|
|
};
|
|
|
|
// Usage in components
|
|
const SensorCard = () => {
|
|
const mode = useDisplayMode();
|
|
return mode === 'kiosk' ? <CompactCard /> : <DetailedCard />;
|
|
};
|
|
```
|
|
|
|
### Pattern 2: Polling-Based Real-Time Updates
|
|
|
|
**What:** Periodic data fetching with optimistic UI updates for sensor readings.
|
|
|
|
**When to use:** When WebSocket is not available or too resource-intensive for embedded hardware.
|
|
|
|
**Trade-offs:**
|
|
- **Pros:** Simple, works on constrained hardware, battery-friendly
|
|
- **Cons:** Not truly real-time, potential for stale data
|
|
|
|
**Implementation:**
|
|
```typescript
|
|
const useSensorPolling = (interval = 5000) => {
|
|
const [data, setData] = useState<SensorData | null>(null);
|
|
const [isStale, setIsStale] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const fetchData = async () => {
|
|
try {
|
|
const response = await sensorService.getCurrent();
|
|
setData(response);
|
|
setIsStale(false);
|
|
} catch (error) {
|
|
setIsStale(true);
|
|
}
|
|
};
|
|
|
|
fetchData();
|
|
const intervalId = setInterval(fetchData, interval);
|
|
|
|
return () => clearInterval(intervalId);
|
|
}, [interval]);
|
|
|
|
return { data, isStale };
|
|
};
|
|
```
|
|
|
|
### Pattern 3: Offline-First Configuration
|
|
|
|
**What:** Configuration changes persisted locally first, synced to backend asynchronously.
|
|
|
|
**When to use:** When network connectivity is intermittent or when critical settings must survive power cycles.
|
|
|
|
**Trade-offs:**
|
|
- **Pros:** Resilient to failures, fast UI response, works offline
|
|
- **Cons:** Conflict resolution complexity, potential for desync
|
|
|
|
**Implementation:**
|
|
```typescript
|
|
interface ConfigStore {
|
|
local: Config;
|
|
remote: Config | null;
|
|
syncStatus: 'synced' | 'pending' | 'error';
|
|
updateConfig: (update: Partial<Config>) => Promise<void>;
|
|
}
|
|
|
|
const useConfigStore = create<ConfigStore>((set, get) => ({
|
|
local: loadFromLocalStorage(),
|
|
remote: null,
|
|
syncStatus: 'synced',
|
|
|
|
updateConfig: async (update) => {
|
|
// Update local immediately
|
|
const newConfig = { ...get().local, ...update };
|
|
set({ local: newConfig, syncStatus: 'pending' });
|
|
saveToLocalStorage(newConfig);
|
|
|
|
// Sync to backend
|
|
try {
|
|
await configService.save(newConfig);
|
|
set({ remote: newConfig, syncStatus: 'synced' });
|
|
} catch (error) {
|
|
set({ syncStatus: 'error' });
|
|
// Will retry on next connection
|
|
}
|
|
}
|
|
}));
|
|
```
|
|
|
|
## Data Flow
|
|
|
|
### Request Flow
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ USER ACTION │
|
|
│ (Touch/Button Click) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ REACT COMPONENT │
|
|
│ (Event Handler Triggered) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ CUSTOM HOOK │
|
|
│ (Business Logic / Validation) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ SERVICE LAYER │
|
|
│ (API Call to Python Backend) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ HARDWARE LAYER │
|
|
│ (GPIO / Serial / ADC Read/Write) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ RESPONSE FLOW │
|
|
│ (Data → Store → Component Re-render) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### State Management
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ ZUSTAND STORE │
|
|
├─────────────────────────────────────────────────────────────────────────────┤
|
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
|
│ │ Sensor Data │ │ Configuration │ │ UI State │ │
|
|
│ │ (Real-time) │ │ (Persistent) │ │ (Transient) │ │
|
|
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
|
|
│ │ │ │ │
|
|
│ └────────────────────┼────────────────────┘ │
|
|
│ ↓ │
|
|
│ ┌─────────────────────┐ │
|
|
│ │ Selectors / Hooks │ │
|
|
│ └─────────────────────┘ │
|
|
│ ↓ │
|
|
│ ┌─────────────────────┐ │
|
|
│ │ React Components │ │
|
|
│ └─────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Key Data Flows
|
|
|
|
1. **Sensor Data Flow:**
|
|
- Hardware (GPIO/ADC) → Python Backend → REST API → React Hook → Component
|
|
- Update frequency: 1-5 seconds for display, configurable for logging
|
|
|
|
2. **Configuration Flow:**
|
|
- User Input → Validation → Local Storage → API Sync → Hardware Apply
|
|
- Two-phase commit: UI optimistic, backend async
|
|
|
|
3. **File Management Flow:**
|
|
- CSV files stored on Flash/USB → Python File Service → Frontend Display
|
|
- Upload/Download via HTTP multipart or WebDAV/FTP/SCP for remote
|
|
|
|
## Build Order Implications
|
|
|
|
Based on component dependencies, suggested build order:
|
|
|
|
```
|
|
Phase 1: Foundation
|
|
├── Core UI components (shadcn/ui setup)
|
|
├── Type definitions
|
|
├── API client infrastructure
|
|
└── Basic routing
|
|
|
|
Phase 2: Dashboard
|
|
├── Sensor data hooks
|
|
├── Dashboard layout
|
|
├── Sensor card components
|
|
└── Real-time data display
|
|
|
|
Phase 3: Settings Framework
|
|
├── Settings navigation
|
|
├── Form components
|
|
├── Config store
|
|
└── Settings persistence
|
|
|
|
Phase 4: Individual Settings
|
|
├── Station Info
|
|
├── Date/Time
|
|
├── Network Setup
|
|
├── Mobile Settings
|
|
└── ADC Settings
|
|
|
|
Phase 5: Advanced Features
|
|
├── Calibration views
|
|
├── File manager
|
|
├── Network stack (FTP/SCP/SFTP/WebDAV)
|
|
└── CSV processing
|
|
|
|
Phase 6: Dual-Mode Support
|
|
├── Kiosk mode optimization (1024x600)
|
|
├── Remote mode layout (Full HD)
|
|
├── Responsive adaptations
|
|
└── Performance tuning
|
|
```
|
|
|
|
**Critical Path:**
|
|
- Dashboard requires sensor API → Build sensor service first
|
|
- Settings require config API → Build config service before settings forms
|
|
- File manager requires backend file service → Coordinate with Python backend team
|
|
|
|
## Scaling Considerations
|
|
|
|
| Scale | Users | Architecture Adjustments |
|
|
|-------|-------|-------------------------|
|
|
| **Single RTU** | 1 local + few remote | Current architecture sufficient |
|
|
| **Multi-RTU Fleet** | 10-100 devices | Add centralized management dashboard |
|
|
| **Enterprise** | 1000+ devices | Separate management server, device fleet API |
|
|
|
|
### Scaling Priorities for Single RTU
|
|
|
|
1. **First bottleneck:** UI responsiveness on Pi Zero 2 W
|
|
- **Fix:** Code splitting, lazy loading, virtual scrolling for large datasets
|
|
|
|
2. **Second bottleneck:** Storage for historical data
|
|
- **Fix:** Implement data retention policies, CSV rotation, external storage
|
|
|
|
3. **Third bottleneck:** Network throughput for file transfers
|
|
- **Fix:** Compression, chunked transfers, background sync
|
|
|
|
## Anti-Patterns to Avoid
|
|
|
|
### Anti-Pattern 1: Heavy Frameworks on Embedded
|
|
|
|
**What people do:** Use Next.js, heavy state management (Redux), or large component libraries.
|
|
|
|
**Why it's wrong:** Pi Zero 2 W has limited RAM (512MB) and single-core performance. Bundle size directly impacts load time and runtime performance.
|
|
|
|
**Do this instead:**
|
|
- Use Vite for minimal overhead build
|
|
- Zustand or React Context for state (not Redux)
|
|
- Copy-paste components (shadcn/ui) instead of importing entire libraries
|
|
- Tree-shake aggressively, analyze bundle size
|
|
|
|
### Anti-Pattern 2: Real-Time Everything
|
|
|
|
**What people do:** WebSocket connections for all data, constant polling at high frequency.
|
|
|
|
**Why it's wrong:** Drains battery (if solar-powered), saturates network, unnecessary for rainfall data that changes slowly.
|
|
|
|
**Do this instead:**
|
|
- Adaptive polling: fast during events, slow during idle
|
|
- Only subscribe to actively viewed sensors
|
|
- Use caching and display stale data with indicators
|
|
|
|
### Anti-Pattern 3: Tight Hardware Coupling
|
|
|
|
**What people do:** Direct GPIO access from frontend JavaScript (via unsafe methods).
|
|
|
|
**Why it's wrong:** Security risk, platform lock-in, hard to test.
|
|
|
|
**Do this instead:**
|
|
- Clean API boundary between UI and hardware
|
|
- Python backend service for hardware access
|
|
- Mock API for development/testing
|
|
|
|
### Anti-Pattern 4: Ignoring Kiosk Constraints
|
|
|
|
**What people do:** Design for desktop, shoehorn into kiosk mode.
|
|
|
|
**Why it's wrong:** 1024x600 is cramped, touch targets must be large, no right-click context menus.
|
|
|
|
**Do this instead:**
|
|
- Design mobile-first, even for kiosk
|
|
- Minimum 44px touch targets
|
|
- Large fonts, high contrast
|
|
- No hover-dependent interactions
|
|
|
|
## Integration Points
|
|
|
|
### External Services
|
|
|
|
| Service | Integration Pattern | Notes |
|
|
|---------|---------------------|-------|
|
|
| **Sensor Hardware** | REST API via Python backend | GPIO, I2C, SPI abstraction |
|
|
| **Network Stack** | Background service + API | FTP/SCP/SFTP/WebDAV daemons |
|
|
| **CSV Export** | File system + HTTP download | Scheduled generation, on-demand download |
|
|
| **Remote Server** | HTTP POST / MQTT | Data transmission, status heartbeat |
|
|
|
|
### Internal Boundaries
|
|
|
|
| Boundary | Communication | Notes |
|
|
|----------|---------------|-------|
|
|
| **Frontend ↔ Backend** | HTTP REST API | JSON, stateless, retry logic |
|
|
| **Backend ↔ Hardware** | Python libraries | GPIOZero, smbus2, spidev |
|
|
| **Config ↔ Storage** | localStorage/IndexedDB | Offline resilience |
|
|
| **UI ↔ State** | Zustand hooks | Minimal re-renders |
|
|
|
|
## Sources
|
|
|
|
- Wikipedia: Remote Terminal Unit (RTU) - https://en.wikipedia.org/wiki/Remote_terminal_unit
|
|
- React Documentation - https://react.dev/learn
|
|
- Vite Documentation - https://vitejs.dev/guide/
|
|
- shadcn/ui Documentation - https://ui.shadcn.com/docs
|
|
- MDN: Progressive Web Apps - https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps
|
|
- Raspberry Pi GPIO Documentation (training data)
|
|
- SCADA System Architecture Patterns (training data)
|
|
|
|
---
|
|
*Architecture research for: TCKRTUIYO RTU Rainfall Monitoring System*
|
|
*Researched: 2026-03-13*
|