pub mod db_access;
mod field_io;
mod links;
mod processing;
mod scan_index;
use crate::runtime::sync::RwLock;
use std::collections::{BTreeSet, HashMap};
use std::sync::Arc;
use crate::server::pv::ProcessVariable;
use crate::server::record::{Record, RecordInstance, ScanType};
use crate::types::EpicsValue;
pub fn parse_pv_name(name: &str) -> (&str, &str) {
match name.rsplit_once('.') {
Some((base, field)) => (base, field),
None => (name, "VAL"),
}
}
fn apply_timestamp(common: &mut super::record::CommonFields, _is_soft: bool) {
match common.tse {
0 => {
common.time = crate::runtime::general_time::get_current();
}
-1 => {
if common.time == std::time::SystemTime::UNIX_EPOCH {
common.time = crate::runtime::general_time::get_event(-1);
}
}
-2 => {
}
_ => {
common.time = crate::runtime::general_time::get_event(common.tse as i32);
}
}
}
pub enum PvEntry {
Simple(Arc<ProcessVariable>),
Record(Arc<RwLock<RecordInstance>>),
}
pub type ExternalPvResolver = Arc<dyn Fn(&str) -> Option<EpicsValue> + Send + Sync>;
struct PvDatabaseInner {
simple_pvs: RwLock<HashMap<String, Arc<ProcessVariable>>>,
records: RwLock<HashMap<String, Arc<RwLock<RecordInstance>>>>,
scan_index: RwLock<HashMap<ScanType, BTreeSet<(i16, String)>>>,
cp_links: RwLock<HashMap<String, Vec<String>>>,
external_resolver: RwLock<Option<ExternalPvResolver>>,
}
#[derive(Clone)]
pub struct PvDatabase {
inner: Arc<PvDatabaseInner>,
}
fn select_link_indices(selm: i16, seln: i16, count: usize) -> Vec<usize> {
match selm {
0 => (0..count).collect(),
1 => {
let i = seln as usize;
if i < count { vec![i] } else { vec![] }
}
2 => (0..count)
.filter(|i| (seln as u16) & (1 << i) != 0)
.collect(),
_ => (0..count).collect(),
}
}
impl PvDatabase {
pub fn new() -> Self {
Self {
inner: Arc::new(PvDatabaseInner {
simple_pvs: RwLock::new(HashMap::new()),
external_resolver: RwLock::new(None),
records: RwLock::new(HashMap::new()),
scan_index: RwLock::new(HashMap::new()),
cp_links: RwLock::new(HashMap::new()),
}),
}
}
pub async fn set_external_resolver(&self, resolver: ExternalPvResolver) {
*self.inner.external_resolver.write().await = Some(resolver);
}
pub(crate) async fn resolve_external_pv(&self, name: &str) -> Option<EpicsValue> {
let resolver = self.inner.external_resolver.read().await;
resolver.as_ref().and_then(|r| r(name))
}
pub async fn add_pv(&self, name: &str, initial: EpicsValue) {
let pv = Arc::new(ProcessVariable::new(name.to_string(), initial));
self.inner
.simple_pvs
.write()
.await
.insert(name.to_string(), pv);
}
pub async fn add_record(&self, name: &str, record: Box<dyn Record>) {
let instance = RecordInstance::new_boxed(name.to_string(), record);
let scan = instance.common.scan;
let phas = instance.common.phas;
self.inner
.records
.write()
.await
.insert(name.to_string(), Arc::new(RwLock::new(instance)));
if scan != ScanType::Passive {
self.inner
.scan_index
.write()
.await
.entry(scan)
.or_default()
.insert((phas, name.to_string()));
}
}
pub async fn find_entry(&self, name: &str) -> Option<PvEntry> {
let (base, _field) = parse_pv_name(name);
if let Some(pv) = self.inner.simple_pvs.read().await.get(name) {
return Some(PvEntry::Simple(pv.clone()));
}
if let Some(rec) = self.inner.records.read().await.get(base) {
return Some(PvEntry::Record(rec.clone()));
}
None
}
pub async fn has_name(&self, name: &str) -> bool {
let (base, _) = parse_pv_name(name);
if self.inner.simple_pvs.read().await.contains_key(name) {
return true;
}
self.inner.records.read().await.contains_key(base)
}
pub async fn find_pv(&self, name: &str) -> Option<Arc<ProcessVariable>> {
if let Some(pv) = self.inner.simple_pvs.read().await.get(name) {
return Some(pv.clone());
}
None
}
pub async fn get_record(&self, name: &str) -> Option<Arc<RwLock<RecordInstance>>> {
self.inner.records.read().await.get(name).cloned()
}
pub async fn all_record_names(&self) -> Vec<String> {
self.inner.records.read().await.keys().cloned().collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_select_link_indices() {
assert_eq!(select_link_indices(0, 0, 6), vec![0, 1, 2, 3, 4, 5]);
assert_eq!(select_link_indices(1, 2, 6), vec![2]);
assert_eq!(select_link_indices(1, 10, 6), Vec::<usize>::new());
assert_eq!(select_link_indices(2, 5, 6), vec![0, 2]);
}
}