use std::sync::Arc;
use crate::hmi::{
DriverResult, HmiDriver, HmiHandle,
asg::{self, AsgEncodableType, AsgEntry, bytes_to_i16},
proto::ports,
};
#[derive(Debug, Clone, PartialEq)]
pub struct AsgVarInterface<T: AsgEncodableType, const ARR: usize = 1> {
entry: Arc<AsgEntry>,
_marker: std::marker::PhantomData<T>,
}
impl<T: AsgEncodableType, const ARR: usize> AsgVarInterface<T, ARR> {
pub(crate) fn new(entry: Arc<AsgEntry>) -> Self {
Self {
entry,
_marker: std::marker::PhantomData,
}
}
}
impl<T: AsgEncodableType> AsgVarInterface<T, 1> {
pub fn write(
&self,
driver: &HmiDriver,
value: impl AsgEncodableType,
) -> DriverResult<HmiHandle<()>> {
let bytes = self.entry.encode_to_bytes(&value.into())?;
driver.write_array::<ports::Register>(self.entry.address as usize, &bytes_to_i16(&bytes))
}
pub fn read(&self, driver: &HmiDriver) -> DriverResult<HmiHandle<T>> {
let gen_handle = driver
.read_array::<ports::Register>(
self.entry.address as usize,
self.entry.var_slice_range.1 as usize,
)?
.generic();
Ok(HmiHandle::new_from_generic(
&gen_handle,
self.entry.var_slice_range.0,
self.entry.var_slice_range.1,
T::from_message,
))
}
}
macro_rules! arr_size_impl {
{ $($num:literal),* } => {
$(
impl<T: AsgEncodableType> AsgVarInterface<T, $num> {
pub fn write(
&self,
driver: &HmiDriver,
values: [impl AsgEncodableType; $num],
) -> DriverResult<HmiHandle<()>> {
let mut bytes = Vec::with_capacity((T::REGISTER_CNT * 2) as usize);
for value in values.into_iter() {
bytes.extend(self.entry.encode_to_bytes(&value.into())?);
}
driver.write_array::<ports::Register>(self.entry.address as usize, &bytes_to_i16(&bytes))
}
pub fn read(&self, driver: &HmiDriver) -> DriverResult<HmiHandle<[T; $num]>> {
let gen_handle = driver
.read_array::<ports::Register>(
self.entry.address as usize,
self.entry.var_slice_range.1 as usize,
)?
.generic();
Ok(HmiHandle::new_from_generic(
&gen_handle,
self.entry.var_slice_range.0,
self.entry.var_slice_range.1,
T::many_from_message::<$num>,
))
}
}
)*
};
}
arr_size_impl! {
2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
91, 92, 93, 94, 95, 96, 97, 98, 99, 100
}
pub trait AsgArgument: Sized + Send + Sync + 'static {
type Ret: AsgEncodableType;
const WRITABLE: bool = true;
fn size_in_registers(&self) -> u16;
fn to_asg_entry(self) -> AsgEntry;
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)]
pub struct PosRegArgs {
pub index: u16,
pub group: Option<u8>,
pub range: Option<(u16, u16)>,
}
impl Default for PosRegArgs {
fn default() -> Self {
Self {
index: 1,
group: None,
range: None,
}
}
}
impl AsgArgument for PosRegArgs {
type Ret = asg::position_struct::PositionData;
fn size_in_registers(&self) -> u16 {
match self.range {
Some((_, length)) => length,
None => asg::position_struct::PositionData::REGISTER_CNT,
}
}
fn to_asg_entry(self) -> AsgEntry {
let g_str = match self.group {
Some(g) => format!("G{}:", g),
None => "".to_string(),
};
let r_str = match self.range {
Some((offset, length)) => format!("@{}.{}", offset, length),
None => "".to_string(),
};
AsgEntry {
var_name: format!("PR[{}{}]{}", g_str, self.index, r_str),
tag: asg::AsgTag::Position,
address: 0, size: self.size_in_registers(),
var_slice_range: match self.range {
Some((offset, length)) => (offset, length),
None => (0, asg::position_struct::PositionData::REGISTER_CNT),
},
multiply: 0.0,
}
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, Default)]
pub struct CurPosArgs {
pub frame: i8,
pub group: Option<u8>,
pub range: Option<(u16, u16)>,
}
impl AsgArgument for CurPosArgs {
type Ret = asg::position_struct::PositionData;
const WRITABLE: bool = false;
fn size_in_registers(&self) -> u16 {
match self.range {
Some((_, length)) => length,
None => asg::position_struct::PositionData::REGISTER_CNT,
}
}
fn to_asg_entry(self) -> AsgEntry {
let g_str = match self.group {
Some(g) => format!("G{}:", g),
None => "".to_string(),
};
let r_str = match self.range {
Some((offset, length)) => format!("@{}.{}", offset, length),
None => "".to_string(),
};
AsgEntry {
var_name: format!("POS[{}{}]{}", g_str, self.frame, r_str),
tag: asg::AsgTag::Position,
address: 0, size: self.size_in_registers(),
var_slice_range: match self.range {
Some((offset, length)) => (offset, length),
None => (0, asg::position_struct::PositionData::REGISTER_CNT),
},
multiply: 0.0,
}
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)]
pub struct NumRegArgs {
pub index: u16,
pub range: Option<(u16, u16)>,
}
impl Default for NumRegArgs {
fn default() -> Self {
Self {
index: 1,
range: None,
}
}
}
impl AsgArgument for NumRegArgs {
type Ret = f32;
fn size_in_registers(&self) -> u16 {
match self.range {
Some((_, len)) => len,
None => <Self::Ret as AsgEncodableType>::REGISTER_CNT,
}
}
fn to_asg_entry(self) -> AsgEntry {
let default_len = <Self::Ret as AsgEncodableType>::REGISTER_CNT;
let (offset, len, suffix) = format_slice_suffix(default_len, self.range);
AsgEntry {
var_name: format!("R[{}]{}", self.index, suffix),
tag: <Self::Ret as AsgEncodableType>::TAG,
address: 0,
size: len,
var_slice_range: (offset, len),
multiply: 0.0,
}
}
}
fn format_slice_suffix(default_len: u16, range: Option<(u16, u16)>) -> (u16, u16, String) {
match range {
Some((offset, len)) => (offset, len, format!("@{}.{}", offset, len)),
None => (0, default_len, String::new()),
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)]
pub struct StringRegArgs {
pub index: u16,
pub range: Option<(u16, u16)>,
}
impl Default for StringRegArgs {
fn default() -> Self {
Self {
index: 1,
range: None,
}
}
}
impl AsgArgument for StringRegArgs {
type Ret = String;
fn size_in_registers(&self) -> u16 {
match self.range {
Some((_, len)) => len,
None => <Self::Ret as AsgEncodableType>::REGISTER_CNT,
}
}
fn to_asg_entry(self) -> AsgEntry {
let default_len = <Self::Ret as AsgEncodableType>::REGISTER_CNT;
let (offset, len, suffix) = format_slice_suffix(default_len, self.range);
AsgEntry {
var_name: format!("SR[{}]{}", self.index, suffix),
tag: <Self::Ret as AsgEncodableType>::TAG,
address: 0,
size: len,
var_slice_range: (offset, len),
multiply: 1.0,
}
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "py", pyo3::pyclass(from_py_object, str))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BoolIoSignal {
DigitalInput,
DigitalOutput,
RobotInput,
RobotOutput,
UopInput,
UopOutput,
SopInput,
SopOutput,
WeldInput,
WeldOutput,
WireStickInput,
WireStickOutput,
}
impl BoolIoSignal {
fn as_str(&self) -> &'static str {
match self {
BoolIoSignal::DigitalInput => "DI",
BoolIoSignal::DigitalOutput => "DO",
BoolIoSignal::RobotInput => "RI",
BoolIoSignal::RobotOutput => "RO",
BoolIoSignal::UopInput => "UI",
BoolIoSignal::UopOutput => "UO",
BoolIoSignal::SopInput => "SI",
BoolIoSignal::SopOutput => "SO",
BoolIoSignal::WeldInput => "WI",
BoolIoSignal::WeldOutput => "WO",
BoolIoSignal::WireStickInput => "WSI",
BoolIoSignal::WireStickOutput => "WSO",
}
}
}
impl std::fmt::Display for BoolIoSignal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BoolIoSignal::DigitalInput => write!(f, "DigitalInput"),
BoolIoSignal::DigitalOutput => write!(f, "DigitalOutput"),
BoolIoSignal::RobotInput => write!(f, "RobotInput"),
BoolIoSignal::RobotOutput => write!(f, "RobotOutput"),
BoolIoSignal::UopInput => write!(f, "UopInput"),
BoolIoSignal::UopOutput => write!(f, "UopOutput"),
BoolIoSignal::SopInput => write!(f, "SopInput"),
BoolIoSignal::SopOutput => write!(f, "SopOutput"),
BoolIoSignal::WeldInput => write!(f, "WeldInput"),
BoolIoSignal::WeldOutput => write!(f, "WeldOutput"),
BoolIoSignal::WireStickInput => write!(f, "WireStickInput"),
BoolIoSignal::WireStickOutput => write!(f, "WireStickOutput"),
}
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)]
pub struct BoolIoArgs {
pub signal: BoolIoSignal,
pub index: u16,
pub simulation: bool,
}
impl Default for BoolIoArgs {
fn default() -> Self {
Self {
signal: BoolIoSignal::DigitalInput,
index: 1,
simulation: false,
}
}
}
impl AsgArgument for BoolIoArgs {
type Ret = bool;
fn size_in_registers(&self) -> u16 {
<Self::Ret as AsgEncodableType>::REGISTER_CNT
}
fn to_asg_entry(self) -> AsgEntry {
let register_cnt = <Self::Ret as AsgEncodableType>::REGISTER_CNT;
let idx_fragment = if self.simulation {
format!("S{}", self.index)
} else {
self.index.to_string()
};
AsgEntry {
var_name: format!("{}[{}]", self.signal.as_str(), idx_fragment),
tag: <Self::Ret as AsgEncodableType>::TAG,
address: 0,
size: register_cnt,
var_slice_range: (0, register_cnt),
multiply: 1.0,
}
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "py", pyo3::pyclass(from_py_object, str))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntIoSignal {
GroupInput,
GroupOutput,
AnalogInput,
AnalogOutput,
}
impl IntIoSignal {
fn as_str(&self) -> &'static str {
match self {
IntIoSignal::GroupInput => "GI",
IntIoSignal::GroupOutput => "GO",
IntIoSignal::AnalogInput => "AI",
IntIoSignal::AnalogOutput => "AO",
}
}
}
impl std::fmt::Display for IntIoSignal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IntIoSignal::GroupInput => write!(f, "GroupInput"),
IntIoSignal::GroupOutput => write!(f, "GroupOutput"),
IntIoSignal::AnalogInput => write!(f, "AnalogInput"),
IntIoSignal::AnalogOutput => write!(f, "AnalogOutput"),
}
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)]
pub struct IntIoArgs {
pub signal: IntIoSignal,
pub index: u16,
pub simulation: bool,
}
impl Default for IntIoArgs {
fn default() -> Self {
Self {
signal: IntIoSignal::GroupInput,
index: 1,
simulation: false,
}
}
}
impl AsgArgument for IntIoArgs {
type Ret = i16;
fn size_in_registers(&self) -> u16 {
<Self::Ret as AsgEncodableType>::REGISTER_CNT
}
fn to_asg_entry(self) -> AsgEntry {
let register_cnt = <Self::Ret as AsgEncodableType>::REGISTER_CNT;
let idx_fragment = if self.simulation {
format!("S{}", self.index)
} else {
self.index.to_string()
};
AsgEntry {
var_name: format!("{}[{}]", self.signal.as_str(), idx_fragment),
tag: <Self::Ret as AsgEncodableType>::TAG,
address: 0,
size: register_cnt,
var_slice_range: (0, register_cnt),
multiply: 1.0,
}
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "py", pyo3::pyclass(from_py_object, str))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AlarmSource {
#[default]
Active,
History,
PasswordLog,
Motion,
Application,
System,
}
impl AlarmSource {
fn prefix(&self) -> &'static str {
match self {
AlarmSource::Active => "",
AlarmSource::History => "E",
AlarmSource::PasswordLog => "P",
AlarmSource::Motion => "M",
AlarmSource::Application => "A",
AlarmSource::System => "S",
}
}
}
impl std::fmt::Display for AlarmSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AlarmSource::Active => write!(f, "Active"),
AlarmSource::History => write!(f, "History"),
AlarmSource::PasswordLog => write!(f, "PasswordLog"),
AlarmSource::Motion => write!(f, "Motion"),
AlarmSource::Application => write!(f, "Application"),
AlarmSource::System => write!(f, "System"),
}
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)]
pub struct AlarmArgs {
pub source: AlarmSource,
pub line: u16,
pub range: Option<(u16, u16)>,
}
impl Default for AlarmArgs {
fn default() -> Self {
Self {
source: AlarmSource::default(),
line: 1,
range: None,
}
}
}
impl AsgArgument for AlarmArgs {
type Ret = asg::alarm_struct::AlarmData;
const WRITABLE: bool = false;
fn size_in_registers(&self) -> u16 {
match self.range {
Some((_, len)) => len,
None => <Self::Ret as AsgEncodableType>::REGISTER_CNT,
}
}
fn to_asg_entry(self) -> AsgEntry {
let default_len = <Self::Ret as AsgEncodableType>::REGISTER_CNT;
let (offset, len, suffix) = format_slice_suffix(default_len, self.range);
AsgEntry {
var_name: format!("ALM[{}{}]{}", self.source.prefix(), self.line, suffix),
tag: <Self::Ret as AsgEncodableType>::TAG,
address: 0,
size: len,
var_slice_range: (offset, len),
multiply: 1.0,
}
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "py", pyo3::pyclass(from_py_object, str))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ProgramStatusKind {
#[default]
Default,
MacroCaller,
KarelCaller,
MacroOrKarel,
}
impl ProgramStatusKind {
fn prefix(&self) -> &'static str {
match self {
ProgramStatusKind::Default => "",
ProgramStatusKind::MacroCaller => "M",
ProgramStatusKind::KarelCaller => "K",
ProgramStatusKind::MacroOrKarel => "MK",
}
}
}
impl std::fmt::Display for ProgramStatusKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ProgramStatusKind::Default => write!(f, "Default"),
ProgramStatusKind::MacroCaller => write!(f, "MacroCaller"),
ProgramStatusKind::KarelCaller => write!(f, "KarelCaller"),
ProgramStatusKind::MacroOrKarel => write!(f, "MacroOrKarel"),
}
}
}
#[cfg_attr(feature = "valuable", derive(valuable::Valuable))]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)]
pub struct ProgramStatusArgs {
pub task: u16,
pub kind: ProgramStatusKind,
pub range: Option<(u16, u16)>,
}
impl Default for ProgramStatusArgs {
fn default() -> Self {
Self {
task: 1,
kind: ProgramStatusKind::default(),
range: None,
}
}
}
impl AsgArgument for ProgramStatusArgs {
type Ret = asg::prog_status::ProgramStatus;
const WRITABLE: bool = false;
fn size_in_registers(&self) -> u16 {
match self.range {
Some((_, len)) => len,
None => <Self::Ret as AsgEncodableType>::REGISTER_CNT,
}
}
fn to_asg_entry(self) -> AsgEntry {
let default_len = <Self::Ret as AsgEncodableType>::REGISTER_CNT;
let (offset, len, suffix) = format_slice_suffix(default_len, self.range);
AsgEntry {
var_name: format!("PRG[{}{}]{}", self.kind.prefix(), self.task, suffix),
tag: <Self::Ret as AsgEncodableType>::TAG,
address: 0,
size: len,
var_slice_range: (offset, len),
multiply: 1.0,
}
}
}
#[derive(Debug, Clone)]
pub struct SysVarArgs<T: asg::SysVarVal> {
pub var_name: String,
pub range: Option<(u16, u16)>,
pub _marker: std::marker::PhantomData<T>,
}
impl<T: asg::SysVarVal> Default for SysVarArgs<T> {
fn default() -> Self {
Self {
var_name: String::new(),
range: None,
_marker: std::marker::PhantomData,
}
}
}
impl<T: asg::SysVarVal> AsgArgument for SysVarArgs<T> {
type Ret = T;
fn size_in_registers(&self) -> u16 {
match self.range {
Some((_, len)) => len,
None => <Self::Ret as AsgEncodableType>::REGISTER_CNT,
}
}
fn to_asg_entry(self) -> AsgEntry {
let default_len = <Self::Ret as AsgEncodableType>::REGISTER_CNT;
let (offset, len, suffix) = format_slice_suffix(default_len, self.range);
let multiply = match T::TAG {
asg::AsgTag::Integer
| asg::AsgTag::Short
| asg::AsgTag::Byte
| asg::AsgTag::Real
| asg::AsgTag::Position => 0.0,
_ => 1.0,
};
AsgEntry {
var_name: format!("{}{}", self.var_name, suffix),
tag: <Self::Ret as AsgEncodableType>::TAG,
address: 0,
size: len,
var_slice_range: (offset, len),
multiply,
}
}
}
#[cfg(feature = "py")]
pub(super) mod py {
use crate::hmi::{
asg::{AsgTag, AsgValue},
hmi_handle::py::PyHmiHandleGeneric,
proto::asg::{alarm_struct, position_struct, prog_status},
};
use super::*;
use pyo3::{IntoPyObjectExt, prelude::*, pyclass, pymethods};
macro_rules! _arr_size_impl {
(
$cls:ty;
$item_count:ident;
, $($num:literal),*
) => {
if $item_count == 1 {
asg_py_caster::<$cls>
}
$(
else if $item_count == $num {
asg_array_py_caster::<$cls, $num>
}
)*
else {
return Err(pyo3::exceptions::PyValueError::new_err("Unsupported array size for Asg array caster"))
}
};
}
macro_rules! asg_generic_switch {
(
$cls:ty;
$item_count:ident
) => {
_arr_size_impl! {
$cls;
$item_count;
, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
91, 92, 93, 94, 95, 96, 97, 98, 99, 100
}
};
}
fn asg_py_caster<'a, T: AsgEncodableType>(
py: Python<'a>,
msg: crate::hmi::proto::wire::Message,
offset: u16,
member_count: u16,
) -> pyo3::PyResult<pyo3::Bound<'a, pyo3::types::PyAny>> {
let value: AsgValue = T::from_message(msg, offset, member_count).map(Into::into)?;
asg_value_to_py_value(py, &value)
}
fn asg_array_py_caster<'a, T: AsgEncodableType, const N: usize>(
py: Python<'a>,
msg: crate::hmi::proto::wire::Message,
offset: u16,
member_count: u16,
) -> pyo3::PyResult<pyo3::Bound<'a, pyo3::types::PyAny>> {
if N < 1 || N > 100 {
return Err(pyo3::exceptions::PyValueError::new_err(
"Unsupported array size for Asg array caster",
));
}
let values: [T; N] = T::many_from_message::<N>(msg, offset, member_count)?;
let list = pyo3::types::PyList::empty(py);
for v in values.into_iter() {
let obj = asg_value_to_py_value(py, &v.into())?;
list.append(obj)?;
}
list.into_bound_py_any(py)
}
fn caster_null<'a>(
py: Python<'a>,
_msg: crate::hmi::proto::wire::Message,
_offset: u16,
_member_count: u16,
) -> pyo3::PyResult<pyo3::Bound<'a, pyo3::types::PyAny>> {
py.None().into_bound_py_any(py)
}
fn py_value_to_asg_value(
value: &Bound<'_, pyo3::types::PyAny>,
tag: AsgTag,
) -> PyResult<AsgValue> {
Ok(match tag {
AsgTag::Integer => AsgValue::Integer(value.extract::<i32>()?),
AsgTag::Short => AsgValue::Short(value.extract::<i16>()?),
AsgTag::Byte => AsgValue::Byte(value.extract::<i8>()?),
AsgTag::Real => AsgValue::Real(value.extract::<f32>()?),
AsgTag::Bool => AsgValue::Bool(value.extract::<bool>()?),
AsgTag::String => AsgValue::String(value.extract::<String>()?),
AsgTag::Position => {
AsgValue::Position(value.extract::<position_struct::PositionData>()?)
}
AsgTag::Alarm => AsgValue::Alarm(value.extract::<alarm_struct::AlarmData>()?),
AsgTag::Program => AsgValue::Program(value.extract::<prog_status::ProgramStatus>()?),
})
}
fn asg_value_to_py_value<'a>(
py: Python<'a>,
asg_value: &AsgValue,
) -> PyResult<Bound<'a, pyo3::types::PyAny>> {
match asg_value {
AsgValue::Integer(v) => v.into_bound_py_any(py),
AsgValue::Short(v) => v.into_bound_py_any(py),
AsgValue::Byte(v) => v.into_bound_py_any(py),
AsgValue::Real(v) => v.into_bound_py_any(py),
AsgValue::Bool(v) => v.into_bound_py_any(py),
AsgValue::String(v) => v.into_bound_py_any(py),
AsgValue::Position(v) => v.into_bound_py_any(py),
AsgValue::Alarm(v) => v.clone().into_bound_py_any(py),
AsgValue::Program(v) => v.clone().into_bound_py_any(py),
}
}
#[derive(Debug)]
#[pyclass(name = "AsgInterface")]
pub struct PyAsgVarInterface {
entry: Arc<AsgEntry>,
count: usize,
can_write: bool,
}
impl PyAsgVarInterface {
pub(crate) fn new(entry: Arc<AsgEntry>, count: usize, can_write: bool) -> Self {
Self {
entry,
count,
can_write,
}
}
}
#[pymethods]
impl PyAsgVarInterface {
pub fn write(
&self,
driver: &HmiDriver,
value: Bound<'_, pyo3::types::PyAny>,
) -> DriverResult<PyHmiHandleGeneric> {
if !self.can_write {
return Err(pyo3::exceptions::PyPermissionError::new_err(
"This ASG variable is read-only",
)
.into());
}
let asg_value = py_value_to_asg_value(&value, self.entry.tag)?;
let bytes = self.entry.encode_to_bytes(&asg_value)?;
let gen_handle = driver
.write_array::<ports::Register>(self.entry.address as usize, &bytes_to_i16(&bytes))?
.generic();
Ok(PyHmiHandleGeneric {
inner: gen_handle,
target: self.entry.var_slice_range.0,
count: self.entry.var_slice_range.1,
caster: caster_null,
})
}
pub fn read(&self, driver: &HmiDriver) -> DriverResult<PyHmiHandleGeneric> {
let gen_handle = driver
.read_array::<ports::Register>(
self.entry.address as usize,
self.entry.size as usize,
)?
.generic();
let item_count = self.count;
let caster = match self.entry.tag {
AsgTag::Integer => asg_generic_switch!(i32; item_count),
AsgTag::Short => asg_generic_switch!(i16; item_count),
AsgTag::Real => asg_generic_switch!(f32; item_count),
AsgTag::Byte => asg_generic_switch!(i8; item_count),
AsgTag::Bool => asg_generic_switch!(bool; item_count),
AsgTag::String => asg_generic_switch!(String; item_count),
AsgTag::Position => asg_generic_switch!(position_struct::PositionData; item_count),
AsgTag::Alarm => asg_generic_switch!(alarm_struct::AlarmData; item_count),
AsgTag::Program => asg_generic_switch!(prog_status::ProgramStatus; item_count),
};
Ok(PyHmiHandleGeneric {
inner: gen_handle,
target: self.entry.var_slice_range.0,
count: self.entry.var_slice_range.1,
caster,
})
}
}
pub fn register(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
use crate::hmi::proto::asg::*;
parent_module.add_class::<PyAsgVarInterface>()?;
parent_module.add_class::<AsgValue>()?;
parent_module.add_class::<position_struct::PositionData>()?;
parent_module.add_class::<position_struct::CartesianData>()?;
parent_module.add_class::<position_struct::JointData>()?;
parent_module.add_class::<position_struct::FrameData>()?;
parent_module.add_class::<position_struct::FlipState>()?;
parent_module.add_class::<position_struct::FrontBack>()?;
parent_module.add_class::<position_struct::LeftRight>()?;
parent_module.add_class::<position_struct::UpDown>()?;
parent_module.add_class::<alarm_struct::AlarmData>()?;
parent_module.add_class::<alarm_struct::AlarmSeverity>()?;
parent_module.add_class::<AlarmSource>()?;
parent_module.add_class::<alarm_struct::TimeData>()?;
parent_module.add_class::<prog_status::ProgramStatus>()?;
parent_module.add_class::<ProgramStatusKind>()?;
parent_module.add_class::<prog_status::ProgramState>()?;
parent_module.add_class::<BoolIoSignal>()?;
parent_module.add_class::<IntIoSignal>()?;
Ok(())
}
}