use epics_base_rs::server::database::PvDatabase;
use epics_base_rs::server::ioc_builder::IocBuilder;
use epics_base_rs::types::EpicsValue;
const POST_ONCHANGE: i16 = 1;
const SIMM_YES: i16 = 1;
const SEVR_MINOR: i16 = 1;
const DB: &str = r#"
record(ai, "SIM:SW") {
field(VAL, "0")
}
record(waveform, "ARR:SRC") {
field(FTVL, "DOUBLE")
field(NELM, "8")
}
record(aai, "ARR:AAI") {
field(FTVL, "DOUBLE")
field(NELM, "8")
field(SIML, "SIM:SW")
field(SIMM, "YES")
field(SIOL, "ARR:SRC")
field(SIMS, "MINOR")
field(SDLY, "0.25")
field(MPST, "On Change")
field(APST, "On Change")
}
record(aao, "ARR:AAO") {
field(FTVL, "DOUBLE")
field(NELM, "8")
field(SIML, "SIM:SW")
field(SIMM, "YES")
field(SIOL, "ARR:SRC")
field(SIMS, "MINOR")
field(SDLY, "0.25")
field(MPST, "On Change")
field(APST, "On Change")
}
record(waveform, "ARR:WF") {
field(FTVL, "DOUBLE")
field(NELM, "8")
field(SIML, "SIM:SW")
field(SIMM, "YES")
field(SIOL, "ARR:SRC")
field(SIMS, "MINOR")
field(SDLY, "0.25")
field(MPST, "On Change")
field(APST, "On Change")
}
"#;
async fn field(db: &PvDatabase, pv: &str) -> EpicsValue {
db.get_pv(pv)
.unwrap_or_else(|e| panic!("{pv} not readable: {e}"))
}
async fn assert_sim_block_loaded(db: &PvDatabase, rec: &str) {
assert_eq!(
field(db, &format!("{rec}.SIML")).await,
EpicsValue::String("SIM:SW".into()),
"{rec}: field(SIML) dropped at load"
);
assert_eq!(
field(db, &format!("{rec}.SIOL")).await,
EpicsValue::String("ARR:SRC".into()),
"{rec}: field(SIOL) dropped at load"
);
assert_eq!(
field(db, &format!("{rec}.SIMM")).await.to_f64(),
Some(SIMM_YES as f64),
"{rec}: field(SIMM,\"YES\") dropped at load"
);
assert_eq!(
field(db, &format!("{rec}.SIMS")).await.to_f64(),
Some(SEVR_MINOR as f64),
"{rec}: field(SIMS,\"MINOR\") dropped at load"
);
assert_eq!(
field(db, &format!("{rec}.SDLY")).await,
EpicsValue::Double(0.25),
"{rec}: field(SDLY) dropped at load (no storage before R15-76)"
);
assert_eq!(
field(db, &format!("{rec}.MPST")).await.to_f64(),
Some(POST_ONCHANGE as f64),
"{rec}: field(MPST,\"On Change\") dropped at load"
);
assert_eq!(
field(db, &format!("{rec}.APST")).await.to_f64(),
Some(POST_ONCHANGE as f64),
"{rec}: field(APST,\"On Change\") dropped at load"
);
}
#[epics_macros_rs::epics_test]
async fn aai_db_load_carries_sim_and_post_fields() {
let (db, _) = IocBuilder::new()
.db_string(DB, &std::collections::HashMap::new())
.unwrap()
.build()
.await
.unwrap();
assert_sim_block_loaded(&db, "ARR:AAI").await;
}
#[epics_macros_rs::epics_test]
async fn aao_db_load_carries_sim_and_post_fields() {
let (db, _) = IocBuilder::new()
.db_string(DB, &std::collections::HashMap::new())
.unwrap()
.build()
.await
.unwrap();
assert_sim_block_loaded(&db, "ARR:AAO").await;
}
#[epics_macros_rs::epics_test]
async fn waveform_db_load_carries_sim_and_post_fields() {
let (db, _) = IocBuilder::new()
.db_string(DB, &std::collections::HashMap::new())
.unwrap()
.build()
.await
.unwrap();
assert_sim_block_loaded(&db, "ARR:WF").await;
}
#[epics_macros_rs::epics_test]
async fn loaded_sdly_defers_the_simulated_aai_read() {
let (db, _) = IocBuilder::new()
.db_string(DB, &std::collections::HashMap::new())
.unwrap()
.build()
.await
.unwrap();
db.put_pv("ARR:SRC", EpicsValue::DoubleArray(vec![10.0, 20.0, 30.0]))
.await
.unwrap();
db.put_pv("SIM:SW", EpicsValue::Double(1.0)).await.unwrap();
let mut visited = std::collections::HashSet::new();
db.process_record_with_links("ARR:AAI", &mut visited, 0)
.await
.unwrap();
assert_eq!(
field(&db, "ARR:AAI").await,
EpicsValue::DoubleArray(vec![]),
"SDLY >= 0 must defer the simulated SIOL read (PACT held)"
);
for _ in 0..200 {
epics_base_rs::runtime::task::sleep(std::time::Duration::from_millis(10)).await;
if field(&db, "ARR:AAI").await == EpicsValue::DoubleArray(vec![10.0, 20.0, 30.0]) {
return;
}
}
panic!(
"deferred SIOL read never landed (last {:?})",
field(&db, "ARR:AAI").await
);
}