Skip to content

Commit

Permalink
add example code showing how to create custom enums and structures
Browse files Browse the repository at this point in the history
  • Loading branch information
Olivier committed Dec 29, 2024
1 parent 9772185 commit 5d70d0e
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 0 deletions.
189 changes: 189 additions & 0 deletions samples/demo-server/src/customs.rs
Original file line number Diff line number Diff line change
@@ -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<SimpleNodeManager>, 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<SimpleNodeManager>, 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<i32> for AxisState {
type Error = opcua::types::Error;
fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::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<AxisState> 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<S: std::io::Write + ?Sized>(
&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<S: std::io::Read + ?Sized>(
stream: &mut S,
_ctx: &opcua::types::Context<'_>,
) -> opcua::types::EncodingResult<Self> {
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 {

Check failure on line 182 in samples/demo-server/src/customs.rs

View workflow job for this annotation

GitHub Actions / build-linux (stable)

associated function `new` is never used

Check failure on line 182 in samples/demo-server/src/customs.rs

View workflow job for this annotation

GitHub Actions / build-linux (beta)

associated function `new` is never used
Self {
message: msg.into(),
error_id: error_id,
last_state: last_state,
}
}
}
4 changes: 4 additions & 0 deletions samples/demo-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use opcua::server::{
};

mod control;
mod customs;
mod machine;
mod methods;
mod scalar;
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 5d70d0e

Please sign in to comment.