opcua_types/
diagnostic_info.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! Contains the implementation of `DiagnosticInfo`.
6
7use std::io::{Read, Write};
8
9use crate::{
10    encoding::{BinaryDecodable, BinaryEncodable, EncodingResult},
11    status_code::StatusCode,
12    string::UAString,
13    write_i32, write_u8, Context,
14};
15use bitflags::bitflags;
16
17bitflags! {
18    /// Mask for fields present in DiagnosticInfo.
19    #[derive(Copy, Clone, Debug, PartialEq, Default)]
20    pub struct DiagnosticInfoMask: u8 {
21        /// Symbolic ID is present.
22        const HAS_SYMBOLIC_ID = 0x01;
23        /// Namespace is present.
24        const HAS_NAMESPACE = 0x02;
25        /// Localized text is present.
26        const HAS_LOCALIZED_TEXT = 0x04;
27        /// Locale is present.
28        const HAS_LOCALE = 0x08;
29        /// AdditionalInfo is present.
30        const HAS_ADDITIONAL_INFO = 0x10;
31        /// Inner status code is present.
32        const HAS_INNER_STATUS_CODE = 0x20;
33        /// Inner diagnostic info is present.
34        const HAS_INNER_DIAGNOSTIC_INFO = 0x40;
35    }
36}
37
38bitflags! {
39    #[derive(Copy, Clone, Debug, PartialEq, Default)]
40    /// Requested diagnostic infos.
41    pub struct DiagnosticBits: u32 {
42        /// ServiceLevel / SymbolicId
43        const SERVICE_LEVEL_SYMBOLIC_ID = 0x0000_0001;
44        /// ServiceLevel / LocalizedText
45        const SERVICE_LEVEL_LOCALIZED_TEXT = 0x0000_0002;
46        /// ServiceLevel / AdditionalInfo
47        const SERVICE_LEVEL_ADDITIONAL_INFO = 0x0000_0004;
48        /// ServiceLevel / Inner StatusCode
49        const SERVICE_LEVEL_LOCALIZED_INNER_STATUS_CODE = 0x0000_0008;
50        /// ServiceLevel / Inner Diagnostics
51        const SERVICE_LEVEL_LOCALIZED_INNER_DIAGNOSTICS = 0x0000_0010;
52        /// OperationLevel / SymbolicId
53        const OPERATIONAL_LEVEL_SYMBOLIC_ID = 0x0000_0020;
54        /// OperationLevel / LocalizedText
55        const OPERATIONAL_LEVEL_LOCALIZED_TEXT = 0x0000_0040;
56        /// OperationLevel / AdditionalInfo
57        const OPERATIONAL_LEVEL_ADDITIONAL_INFO = 0x0000_0080;
58        /// OperationLevel / Inner StatusCode
59        const OPERATIONAL_LEVEL_INNER_STATUS_CODE = 0x0000_0100;
60        /// OperationLevel / Inner Diagnostics
61        const OPERATIONAL_LEVEL_INNER_DIAGNOSTICS = 0x0000_0200;
62    }
63}
64
65impl crate::UaNullable for DiagnosticBits {
66    fn is_ua_null(&self) -> bool {
67        self.is_empty()
68    }
69}
70
71#[cfg(feature = "json")]
72mod json {
73    use crate::json::*;
74
75    use super::DiagnosticBits;
76
77    impl JsonEncodable for DiagnosticBits {
78        fn encode(
79            &self,
80            stream: &mut JsonStreamWriter<&mut dyn std::io::Write>,
81            _ctx: &crate::Context<'_>,
82        ) -> super::EncodingResult<()> {
83            stream.number_value(self.bits())?;
84            Ok(())
85        }
86    }
87
88    impl JsonDecodable for DiagnosticBits {
89        fn decode(
90            stream: &mut JsonStreamReader<&mut dyn std::io::Read>,
91            _ctx: &Context<'_>,
92        ) -> super::EncodingResult<Self> {
93            Ok(Self::from_bits_truncate(stream.next_number()??))
94        }
95    }
96}
97
98#[cfg(feature = "xml")]
99mod xml {
100    use crate::xml::*;
101    use std::io::{Read, Write};
102
103    use super::DiagnosticBits;
104
105    impl XmlType for DiagnosticBits {
106        const TAG: &'static str = u32::TAG;
107    }
108
109    impl XmlEncodable for DiagnosticBits {
110        fn encode(
111            &self,
112            writer: &mut XmlStreamWriter<&mut dyn Write>,
113            context: &Context<'_>,
114        ) -> EncodingResult<()> {
115            self.bits().encode(writer, context)
116        }
117    }
118
119    impl XmlDecodable for DiagnosticBits {
120        fn decode(
121            reader: &mut XmlStreamReader<&mut dyn Read>,
122            context: &Context<'_>,
123        ) -> EncodingResult<Self> {
124            let v = u32::decode(reader, context)?;
125            Ok(Self::from_bits_truncate(v))
126        }
127    }
128}
129
130#[allow(unused)]
131mod opcua {
132    pub(super) use crate as types;
133}
134
135/// Diagnostic information.
136#[derive(PartialEq, Debug, Clone, crate::UaNullable)]
137#[cfg_attr(
138    feature = "json",
139    derive(opcua_macros::JsonEncodable, opcua_macros::JsonDecodable)
140)]
141#[cfg_attr(
142    feature = "xml",
143    derive(crate::XmlEncodable, crate::XmlDecodable, crate::XmlType)
144)]
145pub struct DiagnosticInfo {
146    /// A symbolic name for the status code.
147    pub symbolic_id: Option<i32>,
148    /// A namespace that qualifies the symbolic id.
149    pub namespace_uri: Option<i32>,
150    /// The locale used for the localized text.
151    pub locale: Option<i32>,
152    /// A human readable summary of the status code.
153    pub localized_text: Option<i32>,
154    /// Detailed application specific diagnostic information.
155    pub additional_info: Option<UAString>,
156    /// A status code provided by an underlying system.
157    pub inner_status_code: Option<StatusCode>,
158    /// Diagnostic info associated with the inner status code.
159    pub inner_diagnostic_info: Option<Box<DiagnosticInfo>>,
160}
161
162impl BinaryEncodable for DiagnosticInfo {
163    fn byte_len(&self, ctx: &opcua::types::Context<'_>) -> usize {
164        let mut size: usize = 0;
165        size += 1; // self.encoding_mask())
166        if let Some(ref symbolic_id) = self.symbolic_id {
167            // Write symbolic id
168            size += symbolic_id.byte_len(ctx);
169        }
170        if let Some(ref namespace_uri) = self.namespace_uri {
171            // Write namespace
172            size += namespace_uri.byte_len(ctx)
173        }
174        if let Some(ref locale) = self.locale {
175            // Write locale
176            size += locale.byte_len(ctx)
177        }
178        if let Some(ref localized_text) = self.localized_text {
179            // Write localized text
180            size += localized_text.byte_len(ctx)
181        }
182        if let Some(ref additional_info) = self.additional_info {
183            // Write Additional info
184            size += additional_info.byte_len(ctx)
185        }
186        if let Some(ref inner_status_code) = self.inner_status_code {
187            // Write inner status code
188            size += inner_status_code.byte_len(ctx)
189        }
190        if let Some(ref inner_diagnostic_info) = self.inner_diagnostic_info {
191            // Write inner diagnostic info
192            size += inner_diagnostic_info.byte_len(ctx)
193        }
194        size
195    }
196
197    fn encode<S: Write + ?Sized>(&self, stream: &mut S, ctx: &Context<'_>) -> EncodingResult<()> {
198        write_u8(stream, self.encoding_mask().bits())?;
199        if let Some(ref symbolic_id) = self.symbolic_id {
200            // Write symbolic id
201            write_i32(stream, *symbolic_id)?;
202        }
203        if let Some(ref namespace_uri) = self.namespace_uri {
204            // Write namespace
205            namespace_uri.encode(stream, ctx)?;
206        }
207        if let Some(ref locale) = self.locale {
208            // Write locale
209            locale.encode(stream, ctx)?;
210        }
211        if let Some(ref localized_text) = self.localized_text {
212            // Write localized text
213            localized_text.encode(stream, ctx)?;
214        }
215        if let Some(ref additional_info) = self.additional_info {
216            // Write Additional info
217            additional_info.encode(stream, ctx)?;
218        }
219        if let Some(ref inner_status_code) = self.inner_status_code {
220            // Write inner status code
221            inner_status_code.encode(stream, ctx)?;
222        }
223        if let Some(ref inner_diagnostic_info) = self.inner_diagnostic_info {
224            // Write inner diagnostic info
225            inner_diagnostic_info.clone().encode(stream, ctx)?;
226        }
227        Ok(())
228    }
229}
230
231impl BinaryDecodable for DiagnosticInfo {
232    fn decode<S: Read + ?Sized>(stream: &mut S, ctx: &Context<'_>) -> EncodingResult<Self> {
233        let encoding_mask = DiagnosticInfoMask::from_bits_truncate(u8::decode(stream, ctx)?);
234        let mut diagnostic_info = DiagnosticInfo::default();
235
236        if encoding_mask.contains(DiagnosticInfoMask::HAS_SYMBOLIC_ID) {
237            // Read symbolic id
238            diagnostic_info.symbolic_id = Some(i32::decode(stream, ctx)?);
239        }
240        if encoding_mask.contains(DiagnosticInfoMask::HAS_NAMESPACE) {
241            // Read namespace
242            diagnostic_info.namespace_uri = Some(i32::decode(stream, ctx)?);
243        }
244        if encoding_mask.contains(DiagnosticInfoMask::HAS_LOCALE) {
245            // Read locale
246            diagnostic_info.locale = Some(i32::decode(stream, ctx)?);
247        }
248        if encoding_mask.contains(DiagnosticInfoMask::HAS_LOCALIZED_TEXT) {
249            // Read localized text
250            diagnostic_info.localized_text = Some(i32::decode(stream, ctx)?);
251        }
252        if encoding_mask.contains(DiagnosticInfoMask::HAS_ADDITIONAL_INFO) {
253            // Read Additional info
254            diagnostic_info.additional_info = Some(UAString::decode(stream, ctx)?);
255        }
256        if encoding_mask.contains(DiagnosticInfoMask::HAS_INNER_STATUS_CODE) {
257            // Read inner status code
258            diagnostic_info.inner_status_code = Some(StatusCode::decode(stream, ctx)?);
259        }
260        if encoding_mask.contains(DiagnosticInfoMask::HAS_INNER_DIAGNOSTIC_INFO) {
261            // Read inner diagnostic info
262            diagnostic_info.inner_diagnostic_info =
263                Some(Box::new(DiagnosticInfo::decode(stream, ctx)?));
264        }
265        Ok(diagnostic_info)
266    }
267}
268
269impl Default for DiagnosticInfo {
270    fn default() -> Self {
271        DiagnosticInfo::null()
272    }
273}
274
275impl DiagnosticInfo {
276    /// Return an empty diagnostic info.
277    pub fn null() -> DiagnosticInfo {
278        DiagnosticInfo {
279            symbolic_id: None,
280            namespace_uri: None,
281            locale: None,
282            localized_text: None,
283            additional_info: None,
284            inner_status_code: None,
285            inner_diagnostic_info: None,
286        }
287    }
288
289    /// Get the encoding mask for this diagnostic info.
290    pub fn encoding_mask(&self) -> DiagnosticInfoMask {
291        let mut encoding_mask = DiagnosticInfoMask::empty();
292        if self.symbolic_id.is_some() {
293            encoding_mask |= DiagnosticInfoMask::HAS_SYMBOLIC_ID;
294        }
295        if self.namespace_uri.is_some() {
296            encoding_mask |= DiagnosticInfoMask::HAS_NAMESPACE;
297        }
298        if self.locale.is_some() {
299            encoding_mask |= DiagnosticInfoMask::HAS_LOCALE;
300        }
301        if self.localized_text.is_some() {
302            encoding_mask |= DiagnosticInfoMask::HAS_LOCALIZED_TEXT;
303        }
304        if self.additional_info.is_some() {
305            encoding_mask |= DiagnosticInfoMask::HAS_ADDITIONAL_INFO;
306        }
307        if self.inner_status_code.is_some() {
308            encoding_mask |= DiagnosticInfoMask::HAS_INNER_STATUS_CODE;
309        }
310        if self.inner_diagnostic_info.is_some() {
311            encoding_mask |= DiagnosticInfoMask::HAS_INNER_DIAGNOSTIC_INFO;
312        }
313        encoding_mask
314    }
315}