use std::slice;
use tango_omniorb as orb;
use crate::backend::{DevGetAttrInfoBackend, DevReadAttrBackend, DevWriteAttrBackend};
use crate::data::{Data, DataKind, DataLayout, DataLayoutKind, DataLayoutView, DataView};
use crate::dev::{AttrArchiveEventConfig, AttrChangeEventConfig, AttrConfig, AttrDeltaConfig, AttrInfo, AttrMemorizedMode, AttrMode, AttrPeriodicEventConfig, AttrRangeConfig, AttrRead, AttrWrite, DisplayLevel};
use crate::error::{Error, ErrorKind, Result};
use crate::array::MdView;
use super::convert::{
time_val_to_system_or_epoch, time_val_from_system_or_zero, data_kind_from_type,
opt_str_if, opt_f32_empty, opt_duration_if,
S_NO_DESCRIPTION, S_NOT_SPECIFIED, S_NO_DISPLAY_UNIT};
pub fn attr_write_to_value_v1(attr_data: &AttrWrite<'_, Data<'_>>) -> Result<orb::Tango_AttributeValue> {
let name = orb::CORBA_String::try_from(&*attr_data.name)
.map_err(|_| Error::new(ErrorKind::AttrNameInvalid))?;
let mut ret = orb::Tango_AttributeValue {
value: orb::CORBA_Any::new(),
quality: attr_data.quality.into(),
time: time_val_from_system_or_zero(attr_data.time),
name,
dim_x: 0,
dim_y: 0,
};
if let Some(data) = attr_data.value.as_ref() {
match data.as_view() {
DataView::Bool(val) =>
layout_to_any::<_, orb::Tango_DevVarBooleanArray>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y)?,
DataView::Octet(val) =>
layout_to_any::<_, orb::Tango_DevVarCharArray>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y)?,
DataView::UShort(val) =>
layout_to_any::<_, orb::Tango_DevVarUShortArray>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y)?,
DataView::Short(val) =>
layout_to_any::<_, orb::Tango_DevVarShortArray>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y)?,
DataView::ULong(val) =>
layout_to_any::<_, orb::Tango_DevVarULongArray>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y)?,
DataView::Long(val) =>
layout_to_any::<_, orb::Tango_DevVarLongArray>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y)?,
DataView::ULong64(val) =>
layout_to_any::<_, orb::Tango_DevVarULong64Array>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y)?,
DataView::Long64(val) =>
layout_to_any::<_, orb::Tango_DevVarLong64Array>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y)?,
DataView::Float(val) =>
layout_to_any::<_, orb::Tango_DevVarFloatArray>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y)?,
DataView::Double(val) =>
layout_to_any::<_, orb::Tango_DevVarDoubleArray>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y)?,
DataView::State(val) =>
layout_to_any_mapped::<_, _, orb::Tango_DevVarStateArray, _>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y, |&s| s.into())?,
DataView::String(val) =>
layout_to_any_mapped::<_, _, orb::Tango_DevVarStringArray, _>(val, &mut ret.value, &mut ret.dim_x, &mut ret.dim_y, |s| {
orb::CORBA_String::try_from(&**s).unwrap_or_else(|_| orb::CORBA_String::default())
})?,
_ => return Err(Error::new(ErrorKind::InvalidData)
.with_append_description(format_args!("Cannot be converted to Tango::AttributeValue: {data:?}"))),
}
}
Ok(ret)
}
pub fn attr_write_to_value_v4(attr_data: &AttrWrite<'_, Data<'_>>) -> Result<orb::Tango_AttributeValue_4> {
let name = orb::CORBA_String::try_from(&*attr_data.name)
.map_err(|_| Error::new(ErrorKind::AttrNameInvalid))?;
let (value, format, dim_x, dim_y) =
if let Some(data) = attr_data.value.as_ref() {
match data.as_view() {
DataView::Bool(val) =>
layout_to_union::<_, orb::Tango_DevVarBooleanArray>(val, orb::Tango_AttrValUnion::with_bool_att_value)?,
DataView::Octet(val) =>
layout_to_union::<_, orb::Tango_DevVarCharArray>(val, orb::Tango_AttrValUnion::with_uchar_att_value)?,
DataView::UShort(val) =>
layout_to_union::<_, orb::Tango_DevVarUShortArray>(val, orb::Tango_AttrValUnion::with_ushort_att_value)?,
DataView::Short(val) =>
layout_to_union::<_, orb::Tango_DevVarShortArray>(val, orb::Tango_AttrValUnion::with_short_att_value)?,
DataView::ULong(val) =>
layout_to_union::<_, orb::Tango_DevVarULongArray>(val, orb::Tango_AttrValUnion::with_ulong_att_value)?,
DataView::Long(val) =>
layout_to_union::<_, orb::Tango_DevVarLongArray>(val, orb::Tango_AttrValUnion::with_long_att_value)?,
DataView::ULong64(val) =>
layout_to_union::<_, orb::Tango_DevVarULong64Array>(val, orb::Tango_AttrValUnion::with_ulong64_att_value)?,
DataView::Long64(val) =>
layout_to_union::<_, orb::Tango_DevVarLong64Array>(val, orb::Tango_AttrValUnion::with_long64_att_value)?,
DataView::Float(val) =>
layout_to_union::<_, orb::Tango_DevVarFloatArray>(val, orb::Tango_AttrValUnion::with_float_att_value)?,
DataView::Double(val) =>
layout_to_union::<_, orb::Tango_DevVarDoubleArray>(val, orb::Tango_AttrValUnion::with_double_att_value)?,
DataView::State(val) =>
layout_to_union_mapped::<_, _, orb::Tango_DevVarStateArray, _>(val, |&s| s.into(), orb::Tango_AttrValUnion::with_state_att_value)?,
DataView::String(val) =>
layout_to_union_mapped::<_, _, orb::Tango_DevVarStringArray, _>(val, |s| {
orb::CORBA_String::try_from(&**s).unwrap_or_else(|_| orb::CORBA_String::default())
}, orb::Tango_AttrValUnion::with_string_att_value)?,
_ => return Err(Error::new(ErrorKind::InvalidData)
.with_append_description(format_args!("Cannot be converted to Tango::AttributeValue: {data:?}"))),
}
} else {
(orb::Tango_AttrValUnion::with_union_no_data(false), orb::Tango_AttrDataFormat::SCALAR, 0, 0)
};
let ret = orb::Tango_AttributeValue_4 {
value,
quality: attr_data.quality.into(),
data_format: format,
time: time_val_from_system_or_zero(attr_data.time),
name,
r_dim: orb::Tango_AttributeDim::default(),
w_dim: orb::Tango_AttributeDim { dim_x, dim_y },
err_list: orb::Tango_DevErrorList::default(),
};
Ok(ret)
}
fn layout_to_slice_and_dim<'a, T>(val: DataLayoutView<'a, T>) -> (&'a [T], orb::Tango_AttrDataFormat, usize, usize) {
match val {
DataLayoutView::Scalar(val) => (slice::from_ref(val), orb::Tango_AttrDataFormat::SCALAR, 1, 0),
DataLayoutView::Spectrum(val) => (val.as_raw_slice(), orb::Tango_AttrDataFormat::SPECTRUM, val.len(0), 0),
DataLayoutView::Image(val) => (val.as_raw_slice(), orb::Tango_AttrDataFormat::IMAGE, val.len(0), val.len(1)),
}
}
#[inline]
fn layout_to_array_mapped<T, U, A, F>(val: DataLayoutView<'_, T>, func: F) -> Result<(A, orb::Tango_AttrDataFormat, i32, i32)>
where
F: FnMut(&T) -> U,
A: FromIterator<U> + orb::CORBA_AnyWideBinding,
{
let (slice, format, dim_x, dim_y) = layout_to_slice_and_dim(val);
let dim_x = i32::try_from(dim_x).map_err(|_| Error::new(ErrorKind::InvalidData)
.with_append_description("Could not encode X dimension"))?;
let dim_y = i32::try_from(dim_y).map_err(|_| Error::new(ErrorKind::InvalidData)
.with_append_description("Could not encode Y dimension"))?;
Ok((slice.iter().map(func).collect::<A>(), format, dim_x, dim_y))
}
#[inline]
fn layout_to_any_mapped<T, U, A, F>(val: DataLayoutView<'_, T>, any: &mut orb::CORBA_Any, dim_x: &mut i32, dim_y: &mut i32, func: F) -> Result<()>
where
F: FnMut(&T) -> U,
A: FromIterator<U> + orb::CORBA_AnyWideBinding,
{
let (arr, _, dim_x_, dim_y_) = layout_to_array_mapped::<T, U, A, _>(val, func)?;
*dim_x = dim_x_;
*dim_y = dim_y_;
any.insert_move(orb::CppBox::new(arr));
Ok(())
}
#[inline]
fn layout_to_any<T: Clone, A>(val: DataLayoutView<'_, T>, any: &mut orb::CORBA_Any, dim_x: &mut i32, dim_y: &mut i32) -> Result<()>
where
A: FromIterator<T> + orb::CORBA_AnyWideBinding,
{
layout_to_any_mapped::<T, T, A, _>(val, any, dim_x, dim_y, |val| val.clone())
}
#[inline]
fn layout_to_union_mapped<T, U, A, F>(val: DataLayoutView<'_, T>, func: F, variant: fn(orb::CppBox<A>) -> orb::Tango_AttrValUnion) -> Result<(orb::Tango_AttrValUnion, orb::Tango_AttrDataFormat, i32, i32)>
where
F: FnMut(&T) -> U,
A: FromIterator<U> + orb::CORBA_AnyWideBinding,
{
let (arr, format, dim_x, dim_y) = layout_to_array_mapped::<T, U, A, _>(val, func)?;
Ok((variant(orb::CppBox::new(arr)), format, dim_x, dim_y))
}
#[inline]
fn layout_to_union<T: Clone, A>(val: DataLayoutView<'_, T>, variant: fn(orb::CppBox<A>) -> orb::Tango_AttrValUnion) -> Result<(orb::Tango_AttrValUnion, orb::Tango_AttrDataFormat, i32, i32)>
where
A: FromIterator<T> + orb::CORBA_AnyWideBinding,
{
layout_to_union_mapped::<T, T, A, _>(val, |val| val.clone(), variant)
}
#[derive(Debug)]
pub struct DevReadAttrFromValueList<R> {
raw_list: orb::CppBox<R>,
}
impl<R> DevReadAttrFromValueList<R> {
#[inline]
pub fn new(raw_list: orb::CppBox<R>) -> Self {
Self {
raw_list,
}
}
}
impl DevReadAttrBackend for DevReadAttrFromValueList<orb::Tango_AttributeValueList> {
fn len(&self) -> usize {
self.raw_list.len()
}
fn get(&self, index: usize) -> Result<AttrRead<'_, Data<'_>>> {
self.raw_list.get(index)
.ok_or_else(|| Error::new(ErrorKind::InvalidIndex))
.and_then(attr_read_from_value_v1)
}
}
impl DevReadAttrBackend for DevReadAttrFromValueList<orb::Tango_AttributeValueList_3> {
fn len(&self) -> usize {
self.raw_list.len()
}
fn get(&self, index: usize) -> Result<AttrRead<'_, Data<'_>>> {
self.raw_list.get(index)
.ok_or_else(|| Error::new(ErrorKind::InvalidIndex))
.and_then(attr_read_from_value_v3)
}
}
impl DevReadAttrBackend for DevReadAttrFromValueList<orb::Tango_AttributeValueList_4> {
fn len(&self) -> usize {
self.raw_list.len()
}
fn get(&self, index: usize) -> Result<AttrRead<'_, Data<'_>>> {
self.raw_list.get(index)
.ok_or_else(|| Error::new(ErrorKind::InvalidIndex))
.and_then(attr_read_from_value_v4)
}
}
impl DevReadAttrBackend for DevReadAttrFromValueList<orb::Tango_AttributeValueList_5> {
fn len(&self) -> usize {
self.raw_list.len()
}
fn get(&self, index: usize) -> Result<AttrRead<'_, Data<'_>>> {
self.raw_list.get(index)
.ok_or_else(|| Error::new(ErrorKind::InvalidIndex))
.and_then(attr_read_from_value_v5)
}
}
pub fn attr_read_from_value_v1(raw_val: &orb::Tango_AttributeValue) -> Result<AttrRead<'_, Data<'_>>> {
let (mut value, mut w_value) = (None, None);
if raw_val.quality != orb::Tango_AttrQuality::ATTR_INVALID {
(value, w_value) = attr_data_from_any(&raw_val.value, &orb::Tango_AttributeDim { dim_x: raw_val.dim_x, dim_y: raw_val.dim_y }, None)?;
}
Ok(AttrRead {
name: raw_val.name.as_cstr().to_string_lossy(),
value,
w_value,
quality: raw_val.quality.into(),
time: time_val_to_system_or_epoch(&raw_val.time),
})
}
pub fn attr_read_from_value_v3(raw_val: &orb::Tango_AttributeValue_3) -> Result<AttrRead<'_, Data<'_>>> {
if !raw_val.err_list.is_empty() {
return Err(Error::from(&raw_val.err_list));
}
let (mut value, mut w_value) = (None, None);
if raw_val.quality != orb::Tango_AttrQuality::ATTR_INVALID {
(value, w_value) = attr_data_from_any(&raw_val.value, &raw_val.r_dim, Some(&raw_val.w_dim))?;
}
Ok(AttrRead {
name: raw_val.name.as_cstr().to_string_lossy(),
value,
w_value,
quality: raw_val.quality.into(),
time: time_val_to_system_or_epoch(&raw_val.time),
})
}
pub fn attr_read_from_value_v4(raw_val: &orb::Tango_AttributeValue_4) -> Result<AttrRead<'_, Data<'_>>> {
if !raw_val.err_list.is_empty() {
return Err(Error::from(&raw_val.err_list));
}
let (mut value, mut w_value) = (None, None);
if raw_val.quality != orb::Tango_AttrQuality::ATTR_INVALID {
(value, w_value) = attr_data_from_attr_union(&raw_val.value, raw_val.data_format, &raw_val.r_dim, &raw_val.w_dim)?;
}
Ok(AttrRead {
name: raw_val.name.as_cstr().to_string_lossy(),
value,
w_value,
quality: raw_val.quality.into(),
time: time_val_to_system_or_epoch(&raw_val.time),
})
}
pub fn attr_read_from_value_v5(raw_val: &orb::Tango_AttributeValue_5) -> Result<AttrRead<'_, Data<'_>>> {
if !raw_val.err_list.is_empty() {
return Err(Error::from(&raw_val.err_list));
}
let (mut value, mut w_value) = (None, None);
if raw_val.quality != orb::Tango_AttrQuality::ATTR_INVALID {
(value, w_value) = attr_data_from_attr_union(&raw_val.value, raw_val.data_format, &raw_val.r_dim, &raw_val.w_dim)?;
}
Ok(AttrRead {
name: raw_val.name.as_cstr().to_string_lossy(),
value,
w_value,
quality: raw_val.quality.into(),
time: time_val_to_system_or_epoch(&raw_val.time),
})
}
fn attr_data_from_any<'a>(
any: &'a orb::CORBA_Any,
r_dim: &orb::Tango_AttributeDim,
w_dim: Option<&orb::Tango_AttributeDim>,
) -> Result<(Option<Data<'a>>, Option<Data<'a>>)> {
#[inline]
fn seq_tc_item(any: &orb::CORBA_Any) -> Option<orb::CORBA_TypeCodeKind> {
let tc = any.type_code()?;
if tc.kind() != orb::CORBA_TypeCodeKind::Alias { return None }
let tc_seq = tc.content_type()?;
if tc_seq.kind() != orb::CORBA_TypeCodeKind::Sequence { return None }
let tc_item = tc_seq.content_type()?;
Some(tc_item.kind())
}
let Some(tc_item_kind) = seq_tc_item(any) else {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Could not find attribute type from CORBA::Any"));
};
let (r_dim, w_dim) = match (dim_to_tuple(r_dim), w_dim.map(dim_to_tuple)) {
(Some(r_dim), None) => (r_dim, (0, 0)),
(Some(r_dim), Some(Some(w_dim))) => (r_dim, w_dim),
_ => return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Could not parse dimensions")),
};
fn could_not_extract() -> Error {
Error::new(ErrorKind::InvalidData)
.with_append_description("Could not extract attribute type from CORBA::Any")
}
match tc_item_kind {
orb::CORBA_TypeCodeKind::Boolean =>
data_from_array(&any.extract_borrow::<orb::Tango_DevVarBooleanArray>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::Bool),
orb::CORBA_TypeCodeKind::UShort =>
data_from_array(&any.extract_borrow::<orb::Tango_DevVarUShortArray>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::UShort),
orb::CORBA_TypeCodeKind::Short =>
data_from_array(&any.extract_borrow::<orb::Tango_DevVarShortArray>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::Short),
orb::CORBA_TypeCodeKind::ULong =>
data_from_array(&any.extract_borrow::<orb::Tango_DevVarULongArray>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::ULong),
orb::CORBA_TypeCodeKind::Long =>
data_from_array(&any.extract_borrow::<orb::Tango_DevVarLongArray>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::Long),
orb::CORBA_TypeCodeKind::ULongLong =>
data_from_array(&any.extract_borrow::<orb::Tango_DevVarULong64Array>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::ULong64),
orb::CORBA_TypeCodeKind::LongLong =>
data_from_array(&any.extract_borrow::<orb::Tango_DevVarLong64Array>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::Long64),
orb::CORBA_TypeCodeKind::Float =>
data_from_array(&any.extract_borrow::<orb::Tango_DevVarFloatArray>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::Float),
orb::CORBA_TypeCodeKind::Double =>
data_from_array(&any.extract_borrow::<orb::Tango_DevVarDoubleArray>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::Double),
orb::CORBA_TypeCodeKind::String =>
data_from_array_mapped(any.extract_borrow::<orb::Tango_DevVarStringArray>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::String, |s| s.to_string_lossy()),
orb::CORBA_TypeCodeKind::Enum =>
data_from_array_mapped(any.extract_borrow::<orb::Tango_DevVarStateArray>().ok_or_else(could_not_extract)?, None, r_dim, w_dim, Data::State, |&s| s.into()),
_ => Err(could_not_extract()),
}
}
fn attr_data_from_attr_union<'a>(
val_union: &'a orb::Tango_AttrValUnion,
format: orb::Tango_AttrDataFormat,
r_dim: &orb::Tango_AttributeDim,
w_dim: &orb::Tango_AttributeDim,
) -> Result<(Option<Data<'a>>, Option<Data<'a>>)> {
let (
Some(r_dim),
Some(w_dim)
) = (dim_to_tuple(r_dim), dim_to_tuple(w_dim)) else {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Could not parse dimensions"));
};
let format= Some(format);
if let Some(arr) = val_union.as_bool_att_value() {
data_from_array(&arr[..], format, r_dim, w_dim, Data::Bool)
} else if let Some(arr) = val_union.as_short_att_value() {
data_from_array(&arr[..], format, r_dim, w_dim, Data::Short)
} else if let Some(arr) = val_union.as_long_att_value() {
data_from_array(&arr[..], format, r_dim, w_dim, Data::Long)
} else if let Some(arr) = val_union.as_long64_att_value() {
data_from_array(&arr[..], format, r_dim, w_dim, Data::Long64)
} else if let Some(arr) = val_union.as_float_att_value() {
data_from_array(&arr[..], format, r_dim, w_dim, Data::Float)
} else if let Some(arr) = val_union.as_double_att_value() {
data_from_array(&arr[..], format, r_dim, w_dim, Data::Double)
} else if let Some(arr) = val_union.as_uchar_att_value() {
data_from_array(&arr[..], format, r_dim, w_dim, Data::Octet)
} else if let Some(arr) = val_union.as_ushort_att_value() {
data_from_array(&arr[..], format, r_dim, w_dim, Data::UShort)
} else if let Some(arr) = val_union.as_ulong_att_value() {
data_from_array(&arr[..], format, r_dim, w_dim, Data::ULong)
} else if let Some(arr) = val_union.as_ulong64_att_value() {
data_from_array(&arr[..], format, r_dim, w_dim, Data::ULong64)
} else if let Some(arr) = val_union.as_string_att_value() {
data_from_array_mapped(&arr[..], format, r_dim, w_dim, Data::String, |s| s.to_string_lossy())
} else if let Some(arr) = val_union.as_state_att_value() {
data_from_array_mapped(&arr[..], format, r_dim, w_dim, Data::State, |&s| s.into())
} else if let Some(&state) = val_union.as_dev_state_att() {
Ok((
Some(Data::State(DataLayout::Scalar(state.into()))),
None, ))
} else {
unreachable!()
}
}
fn dim_to_tuple(dim: &orb::Tango_AttributeDim) -> Option<(usize, usize)> {
Some((usize::try_from(dim.dim_x).ok()?, usize::try_from(dim.dim_y).ok()?))
}
fn attr_data_from_array<'a, T>(
arr: &'a [T],
format: Option<orb::Tango_AttrDataFormat>,
r_dim: (usize, usize),
w_dim: (usize, usize),
) -> (Option<DataLayout<'a, T>>, Option<DataLayout<'a, T>>) {
match format {
Some(orb::Tango_AttrDataFormat::SCALAR) => {
match (r_dim, w_dim, arr.len()) {
((1, 0), (0, 0), 1) => ( Some(DataLayout::ScalarView(&arr[0])),
None
),
((1, 0), (1, 0), 1) => ( None,
Some(DataLayout::ScalarView(&arr[0])),
),
((1, 0), (1, 0), 2) => ( Some(DataLayout::ScalarView(&arr[0])),
Some(DataLayout::ScalarView(&arr[1])),
),
_ => (None, None)
}
}
Some(orb::Tango_AttrDataFormat::SPECTRUM) => {
match (r_dim, w_dim, arr.len()) {
((r_x, 0), (0, 0), len) if r_x == len => ( Some(DataLayout::SpectrumView(arr[..].into())),
None,
),
((_, 0), (w_x, 0), len) if w_x == len => ( None,
Some(DataLayout::SpectrumView(arr[..].into())),
),
((r_x, 0), (w_x, 0), len) if r_x + w_x == len => ( Some(DataLayout::SpectrumView(arr[..r_x].into())),
Some(DataLayout::SpectrumView(arr[r_x..].into())),
),
_ => (None, None)
}
}
Some(orb::Tango_AttrDataFormat::IMAGE) => {
match (r_dim, w_dim, arr.len()) {
((r_x, r_y), (0, 0), len) if r_x * r_y == len => ( Some(DataLayout::ImageView(MdView::try_from_slice(&arr[..], [r_x, r_y]).unwrap())),
None,
),
((_, _), (w_x, w_y), len) if w_x * w_y == len => ( None,
Some(DataLayout::ImageView(MdView::try_from_slice(&arr[..], [w_x, w_y]).unwrap())),
),
((r_x, r_y), (w_x, w_y), len) if r_x * r_y + w_x * w_y == len => ( Some(DataLayout::ImageView(MdView::try_from_slice(&arr[..(r_x * r_y)], [r_x, r_y]).unwrap())),
Some(DataLayout::ImageView(MdView::try_from_slice(&arr[(r_x * r_y)..], [w_x, w_y]).unwrap())),
),
_ => (None, None)
}
}
Some(orb::Tango_AttrDataFormat::FMT_UNKNOWN) => (None, None),
None => {
match (r_dim, w_dim, arr.len()) {
((1, 0), (0, 0), len) if len == 1 => ( Some(DataLayout::ScalarView(&arr[0])),
None,
),
((1, 0), (1, 0), len) if len == 1 => ( None,
Some(DataLayout::ScalarView(&arr[0])),
),
((1, 0), (0, 0) | (1, 0), len) if len == 2 => ( Some(DataLayout::ScalarView(&arr[0])),
Some(DataLayout::ScalarView(&arr[1])),
),
((r_x, 0), (0, 0), len) if r_x == len => ( Some(DataLayout::SpectrumView(arr[..].into())),
None,
),
((_, 0), (w_x, 0), len) if w_x == len => ( None,
Some(DataLayout::SpectrumView(arr[..].into())),
),
((r_x, 0), (w_x, 0), len) if r_x + w_x == len => ( Some(DataLayout::SpectrumView(arr[..r_x].into())),
Some(DataLayout::SpectrumView(arr[r_x..].into())),
),
((r_x, r_y), (0, 0), len) if r_x * r_y == len => ( Some(DataLayout::ImageView(MdView::try_from_slice(&arr[..], [r_x, r_y]).unwrap())),
None,
),
((_, _), (w_x, w_y), len) if w_x * w_y == len => ( None,
Some(DataLayout::ImageView(MdView::try_from_slice(&arr[..], [w_x, w_y]).unwrap())),
),
((r_x, r_y), (w_x, w_y), len) if r_x * r_y + w_x * w_y == len => ( Some(DataLayout::ImageView(MdView::try_from_slice(&arr[..(r_x * r_y)], [r_x, r_y]).unwrap())),
Some(DataLayout::ImageView(MdView::try_from_slice(&arr[(r_x * r_y)..], [w_x, w_y]).unwrap())),
),
_ => (None, None)
}
}
}
}
fn data_from_array<'a, T>(
arr: &'a [T],
format: Option<orb::Tango_AttrDataFormat>,
r_dim: (usize, usize),
w_dim: (usize, usize),
variant: fn(DataLayout<'a, T>) -> Data<'a>,
) -> Result<(Option<Data<'a>>, Option<Data<'a>>)> {
let (
value,
w_value,
) = attr_data_from_array(arr, format, r_dim, w_dim);
if value.is_none() && w_value.is_none() {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Could not parse attribute data"));
}
Ok((
value.map(variant),
w_value.map(variant),
))
}
fn data_from_array_mapped<'a, T, U, F: FnMut(&'a T) -> U>(
arr: &'a [T],
format: Option<orb::Tango_AttrDataFormat>,
r_dim: (usize, usize),
w_dim: (usize, usize),
variant: fn(DataLayout<'a, U>) -> Data<'a>,
mut func: F,
) -> Result<(Option<Data<'a>>, Option<Data<'a>>)> {
let (
value,
w_value,
) = attr_data_from_array(arr, format, r_dim, w_dim);
if value.is_none() && w_value.is_none() {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Could not parse attribute data"));
}
Ok((
value.map(|data| data.map_view_to_owned(&mut func)).map(variant),
w_value.map(|data| data.map_view_to_owned(&mut func)).map(variant),
))
}
#[derive(Debug)]
pub struct DevWriteAttrOk {
pub len: usize,
}
impl DevWriteAttrBackend for DevWriteAttrOk {
fn len(&self) -> usize {
self.len
}
fn check(&self, index: usize) -> Result<()> {
let _ = index;
Ok(())
}
}
#[derive(Debug)]
pub struct DevWriteAttrFromMultiDevFailed {
pub len: usize,
pub err: orb::Tango_MultiDevFailed
}
impl DevWriteAttrBackend for DevWriteAttrFromMultiDevFailed {
fn len(&self) -> usize {
self.len
}
fn check(&self, index: usize) -> Result<()> {
for err in &self.err.data().errors {
if usize::try_from(err.index_in_call) == Ok(index) {
return Err(Error::from(&err.err_list));
}
}
Ok(())
}
}
#[derive(Debug)]
pub struct DevGetAttrConfigFromList<R> {
raw_list: orb::CppBox<R>,
}
impl<R> DevGetAttrConfigFromList<R> {
#[inline]
pub fn new(raw_list: orb::CppBox<R>) -> Self {
Self {
raw_list,
}
}
}
impl DevGetAttrInfoBackend for DevGetAttrConfigFromList<orb::Tango_AttributeConfigList> {
fn len(&self) -> usize {
self.raw_list.len()
}
fn get(&self, index: usize) -> Result<AttrInfo<'_>> {
self.raw_list.get(index)
.ok_or_else(|| Error::new(ErrorKind::InvalidIndex))
.and_then(attr_info_from_v1)
}
}
impl DevGetAttrInfoBackend for DevGetAttrConfigFromList<orb::Tango_AttributeConfigList_2> {
fn len(&self) -> usize {
self.raw_list.len()
}
fn get(&self, index: usize) -> Result<AttrInfo<'_>> {
self.raw_list.get(index)
.ok_or_else(|| Error::new(ErrorKind::InvalidIndex))
.and_then(attr_info_from_v2)
}
}
impl DevGetAttrInfoBackend for DevGetAttrConfigFromList<orb::Tango_AttributeConfigList_3> {
fn len(&self) -> usize {
self.raw_list.len()
}
fn get(&self, index: usize) -> Result<AttrInfo<'_>> {
self.raw_list.get(index)
.ok_or_else(|| Error::new(ErrorKind::InvalidIndex))
.and_then(attr_info_from_v3)
}
}
impl DevGetAttrInfoBackend for DevGetAttrConfigFromList<orb::Tango_AttributeConfigList_5> {
fn len(&self) -> usize {
self.raw_list.len()
}
fn get(&self, index: usize) -> Result<AttrInfo<'_>> {
self.raw_list.get(index)
.ok_or_else(|| Error::new(ErrorKind::InvalidIndex))
.and_then(attr_info_from_v5)
}
}
pub fn attr_info_from_v1(raw_conf: &orb::Tango_AttributeConfig) -> Result<AttrInfo<'_>> {
let mode = mode_from_attr_write_type(&raw_conf.writable, &raw_conf.writable_attr_name)?;
let data_kind = data_kind_from_type_format(raw_conf.data_format, raw_conf.data_type, raw_conf.max_dim_x, raw_conf.max_dim_y, None)?;
Ok(AttrInfo {
config: AttrConfig {
name: raw_conf.name.to_string_lossy(),
description: opt_str_if(&raw_conf.description, S_NO_DESCRIPTION),
label: opt_str_if(&raw_conf.label, ""),
unit: opt_str_if(&raw_conf.unit, ""),
standard_unit: opt_f32_empty(&raw_conf.standard_unit),
display_unit: opt_str_if(&raw_conf.display_unit, S_NO_DISPLAY_UNIT),
display_level: DisplayLevel::Operator,
display_format: opt_str_if(&raw_conf.format, S_NOT_SPECIFIED),
range: AttrRangeConfig {
min: opt_str_if(&raw_conf.min_value, S_NOT_SPECIFIED),
max: opt_str_if(&raw_conf.max_value, S_NOT_SPECIFIED),
},
alarm: AttrRangeConfig {
min: opt_str_if(&raw_conf.min_alarm, S_NOT_SPECIFIED),
max: opt_str_if(&raw_conf.max_alarm, S_NOT_SPECIFIED),
},
warning: AttrRangeConfig::default(),
delta: None,
change_event: AttrChangeEventConfig::default(),
periodic_event: AttrPeriodicEventConfig::default(),
archive_event: AttrArchiveEventConfig::default(),
memorized: None,
extensions: raw_conf.extensions.iter()
.map(|s| s.to_string_lossy().into_owned())
.collect::<Vec<_>>(),
},
mode,
data_kind,
})
}
pub fn attr_info_from_v2(raw_conf: &orb::Tango_AttributeConfig_2) -> Result<AttrInfo<'_>> {
let mode = mode_from_attr_write_type(&raw_conf.writable, &raw_conf.writable_attr_name)?;
let data_kind = data_kind_from_type_format(raw_conf.data_format, raw_conf.data_type, raw_conf.max_dim_x, raw_conf.max_dim_y, None)?;
Ok(AttrInfo {
config: AttrConfig {
name: raw_conf.name.to_string_lossy(),
description: opt_str_if(&raw_conf.description, S_NO_DESCRIPTION),
label: opt_str_if(&raw_conf.label, ""),
unit: opt_str_if(&raw_conf.unit, ""),
standard_unit: opt_f32_empty(&raw_conf.standard_unit),
display_unit: opt_str_if(&raw_conf.display_unit, S_NO_DISPLAY_UNIT),
display_level: raw_conf.level.into(),
display_format: opt_str_if(&raw_conf.format, S_NOT_SPECIFIED),
range: AttrRangeConfig {
min: opt_str_if(&raw_conf.min_value, S_NOT_SPECIFIED),
max: opt_str_if(&raw_conf.max_value, S_NOT_SPECIFIED),
},
alarm: AttrRangeConfig {
min: opt_str_if(&raw_conf.min_alarm, S_NOT_SPECIFIED),
max: opt_str_if(&raw_conf.max_alarm, S_NOT_SPECIFIED),
},
warning: AttrRangeConfig::default(),
delta: None,
change_event: AttrChangeEventConfig::default(),
periodic_event: AttrPeriodicEventConfig::default(),
archive_event: AttrArchiveEventConfig::default(),
memorized: None,
extensions: raw_conf.extensions.iter()
.map(|s| s.to_string_lossy().into_owned())
.collect::<Vec<_>>(),
},
mode,
data_kind,
})
}
pub fn attr_info_from_v3(raw_conf: &orb::Tango_AttributeConfig_3) -> Result<AttrInfo<'_>> {
let mode = mode_from_attr_write_type(&raw_conf.writable, &raw_conf.writable_attr_name)?;
let data_kind = data_kind_from_type_format(raw_conf.data_format, raw_conf.data_type, raw_conf.max_dim_x, raw_conf.max_dim_y, None)?;
Ok(AttrInfo {
config: AttrConfig {
name: raw_conf.name.to_string_lossy(),
description: opt_str_if(&raw_conf.description, S_NO_DESCRIPTION),
label: opt_str_if(&raw_conf.label, ""),
unit: opt_str_if(&raw_conf.unit, ""),
standard_unit: opt_f32_empty(&raw_conf.standard_unit),
display_unit: opt_str_if(&raw_conf.display_unit, S_NO_DISPLAY_UNIT),
display_level: raw_conf.level.into(),
display_format: opt_str_if(&raw_conf.format, S_NOT_SPECIFIED),
range: AttrRangeConfig {
min: opt_str_if(&raw_conf.min_value, S_NOT_SPECIFIED),
max: opt_str_if(&raw_conf.max_value, S_NOT_SPECIFIED),
},
alarm: AttrRangeConfig {
min: opt_str_if(&raw_conf.att_alarm.min_alarm, S_NOT_SPECIFIED),
max: opt_str_if(&raw_conf.att_alarm.max_alarm, S_NOT_SPECIFIED),
},
warning: AttrRangeConfig {
min: opt_str_if(&raw_conf.att_alarm.min_warning, S_NOT_SPECIFIED),
max: opt_str_if(&raw_conf.att_alarm.max_warning, S_NOT_SPECIFIED),
},
delta: opt_str_if(&raw_conf.att_alarm.delta_val, S_NOT_SPECIFIED).map(|value| {
AttrDeltaConfig {
value,
time: opt_duration_if(&raw_conf.att_alarm.delta_t, S_NOT_SPECIFIED),
}
}),
change_event: AttrChangeEventConfig {
rel_change: opt_str_if(&raw_conf.event_prop.ch_event.rel_change, S_NOT_SPECIFIED),
abs_change: opt_str_if(&raw_conf.event_prop.ch_event.abs_change, S_NOT_SPECIFIED),
},
periodic_event: AttrPeriodicEventConfig {
period: opt_duration_if(&raw_conf.event_prop.per_event.period, S_NOT_SPECIFIED),
},
archive_event: AttrArchiveEventConfig {
rel_change: opt_str_if(&raw_conf.event_prop.arch_event.rel_change, S_NOT_SPECIFIED),
abs_change: opt_str_if(&raw_conf.event_prop.arch_event.abs_change, S_NOT_SPECIFIED),
period: opt_duration_if(&raw_conf.event_prop.arch_event.period, S_NOT_SPECIFIED),
},
memorized: None,
extensions: raw_conf.extensions.iter()
.map(|s| s.to_string_lossy().into_owned())
.collect::<Vec<_>>(),
},
mode,
data_kind,
})
}
pub fn attr_info_from_v5(raw_conf: &orb::Tango_AttributeConfig_5) -> Result<AttrInfo<'_>> {
let mode = mode_from_attr_write_type(&raw_conf.writable, &raw_conf.writable_attr_name)?;
let data_kind = data_kind_from_type_format(raw_conf.data_format, raw_conf.data_type, raw_conf.max_dim_x, raw_conf.max_dim_y, Some(&raw_conf.enum_labels))?;
Ok(AttrInfo {
config: AttrConfig {
name: raw_conf.name.to_string_lossy(),
description: opt_str_if(&raw_conf.description, S_NO_DESCRIPTION),
label: opt_str_if(&raw_conf.label, ""),
unit: opt_str_if(&raw_conf.unit, ""),
standard_unit: opt_f32_empty(&raw_conf.standard_unit),
display_unit: opt_str_if(&raw_conf.display_unit, S_NO_DISPLAY_UNIT),
display_level: raw_conf.level.into(),
display_format: opt_str_if(&raw_conf.format, S_NOT_SPECIFIED),
range: AttrRangeConfig {
min: opt_str_if(&raw_conf.min_value, S_NOT_SPECIFIED),
max: opt_str_if(&raw_conf.max_value, S_NOT_SPECIFIED),
},
alarm: AttrRangeConfig {
min: opt_str_if(&raw_conf.att_alarm.min_alarm, S_NOT_SPECIFIED),
max: opt_str_if(&raw_conf.att_alarm.max_alarm, S_NOT_SPECIFIED),
},
warning: AttrRangeConfig {
min: opt_str_if(&raw_conf.att_alarm.min_warning, S_NOT_SPECIFIED),
max: opt_str_if(&raw_conf.att_alarm.max_warning, S_NOT_SPECIFIED),
},
delta: opt_str_if(&raw_conf.att_alarm.delta_val, S_NOT_SPECIFIED).map(|value| {
AttrDeltaConfig {
value,
time: opt_duration_if(&raw_conf.att_alarm.delta_t, S_NOT_SPECIFIED),
}
}),
change_event: AttrChangeEventConfig {
rel_change: opt_str_if(&raw_conf.event_prop.ch_event.rel_change, S_NOT_SPECIFIED),
abs_change: opt_str_if(&raw_conf.event_prop.ch_event.abs_change, S_NOT_SPECIFIED),
},
periodic_event: AttrPeriodicEventConfig {
period: opt_duration_if(&raw_conf.event_prop.per_event.period, S_NOT_SPECIFIED),
},
archive_event: AttrArchiveEventConfig {
rel_change: opt_str_if(&raw_conf.event_prop.arch_event.rel_change, S_NOT_SPECIFIED),
abs_change: opt_str_if(&raw_conf.event_prop.arch_event.abs_change, S_NOT_SPECIFIED),
period: opt_duration_if(&raw_conf.event_prop.arch_event.period, S_NOT_SPECIFIED),
},
memorized: match (raw_conf.memorized, raw_conf.mem_init) {
(false, _) => None,
(true, false) => Some(AttrMemorizedMode::Light),
(true, true) => Some(AttrMemorizedMode::WriteAtInit),
},
extensions: raw_conf.extensions.iter()
.map(|s| s.to_string_lossy().into_owned())
.collect::<Vec<_>>(),
},
mode,
data_kind,
})
}
fn mode_from_attr_write_type<'name>(typ: &orb::Tango_AttrWriteType, write_attr_name: &'name orb::CORBA_String) -> Result<AttrMode<'name>> {
Ok(match typ {
orb::Tango_AttrWriteType::READ => AttrMode::Read,
orb::Tango_AttrWriteType::READ_WITH_WRITE => AttrMode::ReadWithWrite {
write_attr_name: write_attr_name.to_string_lossy(),
},
orb::Tango_AttrWriteType::WRITE => AttrMode::Write,
orb::Tango_AttrWriteType::READ_WRITE => AttrMode::ReadWrite,
orb::Tango_AttrWriteType::WT_UNKNOWN => return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Unexpected Tango::AttrWriteType::WT_UNKNOWN")),
})
}
fn data_kind_from_type_format(
data_format: orb::Tango_AttrDataFormat,
data_type: orb::CORBA_Long,
max_dim_x: i32,
max_dim_y: i32,
enum_labels: Option<&orb::Tango_DevVarStringArray>,
) -> Result<DataKind> {
let dim_x = u32::try_from(max_dim_x).unwrap_or(0) as usize;
let dim_y = u32::try_from(max_dim_y).unwrap_or(0) as usize;
let layout = match data_format {
tango_omniorb::Tango_AttrDataFormat::SCALAR => DataLayoutKind::Scalar,
tango_omniorb::Tango_AttrDataFormat::SPECTRUM => DataLayoutKind::Spectrum(Some(dim_x)),
tango_omniorb::Tango_AttrDataFormat::IMAGE => DataLayoutKind::Image(Some((dim_x, dim_y))),
tango_omniorb::Tango_AttrDataFormat::FMT_UNKNOWN => return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Unexpected Tango::AttrWriteType::FMT_UNKNOWN")),
};
data_kind_from_type(data_type, Some(layout), enum_labels)
.ok_or_else(|| Error::new(ErrorKind::InvalidData)
.with_append_description(format_args!("Unexpected raw tango data type: {data_type}")))
}