use crate::error::CaResult;
use crate::types::{DbFieldType, EpicsValue};
use super::scan::ScanType;
#[derive(Debug, Clone)]
pub struct FieldDesc {
pub name: &'static str,
pub dbf_type: DbFieldType,
pub read_only: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ProcessAction {
WriteDbLink {
link_field: &'static str,
value: EpicsValue,
},
ReadDbLink {
link_field: &'static str,
target_field: &'static str,
},
ReprocessAfter(std::time::Duration),
DeviceCommand {
command: &'static str,
args: Vec<EpicsValue>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub enum RecordProcessResult {
Complete,
AsyncPending,
AsyncPendingNotify(Vec<(String, EpicsValue)>),
}
#[derive(Clone, Debug)]
pub struct ProcessOutcome {
pub result: RecordProcessResult,
pub actions: Vec<ProcessAction>,
pub device_did_compute: bool,
}
impl ProcessOutcome {
pub fn complete() -> Self {
Self {
result: RecordProcessResult::Complete,
actions: Vec::new(),
device_did_compute: false,
}
}
pub fn complete_with(actions: Vec<ProcessAction>) -> Self {
Self {
result: RecordProcessResult::Complete,
actions,
device_did_compute: false,
}
}
pub fn async_pending() -> Self {
Self {
result: RecordProcessResult::AsyncPending,
actions: Vec::new(),
device_did_compute: false,
}
}
}
impl Default for ProcessOutcome {
fn default() -> Self {
Self::complete()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CommonFieldPutResult {
NoChange,
ScanChanged {
old_scan: ScanType,
new_scan: ScanType,
phas: i16,
},
PhasChanged {
scan: ScanType,
old_phas: i16,
new_phas: i16,
},
}
pub struct ProcessSnapshot {
pub changed_fields: Vec<(String, EpicsValue)>,
pub event_mask: crate::server::recgbl::EventMask,
}
pub trait Record: Send + Sync + 'static {
fn record_type(&self) -> &'static str;
fn process(&mut self) -> CaResult<ProcessOutcome> {
Ok(ProcessOutcome::complete())
}
fn took_metadata_change(&mut self) -> bool {
false
}
fn get_field(&self, name: &str) -> Option<EpicsValue>;
fn put_field(&mut self, name: &str, value: EpicsValue) -> CaResult<()>;
fn field_list(&self) -> &'static [FieldDesc];
fn validate_put(&self, _field: &str, _value: &EpicsValue) -> CaResult<()> {
Ok(())
}
fn on_put(&mut self, _field: &str) {}
fn primary_field(&self) -> &'static str {
"VAL"
}
fn val(&self) -> Option<EpicsValue> {
self.get_field(self.primary_field())
}
fn set_val(&mut self, value: EpicsValue) -> CaResult<()> {
let field = self.primary_field();
match self.put_field(field, value.clone()) {
Ok(()) => Ok(()),
Err(crate::error::CaError::TypeMismatch(_)) => {
let target_type = self
.get_field(field)
.map(|v| v.db_field_type())
.unwrap_or(DbFieldType::Double);
let coerced = value.convert_to(target_type);
self.put_field(field, coerced)
}
Err(e) => Err(e),
}
}
fn can_device_write(&self) -> bool {
matches!(
self.record_type(),
"ao" | "bo" | "longout" | "mbbo" | "stringout"
)
}
fn is_put_complete(&self) -> bool {
true
}
fn should_fire_forward_link(&self) -> bool {
true
}
fn should_output(&self) -> bool {
true
}
fn uses_monitor_deadband(&self) -> bool {
true
}
fn init_record(&mut self, _pass: u8) -> CaResult<()> {
Ok(())
}
fn special(&mut self, _field: &str, _after: bool) -> CaResult<()> {
Ok(())
}
fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
None
}
fn clears_udf(&self) -> bool {
true
}
fn multi_input_links(&self) -> &[(&'static str, &'static str)] {
&[]
}
fn multi_output_links(&self) -> &[(&'static str, &'static str)] {
&[]
}
fn put_field_internal(&mut self, name: &str, value: EpicsValue) -> CaResult<()> {
self.put_field(name, value)
}
fn pre_process_actions(&mut self) -> Vec<ProcessAction> {
Vec::new()
}
fn set_device_did_compute(&mut self, _did_compute: bool) {}
}
pub type SubroutineFn = Box<dyn Fn(&mut dyn Record) -> CaResult<()> + Send + Sync>;