cleanup
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/data_pool.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,667 @@
|
||||
# Data Pool Component Specification
|
||||
|
||||
**Component ID:** COMP-DATA-POOL
|
||||
**Version:** 1.0
|
||||
**Date:** 2025-01-19
|
||||
**Location:** `application_layer/DP_stack/data_pool/`
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
The Data Pool component provides centralized, thread-safe storage for runtime system data including latest sensor values, system state, diagnostic information, and configuration data. It serves as the single source of truth for all runtime data and provides fast access for components that need current system information.
|
||||
|
||||
## 2. Responsibilities
|
||||
|
||||
### 2.1 Primary Responsibilities
|
||||
|
||||
- **Runtime Data Storage:** Maintain latest sensor data, system state, and diagnostic information
|
||||
- **Thread-Safe Access:** Provide concurrent read/write access with appropriate synchronization
|
||||
- **Data Consistency:** Ensure data integrity across multiple readers and writers
|
||||
- **Fast Data Access:** Provide low-latency access to frequently accessed data
|
||||
- **Data Validation:** Validate data integrity and consistency on updates
|
||||
- **Memory Management:** Efficient memory usage with bounded storage requirements
|
||||
|
||||
### 2.2 Non-Responsibilities
|
||||
|
||||
- **Data Persistence:** Delegated to Persistence component (long-term storage)
|
||||
- **Data Processing:** Components handle their own data processing logic
|
||||
- **Network Communication:** Delegated to Communication components
|
||||
- **Hardware Access:** No direct hardware interface
|
||||
|
||||
## 3. Public API
|
||||
|
||||
### 3.1 Sensor Data Management
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Update sensor data record
|
||||
* @param sensor_id Sensor identifier (0-6)
|
||||
* @param record Sensor data record to store
|
||||
* @return true if update successful, false on error
|
||||
*/
|
||||
bool dataPool_updateSensorData(uint8_t sensor_id, const sensor_data_record_t* record);
|
||||
|
||||
/**
|
||||
* @brief Get latest sensor data record
|
||||
* @param sensor_id Sensor identifier (0-6)
|
||||
* @param record Output buffer for sensor data record
|
||||
* @return true if data retrieved, false on error
|
||||
*/
|
||||
bool dataPool_getSensorData(uint8_t sensor_id, sensor_data_record_t* record);
|
||||
|
||||
/**
|
||||
* @brief Get all sensor data records
|
||||
* @param records Output buffer for sensor data records
|
||||
* @param count Input: buffer size, Output: number of records filled
|
||||
* @return true if data retrieved, false on error
|
||||
*/
|
||||
bool dataPool_getAllSensorData(sensor_data_record_t* records, size_t* count);
|
||||
|
||||
/**
|
||||
* @brief Check if sensor data is available and valid
|
||||
* @param sensor_id Sensor identifier (0-6)
|
||||
* @return true if valid data available, false otherwise
|
||||
*/
|
||||
bool dataPool_isSensorDataValid(uint8_t sensor_id);
|
||||
|
||||
/**
|
||||
* @brief Get timestamp of last sensor data update
|
||||
* @param sensor_id Sensor identifier (0-6)
|
||||
* @return Timestamp of last update, 0 if no data available
|
||||
*/
|
||||
uint64_t dataPool_getSensorDataTimestamp(uint8_t sensor_id);
|
||||
```
|
||||
|
||||
### 3.2 System State Management
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Update system state information
|
||||
* @param state_info System state information structure
|
||||
* @return true if update successful, false on error
|
||||
*/
|
||||
bool dataPool_updateSystemState(const system_state_info_t* state_info);
|
||||
|
||||
/**
|
||||
* @brief Get current system state information
|
||||
* @param state_info Output buffer for system state information
|
||||
* @return true if data retrieved, false on error
|
||||
*/
|
||||
bool dataPool_getSystemState(system_state_info_t* state_info);
|
||||
|
||||
/**
|
||||
* @brief Update system health metrics
|
||||
* @param health_metrics System health metrics structure
|
||||
* @return true if update successful, false on error
|
||||
*/
|
||||
bool dataPool_updateHealthMetrics(const system_health_metrics_t* health_metrics);
|
||||
|
||||
/**
|
||||
* @brief Get system health metrics
|
||||
* @param health_metrics Output buffer for health metrics
|
||||
* @return true if data retrieved, false on error
|
||||
*/
|
||||
bool dataPool_getHealthMetrics(system_health_metrics_t* health_metrics);
|
||||
```
|
||||
|
||||
### 3.3 Diagnostic Data Management
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Add diagnostic event to pool
|
||||
* @param event Diagnostic event structure
|
||||
* @return true if event added, false on error
|
||||
*/
|
||||
bool dataPool_addDiagnosticEvent(const diagnostic_event_t* event);
|
||||
|
||||
/**
|
||||
* @brief Get recent diagnostic events
|
||||
* @param events Output buffer for diagnostic events
|
||||
* @param count Input: buffer size, Output: number of events filled
|
||||
* @return true if events retrieved, false on error
|
||||
*/
|
||||
bool dataPool_getRecentDiagnostics(diagnostic_event_t* events, size_t* count);
|
||||
|
||||
/**
|
||||
* @brief Get diagnostic summary (counts by severity)
|
||||
* @param summary Output buffer for diagnostic summary
|
||||
* @return true if summary retrieved, false on error
|
||||
*/
|
||||
bool dataPool_getDiagnosticSummary(diagnostic_summary_t* summary);
|
||||
|
||||
/**
|
||||
* @brief Clear diagnostic events from pool
|
||||
* @param severity Severity level to clear (DIAG_SEVERITY_ALL for all)
|
||||
* @return Number of events cleared
|
||||
*/
|
||||
size_t dataPool_clearDiagnostics(diagnostic_severity_t severity);
|
||||
```
|
||||
|
||||
### 3.4 Communication Status Management
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Update communication link status
|
||||
* @param link_type Communication link type
|
||||
* @param status Link status information
|
||||
* @return true if update successful, false on error
|
||||
*/
|
||||
bool dataPool_updateLinkStatus(comm_link_type_t link_type, const comm_link_status_t* status);
|
||||
|
||||
/**
|
||||
* @brief Get communication link status
|
||||
* @param link_type Communication link type
|
||||
* @param status Output buffer for link status
|
||||
* @return true if status retrieved, false on error
|
||||
*/
|
||||
bool dataPool_getLinkStatus(comm_link_type_t link_type, comm_link_status_t* status);
|
||||
|
||||
/**
|
||||
* @brief Get overall communication status
|
||||
* @param comm_status Output buffer for communication status
|
||||
* @return true if status retrieved, false on error
|
||||
*/
|
||||
bool dataPool_getCommunicationStatus(communication_status_t* comm_status);
|
||||
```
|
||||
|
||||
### 3.5 Configuration Data Management
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Update runtime configuration
|
||||
* @param config_type Configuration type
|
||||
* @param config_data Configuration data
|
||||
* @param data_size Size of configuration data
|
||||
* @return true if update successful, false on error
|
||||
*/
|
||||
bool dataPool_updateConfiguration(config_type_t config_type, const void* config_data, size_t data_size);
|
||||
|
||||
/**
|
||||
* @brief Get runtime configuration
|
||||
* @param config_type Configuration type
|
||||
* @param config_data Output buffer for configuration data
|
||||
* @param data_size Input: buffer size, Output: actual data size
|
||||
* @return true if configuration retrieved, false on error
|
||||
*/
|
||||
bool dataPool_getConfiguration(config_type_t config_type, void* config_data, size_t* data_size);
|
||||
|
||||
/**
|
||||
* @brief Check if configuration is valid
|
||||
* @param config_type Configuration type
|
||||
* @return true if configuration is valid, false otherwise
|
||||
*/
|
||||
bool dataPool_isConfigurationValid(config_type_t config_type);
|
||||
```
|
||||
|
||||
### 3.6 Data Pool Management
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Initialize Data Pool component
|
||||
* @return true if initialization successful, false otherwise
|
||||
*/
|
||||
bool dataPool_initialize(void);
|
||||
|
||||
/**
|
||||
* @brief Get Data Pool statistics
|
||||
* @param stats Output buffer for statistics
|
||||
* @return true if statistics retrieved, false on error
|
||||
*/
|
||||
bool dataPool_getStatistics(data_pool_stats_t* stats);
|
||||
|
||||
/**
|
||||
* @brief Reset Data Pool statistics
|
||||
* @return true if statistics reset, false on error
|
||||
*/
|
||||
bool dataPool_resetStatistics(void);
|
||||
|
||||
/**
|
||||
* @brief Validate Data Pool integrity
|
||||
* @return true if integrity check passed, false if corruption detected
|
||||
*/
|
||||
bool dataPool_validateIntegrity(void);
|
||||
|
||||
/**
|
||||
* @brief Create snapshot of current data pool state
|
||||
* @param snapshot Output buffer for snapshot
|
||||
* @return true if snapshot created, false on error
|
||||
*/
|
||||
bool dataPool_createSnapshot(data_pool_snapshot_t* snapshot);
|
||||
```
|
||||
|
||||
## 4. Data Types
|
||||
|
||||
### 4.1 Sensor Data Record (Extended)
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint8_t sensor_id; // Sensor identifier (0-6)
|
||||
sensor_type_t sensor_type; // Type of sensor
|
||||
float filtered_value; // Processed sensor value
|
||||
char unit[8]; // Unit of measurement
|
||||
uint64_t timestamp_ms; // Timestamp in milliseconds
|
||||
data_validity_t validity; // Data validity status
|
||||
uint16_t sample_count; // Number of samples used
|
||||
float raw_min, raw_max; // Min/max of raw samples
|
||||
float raw_stddev; // Standard deviation
|
||||
uint32_t acquisition_time_us; // Acquisition time
|
||||
uint32_t sequence_number; // Monotonic sequence number
|
||||
uint16_t checksum; // Data integrity checksum
|
||||
} sensor_data_record_t;
|
||||
```
|
||||
|
||||
### 4.2 System State Information
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
system_state_t current_state; // Current system state
|
||||
system_state_t previous_state; // Previous system state
|
||||
transition_reason_t last_reason; // Last transition reason
|
||||
uint64_t state_entry_time; // Time when current state was entered
|
||||
uint64_t state_duration_ms; // Duration in current state
|
||||
uint32_t state_transition_count; // Total number of state transitions
|
||||
bool teardown_in_progress; // Teardown sequence active
|
||||
uint8_t teardown_progress; // Teardown progress (0-100%)
|
||||
} system_state_info_t;
|
||||
```
|
||||
|
||||
### 4.3 System Health Metrics
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
// CPU and Memory
|
||||
float cpu_usage_percent; // Current CPU usage
|
||||
uint32_t free_heap_bytes; // Available heap memory
|
||||
uint32_t min_free_heap_bytes; // Minimum free heap recorded
|
||||
uint32_t heap_fragmentation; // Heap fragmentation percentage
|
||||
|
||||
// Task Information
|
||||
uint32_t task_count; // Number of active tasks
|
||||
uint32_t stack_high_water_mark; // Minimum remaining stack
|
||||
|
||||
// System Uptime
|
||||
uint64_t uptime_ms; // System uptime in milliseconds
|
||||
uint32_t boot_count; // Number of system boots
|
||||
|
||||
// Storage
|
||||
uint64_t sd_free_bytes; // SD card free space
|
||||
uint64_t sd_total_bytes; // SD card total space
|
||||
uint32_t nvm_free_entries; // NVM free entries
|
||||
|
||||
// Communication
|
||||
uint32_t wifi_rssi; // WiFi signal strength
|
||||
uint32_t packets_sent; // Total packets sent
|
||||
uint32_t packets_received; // Total packets received
|
||||
uint32_t communication_errors; // Communication error count
|
||||
|
||||
// Sensors
|
||||
uint8_t sensors_active; // Number of active sensors
|
||||
uint8_t sensors_faulty; // Number of faulty sensors
|
||||
uint32_t total_acquisitions; // Total sensor acquisitions
|
||||
|
||||
// Diagnostics
|
||||
uint32_t warning_count; // Active warning count
|
||||
uint32_t error_count; // Active error count
|
||||
uint32_t fatal_count; // Fatal error count
|
||||
|
||||
uint64_t last_update_time; // Last metrics update time
|
||||
} system_health_metrics_t;
|
||||
```
|
||||
|
||||
### 4.4 Diagnostic Event
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint16_t diagnostic_code; // Diagnostic code (0xSCCC format)
|
||||
diagnostic_severity_t severity; // Severity level
|
||||
uint64_t timestamp_ms; // Event timestamp
|
||||
uint32_t occurrence_count; // Number of occurrences
|
||||
char description[64]; // Human-readable description
|
||||
uint8_t context_data[32]; // Context-specific data
|
||||
uint32_t sequence_number; // Event sequence number
|
||||
} diagnostic_event_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t info_count; // Number of INFO events
|
||||
uint32_t warning_count; // Number of WARNING events
|
||||
uint32_t error_count; // Number of ERROR events
|
||||
uint32_t fatal_count; // Number of FATAL events
|
||||
uint64_t last_event_time; // Timestamp of last event
|
||||
uint16_t most_recent_code; // Most recent diagnostic code
|
||||
} diagnostic_summary_t;
|
||||
```
|
||||
|
||||
### 4.5 Communication Status
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
COMM_LINK_MAIN_HUB = 0, // Main Hub communication
|
||||
COMM_LINK_PEER_HUB, // Peer Hub communication
|
||||
COMM_LINK_DIAGNOSTIC, // Diagnostic communication
|
||||
COMM_LINK_COUNT
|
||||
} comm_link_type_t;
|
||||
|
||||
typedef struct {
|
||||
bool is_connected; // Connection status
|
||||
uint64_t last_activity_time; // Last communication activity
|
||||
uint32_t bytes_sent; // Bytes sent
|
||||
uint32_t bytes_received; // Bytes received
|
||||
uint32_t error_count; // Communication errors
|
||||
int32_t signal_strength; // Signal strength (RSSI)
|
||||
uint32_t round_trip_time_ms; // Average round-trip time
|
||||
} comm_link_status_t;
|
||||
|
||||
typedef struct {
|
||||
comm_link_status_t links[COMM_LINK_COUNT]; // Individual link status
|
||||
bool overall_connectivity; // Overall connectivity status
|
||||
uint64_t last_successful_comm; // Last successful communication
|
||||
uint32_t total_comm_failures; // Total communication failures
|
||||
} communication_status_t;
|
||||
```
|
||||
|
||||
### 4.6 Data Pool Statistics
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
// Access Statistics
|
||||
uint64_t total_reads; // Total read operations
|
||||
uint64_t total_writes; // Total write operations
|
||||
uint64_t read_errors; // Read operation errors
|
||||
uint64_t write_errors; // Write operation errors
|
||||
|
||||
// Performance Metrics
|
||||
uint32_t avg_read_time_us; // Average read time
|
||||
uint32_t avg_write_time_us; // Average write time
|
||||
uint32_t max_read_time_us; // Maximum read time
|
||||
uint32_t max_write_time_us; // Maximum write time
|
||||
|
||||
// Memory Usage
|
||||
uint32_t memory_used_bytes; // Current memory usage
|
||||
uint32_t max_memory_used_bytes; // Peak memory usage
|
||||
|
||||
// Data Integrity
|
||||
uint32_t checksum_failures; // Checksum validation failures
|
||||
uint32_t integrity_checks; // Total integrity checks performed
|
||||
|
||||
// Concurrency
|
||||
uint32_t concurrent_readers; // Current concurrent readers
|
||||
uint32_t max_concurrent_readers; // Maximum concurrent readers
|
||||
uint32_t lock_contentions; // Lock contention events
|
||||
|
||||
uint64_t statistics_reset_time; // Last statistics reset time
|
||||
} data_pool_stats_t;
|
||||
```
|
||||
|
||||
## 5. Internal Architecture
|
||||
|
||||
### 5.1 Data Pool Structure
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Data Pool Component"
|
||||
subgraph "Sensor Data Storage"
|
||||
SD0[Sensor 0 Data]
|
||||
SD1[Sensor 1 Data]
|
||||
SD2[Sensor 2 Data]
|
||||
SD3[Sensor 3 Data]
|
||||
SD4[Sensor 4 Data]
|
||||
SD5[Sensor 5 Data]
|
||||
SD6[Sensor 6 Data]
|
||||
end
|
||||
|
||||
subgraph "System Data Storage"
|
||||
SS[System State Info]
|
||||
HM[Health Metrics]
|
||||
CS[Communication Status]
|
||||
CF[Configuration Data]
|
||||
end
|
||||
|
||||
subgraph "Diagnostic Data Storage"
|
||||
DE[Diagnostic Events]
|
||||
DS[Diagnostic Summary]
|
||||
end
|
||||
|
||||
subgraph "Access Control"
|
||||
RM[Reader-Writer Mutex]
|
||||
IC[Integrity Checker]
|
||||
ST[Statistics Tracker]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph "External Components"
|
||||
SM[Sensor Manager]
|
||||
STM[State Manager]
|
||||
COMM[Communication]
|
||||
DIAG[Diagnostics]
|
||||
HMI[HMI]
|
||||
end
|
||||
|
||||
SM -->|Update| SD0
|
||||
SM -->|Update| SD1
|
||||
STM -->|Update| SS
|
||||
COMM -->|Update| CS
|
||||
DIAG -->|Update| DE
|
||||
|
||||
HMI -->|Read| SS
|
||||
HMI -->|Read| HM
|
||||
COMM -->|Read| SD0
|
||||
COMM -->|Read| SD1
|
||||
|
||||
RM -.->|Protects| SD0
|
||||
RM -.->|Protects| SS
|
||||
IC -.->|Validates| DE
|
||||
ST -.->|Tracks| RM
|
||||
```
|
||||
|
||||
### 5.2 Thread Safety Model
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant W1 as Writer 1
|
||||
participant W2 as Writer 2
|
||||
participant R1 as Reader 1
|
||||
participant R2 as Reader 2
|
||||
participant DP as Data Pool
|
||||
participant Mutex as RW Mutex
|
||||
|
||||
Note over W1,Mutex: Concurrent Access Scenario
|
||||
|
||||
W1->>Mutex: acquireWriteLock()
|
||||
Mutex-->>W1: lockAcquired
|
||||
W1->>DP: updateSensorData()
|
||||
|
||||
R1->>Mutex: acquireReadLock()
|
||||
Note over R1,Mutex: Blocked until write complete
|
||||
|
||||
W2->>Mutex: acquireWriteLock()
|
||||
Note over W2,Mutex: Blocked until write complete
|
||||
|
||||
W1->>DP: dataUpdateComplete
|
||||
W1->>Mutex: releaseWriteLock()
|
||||
|
||||
Mutex-->>R1: readLockAcquired
|
||||
R1->>DP: getSensorData()
|
||||
DP-->>R1: sensorData
|
||||
|
||||
R2->>Mutex: acquireReadLock()
|
||||
Mutex-->>R2: readLockAcquired
|
||||
R2->>DP: getSensorData()
|
||||
DP-->>R2: sensorData
|
||||
|
||||
R1->>Mutex: releaseReadLock()
|
||||
R2->>Mutex: releaseReadLock()
|
||||
|
||||
Mutex-->>W2: writeLockAcquired
|
||||
W2->>DP: updateSystemState()
|
||||
W2->>Mutex: releaseWriteLock()
|
||||
```
|
||||
|
||||
## 6. Threading Model
|
||||
|
||||
- **Access Model:** Multi-reader, single-writer with reader-writer mutex
|
||||
- **Thread Safety:** Fully thread-safe for all operations
|
||||
- **Blocking Operations:** Read operations may block on write operations (bounded)
|
||||
- **ISR Access:** Limited read-only access for critical data (lock-free atomic reads)
|
||||
- **Priority Inheritance:** Mutex supports priority inheritance to prevent priority inversion
|
||||
|
||||
## 7. Resource Ownership
|
||||
|
||||
- **Data Storage:** All runtime data owned exclusively by Data Pool
|
||||
- **Access Control:** Reader-writer mutex owned by Data Pool
|
||||
- **Memory Management:** Static allocation for all data structures (no dynamic allocation)
|
||||
- **Integrity Checking:** Checksum validation owned by Data Pool
|
||||
|
||||
## 8. Error Model
|
||||
|
||||
### 8.1 Error Conditions
|
||||
|
||||
| Error | Condition | Response |
|
||||
|-------|-----------|----------|
|
||||
| `DATAPOOL_ERR_INVALID_SENSOR_ID` | Invalid sensor ID (>6) | Return false, log warning |
|
||||
| `DATAPOOL_ERR_NULL_POINTER` | NULL pointer parameter | Return false, log error |
|
||||
| `DATAPOOL_ERR_BUFFER_TOO_SMALL` | Output buffer too small | Return false, set required size |
|
||||
| `DATAPOOL_ERR_DATA_STALE` | Data older than threshold | Return false, log info |
|
||||
| `DATAPOOL_ERR_CHECKSUM_FAILURE` | Data integrity check failed | Return false, log error |
|
||||
| `DATAPOOL_ERR_LOCK_TIMEOUT` | Failed to acquire lock | Return false, log warning |
|
||||
| `DATAPOOL_ERR_MEMORY_FULL` | Storage capacity exceeded | Overwrite oldest, log warning |
|
||||
|
||||
### 8.2 Diagnostics Emitted
|
||||
|
||||
- `DIAG-DP-POOL-0001`: Data integrity check failed (ERROR)
|
||||
- `DIAG-DP-POOL-0002`: Lock contention detected (WARNING)
|
||||
- `DIAG-DP-POOL-0003`: Memory usage high (WARNING)
|
||||
- `DIAG-DP-POOL-0004`: Stale data detected (INFO)
|
||||
|
||||
## 9. State-Dependent Behavior
|
||||
|
||||
| System State | Data Pool Behavior |
|
||||
|-------------|-------------------|
|
||||
| **INIT** | Initialize data structures, clear all data |
|
||||
| **RUNNING** | Normal operation, full read/write access |
|
||||
| **WARNING** | Continue operation, enhanced integrity checking |
|
||||
| **FAULT** | Read-only mode, preserve data integrity |
|
||||
| **OTA_UPDATE** | Read-only mode, prepare for snapshot |
|
||||
| **MC_UPDATE** | Limited updates, configuration reload |
|
||||
| **TEARDOWN** | Read-only mode, prepare for shutdown |
|
||||
| **SERVICE** | Full access, enhanced diagnostics |
|
||||
| **SD_DEGRADED** | Normal operation, no persistence coordination |
|
||||
|
||||
## 10. Dependencies
|
||||
|
||||
### 10.1 Required Components
|
||||
|
||||
- **Logger:** Debug and diagnostic logging
|
||||
- **Time Utils:** Timestamp generation for data records
|
||||
- **Error Handler:** Error reporting and escalation
|
||||
|
||||
### 10.2 Required Interfaces
|
||||
|
||||
- Logger interface for diagnostic output
|
||||
- Time Utils interface for timestamp generation
|
||||
- Error Handler interface for fault reporting
|
||||
|
||||
## 11. Performance Requirements
|
||||
|
||||
### 11.1 Access Performance
|
||||
|
||||
- **Read Operations:** Maximum 10μs for single sensor data read
|
||||
- **Write Operations:** Maximum 50μs for single sensor data write
|
||||
- **Bulk Operations:** Maximum 500μs for all sensor data read
|
||||
- **Lock Acquisition:** Maximum 1ms timeout for lock acquisition
|
||||
|
||||
### 11.2 Memory Requirements
|
||||
|
||||
- **Static Memory:** Maximum 64KB for all data structures
|
||||
- **Per-Sensor Data:** Maximum 1KB per sensor (including history)
|
||||
- **Diagnostic Storage:** Maximum 8KB for recent diagnostic events
|
||||
- **Configuration Storage:** Maximum 4KB for runtime configuration
|
||||
|
||||
## 12. Acceptance Tests
|
||||
|
||||
### 12.1 Functional Tests
|
||||
|
||||
- **T-DATAPOOL-001:** Sensor data storage and retrieval works correctly
|
||||
- **T-DATAPOOL-002:** System state information maintained accurately
|
||||
- **T-DATAPOOL-003:** Diagnostic events stored and retrieved correctly
|
||||
- **T-DATAPOOL-004:** Communication status tracking works
|
||||
- **T-DATAPOOL-005:** Configuration data management works
|
||||
|
||||
### 12.2 Concurrency Tests
|
||||
|
||||
- **T-DATAPOOL-006:** Multiple readers can access data simultaneously
|
||||
- **T-DATAPOOL-007:** Writers have exclusive access during updates
|
||||
- **T-DATAPOOL-008:** Reader-writer priority handling works correctly
|
||||
- **T-DATAPOOL-009:** Lock contention handled gracefully
|
||||
|
||||
### 12.3 Performance Tests
|
||||
|
||||
- **T-DATAPOOL-010:** Read operations complete within 10μs
|
||||
- **T-DATAPOOL-011:** Write operations complete within 50μs
|
||||
- **T-DATAPOOL-012:** Memory usage stays within 64KB limit
|
||||
- **T-DATAPOOL-013:** No memory leaks during continuous operation
|
||||
|
||||
### 12.4 Integrity Tests
|
||||
|
||||
- **T-DATAPOOL-014:** Data integrity checks detect corruption
|
||||
- **T-DATAPOOL-015:** Checksum validation works correctly
|
||||
- **T-DATAPOOL-016:** Stale data detection works
|
||||
- **T-DATAPOOL-017:** Statistics tracking is accurate
|
||||
|
||||
## 13. Traceability
|
||||
|
||||
### 13.1 System Requirements
|
||||
|
||||
- **SR-DATA-002:** Data Persistence Abstraction (runtime data management)
|
||||
- **SR-PERF-003:** Memory usage constraints
|
||||
- **SR-REL-004:** Data integrity requirements
|
||||
|
||||
### 13.2 Software Requirements
|
||||
|
||||
- **SWR-DATA-004:** DP component API definition
|
||||
- **SWR-DATA-005:** Storage media abstraction
|
||||
- **SWR-DATA-006:** Unified data access interface
|
||||
- **SWR-REL-010:** Error detection implementation
|
||||
|
||||
### 13.3 Features
|
||||
|
||||
- **F-DATA-002:** Data Persistence Abstraction (runtime component)
|
||||
- **F-DIAG-002:** Diagnostic Data Storage (runtime component)
|
||||
|
||||
## 14. Implementation Notes
|
||||
|
||||
### 14.1 Design Patterns
|
||||
|
||||
- **Singleton Pattern:** Single Data Pool instance per system
|
||||
- **Reader-Writer Lock Pattern:** Concurrent access control
|
||||
- **Observer Pattern:** Data change notifications (via Event System)
|
||||
- **Template Method Pattern:** Generic data access operations
|
||||
|
||||
### 14.2 Key Implementation Details
|
||||
|
||||
- All data structures SHALL use static allocation (no malloc/free)
|
||||
- Reader-writer mutex SHALL support priority inheritance
|
||||
- Data integrity SHALL be verified using checksums
|
||||
- Access statistics SHALL be maintained for performance monitoring
|
||||
- Atomic operations SHALL be used for lock-free ISR access
|
||||
|
||||
### 14.3 Memory Layout
|
||||
|
||||
```c
|
||||
// Static memory allocation structure
|
||||
typedef struct {
|
||||
sensor_data_record_t sensor_data[SENSOR_TYPE_COUNT];
|
||||
system_state_info_t system_state;
|
||||
system_health_metrics_t health_metrics;
|
||||
diagnostic_event_t diagnostic_events[MAX_DIAGNOSTIC_EVENTS];
|
||||
comm_link_status_t comm_links[COMM_LINK_COUNT];
|
||||
uint8_t configuration_data[MAX_CONFIG_SIZE];
|
||||
data_pool_stats_t statistics;
|
||||
pthread_rwlock_t access_lock;
|
||||
} data_pool_storage_t;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Document Status:** Final for Implementation Phase
|
||||
**Component Dependencies:** Verified against architecture
|
||||
**Requirements Traceability:** Complete (SR-DATA, SWR-DATA)
|
||||
**Next Review:** After implementation and testing
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file data_pool.cpp
|
||||
* @brief DataPool component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "data_pool.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
static const char* TAG = "DataPool";
|
||||
|
||||
DataPool::DataPool()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
DataPool::~DataPool()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool DataPool::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 4900, asf::logger::Criticality::LOW, "DataPool initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DataPool::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DataPool::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file data_pool.hpp
|
||||
* @brief DataPool component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef DATA_POOL_HPP
|
||||
#define DATA_POOL_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief DataPool class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class DataPool
|
||||
{
|
||||
public:
|
||||
DataPool();
|
||||
~DataPool();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // DATA_POOL_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
4900,DataPool,INFO,Low,DataPool initialized successfully
|
||||
|
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_data_pool_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "DataPool initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_data_pool_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>DATA_POOL_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/DP_stack/data_pool/test/data_pool_init_test.py</test_exec>
|
||||
</test_case>
|
||||
|
||||
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_data_pool.cpp
|
||||
* @brief Unit tests for DataPool component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "data_pool.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_data_pool_initialize(void)
|
||||
{
|
||||
DataPool comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_data_pool_deinitialize(void)
|
||||
{
|
||||
DataPool comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/persistence.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,257 @@
|
||||
# Persistence Component Specification
|
||||
|
||||
**Component ID:** COMP-PERSIST
|
||||
**Version:** 1.0
|
||||
**Date:** 2025-01-19
|
||||
**Location:** `application_layer/DP_stack/persistence/`
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
The Persistence component provides the sole interface for persistent storage access. It abstracts storage media (SD card, NVM), manages serialization/deserialization, implements wear-aware storage, and ensures data integrity.
|
||||
|
||||
## 2. Responsibilities
|
||||
|
||||
### 2.1 Primary Responsibilities
|
||||
|
||||
- Abstract storage media (SD card, NVM)
|
||||
- Serialize/deserialize structured data
|
||||
- Manage wear-aware storage (SD card)
|
||||
- Ensure data integrity
|
||||
- Coordinate data flush operations
|
||||
|
||||
### 2.2 Non-Responsibilities
|
||||
|
||||
- Business logic (data semantics owned by components)
|
||||
- Hardware access (delegated to storage drivers)
|
||||
- Data validation (components validate before persistence)
|
||||
|
||||
## 3. Public API
|
||||
|
||||
### 3.1 Sensor Data Persistence
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Write sensor data record
|
||||
* @param record Sensor data record
|
||||
* @return true if written, false on error
|
||||
*/
|
||||
bool persistence_writeSensorData(const sensor_data_record_t* record);
|
||||
|
||||
/**
|
||||
* @brief Read sensor data records
|
||||
* @param records Output buffer
|
||||
* @param count Input: buffer size, Output: records read
|
||||
* @param start_time Start timestamp filter (0 for all)
|
||||
* @param end_time End timestamp filter (0 for all)
|
||||
* @return true if read successful, false on error
|
||||
*/
|
||||
bool persistence_readSensorData(sensor_data_record_t* records, size_t* count, uint64_t start_time, uint64_t end_time);
|
||||
```
|
||||
|
||||
### 3.2 Diagnostic Persistence
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Write diagnostic event
|
||||
* @param event Diagnostic event
|
||||
* @return true if written, false on error
|
||||
*/
|
||||
bool persistence_writeDiagnostic(const diagnostic_event_t* event);
|
||||
|
||||
/**
|
||||
* @brief Read diagnostic events
|
||||
* @param events Output buffer
|
||||
* @param count Input: buffer size, Output: events read
|
||||
* @param filter Filter criteria (severity, component, etc.)
|
||||
* @return true if read successful, false on error
|
||||
*/
|
||||
bool persistence_readDiagnostics(diagnostic_event_t* events, size_t* count, const diag_filter_t* filter);
|
||||
|
||||
/**
|
||||
* @brief Clear diagnostic log
|
||||
* @return true if cleared, false on error
|
||||
*/
|
||||
bool persistence_clearDiagnostics(void);
|
||||
```
|
||||
|
||||
### 3.3 Machine Constants Persistence
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Write machine constants
|
||||
* @param mc Machine constants structure
|
||||
* @return true if written, false on error
|
||||
*/
|
||||
bool persistence_writeMachineConstants(const machine_constants_t* mc);
|
||||
|
||||
/**
|
||||
* @brief Read machine constants
|
||||
* @param mc Output buffer
|
||||
* @return true if read successful, false on error
|
||||
*/
|
||||
bool persistence_readMachineConstants(machine_constants_t* mc);
|
||||
```
|
||||
|
||||
### 3.4 Flush Operations
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Flush all critical data to storage
|
||||
* @return true if flushed, false on error
|
||||
*/
|
||||
bool persistence_flushCriticalData(void);
|
||||
|
||||
/**
|
||||
* @brief Check if flush is complete
|
||||
* @return true if flush complete, false otherwise
|
||||
*/
|
||||
bool persistence_isFlushComplete(void);
|
||||
|
||||
/**
|
||||
* @brief Get flush progress (0-100%)
|
||||
* @return Flush progress percentage
|
||||
*/
|
||||
uint8_t persistence_getFlushProgress(void);
|
||||
```
|
||||
|
||||
### 3.5 Storage Status
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Check storage availability
|
||||
* @param storage_type Storage type (SD_CARD, NVM)
|
||||
* @return true if available, false otherwise
|
||||
*/
|
||||
bool persistence_isStorageAvailable(storage_type_t storage_type);
|
||||
|
||||
/**
|
||||
* @brief Get storage usage statistics
|
||||
* @param storage_type Storage type
|
||||
* @param stats Output statistics
|
||||
* @return true if retrieved, false on error
|
||||
*/
|
||||
bool persistence_getStorageStats(storage_type_t storage_type, storage_stats_t* stats);
|
||||
```
|
||||
|
||||
## 4. Data Types
|
||||
|
||||
### 4.1 Storage Types
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
STORAGE_TYPE_SD_CARD = 0,
|
||||
STORAGE_TYPE_NVM,
|
||||
STORAGE_TYPE_COUNT
|
||||
} storage_type_t;
|
||||
```
|
||||
|
||||
### 4.2 Storage Statistics
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint64_t total_bytes;
|
||||
uint64_t used_bytes;
|
||||
uint64_t free_bytes;
|
||||
uint32_t write_count;
|
||||
bool is_healthy;
|
||||
} storage_stats_t;
|
||||
```
|
||||
|
||||
## 5. Threading Model
|
||||
|
||||
- **Owner Task:** Persistence Task (MEDIUM priority)
|
||||
- **Thread Safety:** Thread-safe (all operations protected by mutex)
|
||||
- **Blocking Operations:** Storage I/O operations (bounded by timing requirements)
|
||||
- **ISR Access:** Not allowed (blocking operations)
|
||||
|
||||
## 6. Resource Ownership
|
||||
|
||||
- **SD Card Driver:** Owned exclusively by Persistence component
|
||||
- **NVM Driver:** Owned exclusively by Persistence component
|
||||
- **Storage Mutex:** Protects concurrent access
|
||||
- **Write Buffers:** Pre-allocated static buffers
|
||||
|
||||
## 7. Error Model
|
||||
|
||||
### 7.1 Error Conditions
|
||||
|
||||
| Error | Condition | Response |
|
||||
|-------|-----------|----------|
|
||||
| `PERSIST_ERR_STORAGE_FULL` | Storage full | Trigger retention policy, log warning |
|
||||
| `PERSIST_ERR_STORAGE_FAILED` | Storage failure | Enter SD_DEGRADED state, log error |
|
||||
| `PERSIST_ERR_SERIALIZATION` | Serialization failure | Return false, log error |
|
||||
| `PERSIST_ERR_INTEGRITY_CHECK` | Integrity check failed | Return false, log error, attempt recovery |
|
||||
|
||||
### 7.2 Diagnostics Emitted
|
||||
|
||||
- `DIAG-ST-PERSIST-0001`: Storage write failure (WARNING)
|
||||
- `DIAG-ST-PERSIST-0002`: Storage full (WARNING)
|
||||
- `DIAG-ST-PERSIST-0003`: Storage corruption detected (ERROR)
|
||||
- `DIAG-ST-PERSIST-0004`: Storage failure (FATAL)
|
||||
|
||||
## 8. State-Dependent Behavior
|
||||
|
||||
### 8.1 All States
|
||||
|
||||
- Read operations allowed
|
||||
- Write operations allowed (with restrictions)
|
||||
|
||||
### 8.2 TEARDOWN State
|
||||
|
||||
- Only authorized writes allowed (critical data flush)
|
||||
- Read operations allowed
|
||||
|
||||
### 8.3 SD_DEGRADED State
|
||||
|
||||
- SD card writes disabled
|
||||
- NVM writes allowed
|
||||
- Read operations allowed (if storage accessible)
|
||||
|
||||
## 9. Dependencies
|
||||
|
||||
### 9.1 Required Components
|
||||
|
||||
- **SD Card Driver:** For SD card access
|
||||
- **NVM Driver:** For NVM access
|
||||
- **STM:** For state queries and transitions
|
||||
- **Logger:** For diagnostic logging
|
||||
|
||||
### 9.2 Required Interfaces
|
||||
|
||||
- SD Card Driver interface
|
||||
- NVM Driver interface
|
||||
- STM state query interface
|
||||
- Logger interface
|
||||
|
||||
## 10. Acceptance Tests
|
||||
|
||||
- **T-PERSIST-001:** Sensor data write/read operations
|
||||
- **T-PERSIST-002:** Diagnostic event write/read operations
|
||||
- **T-PERSIST-003:** Machine constants write/read operations
|
||||
- **T-PERSIST-004:** Data flush completes within 200ms
|
||||
- **T-PERSIST-005:** Storage failure handling (SD_DEGRADED state)
|
||||
- **T-PERSIST-006:** Data integrity verification
|
||||
- **T-PERSIST-007:** Wear-aware storage management
|
||||
|
||||
## 11. Traceability
|
||||
|
||||
- **SWR-DATA-001:** Persistent timestamped sensor data
|
||||
- **SWR-DATA-004:** DP component as sole persistence interface
|
||||
- **SWR-DATA-005:** Storage access isolation
|
||||
- **SWR-DATA-006:** Structured data serialization
|
||||
- **SWR-DATA-007:** Data flush before teardown
|
||||
- **SWR-DATA-008:** Data integrity during updates
|
||||
- **SWR-DATA-009:** Persistence verification
|
||||
- **SWR-DATA-012:** SD card failure handling
|
||||
- **SWR-DATA-013:** Wear-aware storage management
|
||||
- **CFC-ARCH-01:** Hardware access via drivers only
|
||||
- **CFC-DATA-01:** Single source of truth
|
||||
- **CFC-DATA-02:** Data consistency during transitions
|
||||
|
||||
## 12. Implementation Notes
|
||||
|
||||
- Serialization SHALL use a well-defined format (e.g., CBOR, MessagePack, or custom binary)
|
||||
- SD card writes SHALL be wear-aware (wear leveling, write frequency limits)
|
||||
- Data integrity SHALL be verified using checksums or hashes
|
||||
- Flush operations SHALL be coordinated with STM for state transitions
|
||||
- Storage failures SHALL trigger state transitions (SD_DEGRADED)
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file persistence.cpp
|
||||
* @brief Persistence component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "persistence.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
static const char* TAG = "Persistence";
|
||||
|
||||
Persistence::Persistence()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
Persistence::~Persistence()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool Persistence::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 5000, asf::logger::Criticality::LOW, "Persistence initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Persistence::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Persistence::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file persistence.hpp
|
||||
* @brief Persistence component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef PERSISTENCE_HPP
|
||||
#define PERSISTENCE_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief Persistence class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class Persistence
|
||||
{
|
||||
public:
|
||||
Persistence();
|
||||
~Persistence();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // PERSISTENCE_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
5000,Persistence,INFO,Low,Persistence initialized successfully
|
||||
|
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_persistence_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "Persistence initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_persistence_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>PERSISTENCE_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/DP_stack/persistence/test/persistence_init_test.py</test_exec>
|
||||
</test_case>
|
||||
|
||||
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_persistence.cpp
|
||||
* @brief Unit tests for Persistence component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "persistence.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_persistence_initialize(void)
|
||||
{
|
||||
Persistence comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_persistence_deinitialize(void)
|
||||
{
|
||||
Persistence comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/stm.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,291 @@
|
||||
# State Manager (STM) Component Specification
|
||||
|
||||
**Component ID:** COMP-STM
|
||||
**Version:** 1.0
|
||||
**Date:** 2025-01-19
|
||||
**Location:** `application_layer/business_stack/STM/`
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
The State Manager (STM) component implements the system finite state machine (FSM) as defined in the System State Machine Specification. It manages system operational states, enforces valid state transitions, coordinates teardown sequences, and notifies components of state changes.
|
||||
|
||||
## 2. Responsibilities
|
||||
|
||||
### 2.1 Primary Responsibilities
|
||||
|
||||
- Implement system FSM with 11 states (INIT, BOOT_FAILURE, RUNNING, WARNING, FAULT, OTA_PREP, OTA_UPDATE, MC_UPDATE, TEARDOWN, SERVICE, SD_DEGRADED)
|
||||
- Enforce valid state transitions according to transition table
|
||||
- Coordinate controlled teardown sequences
|
||||
- Notify registered components of state changes via Event System
|
||||
- Provide state query interface for components
|
||||
|
||||
### 2.2 Non-Responsibilities
|
||||
|
||||
- Feature logic (sensor acquisition, communication, etc.)
|
||||
- Hardware access (delegated to drivers)
|
||||
- Fault detection (delegated to Error Handler)
|
||||
- Diagnostic logging (delegated to Diagnostics Task)
|
||||
|
||||
## 3. Public API
|
||||
|
||||
### 3.1 State Query Functions
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Get current system state
|
||||
* @return Current system state
|
||||
*/
|
||||
system_state_t stm_getCurrentState(void);
|
||||
|
||||
/**
|
||||
* @brief Check if a state is valid
|
||||
* @param state State to validate
|
||||
* @return true if state is valid, false otherwise
|
||||
*/
|
||||
bool stm_isStateValid(system_state_t state);
|
||||
|
||||
/**
|
||||
* @brief Check if system is in a specific state
|
||||
* @param state State to check
|
||||
* @return true if current state matches, false otherwise
|
||||
*/
|
||||
bool stm_isInState(system_state_t state);
|
||||
```
|
||||
|
||||
### 3.2 State Transition Functions
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Request a state transition
|
||||
* @param target_state Target state
|
||||
* @param reason Transition reason
|
||||
* @return true if transition initiated, false if rejected
|
||||
*/
|
||||
bool stm_requestTransition(system_state_t target_state, transition_reason_t reason);
|
||||
|
||||
/**
|
||||
* @brief Validate if a state transition is allowed
|
||||
* @param from Source state
|
||||
* @param to Target state
|
||||
* @return true if transition is valid, false otherwise
|
||||
*/
|
||||
bool stm_validateTransition(system_state_t target_state, transition_reason_t reason);
|
||||
|
||||
/**
|
||||
* @brief Get transition reason for last transition
|
||||
* @return Transition reason
|
||||
*/
|
||||
transition_reason_t stm_getLastTransitionReason(void);
|
||||
```
|
||||
|
||||
### 3.3 Teardown Functions
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Initiate controlled teardown sequence
|
||||
* @param reason Teardown reason
|
||||
* @return true if teardown initiated, false if rejected
|
||||
*/
|
||||
bool stm_initiateTeardown(teardown_reason_t reason);
|
||||
|
||||
/**
|
||||
* @brief Check if teardown is complete
|
||||
* @return true if teardown complete, false otherwise
|
||||
*/
|
||||
bool stm_isTeardownComplete(void);
|
||||
|
||||
/**
|
||||
* @brief Get teardown progress (0-100%)
|
||||
* @return Teardown progress percentage
|
||||
*/
|
||||
uint8_t stm_getTeardownProgress(void);
|
||||
```
|
||||
|
||||
### 3.4 Component Registration
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief State listener callback type
|
||||
* @param old_state Previous state
|
||||
* @param new_state New state
|
||||
* @param reason Transition reason
|
||||
*/
|
||||
typedef void (*state_listener_t)(system_state_t old_state, system_state_t new_state, transition_reason_t reason);
|
||||
|
||||
/**
|
||||
* @brief Register a state change listener
|
||||
* @param listener Callback function
|
||||
* @return true if registered, false on error
|
||||
*/
|
||||
bool stm_registerStateListener(state_listener_t listener);
|
||||
|
||||
/**
|
||||
* @brief Unregister a state change listener
|
||||
* @param listener Callback function to remove
|
||||
* @return true if unregistered, false if not found
|
||||
*/
|
||||
bool stm_unregisterStateListener(state_listener_t listener);
|
||||
```
|
||||
|
||||
## 4. Data Types
|
||||
|
||||
### 4.1 System States
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
SYSTEM_STATE_INIT = 0,
|
||||
SYSTEM_STATE_BOOT_FAILURE,
|
||||
SYSTEM_STATE_RUNNING,
|
||||
SYSTEM_STATE_WARNING,
|
||||
SYSTEM_STATE_FAULT,
|
||||
SYSTEM_STATE_OTA_PREP,
|
||||
SYSTEM_STATE_OTA_UPDATE,
|
||||
SYSTEM_STATE_MC_UPDATE,
|
||||
SYSTEM_STATE_TEARDOWN,
|
||||
SYSTEM_STATE_SERVICE,
|
||||
SYSTEM_STATE_SD_DEGRADED,
|
||||
SYSTEM_STATE_COUNT
|
||||
} system_state_t;
|
||||
```
|
||||
|
||||
### 4.2 Transition Reasons
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
TRANSITION_REASON_INIT_SUCCESS,
|
||||
TRANSITION_REASON_SECURE_BOOT_FAIL,
|
||||
TRANSITION_REASON_NON_FATAL_FAULT,
|
||||
TRANSITION_REASON_FATAL_FAULT,
|
||||
TRANSITION_REASON_FAULT_CLEARED,
|
||||
TRANSITION_REASON_FAULT_ESCALATED,
|
||||
TRANSITION_REASON_OTA_REQUEST,
|
||||
TRANSITION_REASON_MC_UPDATE_REQUEST,
|
||||
TRANSITION_REASON_DEBUG_SESSION,
|
||||
TRANSITION_REASON_SD_FAILURE,
|
||||
TRANSITION_REASON_SD_RECOVERED,
|
||||
TRANSITION_REASON_RECOVERY_ATTEMPT,
|
||||
TRANSITION_REASON_MANUAL_RESET,
|
||||
TRANSITION_REASON_OTA_READY,
|
||||
TRANSITION_REASON_OTA_REJECTED,
|
||||
TRANSITION_REASON_TEARDOWN_COMPLETE,
|
||||
TRANSITION_REASON_OTA_SUCCESS,
|
||||
TRANSITION_REASON_OTA_FAILURE,
|
||||
TRANSITION_REASON_MC_UPDATE_COMPLETE,
|
||||
TRANSITION_REASON_SESSION_CLOSED,
|
||||
TRANSITION_REASON_MANUAL_INTERVENTION
|
||||
} transition_reason_t;
|
||||
```
|
||||
|
||||
### 4.3 Teardown Reasons
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
TEARDOWN_REASON_OTA,
|
||||
TEARDOWN_REASON_MC_UPDATE,
|
||||
TEARDOWN_REASON_FATAL_FAULT,
|
||||
TEARDOWN_REASON_MANUAL_COMMAND,
|
||||
TEARDOWN_REASON_SYSTEM_RESET
|
||||
} teardown_reason_t;
|
||||
```
|
||||
|
||||
## 5. Threading Model
|
||||
|
||||
- **Owner Task:** System Management Task (HIGH priority)
|
||||
- **Thread Safety:** Thread-safe (protected by mutex for state transitions)
|
||||
- **Blocking Operations:** None (all operations are non-blocking)
|
||||
- **ISR Access:** State queries allowed from ISR (read-only, lock-free)
|
||||
|
||||
## 6. Resource Ownership
|
||||
|
||||
- **State Machine State:** Owned exclusively by STM component
|
||||
- **State Transition Lock:** Mutex-protected (prevents concurrent transitions)
|
||||
- **State Listeners:** List maintained by STM (protected by mutex)
|
||||
|
||||
## 7. Error Model
|
||||
|
||||
### 7.1 Error Conditions
|
||||
|
||||
| Error | Condition | Response |
|
||||
|-------|-----------|----------|
|
||||
| `STM_ERR_INVALID_STATE` | Invalid state parameter | Return false, log warning |
|
||||
| `STM_ERR_INVALID_TRANSITION` | Invalid transition requested | Return false, log warning |
|
||||
| `STM_ERR_TEARDOWN_IN_PROGRESS` | Teardown already in progress | Return false, log info |
|
||||
| `STM_ERR_LISTENER_FULL` | Too many listeners registered | Return false, log error |
|
||||
|
||||
### 7.2 Diagnostics Emitted
|
||||
|
||||
- `DIAG-SY-STM-0001`: Invalid state transition attempted (WARNING)
|
||||
- `DIAG-SY-STM-0002`: State transition timeout (ERROR)
|
||||
- `DIAG-SY-STM-0003`: Teardown sequence failure (FATAL)
|
||||
|
||||
## 8. State-Dependent Behavior
|
||||
|
||||
### 8.1 INIT State
|
||||
|
||||
- **Allowed Operations:** State queries, listener registration
|
||||
- **Forbidden Operations:** State transitions (except to RUNNING or BOOT_FAILURE)
|
||||
- **Duration:** Bounded (max 5 seconds)
|
||||
|
||||
### 8.2 RUNNING State
|
||||
|
||||
- **Allowed Operations:** All operations
|
||||
- **Forbidden Operations:** None
|
||||
- **Duration:** Indefinite (normal operation)
|
||||
|
||||
### 8.3 TEARDOWN State
|
||||
|
||||
- **Allowed Operations:** State queries, teardown progress queries
|
||||
- **Forbidden Operations:** New state transitions (except completion transitions)
|
||||
- **Duration:** Bounded (max 500ms)
|
||||
|
||||
## 9. Dependencies
|
||||
|
||||
### 9.1 Required Components
|
||||
|
||||
- **Event System:** For state change notifications
|
||||
- **Error Handler:** For fault-triggered transitions
|
||||
- **Persistence:** For data flush during teardown
|
||||
- **Logger:** For diagnostic logging
|
||||
|
||||
### 9.2 Required Interfaces
|
||||
|
||||
- Event System publish interface
|
||||
- Persistence flush interface
|
||||
- Logger interface
|
||||
|
||||
## 10. Acceptance Tests
|
||||
|
||||
### 10.1 State Transition Tests
|
||||
|
||||
- **T-STM-001:** Valid state transitions succeed
|
||||
- **T-STM-002:** Invalid state transitions are rejected
|
||||
- **T-STM-003:** State transition timing meets requirements (≤50ms)
|
||||
|
||||
### 10.2 Teardown Tests
|
||||
|
||||
- **T-STM-004:** Teardown sequence completes within 500ms
|
||||
- **T-STM-005:** Teardown coordinates data flush
|
||||
- **T-STM-006:** Teardown prevents new transitions
|
||||
|
||||
### 10.3 Notification Tests
|
||||
|
||||
- **T-STM-007:** State change notifications are sent to all listeners
|
||||
- **T-STM-008:** State change notifications are non-blocking
|
||||
|
||||
## 11. Traceability
|
||||
|
||||
- **SWR-SYS-001:** FSM implementation
|
||||
- **SWR-SYS-002:** State transition enforcement
|
||||
- **SWR-SYS-003:** State-based operation restriction
|
||||
- **SWR-SYS-004:** State transition notification
|
||||
- **SWR-SYS-005:** Controlled teardown execution
|
||||
- **SWR-SYS-006:** Critical data persistence before teardown
|
||||
- **SWR-SYS-007:** Data integrity protection during shutdown
|
||||
|
||||
## 12. Implementation Notes
|
||||
|
||||
- State machine SHALL be implemented as a table-driven FSM
|
||||
- State transitions SHALL be atomic (protected by mutex)
|
||||
- State listeners SHALL be called asynchronously via Event System
|
||||
- Teardown sequence SHALL coordinate with Persistence component for data flush
|
||||
- State queries SHALL be lock-free for ISR access (read-only)
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file stm.cpp
|
||||
* @brief Stm component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "stm.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
static const char* TAG = "Stm";
|
||||
|
||||
Stm::Stm()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
Stm::~Stm()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool Stm::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 4600, asf::logger::Criticality::LOW, "Stm initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Stm::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Stm::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file stm.hpp
|
||||
* @brief Stm component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef STM_HPP
|
||||
#define STM_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief Stm class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class Stm
|
||||
{
|
||||
public:
|
||||
Stm();
|
||||
~Stm();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // STM_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
4600,STM,INFO,Low,Stm initialized successfully
|
||||
|
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_stm_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "Stm initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_stm_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>STM_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/business_stack/STM/test/stm_init_test.py</test_exec>
|
||||
</test_case>
|
||||
|
||||
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_stm.cpp
|
||||
* @brief Unit tests for Stm component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "stm.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_stm_initialize(void)
|
||||
{
|
||||
Stm comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_stm_deinitialize(void)
|
||||
{
|
||||
Stm comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/actuator_manager.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @file actuator_manager.cpp
|
||||
* @brief ActuatorManager component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
#include "logger.hpp"
|
||||
#include "actuator_manager.hpp"
|
||||
|
||||
static const char* TAG = "actuator_manager";
|
||||
|
||||
ActuatorManager::ActuatorManager()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
ActuatorManager::~ActuatorManager()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool ActuatorManager::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 4000, asf::logger::Criticality::LOW, "actuator manager initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ActuatorManager::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ActuatorManager::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file actuator_manager.hpp
|
||||
* @brief ActuatorManager component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef ACTUATOR_MANAGER_HPP
|
||||
#define ACTUATOR_MANAGER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief ActuatorManager class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class ActuatorManager
|
||||
{
|
||||
public:
|
||||
ActuatorManager();
|
||||
~ActuatorManager();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // ACTUATOR_MANAGER_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
4000,ActuatorManager,INFO,Low,actuator manager initialized successfully
|
||||
|
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_actuator_manager_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "Actuator Manager initialized successfully" in line or "actuator manager initialized successfully" in line.lower():
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_actuator_manager_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>ACTUATOR_MANAGER_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/business_stack/actuator_manager/test/actuator_manager_init_test.py</test_exec>
|
||||
</test_case>
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,56 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Get the absolute path to the folder you want to add
|
||||
# Example: Adding a folder named 'my_components' located in the current directory
|
||||
folder_path = os.path.abspath("components/system_tests")
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
import time
|
||||
|
||||
# --- Main Logic ---
|
||||
if __name__ == "__main__":
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
keyword_found = False
|
||||
# Force print to terminal immediately
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
|
||||
try:
|
||||
start_time = time.time()
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_actuator_manager_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "actuator manager initialized successfully" in line.lower() or "Actuator Manager initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_actuator_manager_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_actuator_manager.cpp
|
||||
* @brief Unit tests for ActuatorManager component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "actuator_manager.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_actuator_manager_initialize(void)
|
||||
{
|
||||
ActuatorManager comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_actuator_manager_deinitialize(void)
|
||||
{
|
||||
ActuatorManager comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/event_system.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,200 @@
|
||||
# Event System Component Specification
|
||||
|
||||
**Component ID:** COMP-EVENT
|
||||
**Version:** 1.0
|
||||
**Date:** 2025-01-19
|
||||
**Location:** `application_layer/business_stack/event_system/`
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
The Event System provides a publish/subscribe event bus for cross-component communication. It decouples components, enables asynchronous event delivery, and ensures non-blocking operation.
|
||||
|
||||
## 2. Responsibilities
|
||||
|
||||
### 2.1 Primary Responsibilities
|
||||
|
||||
- Provide publish/subscribe event bus
|
||||
- Decouple components via events
|
||||
- Ensure non-blocking event delivery
|
||||
- Support multiple subscribers per event type
|
||||
- Queue management for event delivery
|
||||
|
||||
### 2.2 Non-Responsibilities
|
||||
|
||||
- Business logic (components handle events)
|
||||
- Event payload validation (components validate)
|
||||
- Event ordering guarantees (best-effort)
|
||||
|
||||
## 3. Public API
|
||||
|
||||
### 3.1 Event Publishing
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Publish an event
|
||||
* @param type Event type
|
||||
* @param payload Event payload (may be NULL)
|
||||
* @param payload_size Payload size in bytes (0 if payload is NULL)
|
||||
* @return true if published, false on error
|
||||
*/
|
||||
bool event_publish(event_type_t type, const void* payload, size_t payload_size);
|
||||
|
||||
/**
|
||||
* @brief Publish an event with timestamp
|
||||
* @param type Event type
|
||||
* @param payload Event payload
|
||||
* @param payload_size Payload size
|
||||
* @param timestamp Event timestamp
|
||||
* @return true if published, false on error
|
||||
*/
|
||||
bool event_publishWithTimestamp(event_type_t type, const void* payload, size_t payload_size, uint64_t timestamp);
|
||||
```
|
||||
|
||||
### 3.2 Event Subscription
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Event handler callback type
|
||||
* @param type Event type
|
||||
* @param payload Event payload
|
||||
* @param payload_size Payload size
|
||||
* @param timestamp Event timestamp
|
||||
*/
|
||||
typedef void (*event_handler_t)(event_type_t type, const void* payload, size_t payload_size, uint64_t timestamp);
|
||||
|
||||
/**
|
||||
* @brief Subscribe to an event type
|
||||
* @param type Event type to subscribe to
|
||||
* @param handler Callback function
|
||||
* @return true if subscribed, false on error
|
||||
*/
|
||||
bool event_subscribe(event_type_t type, event_handler_t handler);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe from an event type
|
||||
* @param type Event type to unsubscribe from
|
||||
* @param handler Callback function to remove
|
||||
* @return true if unsubscribed, false if not found
|
||||
*/
|
||||
bool event_unsubscribe(event_type_t type, event_handler_t handler);
|
||||
```
|
||||
|
||||
### 3.3 Event Queue Management
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Get number of pending events
|
||||
* @param type Event type (EVENT_TYPE_ALL for all types)
|
||||
* @return Number of pending events
|
||||
*/
|
||||
size_t event_getPendingCount(event_type_t type);
|
||||
|
||||
/**
|
||||
* @brief Clear pending events
|
||||
* @param type Event type (EVENT_TYPE_ALL for all types)
|
||||
* @return Number of events cleared
|
||||
*/
|
||||
size_t event_clearPending(event_type_t type);
|
||||
```
|
||||
|
||||
## 4. Data Types
|
||||
|
||||
### 4.1 Event Types
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
EVENT_SENSOR_DATA_UPDATE = 0,
|
||||
EVENT_DIAGNOSTIC_EVENT,
|
||||
EVENT_STATE_CHANGED,
|
||||
EVENT_OTA_REQUEST,
|
||||
EVENT_OTA_STATUS,
|
||||
EVENT_MC_UPDATE_REQUEST,
|
||||
EVENT_COMMUNICATION_LINK_STATUS,
|
||||
EVENT_STORAGE_STATUS,
|
||||
EVENT_SYSTEM_HEALTH_UPDATE,
|
||||
EVENT_TYPE_ALL = 0xFF,
|
||||
EVENT_TYPE_COUNT
|
||||
} event_type_t;
|
||||
```
|
||||
|
||||
### 4.2 Event Structure
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
event_type_t type;
|
||||
uint64_t timestamp;
|
||||
size_t payload_size;
|
||||
uint8_t payload[]; // Variable-length payload
|
||||
} event_t;
|
||||
```
|
||||
|
||||
## 5. Threading Model
|
||||
|
||||
- **Owner Task:** Event System Task (MEDIUM priority) or shared with components
|
||||
- **Thread Safety:** Thread-safe (lock-free queue for publishing, mutex for subscription management)
|
||||
- **Blocking Operations:** None (all operations are non-blocking)
|
||||
- **ISR Access:** Publish allowed from ISR (lock-free queue)
|
||||
|
||||
## 6. Resource Ownership
|
||||
|
||||
- **Event Queue:** Owned by Event System (lock-free ring buffer)
|
||||
- **Subscriber List:** Protected by mutex
|
||||
- **Event Buffers:** Pre-allocated static buffers (no dynamic allocation)
|
||||
|
||||
## 7. Error Model
|
||||
|
||||
### 7.1 Error Conditions
|
||||
|
||||
| Error | Condition | Response |
|
||||
|-------|-----------|----------|
|
||||
| `EVENT_ERR_QUEUE_FULL` | Event queue full | Drop oldest event, log warning |
|
||||
| `EVENT_ERR_INVALID_TYPE` | Invalid event type | Return false, log warning |
|
||||
| `EVENT_ERR_PAYLOAD_TOO_LARGE` | Payload exceeds maximum | Return false, log error |
|
||||
| `EVENT_ERR_SUBSCRIBER_FULL` | Too many subscribers | Return false, log error |
|
||||
|
||||
### 7.2 Diagnostics Emitted
|
||||
|
||||
- `DIAG-SY-EVENT-0001`: Event queue full (WARNING)
|
||||
- `DIAG-SY-EVENT-0002`: Event dropped due to queue full (WARNING)
|
||||
- `DIAG-SY-EVENT-0003`: Subscriber callback failure (ERROR)
|
||||
|
||||
## 8. State-Dependent Behavior
|
||||
|
||||
- **All States:** Event System operates in all states
|
||||
- **TEARDOWN State:** Event publishing limited to teardown-related events
|
||||
- **FAULT State:** Event publishing limited to diagnostic events
|
||||
|
||||
## 9. Dependencies
|
||||
|
||||
### 9.1 Required Components
|
||||
|
||||
- **Logger:** For diagnostic logging
|
||||
- **Time Utils:** For timestamp generation
|
||||
|
||||
### 9.2 Required Interfaces
|
||||
|
||||
- Logger interface
|
||||
- Time Utils interface
|
||||
|
||||
## 10. Acceptance Tests
|
||||
|
||||
- **T-EVENT-001:** Events are published successfully
|
||||
- **T-EVENT-002:** Subscribers receive events
|
||||
- **T-EVENT-003:** Multiple subscribers receive same event
|
||||
- **T-EVENT-004:** Event publishing is non-blocking
|
||||
- **T-EVENT-005:** Event queue overflow handling
|
||||
- **T-EVENT-006:** Event unsubscription works correctly
|
||||
|
||||
## 11. Traceability
|
||||
|
||||
- **SWR-DESIGN-006:** Event System for cross-component communication
|
||||
- **CFC-TIME-01:** Non-blocking operation
|
||||
- **Architecture Requirement:** Event-driven communication
|
||||
|
||||
## 12. Implementation Notes
|
||||
|
||||
- Event queue SHALL be implemented as lock-free ring buffer
|
||||
- Maximum queue size: 100 events
|
||||
- Maximum payload size: 256 bytes
|
||||
- Maximum subscribers per event type: 10
|
||||
- Event handlers SHALL be called synchronously (in publisher's context) or asynchronously (in event task context) based on configuration
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @file event_system.cpp
|
||||
* @brief EventSystem component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
#include "logger.hpp"
|
||||
#include "event_system.hpp"
|
||||
|
||||
static const char* TAG = "event system";
|
||||
|
||||
EventSystem::EventSystem()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
EventSystem::~EventSystem()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool EventSystem::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 4200, asf::logger::Criticality::LOW, "event system initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EventSystem::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EventSystem::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file event_system.hpp
|
||||
* @brief EventSystem component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef EVENT_SYSTEM_HPP
|
||||
#define EVENT_SYSTEM_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief EventSystem class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class EventSystem
|
||||
{
|
||||
public:
|
||||
EventSystem();
|
||||
~EventSystem();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // EVENT_SYSTEM_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
4200,EventSystem,INFO,Low,event system initialized successfully
|
||||
|
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_event_system_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "event system initialized successfully" in line.lower() or "Event System initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_event_system_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>ACTUATOR_MANAGER_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/business_stack/event_system/test/event_system_init_test.py</test_exec>
|
||||
</test_case>
|
||||
|
||||
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_event_system.cpp
|
||||
* @brief Unit tests for EventSystem component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "event_system.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_event_system_initialize(void)
|
||||
{
|
||||
EventSystem comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_event_system_deinitialize(void)
|
||||
{
|
||||
EventSystem comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/fw_upgrader.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file fw_upgrader.cpp
|
||||
* @brief FwUpgrader component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "fw_upgrader.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
static const char* TAG = "FwUpgrader";
|
||||
|
||||
FwUpgrader::FwUpgrader()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
FwUpgrader::~FwUpgrader()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool FwUpgrader::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 4300, asf::logger::Criticality::LOW, "FwUpgrader initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FwUpgrader::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FwUpgrader::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file fw_upgrader.hpp
|
||||
* @brief FwUpgrader component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef FW_UPGRADER_HPP
|
||||
#define FW_UPGRADER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief FwUpgrader class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class FwUpgrader
|
||||
{
|
||||
public:
|
||||
FwUpgrader();
|
||||
~FwUpgrader();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // FW_UPGRADER_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
4300,FwUpgrader,INFO,Low,FwUpgrader initialized successfully
|
||||
|
@@ -0,0 +1,44 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
|
||||
def test_fw_upgrader_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "FwUpgrader initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_fw_upgrader_initialize()
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>FW_UPGRADER_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/business_stack/fw_upgrader/test/fw_upgrader_init_test.py</test_exec>
|
||||
</test_case>
|
||||
|
||||
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_fw_upgrader.cpp
|
||||
* @brief Unit tests for FwUpgrader component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "fw_upgrader.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_fw_upgrader_initialize(void)
|
||||
{
|
||||
FwUpgrader comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_fw_upgrader_deinitialize(void)
|
||||
{
|
||||
FwUpgrader comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/machine_constant_manager.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file machine_constant_manager.cpp
|
||||
* @brief MachineConstantManager component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "machine_constant_manager.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
static const char* TAG = "MachineConstantManager";
|
||||
|
||||
MachineConstantManager::MachineConstantManager()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
MachineConstantManager::~MachineConstantManager()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool MachineConstantManager::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 4400, asf::logger::Criticality::LOW, "MachineConstantManager initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MachineConstantManager::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MachineConstantManager::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file machine_constant_manager.hpp
|
||||
* @brief MachineConstantManager component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef MACHINE_CONSTANT_MANAGER_HPP
|
||||
#define MACHINE_CONSTANT_MANAGER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief MachineConstantManager class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class MachineConstantManager
|
||||
{
|
||||
public:
|
||||
MachineConstantManager();
|
||||
~MachineConstantManager();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // MACHINE_CONSTANT_MANAGER_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
4400,MachineConstantManager,INFO,Low,MachineConstantManager initialized successfully
|
||||
|
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_machine_constant_manager_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "MachineConstantManager initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_machine_constant_manager_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>MACHINE_CONSTANT_MANAGER_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/business_stack/machine_constant_manager/test/machine_constant_manager_init_test.py</test_exec>
|
||||
</test_case>
|
||||
|
||||
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_machine_constant_manager.cpp
|
||||
* @brief Unit tests for MachineConstantManager component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "machine_constant_manager.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_machine_constant_manager_initialize(void)
|
||||
{
|
||||
MachineConstantManager comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_machine_constant_manager_deinitialize(void)
|
||||
{
|
||||
MachineConstantManager comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/main_hub_apis.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file main_hub_apis.cpp
|
||||
* @brief MainHubApis component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "main_hub_apis.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
static const char* TAG = "MainHubApis";
|
||||
|
||||
MainHubApis::MainHubApis()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
MainHubApis::~MainHubApis()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool MainHubApis::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 4500, asf::logger::Criticality::LOW, "MainHubApis initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainHubApis::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainHubApis::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file main_hub_apis.hpp
|
||||
* @brief MainHubApis component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef MAIN_HUB_APIS_HPP
|
||||
#define MAIN_HUB_APIS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief MainHubApis class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class MainHubApis
|
||||
{
|
||||
public:
|
||||
MainHubApis();
|
||||
~MainHubApis();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // MAIN_HUB_APIS_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
4500,MainHubApis,INFO,Low,MainHubApis initialized successfully
|
||||
|
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_main_hub_apis_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "MainHubApis initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_main_hub_apis_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>MAIN_HUB_APIS_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/business_stack/main_hub_apis/test/main_hub_apis_init_test.py</test_exec>
|
||||
</test_case>
|
||||
|
||||
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_main_hub_apis.cpp
|
||||
* @brief Unit tests for MainHubApis component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "main_hub_apis.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_main_hub_apis_initialize(void)
|
||||
{
|
||||
MainHubApis comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_main_hub_apis_deinitialize(void)
|
||||
{
|
||||
MainHubApis comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/sensor_manager.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,652 @@
|
||||
# Sensor Manager Component Specification
|
||||
|
||||
**Component ID:** COMP-SENSOR-MGR
|
||||
**Version:** 1.0
|
||||
**Date:** 2025-01-19
|
||||
**Location:** `application_layer/business_stack/sensor_manager/`
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
The Sensor Manager component coordinates all sensor-related operations including lifecycle management, data acquisition scheduling, high-frequency sampling, local filtering, and sensor state management. It serves as the central coordinator for the Sensor Data Acquisition feature (F-DAQ).
|
||||
|
||||
## 2. Responsibilities
|
||||
|
||||
### 2.1 Primary Responsibilities
|
||||
|
||||
- **Sensor Lifecycle Management:** Detection, initialization, configuration, and teardown
|
||||
- **Data Acquisition Coordination:** Scheduling and executing sensor sampling cycles
|
||||
- **High-Frequency Sampling:** Multiple samples per sensor per cycle with configurable parameters
|
||||
- **Local Data Filtering:** Apply configurable filters (median, moving average, rate-of-change limiter)
|
||||
- **Sensor State Management:** Track and manage sensor operational states
|
||||
- **Data Record Generation:** Create timestamped sensor data records
|
||||
- **Event Publication:** Publish sensor data updates and state changes via Event System
|
||||
|
||||
### 2.2 Non-Responsibilities
|
||||
|
||||
- **Hardware Access:** Delegated to sensor drivers (no direct I2C/SPI/UART access)
|
||||
- **Data Persistence:** Delegated to Data Persistence component
|
||||
- **Communication:** Delegated to Communication components
|
||||
- **Fault Detection Logic:** Uses Error Handler for fault reporting
|
||||
- **Time Management:** Uses Time Utils for timestamp generation
|
||||
|
||||
## 3. Public API
|
||||
|
||||
### 3.1 Initialization and Configuration
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Initialize Sensor Manager component
|
||||
* @return true if initialization successful, false otherwise
|
||||
*/
|
||||
bool sensorMgr_initialize(void);
|
||||
|
||||
/**
|
||||
* @brief Load sensor configuration from Machine Constants
|
||||
* @param mc Machine constants structure
|
||||
* @return true if configuration loaded, false on error
|
||||
*/
|
||||
bool sensorMgr_loadConfiguration(const machine_constants_t* mc);
|
||||
|
||||
/**
|
||||
* @brief Detect all connected sensors
|
||||
* @return Number of sensors detected
|
||||
*/
|
||||
uint8_t sensorMgr_detectSensors(void);
|
||||
|
||||
/**
|
||||
* @brief Shutdown Sensor Manager (cleanup resources)
|
||||
* @return true if shutdown successful, false otherwise
|
||||
*/
|
||||
bool sensorMgr_shutdown(void);
|
||||
```
|
||||
|
||||
### 3.2 Acquisition Control
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Start sensor data acquisition
|
||||
* @return true if acquisition started, false on error
|
||||
*/
|
||||
bool sensorMgr_startAcquisition(void);
|
||||
|
||||
/**
|
||||
* @brief Stop sensor data acquisition
|
||||
* @return true if acquisition stopped, false on error
|
||||
*/
|
||||
bool sensorMgr_stopAcquisition(void);
|
||||
|
||||
/**
|
||||
* @brief Pause sensor data acquisition
|
||||
* @return true if acquisition paused, false on error
|
||||
*/
|
||||
bool sensorMgr_pauseAcquisition(void);
|
||||
|
||||
/**
|
||||
* @brief Resume sensor data acquisition
|
||||
* @return true if acquisition resumed, false on error
|
||||
*/
|
||||
bool sensorMgr_resumeAcquisition(void);
|
||||
|
||||
/**
|
||||
* @brief Check if acquisition is active
|
||||
* @return true if acquisition is running, false otherwise
|
||||
*/
|
||||
bool sensorMgr_isAcquisitionActive(void);
|
||||
```
|
||||
|
||||
### 3.3 Sensor Control
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Enable a specific sensor
|
||||
* @param sensor_id Sensor identifier (0-6)
|
||||
* @return true if sensor enabled, false on error
|
||||
*/
|
||||
bool sensorMgr_enableSensor(uint8_t sensor_id);
|
||||
|
||||
/**
|
||||
* @brief Disable a specific sensor
|
||||
* @param sensor_id Sensor identifier (0-6)
|
||||
* @return true if sensor disabled, false on error
|
||||
*/
|
||||
bool sensorMgr_disableSensor(uint8_t sensor_id);
|
||||
|
||||
/**
|
||||
* @brief Configure sensor parameters
|
||||
* @param sensor_id Sensor identifier
|
||||
* @param config Sensor configuration structure
|
||||
* @return true if configuration applied, false on error
|
||||
*/
|
||||
bool sensorMgr_configureSensor(uint8_t sensor_id, const sensor_config_t* config);
|
||||
|
||||
/**
|
||||
* @brief Recalibrate a sensor
|
||||
* @param sensor_id Sensor identifier
|
||||
* @param calibration_data Calibration parameters
|
||||
* @return true if calibration applied, false on error
|
||||
*/
|
||||
bool sensorMgr_calibrateSensor(uint8_t sensor_id, const sensor_calibration_t* calibration_data);
|
||||
```
|
||||
|
||||
### 3.4 Data Access
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Get latest data from a specific sensor
|
||||
* @param sensor_id Sensor identifier
|
||||
* @param record Output buffer for sensor data record
|
||||
* @return true if data retrieved, false on error
|
||||
*/
|
||||
bool sensorMgr_getLatestData(uint8_t sensor_id, sensor_data_record_t* record);
|
||||
|
||||
/**
|
||||
* @brief Get latest data from all sensors
|
||||
* @param records Output buffer for sensor data records
|
||||
* @param count Input: buffer size, Output: number of records filled
|
||||
* @return true if data retrieved, false on error
|
||||
*/
|
||||
bool sensorMgr_getAllSensorData(sensor_data_record_t* records, size_t* count);
|
||||
|
||||
/**
|
||||
* @brief Get sensor data with history (last N samples)
|
||||
* @param sensor_id Sensor identifier
|
||||
* @param records Output buffer for historical records
|
||||
* @param count Input: requested count, Output: actual count returned
|
||||
* @return true if data retrieved, false on error
|
||||
*/
|
||||
bool sensorMgr_getSensorHistory(uint8_t sensor_id, sensor_data_record_t* records, size_t* count);
|
||||
```
|
||||
|
||||
### 3.5 State Management
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Get sensor operational state
|
||||
* @param sensor_id Sensor identifier
|
||||
* @return Current sensor state
|
||||
*/
|
||||
sensor_state_t sensorMgr_getSensorState(uint8_t sensor_id);
|
||||
|
||||
/**
|
||||
* @brief Check if sensor is present (detected)
|
||||
* @param sensor_id Sensor identifier
|
||||
* @return true if sensor is present, false otherwise
|
||||
*/
|
||||
bool sensorMgr_isSensorPresent(uint8_t sensor_id);
|
||||
|
||||
/**
|
||||
* @brief Check if sensor is enabled for acquisition
|
||||
* @param sensor_id Sensor identifier
|
||||
* @return true if sensor is enabled, false otherwise
|
||||
*/
|
||||
bool sensorMgr_isSensorEnabled(uint8_t sensor_id);
|
||||
|
||||
/**
|
||||
* @brief Check if sensor is healthy (no faults)
|
||||
* @param sensor_id Sensor identifier
|
||||
* @return true if sensor is healthy, false if faulty
|
||||
*/
|
||||
bool sensorMgr_isSensorHealthy(uint8_t sensor_id);
|
||||
|
||||
/**
|
||||
* @brief Get sensor information
|
||||
* @param sensor_id Sensor identifier
|
||||
* @param info Output buffer for sensor information
|
||||
* @return true if information retrieved, false on error
|
||||
*/
|
||||
bool sensorMgr_getSensorInfo(uint8_t sensor_id, sensor_info_t* info);
|
||||
```
|
||||
|
||||
### 3.6 Statistics and Diagnostics
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief Get sensor acquisition statistics
|
||||
* @param sensor_id Sensor identifier
|
||||
* @param stats Output buffer for statistics
|
||||
* @return true if statistics retrieved, false on error
|
||||
*/
|
||||
bool sensorMgr_getSensorStatistics(uint8_t sensor_id, sensor_stats_t* stats);
|
||||
|
||||
/**
|
||||
* @brief Reset sensor statistics
|
||||
* @param sensor_id Sensor identifier (SENSOR_ID_ALL for all sensors)
|
||||
* @return true if statistics reset, false on error
|
||||
*/
|
||||
bool sensorMgr_resetSensorStatistics(uint8_t sensor_id);
|
||||
|
||||
/**
|
||||
* @brief Get overall acquisition performance metrics
|
||||
* @param metrics Output buffer for performance metrics
|
||||
* @return true if metrics retrieved, false on error
|
||||
*/
|
||||
bool sensorMgr_getPerformanceMetrics(acquisition_metrics_t* metrics);
|
||||
```
|
||||
|
||||
## 4. Data Types
|
||||
|
||||
### 4.1 Sensor States
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
SENSOR_STATE_UNKNOWN = 0, // Initial state, not yet detected
|
||||
SENSOR_STATE_DETECTED, // Sensor presence confirmed
|
||||
SENSOR_STATE_INITIALIZED, // Driver loaded and configured
|
||||
SENSOR_STATE_ENABLED, // Active data acquisition
|
||||
SENSOR_STATE_DISABLED, // Present but not acquiring data
|
||||
SENSOR_STATE_FAULTY, // Detected failure condition
|
||||
SENSOR_STATE_REMOVED, // Previously present, now absent
|
||||
SENSOR_STATE_CALIBRATING, // Calibration in progress
|
||||
SENSOR_STATE_COUNT
|
||||
} sensor_state_t;
|
||||
```
|
||||
|
||||
### 4.2 Sensor Types
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
SENSOR_TYPE_TEMPERATURE = 0,
|
||||
SENSOR_TYPE_HUMIDITY,
|
||||
SENSOR_TYPE_CO2,
|
||||
SENSOR_TYPE_NH3,
|
||||
SENSOR_TYPE_VOC,
|
||||
SENSOR_TYPE_PM,
|
||||
SENSOR_TYPE_LIGHT,
|
||||
SENSOR_TYPE_COUNT
|
||||
} sensor_type_t;
|
||||
```
|
||||
|
||||
### 4.3 Sensor Data Record
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint8_t sensor_id; // Sensor identifier (0-6)
|
||||
sensor_type_t sensor_type; // Type of sensor
|
||||
float filtered_value; // Processed sensor value
|
||||
char unit[8]; // Unit of measurement (e.g., "°C", "%RH")
|
||||
uint64_t timestamp_ms; // Timestamp in milliseconds
|
||||
data_validity_t validity; // Data validity status
|
||||
uint16_t sample_count; // Number of samples used for filtering
|
||||
float raw_min, raw_max; // Min/max of raw samples
|
||||
float raw_stddev; // Standard deviation of raw samples
|
||||
uint32_t acquisition_time_us; // Time taken for acquisition (microseconds)
|
||||
} sensor_data_record_t;
|
||||
```
|
||||
|
||||
### 4.4 Sensor Configuration
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint16_t sampling_count; // Number of samples per cycle (5-20)
|
||||
uint32_t sampling_interval_ms; // Interval between samples
|
||||
filter_type_t filter_type; // Filter algorithm to use
|
||||
filter_params_t filter_params; // Filter-specific parameters
|
||||
float min_valid_value; // Minimum valid sensor value
|
||||
float max_valid_value; // Maximum valid sensor value
|
||||
float rate_limit_per_sec; // Maximum rate of change per second
|
||||
bool enable_outlier_rejection; // Enable outlier detection
|
||||
float outlier_threshold; // Outlier detection threshold (std devs)
|
||||
} sensor_config_t;
|
||||
```
|
||||
|
||||
### 4.5 Filter Types
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
FILTER_TYPE_NONE = 0, // No filtering (use raw average)
|
||||
FILTER_TYPE_MEDIAN, // Median filter
|
||||
FILTER_TYPE_MOVING_AVERAGE, // Moving average filter
|
||||
FILTER_TYPE_RATE_LIMITED, // Rate-of-change limiter
|
||||
FILTER_TYPE_COMBINED, // Combination of filters
|
||||
FILTER_TYPE_COUNT
|
||||
} filter_type_t;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint8_t window_size; // Window size for median filter
|
||||
} median;
|
||||
struct {
|
||||
uint8_t window_size; // Window size for moving average
|
||||
float alpha; // Exponential smoothing factor
|
||||
} moving_avg;
|
||||
struct {
|
||||
float max_rate; // Maximum rate of change per second
|
||||
float recovery_time; // Time to recover from rate limiting
|
||||
} rate_limit;
|
||||
};
|
||||
} filter_params_t;
|
||||
```
|
||||
|
||||
### 4.6 Sensor Statistics
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint32_t total_acquisitions; // Total number of acquisition cycles
|
||||
uint32_t successful_acquisitions; // Successful acquisitions
|
||||
uint32_t failed_acquisitions; // Failed acquisitions
|
||||
uint32_t timeout_count; // Number of timeouts
|
||||
uint32_t outlier_count; // Number of outliers detected
|
||||
float avg_acquisition_time_ms; // Average acquisition time
|
||||
float max_acquisition_time_ms; // Maximum acquisition time
|
||||
float min_value, max_value; // Min/max values recorded
|
||||
float avg_value; // Average value
|
||||
uint64_t last_acquisition_time; // Timestamp of last acquisition
|
||||
uint32_t consecutive_failures; // Current consecutive failure count
|
||||
} sensor_stats_t;
|
||||
```
|
||||
|
||||
## 5. Internal State Machine
|
||||
|
||||
### 5.1 Sensor State Transitions
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> UNKNOWN
|
||||
UNKNOWN --> DETECTED : Presence detected
|
||||
DETECTED --> INITIALIZED : Driver loaded successfully
|
||||
DETECTED --> UNKNOWN : Detection lost
|
||||
|
||||
INITIALIZED --> ENABLED : Enable command
|
||||
INITIALIZED --> DETECTED : Initialization failed
|
||||
|
||||
ENABLED --> DISABLED : Disable command
|
||||
ENABLED --> FAULTY : Failure detected
|
||||
ENABLED --> CALIBRATING : Calibration requested
|
||||
ENABLED --> REMOVED : Sensor removed
|
||||
|
||||
DISABLED --> ENABLED : Enable command
|
||||
DISABLED --> REMOVED : Sensor removed
|
||||
|
||||
FAULTY --> ENABLED : Recovery successful
|
||||
FAULTY --> REMOVED : Sensor removed
|
||||
|
||||
CALIBRATING --> ENABLED : Calibration complete
|
||||
CALIBRATING --> FAULTY : Calibration failed
|
||||
|
||||
REMOVED --> DETECTED : Sensor reconnected
|
||||
```
|
||||
|
||||
### 5.2 Acquisition Cycle State Machine
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> IDLE
|
||||
IDLE --> SAMPLING : Acquisition cycle start
|
||||
SAMPLING --> FILTERING : All samples collected
|
||||
SAMPLING --> ERROR : Sampling timeout/failure
|
||||
FILTERING --> TIMESTAMPING : Filtering complete
|
||||
FILTERING --> ERROR : Filter failure
|
||||
TIMESTAMPING --> PUBLISHING : Timestamp generated
|
||||
PUBLISHING --> IDLE : Event published
|
||||
ERROR --> IDLE : Error handled
|
||||
```
|
||||
|
||||
## 6. Component Interactions
|
||||
|
||||
### 6.1 Sensor Acquisition Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Timer as Acquisition Timer
|
||||
participant SM as Sensor Manager
|
||||
participant Driver as Sensor Driver
|
||||
participant Filter as Filter Engine
|
||||
participant TimeUtil as Time Utils
|
||||
participant EventSys as Event System
|
||||
participant DataPool as Data Pool
|
||||
|
||||
Note over Timer,DataPool: 1-Second Acquisition Cycle
|
||||
|
||||
Timer->>SM: acquisitionCycleStart()
|
||||
|
||||
loop For each enabled sensor
|
||||
SM->>SM: checkSensorState(sensor_id)
|
||||
|
||||
alt Sensor is healthy
|
||||
loop 10 samples
|
||||
SM->>Driver: readSensor(sensor_id)
|
||||
Driver-->>SM: raw_sample
|
||||
end
|
||||
|
||||
SM->>Filter: applyFilter(raw_samples, filter_config)
|
||||
Filter-->>SM: filtered_value
|
||||
|
||||
SM->>TimeUtil: getCurrentTimestamp()
|
||||
TimeUtil-->>SM: timestamp
|
||||
|
||||
SM->>SM: createDataRecord(sensor_id, filtered_value, timestamp)
|
||||
SM->>EventSys: publish(SENSOR_DATA_UPDATE, record)
|
||||
EventSys->>DataPool: updateSensorData(record)
|
||||
|
||||
else Sensor is faulty
|
||||
SM->>SM: handleSensorFault(sensor_id)
|
||||
SM->>EventSys: publish(SENSOR_FAULT_DETECTED, fault_info)
|
||||
end
|
||||
end
|
||||
|
||||
SM->>SM: updateAcquisitionStatistics()
|
||||
|
||||
Note over Timer,DataPool: Cycle complete, next cycle in 1 second
|
||||
```
|
||||
|
||||
### 6.2 Sensor State Management Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant SM as Sensor Manager
|
||||
participant Driver as Sensor Driver
|
||||
participant EventSys as Event System
|
||||
participant ErrorHandler as Error Handler
|
||||
participant MCMgr as MC Manager
|
||||
|
||||
Note over SM,MCMgr: Sensor State Change Scenario
|
||||
|
||||
SM->>Driver: checkSensorPresence(sensor_id)
|
||||
Driver-->>SM: presence_status
|
||||
|
||||
alt Sensor newly detected
|
||||
SM->>SM: transitionState(UNKNOWN -> DETECTED)
|
||||
SM->>Driver: initializeSensor(sensor_id)
|
||||
Driver-->>SM: init_result
|
||||
|
||||
alt Initialization successful
|
||||
SM->>SM: transitionState(DETECTED -> INITIALIZED)
|
||||
SM->>MCMgr: getSensorConfig(sensor_id)
|
||||
MCMgr-->>SM: sensor_config
|
||||
SM->>SM: applySensorConfig(sensor_config)
|
||||
SM->>SM: transitionState(INITIALIZED -> ENABLED)
|
||||
SM->>EventSys: publish(SENSOR_STATE_CHANGED, state_info)
|
||||
else Initialization failed
|
||||
SM->>SM: transitionState(DETECTED -> UNKNOWN)
|
||||
SM->>ErrorHandler: reportFault(SENSOR_INIT_FAILED)
|
||||
end
|
||||
|
||||
else Sensor fault detected
|
||||
SM->>SM: transitionState(ENABLED -> FAULTY)
|
||||
SM->>ErrorHandler: reportFault(SENSOR_COMMUNICATION_FAILED)
|
||||
SM->>EventSys: publish(SENSOR_FAULT_DETECTED, fault_info)
|
||||
|
||||
Note over SM,MCMgr: Attempt recovery after delay
|
||||
SM->>SM: scheduleRecoveryAttempt(sensor_id)
|
||||
|
||||
else Sensor removed
|
||||
SM->>SM: transitionState(current -> REMOVED)
|
||||
SM->>EventSys: publish(SENSOR_REMOVED, sensor_info)
|
||||
end
|
||||
```
|
||||
|
||||
## 7. Threading Model
|
||||
|
||||
- **Owner Task:** Sensor Acquisition Task (HIGH priority, 8KB stack)
|
||||
- **Execution Model:** Periodic execution (1-second cycles) with event-driven state management
|
||||
- **Thread Safety:** Thread-safe (mutex protection for configuration changes)
|
||||
- **Blocking Operations:** Bounded sensor I/O operations (max 800ms per cycle)
|
||||
- **ISR Access:** Not allowed (all operations require task context)
|
||||
|
||||
## 8. Resource Ownership
|
||||
|
||||
- **Sensor Drivers:** Exclusive access during acquisition cycles
|
||||
- **Sensor Configuration:** Protected by mutex (shared with MC Manager)
|
||||
- **Sensor Data Buffers:** Owned exclusively by Sensor Manager
|
||||
- **Filter State:** Per-sensor filter state maintained internally
|
||||
|
||||
## 9. Error Model
|
||||
|
||||
### 9.1 Error Conditions
|
||||
|
||||
| Error | Condition | Response |
|
||||
|-------|-----------|----------|
|
||||
| `SENSOR_ERR_INVALID_ID` | Invalid sensor ID parameter | Return false, log warning |
|
||||
| `SENSOR_ERR_NOT_PRESENT` | Sensor not detected/present | Return false, update state |
|
||||
| `SENSOR_ERR_COMMUNICATION` | I2C/SPI/UART communication failure | Mark faulty, schedule recovery |
|
||||
| `SENSOR_ERR_TIMEOUT` | Sensor read timeout | Mark faulty, continue with other sensors |
|
||||
| `SENSOR_ERR_OUT_OF_RANGE` | Sensor value out of valid range | Mark data invalid, log diagnostic |
|
||||
| `SENSOR_ERR_FILTER_FAILURE` | Filter algorithm failure | Use raw average, log error |
|
||||
| `SENSOR_ERR_CONFIG_INVALID` | Invalid configuration parameters | Use default config, log error |
|
||||
|
||||
### 9.2 Diagnostics Emitted
|
||||
|
||||
- `DIAG-DAQ-SENSOR-0001`: Sensor communication failure (WARNING)
|
||||
- `DIAG-DAQ-SENSOR-0002`: Sensor value out of range (WARNING)
|
||||
- `DIAG-DAQ-SENSOR-0003`: Acquisition cycle timeout (ERROR)
|
||||
- `DIAG-DAQ-SENSOR-0004`: Filter algorithm failure (ERROR)
|
||||
- `DIAG-DAQ-SENSOR-0005`: Sensor configuration error (ERROR)
|
||||
- `DIAG-DAQ-SENSOR-0006`: Consecutive sensor failures (FATAL)
|
||||
|
||||
## 10. State-Dependent Behavior
|
||||
|
||||
| System State | Sensor Manager Behavior |
|
||||
|-------------|------------------------|
|
||||
| **INIT** | Initialize sensors, load configuration, detect presence |
|
||||
| **RUNNING** | Normal acquisition cycles, full functionality |
|
||||
| **WARNING** | Continue acquisition, enhanced diagnostic reporting |
|
||||
| **FAULT** | Stop acquisition, preserve sensor states |
|
||||
| **OTA_UPDATE** | Stop acquisition, save sensor states for restoration |
|
||||
| **MC_UPDATE** | Stop acquisition, reload configuration after update |
|
||||
| **TEARDOWN** | Stop acquisition, flush pending data |
|
||||
| **SERVICE** | Limited acquisition for diagnostics, enhanced access |
|
||||
| **SD_DEGRADED** | Continue acquisition, no persistence (memory only) |
|
||||
|
||||
## 11. Dependencies
|
||||
|
||||
### 11.1 Required Components
|
||||
|
||||
- **Sensor Drivers:** Hardware interface for all sensor types
|
||||
- **Event System:** Cross-component communication
|
||||
- **Time Utils:** Timestamp generation
|
||||
- **Machine Constant Manager:** Sensor configuration and calibration data
|
||||
- **Logger:** Debug and diagnostic logging
|
||||
- **Error Handler:** Fault reporting and escalation
|
||||
|
||||
### 11.2 Required Interfaces
|
||||
|
||||
- Sensor driver interfaces (I2C, SPI, UART, ADC wrappers)
|
||||
- Event System publish/subscribe interface
|
||||
- Time Utils timestamp interface
|
||||
- MC Manager configuration interface
|
||||
- Logger interface
|
||||
- Error Handler fault reporting interface
|
||||
|
||||
## 12. Performance Requirements
|
||||
|
||||
### 12.1 Timing Constraints
|
||||
|
||||
- **Acquisition Cycle:** Complete within 1 second for all enabled sensors
|
||||
- **Sampling Window:** Maximum 800ms per sensor (10 samples)
|
||||
- **Filter Processing:** Maximum 50ms per sensor
|
||||
- **State Transition:** Maximum 100ms for state changes
|
||||
- **Event Publication:** Maximum 10ms delay
|
||||
|
||||
### 12.2 Resource Constraints
|
||||
|
||||
- **Memory Usage:** Maximum 32KB for sensor data buffers and state
|
||||
- **CPU Usage:** Maximum 20% of available CPU time
|
||||
- **Stack Usage:** Maximum 8KB stack for acquisition task
|
||||
|
||||
## 13. Acceptance Tests
|
||||
|
||||
### 13.1 Functional Tests
|
||||
|
||||
- **T-SENSOR-001:** All sensor types detected and initialized correctly
|
||||
- **T-SENSOR-002:** Acquisition cycles complete within 1-second constraint
|
||||
- **T-SENSOR-003:** High-frequency sampling (10 samples) works for all sensors
|
||||
- **T-SENSOR-004:** Filter algorithms reduce noise by >90%
|
||||
- **T-SENSOR-005:** Sensor state transitions occur correctly
|
||||
- **T-SENSOR-006:** Fault detection and recovery mechanisms work
|
||||
|
||||
### 13.2 Performance Tests
|
||||
|
||||
- **T-SENSOR-007:** Acquisition timing meets requirements under full load
|
||||
- **T-SENSOR-008:** Memory usage stays within 32KB limit
|
||||
- **T-SENSOR-009:** CPU usage stays within 20% limit
|
||||
- **T-SENSOR-010:** No memory leaks during continuous operation
|
||||
|
||||
### 13.3 Integration Tests
|
||||
|
||||
- **T-SENSOR-011:** Event System integration works correctly
|
||||
- **T-SENSOR-012:** Data Pool integration maintains latest sensor data
|
||||
- **T-SENSOR-013:** MC Manager integration loads configuration correctly
|
||||
- **T-SENSOR-014:** Error Handler integration reports faults correctly
|
||||
|
||||
### 13.4 Stress Tests
|
||||
|
||||
- **T-SENSOR-015:** 24-hour continuous operation without failures
|
||||
- **T-SENSOR-016:** Sensor fault injection and recovery scenarios
|
||||
- **T-SENSOR-017:** Configuration changes during operation
|
||||
- **T-SENSOR-018:** System state transitions during acquisition
|
||||
|
||||
## 14. Traceability
|
||||
|
||||
### 14.1 System Requirements
|
||||
|
||||
- **SR-DAQ-001:** Multi-sensor support for 7 sensor types
|
||||
- **SR-DAQ-002:** High-frequency sampling (minimum 10 samples/cycle)
|
||||
- **SR-DAQ-003:** Local data filtering with configurable algorithms
|
||||
- **SR-DAQ-004:** Timestamped data generation (±1 second accuracy)
|
||||
- **SR-DAQ-005:** Sensor state management and lifecycle control
|
||||
|
||||
### 14.2 Software Requirements
|
||||
|
||||
- **SWR-DAQ-001 to SWR-DAQ-015:** Complete sensor data acquisition implementation
|
||||
- **SWR-PERF-001:** Acquisition cycle timing constraints
|
||||
- **SWR-REL-001:** System availability requirements
|
||||
|
||||
### 14.3 Features
|
||||
|
||||
- **F-DAQ-001:** Multi-Sensor Data Acquisition
|
||||
- **F-DAQ-002:** High-Frequency Sampling
|
||||
- **F-DAQ-003:** Local Data Filtering
|
||||
- **F-DAQ-004:** Timestamped Data Generation
|
||||
- **F-DAQ-005:** Sensor State Management
|
||||
|
||||
## 15. Implementation Notes
|
||||
|
||||
### 15.1 Design Patterns
|
||||
|
||||
- **State Machine Pattern:** For sensor state management
|
||||
- **Strategy Pattern:** For filter algorithm selection
|
||||
- **Observer Pattern:** For sensor state change notifications
|
||||
- **Template Method Pattern:** For acquisition cycle execution
|
||||
|
||||
### 15.2 Key Implementation Details
|
||||
|
||||
- Sensor drivers SHALL be accessed through uniform interface (SAL)
|
||||
- Filter algorithms SHALL be pluggable and configurable
|
||||
- Sensor states SHALL be persisted across system restarts
|
||||
- Acquisition timing SHALL be deterministic and bounded
|
||||
- Error handling SHALL be comprehensive and non-blocking
|
||||
|
||||
### 15.3 Future Extensibility
|
||||
|
||||
- Support for additional sensor types through driver registration
|
||||
- Advanced filter algorithms (Kalman, adaptive filters)
|
||||
- Sensor fusion capabilities for redundant sensors
|
||||
- Machine learning-based sensor validation and prediction
|
||||
|
||||
---
|
||||
|
||||
**Document Status:** Final for Implementation Phase
|
||||
**Component Dependencies:** Verified against architecture
|
||||
**Requirements Traceability:** Complete (SR-DAQ, SWR-DAQ)
|
||||
**Next Review:** After implementation and testing
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file sensor_manager.cpp
|
||||
* @brief SensorManager component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "sensor_manager.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
static const char* TAG = "SensorManager";
|
||||
|
||||
SensorManager::SensorManager()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
SensorManager::~SensorManager()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool SensorManager::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 4100, asf::logger::Criticality::LOW, "SensorManager initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SensorManager::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SensorManager::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file sensor_manager.hpp
|
||||
* @brief SensorManager component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef SENSOR_MANAGER_HPP
|
||||
#define SENSOR_MANAGER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief SensorManager class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class SensorManager
|
||||
{
|
||||
public:
|
||||
SensorManager();
|
||||
~SensorManager();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // SENSOR_MANAGER_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
4100,SensorManager,INFO,Low,SensorManager initialized successfully
|
||||
|
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_sensor_manager_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "SensorManager initialized successfully" in line or "Sensor Manager initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_sensor_manager_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>SENSOR_MANAGER_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/business_stack/sensor_manager/test/sensor_manager_init_test.py</test_exec>
|
||||
</test_case>
|
||||
|
||||
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_sensor_manager.cpp
|
||||
* @brief Unit tests for SensorManager component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "sensor_manager.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_sensor_manager_initialize(void)
|
||||
{
|
||||
SensorManager comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_sensor_manager_deinitialize(void)
|
||||
{
|
||||
SensorManager comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/diag_task.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file diag_task.cpp
|
||||
* @brief DiagTask component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "diag_task.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
static const char* TAG = "DiagTask";
|
||||
|
||||
DiagTask::DiagTask()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
DiagTask::~DiagTask()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool DiagTask::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 4700, asf::logger::Criticality::LOW, "DiagTask initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiagTask::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiagTask::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file diag_task.hpp
|
||||
* @brief DiagTask component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef DIAG_TASK_HPP
|
||||
#define DIAG_TASK_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief DiagTask class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class DiagTask
|
||||
{
|
||||
public:
|
||||
DiagTask();
|
||||
~DiagTask();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // DIAG_TASK_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
4700,DiagTask,INFO,Low,DiagTask initialized successfully
|
||||
|
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_diag_task_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "DiagTask initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_diag_task_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>DIAG_TASK_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/diag_task/test/diag_task_init_test.py</test_exec>
|
||||
</test_case>
|
||||
|
||||
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_diag_task.cpp
|
||||
* @brief Unit tests for DiagTask component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "diag_task.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_diag_task_initialize(void)
|
||||
{
|
||||
DiagTask comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_diag_task_deinitialize(void)
|
||||
{
|
||||
DiagTask comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "com/error_handler.cpp"
|
||||
INCLUDE_DIRS "com"
|
||||
REQUIRES logger
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file error_handler.cpp
|
||||
* @brief ErrorHandler component implementation
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "error_handler.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
static const char* TAG = "ErrorHandler";
|
||||
|
||||
ErrorHandler::ErrorHandler()
|
||||
: m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorHandler::~ErrorHandler()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool ErrorHandler::initialize()
|
||||
{
|
||||
// TODO: Implement initialization
|
||||
m_isInitialized = true;
|
||||
ASF_LOGI(TAG, 4800, asf::logger::Criticality::LOW, "ErrorHandler initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ErrorHandler::deinitialize()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Implement deinitialization
|
||||
m_isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ErrorHandler::isInitialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file error_handler.hpp
|
||||
* @brief ErrorHandler component header
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#ifndef ERROR_HANDLER_HPP
|
||||
#define ERROR_HANDLER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief ErrorHandler class
|
||||
*
|
||||
* Component description goes here.
|
||||
*/
|
||||
class ErrorHandler
|
||||
{
|
||||
public:
|
||||
ErrorHandler();
|
||||
~ErrorHandler();
|
||||
|
||||
bool initialize();
|
||||
bool deinitialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
#endif // ERROR_HANDLER_HPP
|
||||
@@ -0,0 +1,2 @@
|
||||
ID,Component,Level,Criticality,Message
|
||||
4800,ErrorHandler,INFO,Low,ErrorHandler initialized successfully
|
||||
|
@@ -0,0 +1,34 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
folder_path = os.path.abspath(os.path.join("components", "system_tests"))
|
||||
if folder_path not in sys.path:
|
||||
sys.path.append(folder_path)
|
||||
|
||||
from scan_serial import ESP32Runner
|
||||
|
||||
def test_error_handler_initialize():
|
||||
runner = ESP32Runner(mode="SIM", port="COM9")
|
||||
runner.start()
|
||||
print("--- QEMU Runner Started ---", flush=True)
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 30:
|
||||
line = runner.get_line(timeout=1.0)
|
||||
if line:
|
||||
print(line, flush=True)
|
||||
if "ErrorHandler initialized successfully" in line:
|
||||
print("SUCCESS CRITERIA MET!", flush=True)
|
||||
return 0
|
||||
if runner.process.poll() is not None:
|
||||
print(f"Process exited with code: {runner.process.returncode}", flush=True)
|
||||
return 1
|
||||
finally:
|
||||
runner.stop()
|
||||
print("Done.", flush=True)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = test_error_handler_initialize()
|
||||
sys.exit(exit_code)
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test_scenario>
|
||||
<!-- The configuration for the test environment. -->
|
||||
<!-- Available configurations: SIMULATE, HIL -->
|
||||
<config>SIMULATE</config>
|
||||
|
||||
<test_case>
|
||||
<test_case_id>ERROR_HANDLER_INIT_TEST</test_case_id>
|
||||
<!-- The main command that executes the test itself. -->
|
||||
<test_exec>python components/application_layer/error_handler/test/error_handler_init_test.py</test_exec>
|
||||
</test_case>
|
||||
|
||||
|
||||
</test_scenario>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file test_error_handler.cpp
|
||||
* @brief Unit tests for ErrorHandler component
|
||||
* @author Mahmoud Elmohtady
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright Copyright (c) 2025
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "error_handler.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_error_handler_initialize(void)
|
||||
{
|
||||
ErrorHandler comp;
|
||||
bool result = comp.initialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_TRUE(comp.isInitialized());
|
||||
}
|
||||
|
||||
void test_error_handler_deinitialize(void)
|
||||
{
|
||||
ErrorHandler comp;
|
||||
comp.initialize();
|
||||
|
||||
bool result = comp.deinitialize();
|
||||
TEST_ASSERT_TRUE(result);
|
||||
TEST_ASSERT_FALSE(comp.isInitialized());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
Reference in New Issue
Block a user