use std::borrow::Cow;
use std::time::{Duration, SystemTime};
use crate::dev::{AttrQuality, DevState, DisplayLevel};
use crate::data::{DataKind, DataLayoutKind};
use crate::error::{Error, ErrorKind, ErrorSeverity};
use super::orb;
impl From<orb::CORBA_Exception> for Error {
#[track_caller]
fn from(err: orb::CORBA_Exception) -> Self {
let repo_id = err.repo_id();
let err = match orb::CORBA_SystemException::downcast(err) {
Ok(err) => {
let name = err.name();
if repo_id == c"IDL:omg.org/CORBA/OBJECT_NOT_EXIST:1.0"
|| repo_id == c"IDL:omg.org/CORBA/TRANSIENT:1.0"
|| repo_id == c"IDL:omg.org/CORBA/COMM_FAILURE:1.0" {
if name == c"OBJECT_NOT_EXIST_NoMatch" {
return Error::new(ErrorKind::DevNotConnected);
} else if name == c"TRANSIENT_NoUsableProfile"
|| name == c"TRANSIENT_ConnectFailed"
|| name == c"COMM_FAILURE_WaitingForReply" {
return Error::new(ErrorKind::ServerNotConnected);
} else if name == c"TRANSIENT_CallTimedout" {
return Error::new(ErrorKind::TimedOut);
} else {
return Error::new(ErrorKind::Internal)
.with_description(format!("{err:?}"))
.with_source(Box::new(err));
}
} else if repo_id == c"IDL:omg.org/CORBA/TIMEOUT:1.0" {
return Error::new(ErrorKind::TimedOut);
} else {
return Error::new(ErrorKind::Internal)
.with_description(format!("{err:?}"))
.with_source(Box::new(err));
}
}
Err(err) => err,
};
let err = match orb::Tango_DevFailed::downcast(err) {
Ok(err) if !err.data().errors.is_empty() => {
return Error::from(&err.data().errors);
}
Ok(err) => err.into_base(),
Err(err) => err,
};
Error::new(ErrorKind::Internal)
.with_description(format!("{err:?}"))
.with_source(Box::new(err))
}
}
impl<'a> From<&'a orb::Tango_DevErrorList> for Error {
fn from(value: &'a orb::Tango_DevErrorList) -> Self {
let mut top_err = None;
for raw_err in value.iter().rev() {
let mut err = Error::new(ErrorKind::Reason(&raw_err.reason.to_string_lossy()));
err.set_description(Cow::Owned(raw_err.desc.to_string_lossy().into_owned()));
err.set_origin(Cow::Owned(raw_err.origin.to_string_lossy().into_owned()));
err.set_severity(raw_err.severity.into());
if let Some(parent_err) = top_err.take() {
err.set_parent(parent_err);
}
top_err = Some(err);
}
top_err.expect("The given Tango::DevErrorList should have at least one error")
}
}
impl Into<DevState> for orb::Tango_DevState {
fn into(self) -> DevState {
match self {
orb::Tango_DevState::ON => DevState::On,
orb::Tango_DevState::OFF => DevState::Off,
orb::Tango_DevState::CLOSE => DevState::Close,
orb::Tango_DevState::OPEN => DevState::Open,
orb::Tango_DevState::INSERT => DevState::Insert,
orb::Tango_DevState::EXTRACT => DevState::Extract,
orb::Tango_DevState::MOVING => DevState::Moving,
orb::Tango_DevState::STANDBY => DevState::Standby,
orb::Tango_DevState::FAULT => DevState::Fault,
orb::Tango_DevState::INIT => DevState::Init,
orb::Tango_DevState::RUNNING => DevState::Running,
orb::Tango_DevState::ALARM => DevState::Alarm,
orb::Tango_DevState::DISABLE => DevState::Disable,
orb::Tango_DevState::UNKNOWN => DevState::Unknown,
}
}
}
impl From<DevState> for orb::Tango_DevState {
fn from(value: DevState) -> Self {
match value {
DevState::On => orb::Tango_DevState::ON,
DevState::Off => orb::Tango_DevState::OFF,
DevState::Close => orb::Tango_DevState::CLOSE,
DevState::Open => orb::Tango_DevState::OPEN,
DevState::Insert => orb::Tango_DevState::INSERT,
DevState::Extract => orb::Tango_DevState::EXTRACT,
DevState::Moving => orb::Tango_DevState::MOVING,
DevState::Standby => orb::Tango_DevState::STANDBY,
DevState::Fault => orb::Tango_DevState::FAULT,
DevState::Init => orb::Tango_DevState::INIT,
DevState::Running => orb::Tango_DevState::RUNNING,
DevState::Alarm => orb::Tango_DevState::ALARM,
DevState::Disable => orb::Tango_DevState::DISABLE,
DevState::Unknown => orb::Tango_DevState::UNKNOWN,
}
}
}
impl Into<ErrorSeverity> for orb::Tango_ErrSeverity {
fn into(self) -> ErrorSeverity {
match self {
orb::Tango_ErrSeverity::WARN => ErrorSeverity::Warning,
orb::Tango_ErrSeverity::ERR => ErrorSeverity::Error,
orb::Tango_ErrSeverity::PANIC => ErrorSeverity::Panic,
}
}
}
impl From<ErrorSeverity> for orb::Tango_ErrSeverity {
fn from(value: ErrorSeverity) -> Self {
match value {
ErrorSeverity::Warning => orb::Tango_ErrSeverity::WARN,
ErrorSeverity::Error => orb::Tango_ErrSeverity::ERR,
ErrorSeverity::Panic => orb::Tango_ErrSeverity::PANIC,
}
}
}
impl Into<AttrQuality> for orb::Tango_AttrQuality {
fn into(self) -> AttrQuality {
match self {
orb::Tango_AttrQuality::ATTR_VALID => AttrQuality::Valid,
orb::Tango_AttrQuality::ATTR_INVALID => AttrQuality::Invalid,
orb::Tango_AttrQuality::ATTR_ALARM => AttrQuality::Alarm,
orb::Tango_AttrQuality::ATTR_CHANGING => AttrQuality::Changing,
orb::Tango_AttrQuality::ATTR_WARNING => AttrQuality::Warning,
}
}
}
impl From<AttrQuality> for orb::Tango_AttrQuality {
fn from(value: AttrQuality) -> Self {
match value {
AttrQuality::Valid => tango_omniorb::Tango_AttrQuality::ATTR_VALID,
AttrQuality::Invalid => tango_omniorb::Tango_AttrQuality::ATTR_INVALID,
AttrQuality::Alarm => tango_omniorb::Tango_AttrQuality::ATTR_ALARM,
AttrQuality::Changing => tango_omniorb::Tango_AttrQuality::ATTR_CHANGING,
AttrQuality::Warning => tango_omniorb::Tango_AttrQuality::ATTR_WARNING,
}
}
}
impl Into<DisplayLevel> for orb::Tango_DispLevel {
fn into(self) -> DisplayLevel {
match self {
orb::Tango_DispLevel::OPERATOR => DisplayLevel::Operator,
orb::Tango_DispLevel::EXPERT => DisplayLevel::Expert,
orb::Tango_DispLevel::DL_UNKNOWN => DisplayLevel::Operator,
}
}
}
impl From<DisplayLevel> for orb::Tango_DispLevel {
fn from(value: DisplayLevel) -> Self {
match value {
DisplayLevel::Operator => orb::Tango_DispLevel::OPERATOR,
DisplayLevel::Expert => orb::Tango_DispLevel::EXPERT,
}
}
}
pub fn time_val_to_system(val: &orb::Tango_TimeVal) -> Option<SystemTime> {
let nanos =
u64::try_from(val.tv_nsec).ok()? +
u64::try_from(val.tv_usec).ok()? * 1_000 +
u64::try_from(val.tv_sec).ok()? * 1_000_000_000;
SystemTime::UNIX_EPOCH
.checked_add(Duration::from_nanos(nanos))
}
pub fn time_val_to_system_or_epoch(val: &orb::Tango_TimeVal) -> SystemTime {
time_val_to_system(val).unwrap_or(SystemTime::UNIX_EPOCH)
}
pub fn time_val_from_system(time: SystemTime) -> Option<orb::Tango_TimeVal> {
let duration = time.duration_since(SystemTime::UNIX_EPOCH).ok()?;
let nanos = duration.subsec_nanos();
let micros = nanos / 1_000;
let nanos = nanos - micros * 1_000;
Some(orb::Tango_TimeVal {
tv_sec: i32::try_from(duration.as_secs()).ok()?,
tv_usec: micros as i32,
tv_nsec: nanos as i32,
})
}
pub fn time_val_from_system_or_zero(time: SystemTime) -> orb::Tango_TimeVal {
time_val_from_system(time).unwrap_or_default()
}
pub fn data_kind_from_type(
data_type: orb::CORBA_Long,
attr_layout: Option<DataLayoutKind>,
enum_labels: Option<&orb::Tango_DevVarStringArray>,
) -> Option<DataKind> {
let attr = attr_layout.is_some();
let layout = attr_layout.unwrap_or(DataLayoutKind::Scalar);
Some(match data_type {
0 => DataKind::Void,
1 => DataKind::Bool(layout),
2 => DataKind::Short(layout),
3 => DataKind::Long(layout),
4 => DataKind::Float(layout),
5 => DataKind::Double(layout),
6 => DataKind::UShort(layout),
7 => DataKind::ULong(layout),
8 => DataKind::String(layout),
19 => DataKind::State(layout),
20 => DataKind::String(layout),
22 => DataKind::Octet(layout),
23 => DataKind::Long64(layout),
24 => DataKind::ULong64(layout),
29 => {
if let Some(labels) = enum_labels {
DataKind::Enum(Cow::Owned(labels.iter()
.map(|s| s.to_string_lossy().into_owned())
.collect::<Vec<_>>()))
} else {
DataKind::Short(layout)
}
}
_ if !attr => {
const SPECTRUM_KIND: DataLayoutKind = DataLayoutKind::Spectrum(None);
match data_type {
9 => DataKind::Octet(SPECTRUM_KIND),
10 => DataKind::Short(SPECTRUM_KIND),
11 => DataKind::Long(SPECTRUM_KIND),
12 => DataKind::Float(SPECTRUM_KIND),
13 => DataKind::Double(SPECTRUM_KIND),
14 => DataKind::UShort(SPECTRUM_KIND),
15 => DataKind::ULong(SPECTRUM_KIND),
16 => DataKind::String(SPECTRUM_KIND),
17 => DataKind::Struct(Cow::Borrowed(&[
DataKind::Long(SPECTRUM_KIND),
DataKind::String(SPECTRUM_KIND),
])),
18 => DataKind::Struct(Cow::Borrowed(&[
DataKind::Double(SPECTRUM_KIND),
DataKind::String(SPECTRUM_KIND),
])),
21 => DataKind::Bool(SPECTRUM_KIND),
25 => DataKind::Long64(SPECTRUM_KIND),
26 => DataKind::ULong64(SPECTRUM_KIND),
31 => DataKind::State(SPECTRUM_KIND),
_ => return None,
}
}
_ => return None,
})
}
pub const S_NOT_SPECIFIED: &str = "Not specified";
pub const S_NO_DESCRIPTION: &str = "No description";
pub const S_NO_DISPLAY_UNIT: &str = "No display unit";
pub const S_UNINITIALIZED: &str = "Uninitialised";
pub fn opt_str_if(s: &orb::CORBA_String, is_not: &str) -> Option<String> {
if s.to_bytes() == is_not.as_bytes() {
None
} else {
Some(s.to_string_lossy().into_owned())
}
}
pub fn opt_f32_empty(s: &orb::CORBA_String) -> Option<f32> {
if s.is_empty() {
None
} else {
s.to_string_lossy().parse::<f32>().ok()
}
}
pub fn opt_duration_if(s: &orb::CORBA_String, is_not: &str) -> Option<Duration> {
if s.to_bytes() == is_not.as_bytes() {
None
} else {
let s = s.to_string_lossy();
if let Some(s) = s.strip_suffix("ms") {
Some(Duration::from_millis(s.parse::<u64>().ok()?))
} else if let Some(s) = s.strip_suffix("s") {
Some(Duration::from_secs(s.parse::<u64>().ok()?))
} else {
None
}
}
}