use std::collections::HashSet;
use epics_base_rs::server::database::PvDatabase;
use epics_base_rs::server::event_queue::EventReader;
use epics_base_rs::server::recgbl::EventMask;
use epics_base_rs::server::record::Record;
use epics_base_rs::server::records::transform::TransformRecord;
use epics_base_rs::types::{DbFieldType, EpicsValue};
async fn transform_db() -> PvDatabase {
let db = PvDatabase::new();
let mut t = TransformRecord::default();
t.put_field("A", EpicsValue::Double(5.0)).unwrap();
db.add_record("T", Box::new(t)).await.unwrap();
db
}
fn subscribe(db: &PvDatabase, field: &str, id: u32) -> EventReader {
let inst = db.get_record("T").unwrap();
let mut g = inst.write();
let full = (EventMask::VALUE | EventMask::LOG | EventMask::ALARM).bits();
g.add_subscriber(field, id, DbFieldType::Double, full)
.unwrap_or_else(|| panic!("a {field} subscription must be accepted"))
}
async fn process(db: &PvDatabase) {
let mut visited = HashSet::new();
db.process_record_with_links("T", &mut visited, 0)
.await
.unwrap();
}
#[epics_macros_rs::epics_test]
async fn the_first_cycle_posts_every_channel_though_none_moved() {
let db = transform_db().await;
let mut a = subscribe(&db, "A", 1);
let mut b = subscribe(&db, "B", 2);
process(&db).await;
assert_eq!(
a.try_recv().map(|e| e.snapshot.value.clone()).ok(),
Some(EpicsValue::Double(5.0)),
"firstCalcPosted == 0 posts A whether or not it moved"
);
assert_eq!(
b.try_recv().map(|e| e.snapshot.value.clone()).ok(),
Some(EpicsValue::Double(0.0)),
"and every other channel with it, at its unchanged value"
);
}
#[epics_macros_rs::epics_test]
async fn the_second_cycle_posts_nothing() {
let db = transform_db().await;
let mut a = subscribe(&db, "A", 1);
process(&db).await;
assert!(a.try_recv().is_ok(), "cycle 1 posts");
process(&db).await;
assert!(
a.try_recv().is_err(),
"nothing clears firstCalcPosted, so an unchanged channel is silent"
);
}
#[epics_macros_rs::epics_test]
async fn a_later_change_still_posts() {
let db = transform_db().await;
let mut a = subscribe(&db, "A", 1);
process(&db).await;
assert!(a.try_recv().is_ok());
assert!(a.try_recv().is_err());
db.put_pv("T.A", EpicsValue::Double(9.0)).await.unwrap();
while a.try_recv().is_ok() {}
process(&db).await;
assert!(
a.try_recv().is_err(),
"the put already published 9.0, so the cycle finds A unchanged"
);
db.put_pv("T.A", EpicsValue::Double(11.0)).await.unwrap();
while a.try_recv().is_ok() {}
process(&db).await;
assert!(a.try_recv().is_err(), "same on the next put");
}
#[epics_macros_rs::epics_test]
async fn the_first_cycle_posts_carry_no_alarm_bit() {
let db = transform_db().await;
let mut alarm_only = {
let inst = db.get_record("T").unwrap();
let mut g = inst.write();
g.add_subscriber("A", 3, DbFieldType::Double, EventMask::ALARM.bits())
.expect("an A subscription must be accepted")
};
process(&db).await;
assert!(
alarm_only.try_recv().is_err(),
"transformRecord.c:794 throws the alarm mask away"
);
}