opcua_types/
response_header.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! Message header for responses.
6
7use std::{
8    self,
9    io::{Read, Write},
10};
11
12use crate::{
13    data_types::*,
14    date_time::DateTime,
15    diagnostic_info::DiagnosticInfo,
16    encoding::{BinaryDecodable, BinaryEncodable, EncodingResult},
17    extension_object::ExtensionObject,
18    request_header::RequestHeader,
19    status_code::StatusCode,
20    string::UAString,
21    Error,
22};
23
24#[allow(unused)]
25mod opcua {
26    pub(super) use crate as types;
27}
28
29/// The `ResponseHeader` contains information common to every response from server to client.
30#[derive(Debug, Clone, PartialEq, Default, crate::UaNullable)]
31#[cfg_attr(
32    feature = "json",
33    derive(opcua_macros::JsonEncodable, opcua_macros::JsonDecodable)
34)]
35#[cfg_attr(
36    feature = "xml",
37    derive(crate::XmlEncodable, crate::XmlDecodable, crate::XmlType)
38)]
39pub struct ResponseHeader {
40    /// Response timestamp.
41    pub timestamp: UtcTime,
42    /// Handle of the request this message is responding to.
43    pub request_handle: IntegerId,
44    /// Status of the service call as a whole.
45    pub service_result: StatusCode,
46    /// Requested diagnostics.
47    pub service_diagnostics: DiagnosticInfo,
48    /// String table for the message.
49    pub string_table: Option<Vec<UAString>>,
50    /// Reserved space for additional header details.
51    pub additional_header: ExtensionObject,
52}
53
54impl BinaryEncodable for ResponseHeader {
55    fn byte_len(&self, ctx: &opcua::types::Context<'_>) -> usize {
56        let mut size = 0;
57        size += self.timestamp.byte_len(ctx);
58        size += self.request_handle.byte_len(ctx);
59        size += self.service_result.byte_len(ctx);
60        size += self.service_diagnostics.byte_len(ctx);
61        size += self.string_table.byte_len(ctx);
62        size += self.additional_header.byte_len(ctx);
63        size
64    }
65
66    fn encode<S: Write + ?Sized>(
67        &self,
68        stream: &mut S,
69        ctx: &crate::Context<'_>,
70    ) -> EncodingResult<()> {
71        self.timestamp.encode(stream, ctx)?;
72        self.request_handle.encode(stream, ctx)?;
73        self.service_result.encode(stream, ctx)?;
74        self.service_diagnostics.encode(stream, ctx)?;
75        self.string_table.encode(stream, ctx)?;
76        self.additional_header.encode(stream, ctx)
77    }
78}
79
80impl BinaryDecodable for ResponseHeader {
81    fn decode<S: Read + ?Sized>(stream: &mut S, ctx: &crate::Context<'_>) -> EncodingResult<Self> {
82        let timestamp = UtcTime::decode(stream, ctx)?;
83        let request_handle = IntegerId::decode(stream, ctx)?;
84        // Capture request handle if decoding fails after this.
85        let (service_result, service_diagnostics, string_table, additional_header) = (|| {
86            let service_result = StatusCode::decode(stream, ctx)?;
87            let service_diagnostics = DiagnosticInfo::decode(stream, ctx)?;
88            let string_table = BinaryDecodable::decode(stream, ctx)?;
89            let additional_header = ExtensionObject::decode(stream, ctx)?;
90            Ok((
91                service_result,
92                service_diagnostics,
93                string_table,
94                additional_header,
95            ))
96        })()
97        .map_err(|e: Error| e.with_request_handle(request_handle))?;
98        Ok(ResponseHeader {
99            timestamp,
100            request_handle,
101            service_result,
102            service_diagnostics,
103            string_table,
104            additional_header,
105        })
106    }
107}
108
109/// Trait for types that can contain a request handle.
110pub trait AsRequestHandle {
111    /// Get the handle of this request.
112    fn as_request_handle(&self) -> u32;
113}
114
115impl AsRequestHandle for &RequestHeader {
116    fn as_request_handle(&self) -> u32 {
117        self.request_handle
118    }
119}
120
121impl AsRequestHandle for u32 {
122    fn as_request_handle(&self) -> u32 {
123        *self
124    }
125}
126
127impl ResponseHeader {
128    /// Create a new response header with status `Good`.
129    pub fn new_good(request_header: impl AsRequestHandle) -> ResponseHeader {
130        ResponseHeader::new_service_result(request_header, StatusCode::Good)
131    }
132
133    /// Create a new response header with given status.
134    pub fn new_service_result(
135        request_header: impl AsRequestHandle,
136        service_result: StatusCode,
137    ) -> ResponseHeader {
138        ResponseHeader::new_timestamped_service_result(
139            DateTime::now(),
140            request_header,
141            service_result,
142        )
143    }
144
145    /// Create a new response header with given status and timestamp.
146    pub fn new_timestamped_service_result(
147        timestamp: DateTime,
148        request_header: impl AsRequestHandle,
149        service_result: StatusCode,
150    ) -> ResponseHeader {
151        ResponseHeader {
152            timestamp,
153            request_handle: request_header.as_request_handle(),
154            service_result,
155            service_diagnostics: DiagnosticInfo::default(),
156            string_table: None,
157            additional_header: ExtensionObject::null(),
158        }
159    }
160
161    /// For testing, nothing else
162    pub fn null() -> ResponseHeader {
163        ResponseHeader {
164            timestamp: DateTime::now(),
165            request_handle: 0,
166            service_result: StatusCode::Good,
167            service_diagnostics: DiagnosticInfo::default(),
168            string_table: None,
169            additional_header: ExtensionObject::null(),
170        }
171    }
172}