Skip to main content

opcua/types/
diagnostic_info.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! Contains the implementation of `DiagnosticInfo`.
6
7use std::io::{Read, Write};
8
9use crate::types::{encoding::*, status_codes::StatusCode, string::UAString};
10
11bitflags! {
12    pub struct DiagnosticInfoMask: u8 {
13        const HAS_SYMBOLIC_ID = 0x01;
14        const HAS_NAMESPACE = 0x02;
15        const HAS_LOCALIZED_TEXT = 0x04;
16        const HAS_LOCALE = 0x08;
17        const HAS_ADDITIONAL_INFO = 0x10;
18        const HAS_INNER_STATUS_CODE = 0x20;
19        const HAS_INNER_DIAGNOSTIC_INFO = 0x40;
20    }
21}
22
23bitflags! {
24     pub struct DiagnosticBits: u32 {
25        /// ServiceLevel / SymbolicId
26        const SERVICE_LEVEL_SYMBOLIC_ID = 0x0000_0001;
27        /// ServiceLevel / LocalizedText
28        const SERVICE_LEVEL_LOCALIZED_TEXT = 0x0000_0002;
29        /// ServiceLevel / AdditionalInfo
30        const SERVICE_LEVEL_ADDITIONAL_INFO = 0x0000_0004;
31        /// ServiceLevel / Inner StatusCode
32        const SERVICE_LEVEL_LOCALIZED_INNER_STATUS_CODE = 0x0000_0008;
33        /// ServiceLevel / Inner Diagnostics
34        const SERVICE_LEVEL_LOCALIZED_INNER_DIAGNOSTICS = 0x0000_0010;
35        /// OperationLevel / SymbolicId
36        const OPERATIONAL_LEVEL_SYMBOLIC_ID = 0x0000_0020;
37        /// OperationLevel / LocalizedText
38        const OPERATIONAL_LEVEL_LOCALIZED_TEXT = 0x0000_0040;
39        /// OperationLevel / AdditionalInfo
40        const OPERATIONAL_LEVEL_ADDITIONAL_INFO = 0x0000_0080;
41        /// OperationLevel / Inner StatusCode
42        const OPERATIONAL_LEVEL_INNER_STATUS_CODE = 0x0000_0100;
43        /// OperationLevel / Inner Diagnostics
44        const OPERATIONAL_LEVEL_INNER_DIAGNOSTICS = 0x0000_0200;
45    }
46}
47
48/// Diagnostic information.
49#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
50#[serde(rename_all = "PascalCase")]
51pub struct DiagnosticInfo {
52    /// A symbolic name for the status code.
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub symbolic_id: Option<i32>,
55    /// A namespace that qualifies the symbolic id.
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub namespace_uri: Option<i32>,
58    /// The locale used for the localized text.
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub locale: Option<i32>,
61    /// A human readable summary of the status code.
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub localized_text: Option<i32>,
64    /// Detailed application specific diagnostic information.
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub additional_info: Option<UAString>,
67    /// A status code provided by an underlying system.
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub inner_status_code: Option<StatusCode>,
70    /// Diagnostic info associated with the inner status code.
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub inner_diagnostic_info: Option<Box<DiagnosticInfo>>,
73}
74
75impl BinaryEncoder<DiagnosticInfo> for DiagnosticInfo {
76    fn byte_len(&self) -> usize {
77        let mut size: usize = 0;
78        size += 1; // self.encoding_mask())
79        if let Some(ref symbolic_id) = self.symbolic_id {
80            // Write symbolic id
81            size += symbolic_id.byte_len();
82        }
83        if let Some(ref namespace_uri) = self.namespace_uri {
84            // Write namespace
85            size += namespace_uri.byte_len()
86        }
87        if let Some(ref locale) = self.locale {
88            // Write locale
89            size += locale.byte_len()
90        }
91        if let Some(ref localized_text) = self.localized_text {
92            // Write localized text
93            size += localized_text.byte_len()
94        }
95        if let Some(ref additional_info) = self.additional_info {
96            // Write Additional info
97            size += additional_info.byte_len()
98        }
99        if let Some(ref inner_status_code) = self.inner_status_code {
100            // Write inner status code
101            size += inner_status_code.byte_len()
102        }
103        if let Some(ref inner_diagnostic_info) = self.inner_diagnostic_info {
104            // Write inner diagnostic info
105            size += inner_diagnostic_info.byte_len()
106        }
107        size
108    }
109
110    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
111        let mut size: usize = 0;
112        size += write_u8(stream, self.encoding_mask().bits)?;
113        if let Some(ref symbolic_id) = self.symbolic_id {
114            // Write symbolic id
115            size += write_i32(stream, *symbolic_id)?;
116        }
117        if let Some(ref namespace_uri) = self.namespace_uri {
118            // Write namespace
119            size += namespace_uri.encode(stream)?;
120        }
121        if let Some(ref locale) = self.locale {
122            // Write locale
123            size += locale.encode(stream)?;
124        }
125        if let Some(ref localized_text) = self.localized_text {
126            // Write localized text
127            size += localized_text.encode(stream)?;
128        }
129        if let Some(ref additional_info) = self.additional_info {
130            // Write Additional info
131            size += additional_info.encode(stream)?;
132        }
133        if let Some(ref inner_status_code) = self.inner_status_code {
134            // Write inner status code
135            size += inner_status_code.encode(stream)?;
136        }
137        if let Some(ref inner_diagnostic_info) = self.inner_diagnostic_info {
138            // Write inner diagnostic info
139            size += inner_diagnostic_info.clone().encode(stream)?;
140        }
141        Ok(size)
142    }
143
144    fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<Self> {
145        let encoding_mask =
146            DiagnosticInfoMask::from_bits_truncate(u8::decode(stream, decoding_options)?);
147        let mut diagnostic_info = DiagnosticInfo::default();
148
149        if encoding_mask.contains(DiagnosticInfoMask::HAS_SYMBOLIC_ID) {
150            // Read symbolic id
151            diagnostic_info.symbolic_id = Some(i32::decode(stream, decoding_options)?);
152        }
153        if encoding_mask.contains(DiagnosticInfoMask::HAS_NAMESPACE) {
154            // Read namespace
155            diagnostic_info.namespace_uri = Some(i32::decode(stream, decoding_options)?);
156        }
157        if encoding_mask.contains(DiagnosticInfoMask::HAS_LOCALE) {
158            // Read locale
159            diagnostic_info.locale = Some(i32::decode(stream, decoding_options)?);
160        }
161        if encoding_mask.contains(DiagnosticInfoMask::HAS_LOCALIZED_TEXT) {
162            // Read localized text
163            diagnostic_info.localized_text = Some(i32::decode(stream, decoding_options)?);
164        }
165        if encoding_mask.contains(DiagnosticInfoMask::HAS_ADDITIONAL_INFO) {
166            // Read Additional info
167            diagnostic_info.additional_info = Some(UAString::decode(stream, decoding_options)?);
168        }
169        if encoding_mask.contains(DiagnosticInfoMask::HAS_INNER_STATUS_CODE) {
170            // Read inner status code
171            diagnostic_info.inner_status_code = Some(StatusCode::decode(stream, decoding_options)?);
172        }
173        if encoding_mask.contains(DiagnosticInfoMask::HAS_INNER_DIAGNOSTIC_INFO) {
174            // Read inner diagnostic info
175            diagnostic_info.inner_diagnostic_info =
176                Some(Box::new(DiagnosticInfo::decode(stream, decoding_options)?));
177        }
178        Ok(diagnostic_info)
179    }
180}
181
182impl Default for DiagnosticInfo {
183    fn default() -> Self {
184        DiagnosticInfo::null()
185    }
186}
187
188impl DiagnosticInfo {
189    pub fn null() -> DiagnosticInfo {
190        DiagnosticInfo {
191            symbolic_id: None,
192            namespace_uri: None,
193            locale: None,
194            localized_text: None,
195            additional_info: None,
196            inner_status_code: None,
197            inner_diagnostic_info: None,
198        }
199    }
200
201    pub fn encoding_mask(&self) -> DiagnosticInfoMask {
202        let mut encoding_mask = DiagnosticInfoMask::empty();
203        if self.symbolic_id.is_some() {
204            encoding_mask |= DiagnosticInfoMask::HAS_SYMBOLIC_ID;
205        }
206        if self.namespace_uri.is_some() {
207            encoding_mask |= DiagnosticInfoMask::HAS_NAMESPACE;
208        }
209        if self.locale.is_some() {
210            encoding_mask |= DiagnosticInfoMask::HAS_LOCALE;
211        }
212        if self.localized_text.is_some() {
213            encoding_mask |= DiagnosticInfoMask::HAS_LOCALIZED_TEXT;
214        }
215        if self.additional_info.is_some() {
216            encoding_mask |= DiagnosticInfoMask::HAS_ADDITIONAL_INFO;
217        }
218        if self.inner_status_code.is_some() {
219            encoding_mask |= DiagnosticInfoMask::HAS_INNER_STATUS_CODE;
220        }
221        if self.inner_diagnostic_info.is_some() {
222            encoding_mask |= DiagnosticInfoMask::HAS_INNER_DIAGNOSTIC_INFO;
223        }
224        encoding_mask
225    }
226}