use super::channel_wire_tests::{ch, channel, member, settled, slot, write_channel, write_status};
use super::command_action_wire_tests::{ao, cmd, outputs, read_db, state, write};
use super::cov_wire_test_support::*;
use super::*;
use bacnet_objects::command::CommandObject;
use bacnet_types::constructed::{BACnetActionCommand, BACnetActionList};
use bacnet_types::enums::WriteStatus;
fn command(instance: u32, commands: Vec<BACnetActionCommand>) -> CommandObject {
let mut command = CommandObject::new(instance, format!("CMD-{instance}")).unwrap();
command
.set_action(vec![BACnetActionList { commands }])
.unwrap();
command
}
fn plain(object: ObjectIdentifier, value: PropertyValue) -> BACnetActionCommand {
BACnetActionCommand {
priority: None,
..write(object, value, 16)
}
}
fn pause() -> BACnetActionCommand {
BACnetActionCommand {
post_delay: Some(1),
..write(ao(1), PropertyValue::Real(5.0), 8)
}
}
async fn busy(h: &Harness, channels: &[u32], commands: &[u32]) -> bool {
for &instance in channels {
if read_db(h, ch(instance), PropertyIdentifier::WRITE_STATUS, None).await
== PropertyValue::Enumerated(WriteStatus::IN_PROGRESS.to_raw())
{
return true;
}
}
for &instance in commands {
if read_db(h, cmd(instance), PropertyIdentifier::IN_PROCESS, None).await
== PropertyValue::Boolean(true)
{
return true;
}
}
false
}
async fn assert_quiet(h: &Harness, seconds: u64, channels: &[u32], commands: &[u32]) {
for _ in 0..seconds * 100 {
assert!(!busy(h, channels, commands).await, "a run started again");
tokio::time::sleep(Duration::from_millis(10)).await;
}
}
#[tokio::test(start_paused = true)]
async fn run_cycle_two_channels_naming_each_other_stop_after_one_round_trip() {
let mut h = Harness::start_with(ServerConfig::default(), |db| {
db.add(Box::new(channel(5, 1, vec![(member(ch(6), PV), 100)])))
.unwrap();
db.add(Box::new(channel(6, 2, vec![(member(ch(5), PV), 100)])))
.unwrap();
})
.await;
write_channel(&mut h, 5, &PropertyValue::Real(1.0), None)
.await
.unwrap();
tokio::time::sleep(Duration::from_millis(250)).await;
assert_eq!(settled(&mut h, 5).await, WriteStatus::FAILED);
assert_eq!(settled(&mut h, 6).await, WriteStatus::FAILED);
assert_quiet(&h, 2, &[5, 6], &[]).await;
assert_eq!(read_db(&h, ch(6), PV, None).await, PropertyValue::Real(1.0));
}
#[tokio::test(start_paused = true)]
async fn run_cycle_a_channel_and_a_command_naming_each_other_stop() {
let mut h = Harness::start_with(ServerConfig::default(), |db| {
outputs(db);
db.add(Box::new(channel(7, 3, vec![(member(cmd(4), PV), 100)])))
.unwrap();
db.add(Box::new(command(
4,
vec![pause(), plain(ch(7), PropertyValue::Unsigned(1))],
)))
.unwrap();
})
.await;
write_channel(&mut h, 7, &PropertyValue::Unsigned(1), None)
.await
.unwrap();
tokio::time::sleep(Duration::from_millis(1200)).await;
assert_eq!(settled(&mut h, 7).await, WriteStatus::FAILED);
assert_eq!(state(&mut h, 4).await, (false, false));
assert_quiet(&h, 3, &[7], &[4]).await;
}
#[tokio::test(start_paused = true)]
async fn run_cycle_two_commands_naming_each_other_stop() {
let mut h = Harness::start_with(ServerConfig::default(), |db| {
outputs(db);
db.add(Box::new(command(
5,
vec![pause(), plain(cmd(6), PropertyValue::Unsigned(1))],
)))
.unwrap();
db.add(Box::new(command(
6,
vec![pause(), plain(cmd(5), PropertyValue::Unsigned(1))],
)))
.unwrap();
})
.await;
h.server
.write_local(
&cmd(5),
PV,
None,
PropertyValue::Unsigned(1),
None,
crate::LocalCommandSource::ServerDevice,
)
.await
.unwrap();
tokio::time::sleep(Duration::from_millis(2200)).await;
h.settle().await;
assert_eq!(state(&mut h, 5).await, (false, false));
assert_eq!(state(&mut h, 6).await, (false, false));
assert_quiet(&h, 3, &[], &[5, 6]).await;
}
#[tokio::test(start_paused = true)]
async fn run_cycle_a_chain_of_ten_channels_stops_past_eight_runs_deep() {
let mut h = Harness::start_with(ServerConfig::default(), |db| {
outputs(db);
for instance in 11..20 {
db.add(Box::new(channel(
instance,
1,
vec![(member(ch(instance + 1), PV), 0)],
)))
.unwrap();
}
db.add(Box::new(channel(20, 1, vec![(member(ao(1), PV), 0)])))
.unwrap();
})
.await;
write_channel(&mut h, 11, &PropertyValue::Real(7.0), Some(8))
.await
.unwrap();
for instance in 11..19 {
assert_eq!(
settled(&mut h, instance).await,
WriteStatus::SUCCESSFUL,
"CH-{instance}"
);
}
assert_eq!(settled(&mut h, 19).await, WriteStatus::FAILED);
assert_eq!(write_status(&mut h, 20).await, WriteStatus::FAILED);
assert_eq!(
read_db(&h, ch(20), PV, None).await,
PropertyValue::Real(7.0)
);
assert_eq!(slot(&h, ao(1), 8).await, PropertyValue::Null);
}