use crate::types::EpicsValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SimMode {
No,
Yes,
Raw,
Illegal,
}
impl SimMode {
pub fn is_simulated(self) -> bool {
!matches!(self, SimMode::No)
}
}
pub fn resolve_sim_mode(record: &dyn crate::server::record::Record) -> SimMode {
let simm = record
.get_field("SIMM")
.and_then(|v| v.to_f64())
.unwrap_or(0.0) as i16;
let choices = record
.menu_field_choices("SIMM")
.map(|c| c.len())
.unwrap_or(0);
match simm {
0 => SimMode::No,
1 => SimMode::Yes,
2 if choices >= 3 => SimMode::Raw,
_ if !record.rejects_illegal_sim_mode() => SimMode::Yes,
_ => SimMode::Illegal,
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum LinkFetch {
Value(EpicsValue),
NoData,
Failed,
}
impl LinkFetch {
pub fn is_ok(&self) -> bool {
!matches!(self, LinkFetch::Failed)
}
pub fn value(self) -> Option<EpicsValue> {
match self {
LinkFetch::Value(v) => Some(v),
LinkFetch::NoData | LinkFetch::Failed => None,
}
}
}
pub fn constant_load_value(link: &crate::server::record::ParsedLink) -> Option<EpicsValue> {
match link {
crate::server::record::ParsedLink::Constant(_) => link.constant_value(),
_ => None,
}
}
pub fn is_constant(link: &crate::server::record::ParsedLink) -> bool {
matches!(
link,
crate::server::record::ParsedLink::None | crate::server::record::ParsedLink::Constant(_)
)
}
const RECORDS_WITH_SSCN: &[&str] = &[
"aai",
"aao",
"ai",
"ao",
"bi",
"bo",
"event",
"histogram",
"int64in",
"int64out",
"longin",
"longout",
"lsi",
"lso",
"mbbi",
"mbbiDirect",
"mbbo",
"mbboDirect",
"stringin",
"stringout",
"waveform",
];
pub fn record_type_has_sscn(record_type: &str) -> bool {
RECORDS_WITH_SSCN.contains(&record_type)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_the_21_c_records_with_field_sscn_swap_scan() {
assert!(record_type_has_sscn("longin"));
assert!(record_type_has_sscn("mbbiDirect"));
assert!(record_type_has_sscn("waveform"));
assert!(!record_type_has_sscn("busy"));
assert!(!record_type_has_sscn("swait"));
assert!(!record_type_has_sscn("calc"));
}
#[test]
fn simm_2_is_raw_on_a_menu_simm_record_and_illegal_on_a_menu_yesno_one() {
use crate::server::record::Record;
use crate::server::records::ai::AiRecord; use crate::server::records::longin::LonginRecord;
let mut ai = AiRecord::new(0.0);
let mut li = LonginRecord::new(0);
for (simm, ai_mode, li_mode) in [
(0i16, SimMode::No, SimMode::No),
(1, SimMode::Yes, SimMode::Yes),
(2, SimMode::Raw, SimMode::Illegal),
(7, SimMode::Illegal, SimMode::Illegal),
] {
ai.put_field("SIMM", EpicsValue::Short(simm)).unwrap();
li.put_field("SIMM", EpicsValue::Short(simm)).unwrap();
assert_eq!(resolve_sim_mode(&ai), ai_mode, "ai SIMM={simm}");
assert_eq!(resolve_sim_mode(&li), li_mode, "longin SIMM={simm}");
}
assert!(!SimMode::No.is_simulated());
assert!(SimMode::Yes.is_simulated());
assert!(SimMode::Raw.is_simulated());
assert!(SimMode::Illegal.is_simulated());
}
#[test]
fn constant_and_unset_links_are_both_constant() {
use crate::server::record::ParsedLink;
assert!(is_constant(&ParsedLink::None));
assert!(is_constant(&ParsedLink::Constant("42".into())));
assert!(!is_constant(&crate::server::record::parse_link_v2(
"SOME:PV"
)));
}
#[test]
fn no_data_is_a_success_not_a_failure() {
assert!(LinkFetch::NoData.is_ok());
assert!(LinkFetch::Value(EpicsValue::Long(1)).is_ok());
assert!(!LinkFetch::Failed.is_ok());
}
}