254 lines
8.3 KiB
Rust
254 lines
8.3 KiB
Rust
|
|
// Integration tests for Modbus hardware
|
||
|
|
// These tests require physical Modbus relay device to be connected
|
||
|
|
// Run with: cargo test -- --ignored
|
||
|
|
|
||
|
|
use std::time::Duration;
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
use sta::domain::relay::controller::RelayController;
|
||
|
|
use sta::domain::relay::types::{RelayId, RelayState};
|
||
|
|
use sta::infrastructure::modbus::client::ModbusRelayController;
|
||
|
|
|
||
|
|
static HOST: &str = "192.168.1.200";
|
||
|
|
static PORT: u16 = 502;
|
||
|
|
static SLAVE_ID: u8 = 1;
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
#[ignore = "Requires physical Modbus device"]
|
||
|
|
async fn test_modbus_connection() {
|
||
|
|
// This test verifies we can connect to the actual Modbus device
|
||
|
|
// Configured with settings from settings/base.yaml
|
||
|
|
let timeout_secs = 5;
|
||
|
|
|
||
|
|
let _controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect to Modbus device");
|
||
|
|
|
||
|
|
// If we got here, connection was successful
|
||
|
|
println!("✓ Successfully connected to Modbus device");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
#[ignore = "Requires physical Modbus device"]
|
||
|
|
async fn test_read_relay_states() {
|
||
|
|
let timeout_secs = 5;
|
||
|
|
|
||
|
|
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect to Modbus device");
|
||
|
|
|
||
|
|
// Test reading individual relay states
|
||
|
|
for relay_id in 1..=8 {
|
||
|
|
let relay_id = RelayId::new(relay_id).unwrap();
|
||
|
|
let state = controller
|
||
|
|
.read_relay_state(relay_id)
|
||
|
|
.await
|
||
|
|
.expect("Failed to read relay state");
|
||
|
|
|
||
|
|
println!("Relay {}: {:?}", relay_id.as_u8(), state);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
#[ignore = "Requires physical Modbus device"]
|
||
|
|
async fn test_read_all_relays() {
|
||
|
|
let timeout_secs = 5;
|
||
|
|
|
||
|
|
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect to Modbus device");
|
||
|
|
|
||
|
|
let relays = controller
|
||
|
|
.read_all_states()
|
||
|
|
.await
|
||
|
|
.expect("Failed to read all relay states");
|
||
|
|
|
||
|
|
assert_eq!(relays.len(), 8, "Should have exactly 8 relays");
|
||
|
|
|
||
|
|
for (i, state) in relays.iter().enumerate() {
|
||
|
|
let relay_id = i + 1;
|
||
|
|
println!("Relay {}: {:?}", relay_id, state);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
#[ignore = "Requires physical Modbus device"]
|
||
|
|
async fn test_write_relay_state() {
|
||
|
|
let timeout_secs = 5;
|
||
|
|
|
||
|
|
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect to Modbus device");
|
||
|
|
|
||
|
|
let relay_id = RelayId::new(1).unwrap();
|
||
|
|
|
||
|
|
// Turn relay on
|
||
|
|
controller
|
||
|
|
.write_relay_state(relay_id, RelayState::On)
|
||
|
|
.await
|
||
|
|
.expect("Failed to write relay state");
|
||
|
|
|
||
|
|
// Verify it's on
|
||
|
|
let state = controller
|
||
|
|
.read_relay_state(relay_id)
|
||
|
|
.await
|
||
|
|
.expect("Failed to read relay state");
|
||
|
|
|
||
|
|
assert_eq!(state, RelayState::On, "Relay should be ON");
|
||
|
|
|
||
|
|
// Turn relay off
|
||
|
|
controller
|
||
|
|
.write_relay_state(relay_id, RelayState::Off)
|
||
|
|
.await
|
||
|
|
.expect("Failed to write relay state");
|
||
|
|
|
||
|
|
// Verify it's off
|
||
|
|
let state = controller
|
||
|
|
.read_relay_state(relay_id)
|
||
|
|
.await
|
||
|
|
.expect("Failed to read relay state");
|
||
|
|
|
||
|
|
assert_eq!(state, RelayState::Off, "Relay should be OFF");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
#[ignore = "Requires physical Modbus device"]
|
||
|
|
async fn test_write_all_relays() {
|
||
|
|
let timeout_secs = 5;
|
||
|
|
|
||
|
|
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect to Modbus device");
|
||
|
|
|
||
|
|
// Turn all relays on
|
||
|
|
let all_on_states = vec![RelayState::On; 8];
|
||
|
|
controller
|
||
|
|
.write_all_states(all_on_states)
|
||
|
|
.await
|
||
|
|
.expect("Failed to write all relay states");
|
||
|
|
|
||
|
|
// Verify all are on
|
||
|
|
let relays = controller
|
||
|
|
.read_all_states()
|
||
|
|
.await
|
||
|
|
.expect("Failed to read all relay states");
|
||
|
|
|
||
|
|
for state in &relays {
|
||
|
|
assert_eq!(*state, RelayState::On, "All relays should be ON");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Turn all relays off
|
||
|
|
let all_off_states = vec![RelayState::Off; 8];
|
||
|
|
controller
|
||
|
|
.write_all_states(all_off_states)
|
||
|
|
.await
|
||
|
|
.expect("Failed to write all relay states");
|
||
|
|
|
||
|
|
// Verify all are off
|
||
|
|
let relays = controller
|
||
|
|
.read_all_states()
|
||
|
|
.await
|
||
|
|
.expect("Failed to read all relay states");
|
||
|
|
|
||
|
|
for state in &relays {
|
||
|
|
assert_eq!(*state, RelayState::Off, "All relays should be OFF");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
#[ignore = "Requires physical Modbus device"]
|
||
|
|
async fn test_timeout_handling() {
|
||
|
|
let timeout_secs = 1; // Short timeout for testing
|
||
|
|
|
||
|
|
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect to Modbus device");
|
||
|
|
|
||
|
|
// This test verifies that timeout works correctly
|
||
|
|
// We'll try to read a relay state with a very short timeout
|
||
|
|
let relay_id = RelayId::new(1).unwrap();
|
||
|
|
|
||
|
|
// The operation should either succeed quickly or timeout
|
||
|
|
let result = tokio::time::timeout(
|
||
|
|
Duration::from_secs(2),
|
||
|
|
controller.read_relay_state(relay_id),
|
||
|
|
)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
match result {
|
||
|
|
Ok(Ok(state)) => {
|
||
|
|
println!("✓ Operation completed within timeout: {:?}", state);
|
||
|
|
}
|
||
|
|
Ok(Err(e)) => {
|
||
|
|
println!("✓ Operation failed (expected): {}", e);
|
||
|
|
}
|
||
|
|
Err(_) => {
|
||
|
|
println!("✓ Operation timed out (expected)");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
#[ignore = "Requires physical Modbus device"]
|
||
|
|
async fn test_concurrent_access() {
|
||
|
|
let timeout_secs = 5;
|
||
|
|
|
||
|
|
let _controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect to Modbus device");
|
||
|
|
|
||
|
|
// Test concurrent access to the controller
|
||
|
|
// We'll test a few relays concurrently using tokio::join!
|
||
|
|
// Note: We can't clone the controller, so we'll just test sequential access
|
||
|
|
// This is still valuable for testing the controller works with multiple relays
|
||
|
|
|
||
|
|
let relay_id1 = RelayId::new(1).unwrap();
|
||
|
|
let relay_id2 = RelayId::new(2).unwrap();
|
||
|
|
let relay_id3 = RelayId::new(3).unwrap();
|
||
|
|
let relay_id4 = RelayId::new(4).unwrap();
|
||
|
|
|
||
|
|
let task1 = tokio::spawn(async move {
|
||
|
|
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect");
|
||
|
|
controller.read_relay_state(relay_id1).await
|
||
|
|
});
|
||
|
|
let task2 = tokio::spawn(async move {
|
||
|
|
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect");
|
||
|
|
controller.read_relay_state(relay_id2).await
|
||
|
|
});
|
||
|
|
let task3 = tokio::spawn(async move {
|
||
|
|
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect");
|
||
|
|
controller.read_relay_state(relay_id3).await
|
||
|
|
});
|
||
|
|
let task4 = tokio::spawn(async move {
|
||
|
|
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
|
||
|
|
.await
|
||
|
|
.expect("Failed to connect");
|
||
|
|
controller.read_relay_state(relay_id4).await
|
||
|
|
});
|
||
|
|
|
||
|
|
let (result1, result2, result3, result4) = tokio::join!(task1, task2, task3, task4);
|
||
|
|
|
||
|
|
// Process results
|
||
|
|
if let Ok(Ok(state)) = result1 {
|
||
|
|
println!("Relay 1: {:?}", state);
|
||
|
|
}
|
||
|
|
if let Ok(Ok(state)) = result2 {
|
||
|
|
println!("Relay 2: {:?}", state);
|
||
|
|
}
|
||
|
|
if let Ok(Ok(state)) = result3 {
|
||
|
|
println!("Relay 3: {:?}", state);
|
||
|
|
}
|
||
|
|
if let Ok(Ok(state)) = result4 {
|
||
|
|
println!("Relay 4: {:?}", state);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|