diff --git a/samples/demo-server/src/customs.rs b/samples/demo-server/src/customs.rs new file mode 100644 index 00000000..2d78fb12 --- /dev/null +++ b/samples/demo-server/src/customs.rs @@ -0,0 +1,189 @@ +use std::sync::Arc; + +use opcua::{ + nodes::{DataTypeBuilder, ObjectBuilder, ReferenceDirection}, + server::node_manager::memory::SimpleNodeManager, + types::{ + DataTypeDefinition, DataTypeId, EnumDefinition, EnumField, NodeId, ReferenceTypeId, + StructureDefinition, StructureField, StructureType, + }, +}; + +pub fn add_custom_types(nm: Arc, ns: u16) { + let enum_id = add_enum_data_type(&nm, ns); + add_custom_data_type(&nm, ns, &enum_id); +} + +fn enum_field(name: &str, value: i64) -> EnumField { + EnumField { + name: name.into(), + description: name.into(), + display_name: name.into(), + value, + } +} + +fn add_enum_data_type(nm: &Arc, ns: u16) -> NodeId { + let mut addr = nm.address_space().write(); + + let id = NodeId::next_numeric(ns); + + let type_def = DataTypeDefinition::Enum(EnumDefinition { + fields: Some(vec![ + enum_field("Disabled", 1), + enum_field("Enabled", 2), + enum_field("Idle", 3), + enum_field("MoveAbs", 4), + enum_field("Error", 5), + ]), + }); + DataTypeBuilder::new(&id, "AxisState", "AxisState") + .organized_by(&DataTypeId::Enumeration) + .data_type_definition(type_def) + .insert(&mut *addr); + + id +} + +fn add_encoding(nm: &SimpleNodeManager, ns: u16, struct_id: &NodeId) -> NodeId { + let mut addr = nm.address_space().write(); + let id = NodeId::next_numeric(ns); + ObjectBuilder::new(&id, "DefaultBinary", "DefaultBindary") + .reference( + struct_id, + ReferenceTypeId::HasEncoding, + ReferenceDirection::Inverse, + ) + .insert(&mut *addr); + id +} + +fn add_custom_data_type(nm: &SimpleNodeManager, ns: u16, e_state_id: &NodeId) -> NodeId { + let struct_id = NodeId::next_numeric(ns); + let enc_id = add_encoding(nm, ns, &struct_id); + + let type_def = DataTypeDefinition::Structure(StructureDefinition { + default_encoding_id: NodeId::null(), + base_data_type: DataTypeId::Structure.into(), + structure_type: StructureType::Structure, + fields: Some(vec![ + StructureField { + name: "sErrorMessage".into(), + data_type: DataTypeId::String.into(), + value_rank: -1, + ..Default::default() + }, + StructureField { + name: "nErrorID".into(), + data_type: DataTypeId::UInt32.into(), + value_rank: -1, + ..Default::default() + }, + StructureField { + name: "eLastState".into(), + data_type: e_state_id.clone(), + value_rank: -1, + ..Default::default() + }, + ]), + }); + let mut addr = nm.address_space().write(); + DataTypeBuilder::new(&struct_id, "ErrorData", "ErrorData") + .organized_by(&DataTypeId::Structure) + .data_type_definition(type_def) + .reference( + enc_id, + ReferenceTypeId::HasEncoding, + ReferenceDirection::Forward, + ) + .insert(&mut *addr); + + struct_id +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(i32)] +pub enum AxisState { + Disabled = 1i32, + Enabled = 2i32, + Idle = 3i32, + MoveAbs = 4i32, + Error = 5i32, +} + +impl Default for AxisState { + fn default() -> Self { + Self::Disabled + } +} + +impl TryFrom for AxisState { + type Error = opcua::types::Error; + fn try_from(value: i32) -> Result>::Error> { + Ok(match value { + 1i32 => Self::Disabled, + 2i32 => Self::Enabled, + 3i32 => Self::Idle, + 4i32 => Self::MoveAbs, + 5i32 => Self::Error, + r => { + return Err(opcua::types::Error::decoding(format!( + "Got unexpected value for enum AxisState: {}", + r + ))); + } + }) + } +} + +impl From for i32 { + fn from(value: AxisState) -> Self { + value as i32 + } +} +impl opcua::types::IntoVariant for AxisState { + fn into_variant(self) -> opcua::types::Variant { + (self as i32).into_variant() + } +} + +impl opcua::types::BinaryEncodable for AxisState { + fn byte_len(&self, _ctx: &opcua::types::Context<'_>) -> usize { + 4usize + } + fn encode( + &self, + stream: &mut S, + _ctx: &opcua::types::Context<'_>, + ) -> opcua::types::EncodingResult<()> { + opcua::types::write_i32(stream, *self as i32) + } +} +impl opcua::types::BinaryDecodable for AxisState { + fn decode( + stream: &mut S, + _ctx: &opcua::types::Context<'_>, + ) -> opcua::types::EncodingResult { + let value = opcua::types::read_i32(stream)?; + Self::try_from(value) + } +} + +#[derive( + Debug, Clone, PartialEq, Default, opcua::types::BinaryEncodable, opcua::types::BinaryDecodable, +)] +pub struct ErrorData { + message: opcua::types::UAString, + error_id: u32, + last_state: AxisState, +} + +impl ErrorData { + pub fn new(msg: &str, error_id: u32, last_state: AxisState) -> Self { + Self { + message: msg.into(), + error_id: error_id, + last_state: last_state, + } + } +} diff --git a/samples/demo-server/src/main.rs b/samples/demo-server/src/main.rs index 3c00ad7e..b7f4cd1e 100644 --- a/samples/demo-server/src/main.rs +++ b/samples/demo-server/src/main.rs @@ -26,6 +26,7 @@ use opcua::server::{ }; mod control; +mod customs; mod machine; mod methods; mod scalar; @@ -134,6 +135,9 @@ async fn main() { let token = handle.token(); + // Define some custom types + customs::add_custom_types(node_manager.clone(), ns); + // Add some objects representing machinery machine::add_machinery( ns,