#![cfg(feature = "drlc")]
use std::{sync::Arc, time::Duration};
use sep2_client::{
client::Client,
device::SEDevice,
event::{EIStatus, EventCallback, EventInstance, Schedule, Scheduler},
time::current_time,
};
use sep2_common::{
packages::{
drlc::{DemandResponseProgram, EndDeviceControl},
identification::ResponseStatus,
objects::EventStatusType,
primitives::{HexBinary128, Int64, Uint32},
types::{DeviceCategoryType, PrimacyType},
},
traits::SEIdentifiedObject,
};
use tokio::sync::RwLock;
fn test_setup() -> (Schedule<EndDeviceControl>, EndDeviceControlhandler) {
let client = Client::new_https(
"https://127.0.0.1:1337",
"../certs/client_cert.pem",
"../certs/client_private_key.pem",
"../certs/rootCA.pem",
None,
None,
)
.unwrap();
let device =
SEDevice::new_from_cert("../certs/client_cert.pem", DeviceCategoryType::all()).unwrap();
let handler = EndDeviceControlhandler {
logs: Arc::new(RwLock::new(vec![])),
};
(
Schedule::new(
client,
Arc::new(RwLock::new(device)),
handler.clone(),
Duration::from_secs(0),
),
handler,
)
}
#[derive(Clone)]
struct EndDeviceControlhandler {
logs: Arc<RwLock<Vec<String>>>,
}
impl EventCallback<EndDeviceControl> for EndDeviceControlhandler {
async fn event_update(&self, event: &EventInstance<EndDeviceControl>) -> ResponseStatus {
let log = match event.status() {
EIStatus::Scheduled => {
format!("Received EndDeviceControl: {}", event.event().mrid().0)
}
EIStatus::Active => {
format!("EndDeviceControl Started: {}", event.event().mrid().0)
}
EIStatus::Cancelled => {
format!("EndDeviceControl Cancelled: {}", event.event().mrid().0)
}
EIStatus::Complete => {
format!("EndDeviceControl Complete: {}", event.event().mrid().0)
}
EIStatus::CancelledRandom => {
format!("EndDeviceControl Cancelled: {}", event.event().mrid().0)
}
EIStatus::Superseded => {
format!("EndDeviceControl Superseded: {}", event.event().mrid().0)
}
};
log::debug!("{log}");
self.logs.write().await.push(log);
event.status().into()
}
}
fn create_event(
status: EventStatusType,
count: i64,
start: i64,
duration: u32,
) -> EndDeviceControl {
let mut out = EndDeviceControl::default();
out.device_category = DeviceCategoryType::all();
out.mrid = HexBinary128(count.try_into().unwrap());
out.creation_time = Int64(count);
out.event_status.current_status = status;
out.interval.start = Int64(start);
out.interval.duration = Uint32(duration);
out
}
#[tokio::test]
async fn basic_drlc_scheduler() {
let program = DemandResponseProgram::default();
let (mut schedule, logs) = test_setup();
let first = create_event(
EventStatusType::Scheduled,
1,
i64::from(current_time()) + 1,
2,
);
let second = create_event(
EventStatusType::Scheduled,
2,
i64::from(current_time()) + 4,
2,
);
let third = create_event(
EventStatusType::Scheduled,
3,
i64::from(current_time()) + 7,
2,
);
schedule.add_event(second, &program, 0).await;
schedule.add_event(third, &program, 0).await;
schedule.add_event(first, &program, 0).await;
tokio::time::sleep(Duration::from_secs(10)).await;
assert_eq!(
logs.logs.read().await.as_ref(),
vec![
"EndDeviceControl Started: 1",
"EndDeviceControl Complete: 1",
"EndDeviceControl Started: 2",
"EndDeviceControl Complete: 2",
"EndDeviceControl Started: 3",
"EndDeviceControl Complete: 3"
]
);
}
#[tokio::test]
async fn superseded_drlc_scheduler() {
let program = DemandResponseProgram::default();
let (mut schedule, logs) = test_setup();
let superseded = create_event(
EventStatusType::Scheduled,
0,
i64::from(current_time()) + 2,
1,
);
let first = create_event(
EventStatusType::Scheduled,
1,
i64::from(current_time()) + 1,
4,
);
let second = create_event(
EventStatusType::Scheduled,
2,
i64::from(current_time()) + 4,
2,
);
let third = create_event(
EventStatusType::Scheduled,
3,
i64::from(current_time()) + 7,
2,
);
schedule.add_event(first, &program, 0).await;
schedule.add_event(superseded, &program, 0).await;
tokio::time::sleep(Duration::from_secs(3)).await;
schedule.add_event(second, &program, 0).await;
schedule.add_event(third, &program, 0).await;
tokio::time::sleep(Duration::from_secs(7)).await;
assert_eq!(
logs.logs.read().await.as_ref(),
vec![
"EndDeviceControl Started: 1",
"EndDeviceControl Superseded: 1",
"EndDeviceControl Started: 2",
"EndDeviceControl Complete: 2",
"EndDeviceControl Started: 3",
"EndDeviceControl Complete: 3"
]
);
}
#[tokio::test]
async fn cancelling_drlc_scheduler() {
let program = DemandResponseProgram::default();
let (mut schedule, logs) = test_setup();
let mut first = create_event(
EventStatusType::Scheduled,
1,
i64::from(current_time()) + 1,
2,
);
let mut second = create_event(
EventStatusType::Scheduled,
2,
i64::from(current_time()) + 4,
2,
);
let mut third = create_event(
EventStatusType::Scheduled,
3,
i64::from(current_time()) + 7,
2,
);
schedule.add_event(second.clone(), &program, 0).await;
schedule.add_event(third.clone(), &program, 0).await;
schedule.add_event(first.clone(), &program, 0).await;
tokio::time::sleep(Duration::from_secs(3)).await;
first.event_status.current_status = EventStatusType::Cancelled;
schedule.add_event(first, &program, 0).await;
tokio::time::sleep(Duration::from_secs(3)).await;
second.event_status.current_status = EventStatusType::Cancelled;
schedule.add_event(second, &program, 0).await;
third.event_status.current_status = EventStatusType::Cancelled;
schedule.add_event(third, &program, 0).await;
tokio::time::sleep(Duration::from_secs(4)).await;
assert_eq!(
logs.logs.read().await.as_ref(),
vec![
"EndDeviceControl Started: 1",
"EndDeviceControl Cancelled: 1",
"EndDeviceControl Started: 2",
"EndDeviceControl Cancelled: 2",
]
);
}
#[tokio::test]
async fn unsupersede_drlc_scheduler() {
let program = DemandResponseProgram::default();
let (mut schedule, logs) = test_setup();
let second = create_event(
EventStatusType::Scheduled,
0,
i64::from(current_time()) + 4,
2,
);
let mut first = create_event(
EventStatusType::Scheduled,
1,
i64::from(current_time()) + 1,
4,
);
let third = create_event(
EventStatusType::Scheduled,
3,
i64::from(current_time()) + 7,
2,
);
schedule.add_event(first.clone(), &program, 0).await;
schedule.add_event(second, &program, 0).await;
schedule.add_event(third, &program, 0).await;
tokio::time::sleep(Duration::from_secs(3)).await;
first.event_status.current_status = EventStatusType::Cancelled;
schedule.add_event(first.clone(), &program, 0).await;
tokio::time::sleep(Duration::from_secs(7)).await;
assert_eq!(
logs.logs.read().await.as_ref(),
vec![
"EndDeviceControl Started: 1",
"EndDeviceControl Cancelled: 1",
"EndDeviceControl Started: 0",
"EndDeviceControl Complete: 0",
"EndDeviceControl Started: 3",
"EndDeviceControl Complete: 3"
]
);
}
#[tokio::test]
async fn schedule_drlc_differing_primacy() {
let (program1, mut program2, mut program3) = (
DemandResponseProgram::default(),
DemandResponseProgram::default(),
DemandResponseProgram::default(),
);
program2.primacy = PrimacyType::ContractedPremisesServiceProvider;
program3.primacy = PrimacyType::NonContractualServiceProvider;
let (mut schedule, logs) = test_setup();
let first = create_event(
EventStatusType::Scheduled,
1,
i64::from(current_time()) + 1,
3,
);
let second = create_event(
EventStatusType::Scheduled,
2,
i64::from(current_time()) + 2,
2,
);
let third = create_event(
EventStatusType::Scheduled,
3,
i64::from(current_time()) + 3,
2,
);
schedule.add_event(first, &program3, 0).await;
schedule.add_event(second, &program1, 0).await;
schedule.add_event(third, &program2, 0).await;
tokio::time::sleep(Duration::from_secs(5)).await;
assert_eq!(
logs.logs.read().await.as_ref(),
vec![
"EndDeviceControl Started: 2",
"EndDeviceControl Complete: 2",
]
);
}