use alloc::string::String;
use alloc::vec::Vec;
use zerodds_opcua_gateway::data_value::{DataValue, ExtensionObject, ExtensionObjectBody, Variant};
use zerodds_opcua_gateway::node_id::{ExpandedNodeId, NodeId};
use zerodds_opcua_gateway::types::{LocalizedText, QualifiedName};
use zerodds_opcua_pubsub::uadp::datatypes::{ApplicationDescription, EndpointDescription};
use zerodds_opcua_pubsub::{DecodeError, EncodeError, UaDecode, UaEncode, UaReader, UaWriter};
use zerodds_opcua_uacp::securechannel::node_ids;
use zerodds_opcua_uacp::securechannel::{RequestHeader, ResponseHeader, null_extension_object};
use crate::wire::{
read_array, read_byte_string, read_string, read_string_array, read_u32_array,
skip_diagnostic_info_array, write_array, write_byte_string, write_empty_diagnostic_info_array,
write_string, write_string_array, write_u32_array,
};
pub const ATTRIBUTE_VALUE: u32 = 13;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct SignatureData {
pub algorithm: String,
pub signature: Vec<u8>,
}
impl UaEncode for SignatureData {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
write_string(w, &self.algorithm)?;
write_byte_string(w, &self.signature)?;
Ok(())
}
}
impl UaDecode for SignatureData {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
algorithm: read_string(r)?,
signature: read_byte_string(r)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReadValueId {
pub node_id: NodeId,
pub attribute_id: u32,
pub index_range: String,
pub data_encoding: QualifiedName,
}
impl UaEncode for ReadValueId {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
self.node_id.encode(w)?;
w.write_u32(self.attribute_id);
write_string(w, &self.index_range)?;
self.data_encoding.encode(w)?;
Ok(())
}
}
impl UaDecode for ReadValueId {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
node_id: NodeId::decode(r)?,
attribute_id: r.read_u32()?,
index_range: read_string(r)?,
data_encoding: QualifiedName::decode(r)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WriteValue {
pub node_id: NodeId,
pub attribute_id: u32,
pub index_range: String,
pub value: DataValue,
}
impl UaEncode for WriteValue {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
self.node_id.encode(w)?;
w.write_u32(self.attribute_id);
write_string(w, &self.index_range)?;
self.value.encode(w)?;
Ok(())
}
}
impl UaDecode for WriteValue {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
node_id: NodeId::decode(r)?,
attribute_id: r.read_u32()?,
index_range: read_string(r)?,
value: DataValue::decode(r)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CallMethodRequest {
pub object_id: NodeId,
pub method_id: NodeId,
pub input_arguments: Vec<Variant>,
}
impl UaEncode for CallMethodRequest {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
self.object_id.encode(w)?;
self.method_id.encode(w)?;
write_array(w, &self.input_arguments, "InputArguments")?;
Ok(())
}
}
impl UaDecode for CallMethodRequest {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
object_id: NodeId::decode(r)?,
method_id: NodeId::decode(r)?,
input_arguments: read_array::<Variant>(r)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CallMethodResult {
pub status_code: u32,
pub input_argument_results: Vec<u32>,
pub output_arguments: Vec<Variant>,
}
impl UaEncode for CallMethodResult {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
w.write_u32(self.status_code);
write_u32_array(w, &self.input_argument_results)?;
write_empty_diagnostic_info_array(w); write_array(w, &self.output_arguments, "OutputArguments")?;
Ok(())
}
}
impl UaDecode for CallMethodResult {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let status_code = r.read_u32()?;
let input_argument_results = read_u32_array(r)?;
skip_diagnostic_info_array(r)?;
let output_arguments = read_array::<Variant>(r)?;
Ok(Self {
status_code,
input_argument_results,
output_arguments,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateSessionRequest {
pub request_header: RequestHeader,
pub client_description: ApplicationDescription,
pub server_uri: String,
pub endpoint_url: String,
pub session_name: String,
pub client_nonce: Vec<u8>,
pub client_certificate: Vec<u8>,
pub requested_session_timeout: f64,
pub max_response_message_size: u32,
}
impl CreateSessionRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
client_description: ApplicationDescription::decode(r)?,
server_uri: read_string(r)?,
endpoint_url: read_string(r)?,
session_name: read_string(r)?,
client_nonce: read_byte_string(r)?,
client_certificate: read_byte_string(r)?,
requested_session_timeout: r.read_f64()?,
max_response_message_size: r.read_u32()?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::CREATE_SESSION_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
self.client_description.encode(w)?;
write_string(w, &self.server_uri)?;
write_string(w, &self.endpoint_url)?;
write_string(w, &self.session_name)?;
write_byte_string(w, &self.client_nonce)?;
write_byte_string(w, &self.client_certificate)?;
w.write_f64(self.requested_session_timeout);
w.write_u32(self.max_response_message_size);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateSessionResponse {
pub response_header: ResponseHeader,
pub session_id: NodeId,
pub authentication_token: NodeId,
pub revised_session_timeout: f64,
pub server_nonce: Vec<u8>,
pub server_certificate: Vec<u8>,
pub server_endpoints: Vec<EndpointDescription>,
pub server_signature: SignatureData,
pub max_request_message_size: u32,
}
impl CreateSessionResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let response_header = decode_response_header(r)?;
let session_id = NodeId::decode(r)?;
let authentication_token = NodeId::decode(r)?;
let revised_session_timeout = r.read_f64()?;
let server_nonce = read_byte_string(r)?;
let server_certificate = read_byte_string(r)?;
let server_endpoints = read_array::<EndpointDescription>(r)?;
let _swc = r.read_i32()?;
let server_signature = SignatureData::decode(r)?;
let max_request_message_size = r.read_u32()?;
Ok(Self {
response_header,
session_id,
authentication_token,
revised_session_timeout,
server_nonce,
server_certificate,
server_endpoints,
server_signature,
max_request_message_size,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::CREATE_SESSION_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
self.session_id.encode(w)?;
self.authentication_token.encode(w)?;
w.write_f64(self.revised_session_timeout);
write_byte_string(w, &self.server_nonce)?;
write_byte_string(w, &self.server_certificate)?;
write_array(w, &self.server_endpoints, "ServerEndpoints")?;
w.write_i32(0); self.server_signature.encode(w)?;
w.write_u32(self.max_request_message_size);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ActivateSessionRequest {
pub request_header: RequestHeader,
pub client_signature: SignatureData,
pub locale_ids: Vec<String>,
pub user_identity_token: zerodds_opcua_gateway::data_value::ExtensionObject,
pub user_token_signature: SignatureData,
}
impl ActivateSessionRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let request_header = decode_request_header(r)?;
let client_signature = SignatureData::decode(r)?;
let _swc = r.read_i32()?;
let locale_ids = read_string_array(r)?;
let user_identity_token = zerodds_opcua_gateway::data_value::ExtensionObject::decode(r)?;
let user_token_signature = SignatureData::decode(r)?;
Ok(Self {
request_header,
client_signature,
locale_ids,
user_identity_token,
user_token_signature,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::ACTIVATE_SESSION_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
self.client_signature.encode(w)?;
w.write_i32(0); write_string_array(w, &self.locale_ids)?;
self.user_identity_token.encode(w)?;
self.user_token_signature.encode(w)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ActivateSessionResponse {
pub response_header: ResponseHeader,
pub server_nonce: Vec<u8>,
pub results: Vec<u32>,
}
impl ActivateSessionResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let response_header = decode_response_header(r)?;
let server_nonce = read_byte_string(r)?;
let results = read_u32_array(r)?;
skip_diagnostic_info_array(r)?;
Ok(Self {
response_header,
server_nonce,
results,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::ACTIVATE_SESSION_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
write_byte_string(w, &self.server_nonce)?;
write_u32_array(w, &self.results)?;
write_empty_diagnostic_info_array(w);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CloseSessionRequest {
pub request_header: RequestHeader,
pub delete_subscriptions: bool,
}
impl CloseSessionRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
delete_subscriptions: r.read_u8()? != 0,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::CLOSE_SESSION_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
w.write_u8(u8::from(self.delete_subscriptions));
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CloseSessionResponse {
pub response_header: ResponseHeader,
}
impl CloseSessionResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
response_header: decode_response_header(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::CLOSE_SESSION_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReadRequest {
pub request_header: RequestHeader,
pub max_age: f64,
pub timestamps_to_return: i32,
pub nodes_to_read: Vec<ReadValueId>,
}
impl ReadRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
max_age: r.read_f64()?,
timestamps_to_return: r.read_i32()?,
nodes_to_read: read_array::<ReadValueId>(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::READ_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
w.write_f64(self.max_age);
w.write_i32(self.timestamps_to_return);
write_array(w, &self.nodes_to_read, "NodesToRead")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReadResponse {
pub response_header: ResponseHeader,
pub results: Vec<DataValue>,
}
impl ReadResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let response_header = decode_response_header(r)?;
let results = read_array::<DataValue>(r)?;
skip_diagnostic_info_array(r)?;
Ok(Self {
response_header,
results,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::READ_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
write_array(w, &self.results, "Results")?;
write_empty_diagnostic_info_array(w);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WriteRequest {
pub request_header: RequestHeader,
pub nodes_to_write: Vec<WriteValue>,
}
impl WriteRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
nodes_to_write: read_array::<WriteValue>(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::WRITE_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
write_array(w, &self.nodes_to_write, "NodesToWrite")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WriteResponse {
pub response_header: ResponseHeader,
pub results: Vec<u32>,
}
impl WriteResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let response_header = decode_response_header(r)?;
let results = read_u32_array(r)?;
skip_diagnostic_info_array(r)?;
Ok(Self {
response_header,
results,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::WRITE_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
write_u32_array(w, &self.results)?;
write_empty_diagnostic_info_array(w);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CallRequest {
pub request_header: RequestHeader,
pub methods_to_call: Vec<CallMethodRequest>,
}
impl CallRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
methods_to_call: read_array::<CallMethodRequest>(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::CALL_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
write_array(w, &self.methods_to_call, "MethodsToCall")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CallResponse {
pub response_header: ResponseHeader,
pub results: Vec<CallMethodResult>,
}
impl CallResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let response_header = decode_response_header(r)?;
let results = read_array::<CallMethodResult>(r)?;
skip_diagnostic_info_array(r)?;
Ok(Self {
response_header,
results,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::CALL_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
write_array(w, &self.results, "Results")?;
write_empty_diagnostic_info_array(w);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ViewDescription {
pub view_id: NodeId,
pub timestamp: i64,
pub view_version: u32,
}
impl Default for ViewDescription {
fn default() -> Self {
Self {
view_id: NodeId::numeric(0, 0),
timestamp: 0,
view_version: 0,
}
}
}
impl UaEncode for ViewDescription {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
self.view_id.encode(w)?;
w.write_i64(self.timestamp);
w.write_u32(self.view_version);
Ok(())
}
}
impl UaDecode for ViewDescription {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
view_id: NodeId::decode(r)?,
timestamp: r.read_i64()?,
view_version: r.read_u32()?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BrowseDescription {
pub node_id: NodeId,
pub browse_direction: i32,
pub reference_type_id: NodeId,
pub include_subtypes: bool,
pub node_class_mask: u32,
pub result_mask: u32,
}
impl UaEncode for BrowseDescription {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
self.node_id.encode(w)?;
w.write_i32(self.browse_direction);
self.reference_type_id.encode(w)?;
w.write_u8(u8::from(self.include_subtypes));
w.write_u32(self.node_class_mask);
w.write_u32(self.result_mask);
Ok(())
}
}
impl UaDecode for BrowseDescription {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
node_id: NodeId::decode(r)?,
browse_direction: r.read_i32()?,
reference_type_id: NodeId::decode(r)?,
include_subtypes: r.read_u8()? != 0,
node_class_mask: r.read_u32()?,
result_mask: r.read_u32()?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReferenceDescription {
pub reference_type_id: NodeId,
pub is_forward: bool,
pub node_id: ExpandedNodeId,
pub browse_name: QualifiedName,
pub display_name: LocalizedText,
pub node_class: i32,
pub type_definition: ExpandedNodeId,
}
impl UaEncode for ReferenceDescription {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
self.reference_type_id.encode(w)?;
w.write_u8(u8::from(self.is_forward));
self.node_id.encode(w)?;
self.browse_name.encode(w)?;
self.display_name.encode(w)?;
w.write_i32(self.node_class);
self.type_definition.encode(w)?;
Ok(())
}
}
impl UaDecode for ReferenceDescription {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
reference_type_id: NodeId::decode(r)?,
is_forward: r.read_u8()? != 0,
node_id: ExpandedNodeId::decode(r)?,
browse_name: QualifiedName::decode(r)?,
display_name: LocalizedText::decode(r)?,
node_class: r.read_i32()?,
type_definition: ExpandedNodeId::decode(r)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BrowseResult {
pub status_code: u32,
pub continuation_point: Vec<u8>,
pub references: Vec<ReferenceDescription>,
}
impl UaEncode for BrowseResult {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
w.write_u32(self.status_code);
write_byte_string(w, &self.continuation_point)?;
write_array(w, &self.references, "References")?;
Ok(())
}
}
impl UaDecode for BrowseResult {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
status_code: r.read_u32()?,
continuation_point: read_byte_string(r)?,
references: read_array::<ReferenceDescription>(r)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BrowseRequest {
pub request_header: RequestHeader,
pub view: ViewDescription,
pub requested_max_references_per_node: u32,
pub nodes_to_browse: Vec<BrowseDescription>,
}
impl BrowseRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
view: ViewDescription::decode(r)?,
requested_max_references_per_node: r.read_u32()?,
nodes_to_browse: read_array::<BrowseDescription>(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::BROWSE_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
self.view.encode(w)?;
w.write_u32(self.requested_max_references_per_node);
write_array(w, &self.nodes_to_browse, "NodesToBrowse")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BrowseResponse {
pub response_header: ResponseHeader,
pub results: Vec<BrowseResult>,
}
impl BrowseResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let response_header = decode_response_header(r)?;
let results = read_array::<BrowseResult>(r)?;
skip_diagnostic_info_array(r)?;
Ok(Self {
response_header,
results,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::BROWSE_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
write_array(w, &self.results, "Results")?;
write_empty_diagnostic_info_array(w);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GetEndpointsRequest {
pub request_header: RequestHeader,
pub endpoint_url: String,
pub locale_ids: Vec<String>,
pub profile_uris: Vec<String>,
}
impl GetEndpointsRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
endpoint_url: read_string(r)?,
locale_ids: read_string_array(r)?,
profile_uris: read_string_array(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::GET_ENDPOINTS_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
write_string(w, &self.endpoint_url)?;
write_string_array(w, &self.locale_ids)?;
write_string_array(w, &self.profile_uris)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GetEndpointsResponse {
pub response_header: ResponseHeader,
pub endpoints: Vec<EndpointDescription>,
}
impl GetEndpointsResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
response_header: decode_response_header(r)?,
endpoints: read_array::<EndpointDescription>(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::GET_ENDPOINTS_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
write_array(w, &self.endpoints, "Endpoints")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FindServersRequest {
pub request_header: RequestHeader,
pub endpoint_url: String,
pub locale_ids: Vec<String>,
pub server_uris: Vec<String>,
}
impl FindServersRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
endpoint_url: read_string(r)?,
locale_ids: read_string_array(r)?,
server_uris: read_string_array(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::FIND_SERVERS_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
write_string(w, &self.endpoint_url)?;
write_string_array(w, &self.locale_ids)?;
write_string_array(w, &self.server_uris)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FindServersResponse {
pub response_header: ResponseHeader,
pub servers: Vec<ApplicationDescription>,
}
impl FindServersResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
response_header: decode_response_header(r)?,
servers: read_array::<ApplicationDescription>(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::FIND_SERVERS_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
write_array(w, &self.servers, "Servers")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateSubscriptionRequest {
pub request_header: RequestHeader,
pub requested_publishing_interval: f64,
pub requested_lifetime_count: u32,
pub requested_max_keep_alive_count: u32,
pub max_notifications_per_publish: u32,
pub publishing_enabled: bool,
pub priority: u8,
}
impl CreateSubscriptionRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
requested_publishing_interval: r.read_f64()?,
requested_lifetime_count: r.read_u32()?,
requested_max_keep_alive_count: r.read_u32()?,
max_notifications_per_publish: r.read_u32()?,
publishing_enabled: r.read_u8()? != 0,
priority: r.read_u8()?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::CREATE_SUBSCRIPTION_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
w.write_f64(self.requested_publishing_interval);
w.write_u32(self.requested_lifetime_count);
w.write_u32(self.requested_max_keep_alive_count);
w.write_u32(self.max_notifications_per_publish);
w.write_u8(u8::from(self.publishing_enabled));
w.write_u8(self.priority);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateSubscriptionResponse {
pub response_header: ResponseHeader,
pub subscription_id: u32,
pub revised_publishing_interval: f64,
pub revised_lifetime_count: u32,
pub revised_max_keep_alive_count: u32,
}
impl CreateSubscriptionResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
response_header: decode_response_header(r)?,
subscription_id: r.read_u32()?,
revised_publishing_interval: r.read_f64()?,
revised_lifetime_count: r.read_u32()?,
revised_max_keep_alive_count: r.read_u32()?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::CREATE_SUBSCRIPTION_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
w.write_u32(self.subscription_id);
w.write_f64(self.revised_publishing_interval);
w.write_u32(self.revised_lifetime_count);
w.write_u32(self.revised_max_keep_alive_count);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SetPublishingModeRequest {
pub request_header: RequestHeader,
pub publishing_enabled: bool,
pub subscription_ids: Vec<u32>,
}
impl SetPublishingModeRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
publishing_enabled: r.read_u8()? != 0,
subscription_ids: read_u32_array(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::SET_PUBLISHING_MODE_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
w.write_u8(u8::from(self.publishing_enabled));
write_u32_array(w, &self.subscription_ids)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SetPublishingModeResponse {
pub response_header: ResponseHeader,
pub results: Vec<u32>,
}
impl SetPublishingModeResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let response_header = decode_response_header(r)?;
let results = read_u32_array(r)?;
skip_diagnostic_info_array(r)?;
Ok(Self {
response_header,
results,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::SET_PUBLISHING_MODE_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
write_u32_array(w, &self.results)?;
write_empty_diagnostic_info_array(w);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MonitoringParameters {
pub client_handle: u32,
pub sampling_interval: f64,
pub filter: ExtensionObject,
pub queue_size: u32,
pub discard_oldest: bool,
}
impl UaEncode for MonitoringParameters {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
w.write_u32(self.client_handle);
w.write_f64(self.sampling_interval);
self.filter.encode(w)?;
w.write_u32(self.queue_size);
w.write_u8(u8::from(self.discard_oldest));
Ok(())
}
}
impl UaDecode for MonitoringParameters {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
client_handle: r.read_u32()?,
sampling_interval: r.read_f64()?,
filter: ExtensionObject::decode(r)?,
queue_size: r.read_u32()?,
discard_oldest: r.read_u8()? != 0,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MonitoredItemCreateRequest {
pub item_to_monitor: ReadValueId,
pub monitoring_mode: i32,
pub requested_parameters: MonitoringParameters,
}
impl UaEncode for MonitoredItemCreateRequest {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
self.item_to_monitor.encode(w)?;
w.write_i32(self.monitoring_mode);
self.requested_parameters.encode(w)?;
Ok(())
}
}
impl UaDecode for MonitoredItemCreateRequest {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
item_to_monitor: ReadValueId::decode(r)?,
monitoring_mode: r.read_i32()?,
requested_parameters: MonitoringParameters::decode(r)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MonitoredItemCreateResult {
pub status_code: u32,
pub monitored_item_id: u32,
pub revised_sampling_interval: f64,
pub revised_queue_size: u32,
pub filter_result: ExtensionObject,
}
impl UaEncode for MonitoredItemCreateResult {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
w.write_u32(self.status_code);
w.write_u32(self.monitored_item_id);
w.write_f64(self.revised_sampling_interval);
w.write_u32(self.revised_queue_size);
self.filter_result.encode(w)?;
Ok(())
}
}
impl UaDecode for MonitoredItemCreateResult {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
status_code: r.read_u32()?,
monitored_item_id: r.read_u32()?,
revised_sampling_interval: r.read_f64()?,
revised_queue_size: r.read_u32()?,
filter_result: ExtensionObject::decode(r)?,
})
}
}
#[must_use]
pub fn null_filter() -> ExtensionObject {
null_extension_object()
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateMonitoredItemsRequest {
pub request_header: RequestHeader,
pub subscription_id: u32,
pub timestamps_to_return: i32,
pub items_to_create: Vec<MonitoredItemCreateRequest>,
}
impl CreateMonitoredItemsRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
subscription_id: r.read_u32()?,
timestamps_to_return: r.read_i32()?,
items_to_create: read_array::<MonitoredItemCreateRequest>(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::CREATE_MONITORED_ITEMS_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
w.write_u32(self.subscription_id);
w.write_i32(self.timestamps_to_return);
write_array(w, &self.items_to_create, "ItemsToCreate")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateMonitoredItemsResponse {
pub response_header: ResponseHeader,
pub results: Vec<MonitoredItemCreateResult>,
}
impl CreateMonitoredItemsResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let response_header = decode_response_header(r)?;
let results = read_array::<MonitoredItemCreateResult>(r)?;
skip_diagnostic_info_array(r)?;
Ok(Self {
response_header,
results,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::CREATE_MONITORED_ITEMS_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
write_array(w, &self.results, "Results")?;
write_empty_diagnostic_info_array(w);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SubscriptionAcknowledgement {
pub subscription_id: u32,
pub sequence_number: u32,
}
impl UaEncode for SubscriptionAcknowledgement {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
w.write_u32(self.subscription_id);
w.write_u32(self.sequence_number);
Ok(())
}
}
impl UaDecode for SubscriptionAcknowledgement {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
subscription_id: r.read_u32()?,
sequence_number: r.read_u32()?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MonitoredItemNotification {
pub client_handle: u32,
pub value: DataValue,
}
impl UaEncode for MonitoredItemNotification {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
w.write_u32(self.client_handle);
self.value.encode(w)?;
Ok(())
}
}
impl UaDecode for MonitoredItemNotification {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
client_handle: r.read_u32()?,
value: DataValue::decode(r)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DataChangeNotification {
pub monitored_items: Vec<MonitoredItemNotification>,
}
impl DataChangeNotification {
pub fn to_extension_object(&self) -> Result<ExtensionObject, EncodeError> {
let mut w = UaWriter::new();
write_array(&mut w, &self.monitored_items, "MonitoredItems")?;
write_empty_diagnostic_info_array(&mut w);
Ok(ExtensionObject {
type_id: node_ids::DATA_CHANGE_NOTIFICATION,
body: ExtensionObjectBody::ByteString(w.into_vec()),
})
}
pub fn from_extension_object(eo: &ExtensionObject) -> Result<Self, DecodeError> {
match &eo.body {
ExtensionObjectBody::ByteString(b) => {
let mut r = UaReader::new(b);
let monitored_items = read_array::<MonitoredItemNotification>(&mut r)?;
skip_diagnostic_info_array(&mut r)?;
Ok(Self { monitored_items })
}
_ => Err(DecodeError::MalformedMessage {
message: "DataChangeNotification ExtensionObject is not a ByteString body",
}),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct NotificationMessage {
pub sequence_number: u32,
pub publish_time: i64,
pub notification_data: Vec<ExtensionObject>,
}
impl UaEncode for NotificationMessage {
fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
w.write_u32(self.sequence_number);
w.write_i64(self.publish_time);
write_array(w, &self.notification_data, "NotificationData")?;
Ok(())
}
}
impl UaDecode for NotificationMessage {
fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
sequence_number: r.read_u32()?,
publish_time: r.read_i64()?,
notification_data: read_array::<ExtensionObject>(r)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PublishRequest {
pub request_header: RequestHeader,
pub subscription_acknowledgements: Vec<SubscriptionAcknowledgement>,
}
impl PublishRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
subscription_acknowledgements: read_array::<SubscriptionAcknowledgement>(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::PUBLISH_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
write_array(w, &self.subscription_acknowledgements, "Acks")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PublishResponse {
pub response_header: ResponseHeader,
pub subscription_id: u32,
pub available_sequence_numbers: Vec<u32>,
pub more_notifications: bool,
pub notification_message: NotificationMessage,
pub results: Vec<u32>,
}
impl PublishResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let response_header = decode_response_header(r)?;
let subscription_id = r.read_u32()?;
let available_sequence_numbers = read_u32_array(r)?;
let more_notifications = r.read_u8()? != 0;
let notification_message = NotificationMessage::decode(r)?;
let results = read_u32_array(r)?;
skip_diagnostic_info_array(r)?;
Ok(Self {
response_header,
subscription_id,
available_sequence_numbers,
more_notifications,
notification_message,
results,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::PUBLISH_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
w.write_u32(self.subscription_id);
write_u32_array(w, &self.available_sequence_numbers)?;
w.write_u8(u8::from(self.more_notifications));
self.notification_message.encode(w)?;
write_u32_array(w, &self.results)?;
write_empty_diagnostic_info_array(w);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DeleteSubscriptionsRequest {
pub request_header: RequestHeader,
pub subscription_ids: Vec<u32>,
}
impl DeleteSubscriptionsRequest {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
Ok(Self {
request_header: decode_request_header(r)?,
subscription_ids: read_u32_array(r)?,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::DELETE_SUBSCRIPTIONS_REQUEST.encode(w)?;
encode_request_header(&self.request_header, w)?;
write_u32_array(w, &self.subscription_ids)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DeleteSubscriptionsResponse {
pub response_header: ResponseHeader,
pub results: Vec<u32>,
}
impl DeleteSubscriptionsResponse {
fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
let response_header = decode_response_header(r)?;
let results = read_u32_array(r)?;
skip_diagnostic_info_array(r)?;
Ok(Self {
response_header,
results,
})
}
fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
node_ids::DELETE_SUBSCRIPTIONS_RESPONSE.encode(w)?;
encode_response_header(&self.response_header, w)?;
write_u32_array(w, &self.results)?;
write_empty_diagnostic_info_array(w);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ServiceRequest {
CreateSession(CreateSessionRequest),
ActivateSession(ActivateSessionRequest),
CloseSession(CloseSessionRequest),
Read(ReadRequest),
Write(WriteRequest),
Browse(BrowseRequest),
GetEndpoints(GetEndpointsRequest),
FindServers(FindServersRequest),
CreateSubscription(CreateSubscriptionRequest),
SetPublishingMode(SetPublishingModeRequest),
CreateMonitoredItems(CreateMonitoredItemsRequest),
Publish(PublishRequest),
DeleteSubscriptions(DeleteSubscriptionsRequest),
Call(CallRequest),
}
impl ServiceRequest {
pub fn encode(&self) -> Result<Vec<u8>, EncodeError> {
let mut w = UaWriter::new();
match self {
Self::CreateSession(m) => m.encode_into(&mut w)?,
Self::ActivateSession(m) => m.encode_into(&mut w)?,
Self::CloseSession(m) => m.encode_into(&mut w)?,
Self::Read(m) => m.encode_into(&mut w)?,
Self::Write(m) => m.encode_into(&mut w)?,
Self::Browse(m) => m.encode_into(&mut w)?,
Self::GetEndpoints(m) => m.encode_into(&mut w)?,
Self::FindServers(m) => m.encode_into(&mut w)?,
Self::CreateSubscription(m) => m.encode_into(&mut w)?,
Self::SetPublishingMode(m) => m.encode_into(&mut w)?,
Self::CreateMonitoredItems(m) => m.encode_into(&mut w)?,
Self::Publish(m) => m.encode_into(&mut w)?,
Self::DeleteSubscriptions(m) => m.encode_into(&mut w)?,
Self::Call(m) => m.encode_into(&mut w)?,
}
Ok(w.into_vec())
}
pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {
let mut r = UaReader::new(body);
let type_id = NodeId::decode(&mut r)?;
Ok(if type_id == node_ids::CREATE_SESSION_REQUEST {
Self::CreateSession(CreateSessionRequest::decode_body(&mut r)?)
} else if type_id == node_ids::ACTIVATE_SESSION_REQUEST {
Self::ActivateSession(ActivateSessionRequest::decode_body(&mut r)?)
} else if type_id == node_ids::CLOSE_SESSION_REQUEST {
Self::CloseSession(CloseSessionRequest::decode_body(&mut r)?)
} else if type_id == node_ids::READ_REQUEST {
Self::Read(ReadRequest::decode_body(&mut r)?)
} else if type_id == node_ids::WRITE_REQUEST {
Self::Write(WriteRequest::decode_body(&mut r)?)
} else if type_id == node_ids::BROWSE_REQUEST {
Self::Browse(BrowseRequest::decode_body(&mut r)?)
} else if type_id == node_ids::GET_ENDPOINTS_REQUEST {
Self::GetEndpoints(GetEndpointsRequest::decode_body(&mut r)?)
} else if type_id == node_ids::FIND_SERVERS_REQUEST {
Self::FindServers(FindServersRequest::decode_body(&mut r)?)
} else if type_id == node_ids::CREATE_SUBSCRIPTION_REQUEST {
Self::CreateSubscription(CreateSubscriptionRequest::decode_body(&mut r)?)
} else if type_id == node_ids::SET_PUBLISHING_MODE_REQUEST {
Self::SetPublishingMode(SetPublishingModeRequest::decode_body(&mut r)?)
} else if type_id == node_ids::CREATE_MONITORED_ITEMS_REQUEST {
Self::CreateMonitoredItems(CreateMonitoredItemsRequest::decode_body(&mut r)?)
} else if type_id == node_ids::PUBLISH_REQUEST {
Self::Publish(PublishRequest::decode_body(&mut r)?)
} else if type_id == node_ids::DELETE_SUBSCRIPTIONS_REQUEST {
Self::DeleteSubscriptions(DeleteSubscriptionsRequest::decode_body(&mut r)?)
} else if type_id == node_ids::CALL_REQUEST {
Self::Call(CallRequest::decode_body(&mut r)?)
} else {
return Err(DecodeError::MalformedMessage {
message: "unsupported OPC-UA service request type",
});
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ServiceResponse {
CreateSession(CreateSessionResponse),
ActivateSession(ActivateSessionResponse),
CloseSession(CloseSessionResponse),
Read(ReadResponse),
Write(WriteResponse),
Browse(BrowseResponse),
GetEndpoints(GetEndpointsResponse),
FindServers(FindServersResponse),
CreateSubscription(CreateSubscriptionResponse),
SetPublishingMode(SetPublishingModeResponse),
CreateMonitoredItems(CreateMonitoredItemsResponse),
Publish(PublishResponse),
DeleteSubscriptions(DeleteSubscriptionsResponse),
Call(CallResponse),
}
impl ServiceResponse {
pub fn encode(&self) -> Result<Vec<u8>, EncodeError> {
let mut w = UaWriter::new();
match self {
Self::CreateSession(m) => m.encode_into(&mut w)?,
Self::ActivateSession(m) => m.encode_into(&mut w)?,
Self::CloseSession(m) => m.encode_into(&mut w)?,
Self::Read(m) => m.encode_into(&mut w)?,
Self::Write(m) => m.encode_into(&mut w)?,
Self::Browse(m) => m.encode_into(&mut w)?,
Self::GetEndpoints(m) => m.encode_into(&mut w)?,
Self::FindServers(m) => m.encode_into(&mut w)?,
Self::CreateSubscription(m) => m.encode_into(&mut w)?,
Self::SetPublishingMode(m) => m.encode_into(&mut w)?,
Self::CreateMonitoredItems(m) => m.encode_into(&mut w)?,
Self::Publish(m) => m.encode_into(&mut w)?,
Self::DeleteSubscriptions(m) => m.encode_into(&mut w)?,
Self::Call(m) => m.encode_into(&mut w)?,
}
Ok(w.into_vec())
}
pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {
let mut r = UaReader::new(body);
let type_id = NodeId::decode(&mut r)?;
Ok(if type_id == node_ids::CREATE_SESSION_RESPONSE {
Self::CreateSession(CreateSessionResponse::decode_body(&mut r)?)
} else if type_id == node_ids::ACTIVATE_SESSION_RESPONSE {
Self::ActivateSession(ActivateSessionResponse::decode_body(&mut r)?)
} else if type_id == node_ids::CLOSE_SESSION_RESPONSE {
Self::CloseSession(CloseSessionResponse::decode_body(&mut r)?)
} else if type_id == node_ids::READ_RESPONSE {
Self::Read(ReadResponse::decode_body(&mut r)?)
} else if type_id == node_ids::WRITE_RESPONSE {
Self::Write(WriteResponse::decode_body(&mut r)?)
} else if type_id == node_ids::BROWSE_RESPONSE {
Self::Browse(BrowseResponse::decode_body(&mut r)?)
} else if type_id == node_ids::GET_ENDPOINTS_RESPONSE {
Self::GetEndpoints(GetEndpointsResponse::decode_body(&mut r)?)
} else if type_id == node_ids::FIND_SERVERS_RESPONSE {
Self::FindServers(FindServersResponse::decode_body(&mut r)?)
} else if type_id == node_ids::CREATE_SUBSCRIPTION_RESPONSE {
Self::CreateSubscription(CreateSubscriptionResponse::decode_body(&mut r)?)
} else if type_id == node_ids::SET_PUBLISHING_MODE_RESPONSE {
Self::SetPublishingMode(SetPublishingModeResponse::decode_body(&mut r)?)
} else if type_id == node_ids::CREATE_MONITORED_ITEMS_RESPONSE {
Self::CreateMonitoredItems(CreateMonitoredItemsResponse::decode_body(&mut r)?)
} else if type_id == node_ids::PUBLISH_RESPONSE {
Self::Publish(PublishResponse::decode_body(&mut r)?)
} else if type_id == node_ids::DELETE_SUBSCRIPTIONS_RESPONSE {
Self::DeleteSubscriptions(DeleteSubscriptionsResponse::decode_body(&mut r)?)
} else if type_id == node_ids::CALL_RESPONSE {
Self::Call(CallResponse::decode_body(&mut r)?)
} else {
return Err(DecodeError::MalformedMessage {
message: "unsupported OPC-UA service response type",
});
})
}
}
fn encode_request_header(h: &RequestHeader, w: &mut UaWriter) -> Result<(), EncodeError> {
h.authentication_token.encode(w)?;
w.write_i64(h.timestamp);
w.write_u32(h.request_handle);
w.write_u32(h.return_diagnostics);
write_string(w, &h.audit_entry_id)?;
w.write_u32(h.timeout_hint);
h.additional_header.encode(w)?;
Ok(())
}
fn decode_request_header(r: &mut UaReader<'_>) -> Result<RequestHeader, DecodeError> {
Ok(RequestHeader {
authentication_token: NodeId::decode(r)?,
timestamp: r.read_i64()?,
request_handle: r.read_u32()?,
return_diagnostics: r.read_u32()?,
audit_entry_id: read_string(r)?,
timeout_hint: r.read_u32()?,
additional_header: zerodds_opcua_gateway::data_value::ExtensionObject::decode(r)?,
})
}
fn encode_response_header(h: &ResponseHeader, w: &mut UaWriter) -> Result<(), EncodeError> {
w.write_i64(h.timestamp);
w.write_u32(h.request_handle);
w.write_u32(h.service_result);
w.write_u8(0); write_string_array(w, &h.string_table)?;
h.additional_header.encode(w)?;
Ok(())
}
fn decode_response_header(r: &mut UaReader<'_>) -> Result<ResponseHeader, DecodeError> {
let timestamp = r.read_i64()?;
let request_handle = r.read_u32()?;
let service_result = r.read_u32()?;
crate::wire::skip_one_diagnostic_info(r)?;
let string_table = read_string_array(r)?;
let additional_header = zerodds_opcua_gateway::data_value::ExtensionObject::decode(r)?;
Ok(ResponseHeader {
timestamp,
request_handle,
service_result,
string_table,
additional_header,
})
}
#[cfg(test)]
mod tests {
use super::*;
use zerodds_opcua_gateway::data_value::VariantValue;
use zerodds_opcua_uacp::securechannel::null_extension_object;
fn req_header() -> RequestHeader {
RequestHeader::new(NodeId::numeric(0, 0), 1)
}
#[test]
fn read_request_round_trips() {
let req = ServiceRequest::Read(ReadRequest {
request_header: req_header(),
max_age: 0.0,
timestamps_to_return: 0,
nodes_to_read: alloc::vec![ReadValueId {
node_id: NodeId::numeric(1, 42),
attribute_id: ATTRIBUTE_VALUE,
index_range: String::new(),
data_encoding: QualifiedName {
namespace_index: 0,
name: String::new(),
},
}],
});
let body = req.encode().expect("encode");
assert_eq!(ServiceRequest::decode(&body).expect("decode"), req);
}
#[test]
fn read_response_round_trips() {
let resp = ServiceResponse::Read(ReadResponse {
response_header: ResponseHeader::new(1, 0),
results: alloc::vec![DataValue::new_value(
Variant::scalar(VariantValue::Int32(99)),
0,
0,
)],
});
let body = resp.encode().expect("encode");
assert_eq!(ServiceResponse::decode(&body).expect("decode"), resp);
}
#[test]
fn write_round_trips() {
let req = ServiceRequest::Write(WriteRequest {
request_header: req_header(),
nodes_to_write: alloc::vec![WriteValue {
node_id: NodeId::numeric(1, 7),
attribute_id: ATTRIBUTE_VALUE,
index_range: String::new(),
value: DataValue::new_value(Variant::scalar(VariantValue::Int32(5)), 0, 0),
}],
});
let body = req.encode().expect("encode");
assert_eq!(ServiceRequest::decode(&body).expect("decode"), req);
let resp = ServiceResponse::Write(WriteResponse {
response_header: ResponseHeader::new(1, 0),
results: alloc::vec![0],
});
let rb = resp.encode().expect("encode");
assert_eq!(ServiceResponse::decode(&rb).expect("decode"), resp);
}
#[test]
fn browse_round_trips() {
let req = ServiceRequest::Browse(BrowseRequest {
request_header: req_header(),
view: ViewDescription::default(),
requested_max_references_per_node: 100,
nodes_to_browse: alloc::vec![BrowseDescription {
node_id: NodeId::numeric(0, 85),
browse_direction: 2,
reference_type_id: NodeId::numeric(0, 33),
include_subtypes: true,
node_class_mask: 0,
result_mask: 0x3F,
}],
});
let body = req.encode().expect("encode");
assert_eq!(ServiceRequest::decode(&body).expect("decode"), req);
let resp = ServiceResponse::Browse(BrowseResponse {
response_header: ResponseHeader::new(1, 0),
results: alloc::vec![BrowseResult {
status_code: 0,
continuation_point: Vec::new(),
references: alloc::vec![ReferenceDescription {
reference_type_id: NodeId::numeric(0, 35),
is_forward: true,
node_id: ExpandedNodeId {
node_id: NodeId::numeric(1, 1),
namespace_uri: String::new(),
server_index: 0,
},
browse_name: QualifiedName {
namespace_index: 1,
name: String::from("Boiler"),
},
display_name: LocalizedText {
locale: None,
text: Some(String::from("Boiler")),
},
node_class: 1,
type_definition: ExpandedNodeId {
node_id: NodeId::numeric(0, 58),
namespace_uri: String::new(),
server_index: 0,
},
}],
}],
});
let rb = resp.encode().expect("encode");
assert_eq!(ServiceResponse::decode(&rb).expect("decode"), resp);
}
#[test]
fn discovery_requests_round_trip() {
let ge = ServiceRequest::GetEndpoints(GetEndpointsRequest {
request_header: req_header(),
endpoint_url: String::from("opc.tcp://x:4840"),
locale_ids: alloc::vec![String::from("en")],
profile_uris: Vec::new(),
});
assert_eq!(
ServiceRequest::decode(&ge.encode().expect("e")).expect("d"),
ge
);
let fs = ServiceRequest::FindServers(FindServersRequest {
request_header: req_header(),
endpoint_url: String::from("opc.tcp://x:4840"),
locale_ids: Vec::new(),
server_uris: alloc::vec![String::from("urn:server")],
});
assert_eq!(
ServiceRequest::decode(&fs.encode().expect("e")).expect("d"),
fs
);
let ger = ServiceResponse::GetEndpoints(GetEndpointsResponse {
response_header: ResponseHeader::new(1, 0),
endpoints: Vec::new(),
});
assert_eq!(
ServiceResponse::decode(&ger.encode().expect("e")).expect("d"),
ger
);
}
#[test]
fn subscription_round_trips() {
let cs = ServiceRequest::CreateSubscription(CreateSubscriptionRequest {
request_header: req_header(),
requested_publishing_interval: 500.0,
requested_lifetime_count: 6000,
requested_max_keep_alive_count: 20,
max_notifications_per_publish: 0,
publishing_enabled: true,
priority: 5,
});
assert_eq!(
ServiceRequest::decode(&cs.encode().expect("e")).expect("d"),
cs
);
let cm = ServiceRequest::CreateMonitoredItems(CreateMonitoredItemsRequest {
request_header: req_header(),
subscription_id: 1,
timestamps_to_return: 0,
items_to_create: alloc::vec![MonitoredItemCreateRequest {
item_to_monitor: ReadValueId {
node_id: NodeId::numeric(1, 1),
attribute_id: ATTRIBUTE_VALUE,
index_range: String::new(),
data_encoding: QualifiedName {
namespace_index: 0,
name: String::new(),
},
},
monitoring_mode: 2,
requested_parameters: MonitoringParameters {
client_handle: 7,
sampling_interval: 250.0,
filter: null_filter(),
queue_size: 4,
discard_oldest: true,
},
}],
});
assert_eq!(
ServiceRequest::decode(&cm.encode().expect("e")).expect("d"),
cm
);
}
#[test]
fn publish_data_change_notification_round_trips() {
let dcn = DataChangeNotification {
monitored_items: alloc::vec![MonitoredItemNotification {
client_handle: 7,
value: DataValue::new_value(Variant::scalar(VariantValue::Int32(99)), 0, 0),
}],
};
let eo = dcn.to_extension_object().expect("wrap");
assert_eq!(
DataChangeNotification::from_extension_object(&eo).expect("unwrap"),
dcn
);
let resp = ServiceResponse::Publish(PublishResponse {
response_header: ResponseHeader::new(1, 0),
subscription_id: 3,
available_sequence_numbers: alloc::vec![1],
more_notifications: false,
notification_message: NotificationMessage {
sequence_number: 1,
publish_time: 0,
notification_data: alloc::vec![eo],
},
results: Vec::new(),
});
assert_eq!(
ServiceResponse::decode(&resp.encode().expect("e")).expect("d"),
resp
);
}
#[test]
fn call_round_trips() {
let req = ServiceRequest::Call(CallRequest {
request_header: req_header(),
methods_to_call: alloc::vec![CallMethodRequest {
object_id: NodeId::numeric(0, 0),
method_id: NodeId::numeric(1, 100),
input_arguments: alloc::vec![Variant::scalar(VariantValue::String(String::from(
"group-1"
)))],
}],
});
let body = req.encode().expect("encode");
assert_eq!(ServiceRequest::decode(&body).expect("decode"), req);
let resp = ServiceResponse::Call(CallResponse {
response_header: ResponseHeader::new(1, 0),
results: alloc::vec![CallMethodResult {
status_code: 0,
input_argument_results: alloc::vec![0],
output_arguments: alloc::vec![Variant::scalar(VariantValue::UInt32(7))],
}],
});
let rb = resp.encode().expect("encode");
assert_eq!(ServiceResponse::decode(&rb).expect("decode"), resp);
}
#[test]
fn session_lifecycle_round_trips() {
let close = ServiceRequest::CloseSession(CloseSessionRequest {
request_header: req_header(),
delete_subscriptions: true,
});
assert_eq!(
ServiceRequest::decode(&close.encode().expect("e")).expect("d"),
close
);
let act = ServiceResponse::ActivateSession(ActivateSessionResponse {
response_header: ResponseHeader::new(1, 0),
server_nonce: alloc::vec![1, 2, 3],
results: alloc::vec![0],
});
let _ = null_extension_object();
assert_eq!(
ServiceResponse::decode(&act.encode().expect("e")).expect("d"),
act
);
}
}