use epics_macros_rs::EpicsRecord;
const SEQ_SELM_CHOICES: &[&str] = &["All", "Specified", "Mask"];
fn seq_metadata_override(
_rec: &SeqRecord,
field: &str,
) -> Option<crate::server::record::FieldMetadataOverride> {
let f = field.to_ascii_uppercase();
let is_dly = matches!(f.as_bytes(), [b'D', b'L', b'Y', c]
if c.is_ascii_digit() || (b'A'..=b'F').contains(c));
is_dly.then(|| crate::server::record::FieldMetadataOverride {
units: Some("s".into()),
precision: Some(SEQ_DLY_PRECISION),
disp_limits: Some((10.0, 0.0)),
ctrl_limits: Some((SEQ_DLY_LIMIT, 0.0)),
..Default::default()
})
}
const SEQ_DLY_PRECISION: i16 = 2;
const SEQ_DLY_LIMIT: f64 = 100000.0;
#[derive(EpicsRecord)]
#[record(
type = "seq",
metadata_override = seq_metadata_override,
constant_init = "SELL:SELN,DOL0:DO0,DOL1:DO1,DOL2:DO2,DOL3:DO3,DOL4:DO4,\
DOL5:DO5,DOL6:DO6,DOL7:DO7,DOL8:DO8,DOL9:DO9,DOLA:DOA,\
DOLB:DOB,DOLC:DOC,DOLD:DOD,DOLE:DOE,DOLF:DOF",
init = seq_init_record,
// seq's VAL is a `pp(TRUE)` "trigger" — C `process()` posts VAL only with
// alarm events (`if (events) db_post_events(&prec->val, events)`,
// seqRecord.c:227-229), never DBE_VALUE/DBE_LOG. So a run of `caput VAL`
// posts no per-put value monitor (only the connect-time snapshot).
no_value_monitor
)]
pub struct SeqRecord {
#[field(type = "Long")]
pub val: i32,
#[field(type = "UShort")]
pub oldn: u16,
#[field(type = "Short", menu_choices = SEQ_SELM_CHOICES)]
pub selm: i16,
#[field(type = "UShort")]
pub seln: u16,
#[field(type = "String")]
pub sell: String,
#[field(type = "Short")]
pub offs: i16,
#[field(type = "Short")]
pub shft: i16,
#[field(type = "Double")]
pub dly0: f64,
#[field(type = "Double")]
pub dly1: f64,
#[field(type = "Double")]
pub dly2: f64,
#[field(type = "Double")]
pub dly3: f64,
#[field(type = "Double")]
pub dly4: f64,
#[field(type = "Double")]
pub dly5: f64,
#[field(type = "Double")]
pub dly6: f64,
#[field(type = "Double")]
pub dly7: f64,
#[field(type = "Double")]
pub dly8: f64,
#[field(type = "Double")]
pub dly9: f64,
#[field(type = "Double")]
pub dlya: f64,
#[field(type = "Double")]
pub dlyb: f64,
#[field(type = "Double")]
pub dlyc: f64,
#[field(type = "Double")]
pub dlyd: f64,
#[field(type = "Double")]
pub dlye: f64,
#[field(type = "Double")]
pub dlyf: f64,
#[field(type = "String")]
pub dol0: String,
#[field(type = "String")]
pub dol1: String,
#[field(type = "String")]
pub dol2: String,
#[field(type = "String")]
pub dol3: String,
#[field(type = "String")]
pub dol4: String,
#[field(type = "String")]
pub dol5: String,
#[field(type = "String")]
pub dol6: String,
#[field(type = "String")]
pub dol7: String,
#[field(type = "String")]
pub dol8: String,
#[field(type = "String")]
pub dol9: String,
#[field(type = "String")]
pub dola: String,
#[field(type = "String")]
pub dolb: String,
#[field(type = "String")]
pub dolc: String,
#[field(type = "String")]
pub dold: String,
#[field(type = "String")]
pub dole: String,
#[field(type = "String")]
pub dolf: String,
#[field(type = "Double")]
pub do0: f64,
#[field(type = "Double")]
pub do1: f64,
#[field(type = "Double")]
pub do2: f64,
#[field(type = "Double")]
pub do3: f64,
#[field(type = "Double")]
pub do4: f64,
#[field(type = "Double")]
pub do5: f64,
#[field(type = "Double")]
pub do6: f64,
#[field(type = "Double")]
pub do7: f64,
#[field(type = "Double")]
pub do8: f64,
#[field(type = "Double")]
pub do9: f64,
#[field(type = "Double")]
pub doa: f64,
#[field(type = "Double")]
pub dob: f64,
#[field(type = "Double")]
pub doc: f64,
#[field(type = "Double")]
pub dod: f64,
#[field(type = "Double")]
pub doe: f64,
#[field(type = "Double")]
pub dof: f64,
#[field(type = "String")]
pub lnk0: String,
#[field(type = "String")]
pub lnk1: String,
#[field(type = "String")]
pub lnk2: String,
#[field(type = "String")]
pub lnk3: String,
#[field(type = "String")]
pub lnk4: String,
#[field(type = "String")]
pub lnk5: String,
#[field(type = "String")]
pub lnk6: String,
#[field(type = "String")]
pub lnk7: String,
#[field(type = "String")]
pub lnk8: String,
#[field(type = "String")]
pub lnk9: String,
#[field(type = "String")]
pub lnka: String,
#[field(type = "String")]
pub lnkb: String,
#[field(type = "String")]
pub lnkc: String,
#[field(type = "String")]
pub lnkd: String,
#[field(type = "String")]
pub lnke: String,
#[field(type = "String")]
pub lnkf: String,
}
impl Default for SeqRecord {
fn default() -> Self {
Self {
val: 0,
oldn: 0,
selm: 0,
seln: 1,
sell: String::new(),
offs: 0,
shft: -1,
dly0: 0.0,
dly1: 0.0,
dly2: 0.0,
dly3: 0.0,
dly4: 0.0,
dly5: 0.0,
dly6: 0.0,
dly7: 0.0,
dly8: 0.0,
dly9: 0.0,
dlya: 0.0,
dlyb: 0.0,
dlyc: 0.0,
dlyd: 0.0,
dlye: 0.0,
dlyf: 0.0,
dol0: String::new(),
dol1: String::new(),
dol2: String::new(),
dol3: String::new(),
dol4: String::new(),
dol5: String::new(),
dol6: String::new(),
dol7: String::new(),
dol8: String::new(),
dol9: String::new(),
dola: String::new(),
dolb: String::new(),
dolc: String::new(),
dold: String::new(),
dole: String::new(),
dolf: String::new(),
do0: 0.0,
do1: 0.0,
do2: 0.0,
do3: 0.0,
do4: 0.0,
do5: 0.0,
do6: 0.0,
do7: 0.0,
do8: 0.0,
do9: 0.0,
doa: 0.0,
dob: 0.0,
doc: 0.0,
dod: 0.0,
doe: 0.0,
dof: 0.0,
lnk0: String::new(),
lnk1: String::new(),
lnk2: String::new(),
lnk3: String::new(),
lnk4: String::new(),
lnk5: String::new(),
lnk6: String::new(),
lnk7: String::new(),
lnk8: String::new(),
lnk9: String::new(),
lnka: String::new(),
lnkb: String::new(),
lnkc: String::new(),
lnkd: String::new(),
lnke: String::new(),
lnkf: String::new(),
}
}
}
impl SeqRecord {
pub fn new() -> Self {
Self::default()
}
}
fn seq_init_record(rec: &mut SeqRecord, pass: u8) -> crate::error::CaResult<()> {
if pass == 1 {
rec.oldn = rec.seln;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::server::database::{SelmKind, select_link_indices_ex};
#[test]
fn default_shft_is_minus_one_and_mask_shifts_left() {
let rec = SeqRecord::default();
assert_eq!(rec.shft, -1);
let sel = select_link_indices_ex(SelmKind::FanoutSeq, 2, 1, rec.offs, rec.shft, 16);
assert_eq!(sel.indices, vec![1]);
}
}