cognite/dto/core/datapoint/
status_code.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock, Einar Omang
4
5// This file is mostly taken from
6// https://github.com/einarmo/opcua/blob/status-code-reimp/lib/src/types/status_code.rs
7// which means it's under the above MPL license.
8
9use std::{
10    convert::{TryFrom, TryInto},
11    error::Error,
12    fmt::Display,
13    str::FromStr,
14};
15
16use serde::{ser::SerializeStruct, Deserialize, Serialize};
17use thiserror::Error;
18
19#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
20/// Wrapper around an OPC-UA status code, with utilities for displaying,
21/// parsing, and reading.
22pub struct StatusCode(u32);
23
24const SUBCODE_MASK: u32 = 0xffff_0000;
25const INFO_BITS_MASK: u32 = 0b0011_1111_1111;
26
27impl StatusCode {
28    /// `true` if the status code has severity `Good`
29    pub fn is_good(&self) -> bool {
30        matches!(self.severity(), StatusCodeSeverity::Good)
31    }
32
33    /// `true` if the status code has severity `Bad`
34    pub fn is_bad(&self) -> bool {
35        matches!(self.severity(), StatusCodeSeverity::Bad)
36    }
37
38    /// `true` if the status code has severity `Uncertain`
39    pub fn is_uncertain(&self) -> bool {
40        matches!(self.severity(), StatusCodeSeverity::Uncertain)
41    }
42
43    /// Get the inner status code.
44    pub fn bits(&self) -> u32 {
45        self.0
46    }
47
48    /// Create a status code from a given category.
49    pub fn from_category(category: SubStatusCode) -> Self {
50        Self(category as u32)
51    }
52
53    fn get_bool(&self, offset: u8) -> bool {
54        (self.0 & (1 << offset)) != 0
55    }
56
57    #[must_use = "Status code is copied, not modified in place."]
58    fn set_bool(mut self, value: bool, offset: u8) -> Self {
59        self.0 = self.0 & !(1 << offset) | ((value as u32) << offset);
60        self
61    }
62
63    /// Get the severity of the status code.
64    pub fn severity(&self) -> StatusCodeSeverity {
65        // A severity of 0b11 is considered bad according to the standard
66        StatusCodeSeverity::from_value((self.0 >> 30) & 0b11).unwrap_or(StatusCodeSeverity::Bad)
67    }
68
69    /// Set the severity. Note that this will clear the subcode.
70    ///
71    /// It is equivalent to `set_sub_code(SubStatusCode::Good/Bad/Uncertain)`
72    #[must_use = "Status code is copied, not modified in place."]
73    pub fn set_severity(mut self, value: StatusCodeSeverity) -> Self {
74        // Setting the severity to an arbitrary value is not defined since it
75        // overwrites the subcode, so we clear the category
76        self.0 = self.0 & !SUBCODE_MASK | ((value as u32) << 30);
77        self
78    }
79
80    /// Get the structure changed flag.
81    pub fn structure_changed(&self) -> bool {
82        self.get_bool(15)
83    }
84
85    /// Set the structure changed flag.
86    #[must_use = "Status code is copied, not modified in place."]
87    pub fn set_structure_changed(self, value: bool) -> Self {
88        self.set_bool(value, 15)
89    }
90
91    /// Get the semantics changed flag.
92    pub fn semantics_changed(&self) -> bool {
93        self.get_bool(14)
94    }
95
96    /// Set the semantics changed flag.
97    #[must_use = "Status code is copied, not modified in place."]
98    pub fn set_semantics_changed(self, value: bool) -> Self {
99        self.set_bool(value, 14)
100    }
101
102    /// Get the sub code of this status.
103    pub fn sub_code(&self) -> SubStatusCode {
104        SubStatusCode::from_value(self.0 & SUBCODE_MASK).unwrap_or(SubStatusCode::Invalid)
105    }
106
107    /// Set the sub code.
108    #[must_use = "Status code is copied, not modified in place."]
109    pub fn set_sub_code(mut self, value: SubStatusCode) -> Self {
110        self.0 = self.0 & !SUBCODE_MASK | ((value as u32) & SUBCODE_MASK);
111        self
112    }
113
114    /// Get the info type, whether this status code represents a data value or not.
115    pub fn info_type(&self) -> StatusCodeInfoType {
116        StatusCodeInfoType::from_value((self.0 >> 10) & 1).unwrap_or(StatusCodeInfoType::NotUsed)
117    }
118
119    /// Set the info type, this will clear the info bits if set to NotUsed.
120    #[must_use = "Status code is copied, not modified in place."]
121    pub fn set_info_type(mut self, value: StatusCodeInfoType) -> Self {
122        self.0 = self.0 & !(1 << 10) | (((value as u32) & 1) << 10);
123        // Clear the info bits if we are setting info type to not used.
124        if matches!(value, StatusCodeInfoType::NotUsed) {
125            self.0 &= !INFO_BITS_MASK;
126        }
127        self
128    }
129
130    /// Whether the value is bounded by some limit.
131    pub fn limit(&self) -> StatusCodeLimit {
132        // Cannot be None here.
133        StatusCodeLimit::from_value((self.0 >> 8) & 0b11).unwrap_or_default()
134    }
135
136    /// Set whether the value is bounded by some limit.
137    #[must_use = "Status code is copied, not modified in place."]
138    pub fn set_limit(mut self, limit: StatusCodeLimit) -> Self {
139        self.0 = self.0 & !(0b11 << 8) | ((limit as u32) << 8);
140        self
141    }
142
143    /// Get whether the "overflow" flag is set.
144    pub fn overflow(&self) -> bool {
145        self.get_bool(7)
146    }
147
148    /// Set the "overflow" flag.
149    #[must_use = "Status code is copied, not modified in place."]
150    pub fn set_overflow(self, value: bool) -> Self {
151        self.set_bool(value, 7)
152    }
153
154    /// Get whether the "multi_value" flag is set.
155    pub fn multi_value(&self) -> bool {
156        self.get_bool(4)
157    }
158
159    /// Set the "multi_value" flag.
160    #[must_use = "Status code is copied, not modified in place."]
161    pub fn set_multi_value(self, value: bool) -> Self {
162        self.set_bool(value, 4)
163    }
164
165    /// Get whether the "extra_data" flag is set.
166    pub fn extra_data(&self) -> bool {
167        self.get_bool(3)
168    }
169
170    /// Set the "extra_data" flag.
171    #[must_use = "Status code is copied, not modified in place."]
172    pub fn set_extra_data(self, value: bool) -> Self {
173        self.set_bool(value, 3)
174    }
175
176    /// Get whether the "partial" flag is set.
177    pub fn partial(&self) -> bool {
178        self.get_bool(2)
179    }
180
181    /// Set the "partial" flag.
182    #[must_use = "Status code is copied, not modified in place."]
183    pub fn set_partial(self, value: bool) -> Self {
184        self.set_bool(value, 2)
185    }
186
187    /// Get the historical value type, only applicable to historical values.
188    pub fn value_type(&self) -> StatusCodeValueType {
189        StatusCodeValueType::from_value(self.0 & 0b11).unwrap_or(StatusCodeValueType::Undefined)
190    }
191
192    /// Set the historical value type, only applicable to historical values.
193    #[must_use = "Status code is copied, not modified in place."]
194    pub fn set_value_type(mut self, value: StatusCodeValueType) -> Self {
195        self.0 = self.0 & !0b11 | ((value as u32) & 0b11);
196        self
197    }
198
199    /// Validate the status code.
200    /// Status codes may be invalid in order to be compatible with future OPC-UA versions,
201    /// but this can be called to check if they are valid according to the standard
202    /// when this was written (1.05)
203    pub fn validate(&self) -> Result<(), StatusCodeValidationError> {
204        if self.0 >> 30 == 0b11 {
205            return Err(StatusCodeValidationError::InvalidSeverity);
206        }
207
208        if self.0 & (0b11 << 28) != 0 || self.0 & (0b111 << 11) != 0 || self.0 & (0b11 << 5) != 0 {
209            return Err(StatusCodeValidationError::UsedReservedBit);
210        }
211
212        if self.sub_code() == SubStatusCode::Invalid {
213            return Err(StatusCodeValidationError::UnknownSubCode);
214        }
215
216        if matches!(self.info_type(), StatusCodeInfoType::NotUsed) && self.0 & INFO_BITS_MASK != 0 {
217            return Err(StatusCodeValidationError::InvalidInfoBits);
218        }
219
220        if self.value_type() == StatusCodeValueType::Undefined {
221            return Err(StatusCodeValidationError::UndefinedValueType);
222        }
223
224        Ok(())
225    }
226
227    /// Try to create a status code by parsing a string on the form
228    /// Category, Flag, Flag, Flag, ... where Flag is one of
229    /// "Low", "High", "Constant", "StructureChanged", "SemanticsChanged",
230    /// "Overflow", "MultipleValues", "ExtraData", "Partial", "Interpolated",
231    /// or "Calculated".
232    ///
233    /// See <https://reference.opcfoundation.org/Core/Part4/v104/docs/7.34>
234    /// for details.
235    pub fn try_parse(symbol: &str) -> Result<StatusCode, ParseStatusCodeError> {
236        let mut parts = symbol.split(&[' ', ',']);
237        let Some(root) = parts.next() else {
238            return Err(ParseStatusCodeError::InvalidFormat);
239        };
240        let root = root
241            .replace("Good_", "Good")
242            .replace("Bad_", "Bad")
243            .replace("Uncertain_", "Uncertain");
244        let sub_status = SubStatusCode::from_str(&root)
245            .map_err(|_| ParseStatusCodeError::UnknownSubStatusCode(root))?;
246
247        let mut code = sub_status as u32;
248        let mut limit_args = 0;
249
250        for flag in parts {
251            if flag.trim().is_empty() {
252                continue;
253            }
254            match flag {
255                "Low" => {
256                    limit_args += 1;
257                    code |= 0x100;
258                }
259                "High" => {
260                    limit_args += 1;
261                    code |= 0x200;
262                }
263                "Constant" => {
264                    limit_args += 1;
265                    code |= 0x300;
266                }
267                "StructureChanged" => code |= 1 << 15,
268                "SemanticsChanged" => code |= 1 << 14,
269                "Overflow" => code |= 1 << 7,
270                "MultipleValues" => code |= 0b1_0000,
271                "ExtraData" => code |= 0b1000,
272                "Partial" => code |= 0b100,
273                "Interpolated" => code |= 0b10,
274                "Calculated" => code |= 1,
275                _ => return Err(ParseStatusCodeError::UnsupportedFlag(flag.to_owned())),
276            }
277        }
278
279        if limit_args > 1 {
280            return Err(ParseStatusCodeError::TooManyLimits);
281        }
282
283        if code & INFO_BITS_MASK != 0 {
284            code |= 1 << 10;
285        }
286
287        let code: StatusCode = code.into();
288        code.validate()?;
289
290        Ok(code)
291    }
292}
293
294impl Serialize for StatusCode {
295    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
296    where
297        S: serde::Serializer,
298    {
299        let mut s = serializer.serialize_struct("StatusCode", 1)?;
300        s.serialize_field("code", &self.bits())?;
301        s.end()
302    }
303}
304
305#[derive(Deserialize)]
306struct JsonStatusCode {
307    code: Option<i64>,
308    symbol: Option<String>,
309}
310
311impl<'de> Deserialize<'de> for StatusCode {
312    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
313    where
314        D: serde::Deserializer<'de>,
315    {
316        let v = JsonStatusCode::deserialize(deserializer)?;
317        if let Some(c) = v.code {
318            Ok(StatusCode::try_from(c).unwrap_or(StatusCode::Invalid))
319        } else if let Some(s) = v.symbol {
320            Ok(StatusCode::try_parse(&s).unwrap_or(StatusCode::Invalid))
321        } else {
322            Ok(StatusCode::Good)
323        }
324    }
325}
326
327#[derive(Debug, Error)]
328/// Error on failure to parse a status code.
329pub enum ParseStatusCodeError {
330    /// Invalid format, must be on the form 'Category, Flag, Flag, ...'
331    #[error("Invalid format, must be on the form 'Category, Flag, Flag, ...'")]
332    InvalidFormat,
333    /// Unknown sub status code.
334    #[error("Unknown sub status code {0}")]
335    UnknownSubStatusCode(String),
336    /// Unsupported flag.
337    #[error("Unsupported flag {0}")]
338    UnsupportedFlag(String),
339    /// Status code failed to validate.
340    #[error("{0}")]
341    Invalid(#[from] StatusCodeValidationError),
342    /// Only one of Low, High, and Constant may be set.
343    #[error("Only one of Low, High, and Constant may be set")]
344    TooManyLimits,
345}
346
347impl Display for StatusCode {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        // Special case 0 for efficiency.
350        if self.0 == 0 {
351            return write!(f, "Good");
352        }
353
354        write!(f, "{}", self.sub_code())?;
355        if self.structure_changed() {
356            write!(f, ", StructureChanged")?;
357        }
358        if self.semantics_changed() {
359            write!(f, ", SemanticsChanged")?;
360        }
361        if self.limit() != StatusCodeLimit::None {
362            write!(f, ", {}", self.limit())?;
363        }
364        if self.overflow() {
365            write!(f, ", Overflow")?;
366        }
367        if self.multi_value() {
368            write!(f, ", MultiValue")?;
369        }
370        if self.extra_data() {
371            write!(f, ", ExtraData")?;
372        }
373        if self.partial() {
374            write!(f, ", Partial")?;
375        }
376        if self.value_type() != StatusCodeValueType::Raw {
377            write!(f, ", {}", self.value_type())?;
378        }
379        Ok(())
380    }
381}
382
383impl std::fmt::Debug for StatusCode {
384    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
385        write!(f, "{} ({})", self, self.0)
386    }
387}
388
389impl From<u32> for StatusCode {
390    fn from(value: u32) -> Self {
391        StatusCode(value)
392    }
393}
394
395impl TryFrom<i64> for StatusCode {
396    type Error = <u32 as TryFrom<i64>>::Error;
397
398    fn try_from(value: i64) -> Result<Self, Self::Error> {
399        let uv: u32 = value.try_into()?;
400        Ok(uv.into())
401    }
402}
403
404impl Error for StatusCode {}
405
406#[derive(Debug, Clone, Error)]
407/// Error on failure to validate a status code.
408pub enum StatusCodeValidationError {
409    /// Severity is the reserved value 0b11
410    #[error("Severity is the reserved value 0b11")]
411    InvalidSeverity,
412    /// Used one of the reserved bits 5, 6, 11, 12, 13, 28, or 29
413    #[error("Used one of the reserved bits 5, 6, 11, 12, 13, 28, or 29")]
414    UsedReservedBit,
415    /// Sub code is not recognized
416    #[error("Sub code is not recognized")]
417    UnknownSubCode,
418    /// Info type is 0, but info bits are non-zero.
419    #[error("Info type is 0, but info bits are non-zero")]
420    InvalidInfoBits,
421    /// Historian value type is undefined (0b11).
422    #[error("Historian value type is undefined (0b11)")]
423    UndefinedValueType,
424}
425
426/// Macro that expands to an enum with specific values,
427/// conversions from those values, and a simple string representation.
428/// Each entry must have a comment, which expands to a comment on the
429/// variant, and to a `description` method to get the description at compile time.
430macro_rules! value_enum_impl {
431    (#[doc = $edoc:literal] $type:ident, $(#[doc = $doc:literal] $code:ident = $val:literal),* $(,)?) => {
432        value_enum_impl!(#[doc = $edoc] $type, _enum $($doc $code = $val),*);
433        value_enum_impl!($type, _name $($code),*);
434        value_enum_impl!($type, _from_val $($code = $val),*);
435        value_enum_impl!($type, _description $($doc $code),*);
436    };
437
438    (#[doc = $edoc:literal] $type:ident, _enum $($comment:literal $code:ident = $val:literal),*) => {
439        #[allow(non_camel_case_types)]
440        #[derive(Copy, Clone, PartialEq, Eq, Hash)]
441        #[doc = $edoc]
442        #[repr(u32)]
443        pub enum $type {
444            $(#[doc = $comment] $code = $val),*
445        }
446
447        impl std::fmt::Debug for $type {
448            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
449                write!(f, "{} ({})", self.name(), (*self) as u32)
450            }
451        }
452    };
453
454    ($type:ident, _enum $($comment:literal $code:ident = $val:literal),*) => {
455        #[allow(non_camel_case_types)]
456        #[derive(Debug, Copy, Clone)]
457        pub enum $type {
458            $($(#[doc = $comment])? $code = $val),*
459        }
460    };
461
462    ($type:ident, _name $($code:ident),*) => {
463        impl $type {
464            /// Return a string representation of this enum value.
465            pub fn name(&self) -> &'static str {
466                match self {
467                    $(Self::$code => stringify!($code)),*
468                }
469            }
470        }
471
472        impl std::str::FromStr for $type {
473            type Err = ();
474
475            fn from_str(s: &str) -> Result<Self, Self::Err> {
476                match s {
477                    $(stringify!($code) => Ok(Self::$code)),*,
478                    _ => Err(())
479                }
480            }
481        }
482
483        impl std::fmt::Display for $type {
484            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485                write!(f, "{}", self.name())
486            }
487        }
488    };
489
490    ($type:ident, _description $($comment:literal $code:ident),*) => {
491        impl $type {
492            /// Return a string description of this enum value.
493            pub fn description(&self) -> &'static str {
494                match self {
495                    $(Self::$code => $comment),*
496                }
497            }
498        }
499    };
500
501    ($type:ident, _from_val $($code:ident = $val:literal),*) => {
502        impl $type {
503            /// Try to create this enum from a u32 value.
504            pub fn from_value(val: u32) -> Option<Self> {
505                match val {
506                    $($val => Some(Self::$code)),*,
507                    _ => None
508                }
509            }
510        }
511    }
512}
513
514// Note that the comments on variants are deliberately without an initial space.
515// The initial space is included in the `description`, fixing this
516// would require a proc-macro.
517// This looks fine in IDE expanded documentation, and in API docs.
518
519value_enum_impl!(
520    /// Limit bits on a status code.
521    StatusCodeLimit,
522    ///Value is constant
523    Constant = 0b11,
524    ///Value is at high limit.
525    High = 0b10,
526    ///Value is at low limit.
527    Low = 0b01,
528    ///Value is not at a limit.
529    None = 0b00,
530);
531
532#[allow(clippy::derivable_impls)]
533impl Default for StatusCodeLimit {
534    fn default() -> Self {
535        Self::None
536    }
537}
538
539value_enum_impl!(
540    /// Severity of a status code.
541    StatusCodeSeverity,
542    ///Status code is good.
543    Good = 0b00,
544    ///Status code is uncertain.
545    Uncertain = 0b01,
546    ///Status code is bad.
547    Bad = 0b10,
548);
549
550#[allow(clippy::derivable_impls)]
551impl Default for StatusCodeSeverity {
552    fn default() -> Self {
553        Self::Good
554    }
555}
556
557value_enum_impl!(
558    /// The type of data value
559    StatusCodeValueType,
560    /// Value is raw
561    Raw = 0b00,
562    /// Value is calculated
563    Calculated = 0b01,
564    /// Value is interpolated
565    Interpolated = 0b10,
566    /// Undefined value type
567    Undefined = 0b11
568);
569
570value_enum_impl!(
571    /// Whether the status code represents a data value or something else.
572    StatusCodeInfoType,
573    /// Info bits are not used and shall be zero.
574    NotUsed = 0,
575    /// Status code is associated with a data value.
576    DataValue = 1,
577);
578
579/// This macro accepts the OPC-UA status codes CSV verbatim, and converts it to
580/// an enum, SubStatusCode, and constants under `StatusCode` for convenience.
581///
582/// Conveniently, this CSV is a valid rust token tree, though newlines cannot be significant in rust.
583macro_rules! sub_code_impl {
584    ($($code:ident,$val:literal,$doc:literal)*) => {
585        value_enum_impl!(
586            /// The category of a status code.
587            SubStatusCode,
588            $(#[doc = $doc] $code = $val),*
589        );
590        sub_code_impl!(_code_consts $($doc $code = $val),*);
591    };
592
593    (_code_consts $($comment:literal $code:ident = $val:literal),*) => {
594        #[allow(non_upper_case_globals)]
595        impl StatusCode {
596            $(#[doc = $comment] pub const $code: StatusCode = StatusCode($val);)*
597        }
598    };
599}
600
601sub_code_impl! {
602    Good,0x00000000,"The operation succeeded."
603    Uncertain,0x40000000,"The operation was uncertain."
604    Bad,0x80000000,"The operation failed."
605    BadUnexpectedError,0x80010000,"An unexpected error occurred."
606    BadInternalError,0x80020000,"An internal error occurred as a result of a programming or configuration error."
607    BadOutOfMemory,0x80030000,"Not enough memory to complete the operation."
608    BadResourceUnavailable,0x80040000,"An operating system resource is not available."
609    BadCommunicationError,0x80050000,"A low level communication error occurred."
610    BadEncodingError,0x80060000,"Encoding halted because of invalid data in the objects being serialized."
611    BadDecodingError,0x80070000,"Decoding halted because of invalid data in the stream."
612    BadEncodingLimitsExceeded,0x80080000,"The message encoding/decoding limits imposed by the stack have been exceeded."
613    BadRequestTooLarge,0x80B80000,"The request message size exceeds limits set by the server."
614    BadResponseTooLarge,0x80B90000,"The response message size exceeds limits set by the client or server."
615    BadUnknownResponse,0x80090000,"An unrecognized response was received from the server."
616    BadTimeout,0x800A0000,"The operation timed out."
617    BadServiceUnsupported,0x800B0000,"The server does not support the requested service."
618    BadShutdown,0x800C0000,"The operation was cancelled because the application is shutting down."
619    BadServerNotConnected,0x800D0000,"The operation could not complete because the client is not connected to the server."
620    BadServerHalted,0x800E0000,"The server has stopped and cannot process any requests."
621    BadNothingToDo,0x800F0000,"No processing could be done because there was nothing to do."
622    BadTooManyOperations,0x80100000,"The request could not be processed because it specified too many operations."
623    BadTooManyMonitoredItems,0x80DB0000,"The request could not be processed because there are too many monitored items in the subscription."
624    BadDataTypeIdUnknown,0x80110000,"The extension object cannot be (de)serialized because the data type id is not recognized."
625    BadCertificateInvalid,0x80120000,"The certificate provided as a parameter is not valid."
626    BadSecurityChecksFailed,0x80130000,"An error occurred verifying security."
627    BadCertificatePolicyCheckFailed,0x81140000,"The certificate does not meet the requirements of the security policy."
628    BadCertificateTimeInvalid,0x80140000,"The certificate has expired or is not yet valid."
629    BadCertificateIssuerTimeInvalid,0x80150000,"An issuer certificate has expired or is not yet valid."
630    BadCertificateHostNameInvalid,0x80160000,"The HostName used to connect to a server does not match a HostName in the certificate."
631    BadCertificateUriInvalid,0x80170000,"The URI specified in the ApplicationDescription does not match the URI in the certificate."
632    BadCertificateUseNotAllowed,0x80180000,"The certificate may not be used for the requested operation."
633    BadCertificateIssuerUseNotAllowed,0x80190000,"The issuer certificate may not be used for the requested operation."
634    BadCertificateUntrusted,0x801A0000,"The certificate is not trusted."
635    BadCertificateRevocationUnknown,0x801B0000,"It was not possible to determine if the certificate has been revoked."
636    BadCertificateIssuerRevocationUnknown,0x801C0000,"It was not possible to determine if the issuer certificate has been revoked."
637    BadCertificateRevoked,0x801D0000,"The certificate has been revoked."
638    BadCertificateIssuerRevoked,0x801E0000,"The issuer certificate has been revoked."
639    BadCertificateChainIncomplete,0x810D0000,"The certificate chain is incomplete."
640    BadUserAccessDenied,0x801F0000,"User does not have permission to perform the requested operation."
641    BadIdentityTokenInvalid,0x80200000,"The user identity token is not valid."
642    BadIdentityTokenRejected,0x80210000,"The user identity token is valid but the server has rejected it."
643    BadSecureChannelIdInvalid,0x80220000,"The specified secure channel is no longer valid."
644    BadInvalidTimestamp,0x80230000,"The timestamp is outside the range allowed by the server."
645    BadNonceInvalid,0x80240000,"The nonce does appear to be not a random value or it is not the correct length."
646    BadSessionIdInvalid,0x80250000,"The session id is not valid."
647    BadSessionClosed,0x80260000,"The session was closed by the client."
648    BadSessionNotActivated,0x80270000,"The session cannot be used because ActivateSession has not been called."
649    BadSubscriptionIdInvalid,0x80280000,"The subscription id is not valid."
650    BadRequestHeaderInvalid,0x802A0000,"The header for the request is missing or invalid."
651    BadTimestampsToReturnInvalid,0x802B0000,"The timestamps to return parameter is invalid."
652    BadRequestCancelledByClient,0x802C0000,"The request was cancelled by the client."
653    BadTooManyArguments,0x80E50000,"Too many arguments were provided."
654    BadLicenseExpired,0x810E0000,"The server requires a license to operate in general or to perform a service or operation, but existing license is expired."
655    BadLicenseLimitsExceeded,0x810F0000,"The server has limits on number of allowed operations / objects, based on installed licenses, and these limits where exceeded."
656    BadLicenseNotAvailable,0x81100000,"The server does not have a license which is required to operate in general or to perform a service or operation."
657    BadServerTooBusy,0x80EE0000,"The Server does not have the resources to process the request at this time."
658    GoodPasswordChangeRequired,0x00EF0000,"The log-on for the user succeeded but the user is required to change the password."
659    GoodSubscriptionTransferred,0x002D0000,"The subscription was transferred to another session."
660    GoodCompletesAsynchronously,0x002E0000,"The processing will complete asynchronously."
661    GoodOverload,0x002F0000,"Sampling has slowed down due to resource limitations."
662    GoodClamped,0x00300000,"The value written was accepted but was clamped."
663    BadNoCommunication,0x80310000,"Communication with the data source is defined, but not established, and there is no last known value available."
664    BadWaitingForInitialData,0x80320000,"Waiting for the server to obtain values from the underlying data source."
665    BadNodeIdInvalid,0x80330000,"The syntax the node id is not valid or refers to a node that is not valid for the operation."
666    BadNodeIdUnknown,0x80340000,"The node id refers to a node that does not exist in the server address space."
667    BadAttributeIdInvalid,0x80350000,"The attribute is not supported for the specified Node."
668    BadIndexRangeInvalid,0x80360000,"The syntax of the index range parameter is invalid."
669    BadIndexRangeNoData,0x80370000,"No data exists within the range of indexes specified."
670    BadIndexRangeDataMismatch,0x80EA0000,"The written data does not match the IndexRange specified."
671    BadDataEncodingInvalid,0x80380000,"The data encoding is invalid."
672    BadDataEncodingUnsupported,0x80390000,"The server does not support the requested data encoding for the node."
673    BadNotReadable,0x803A0000,"The access level does not allow reading or subscribing to the Node."
674    BadNotWritable,0x803B0000,"The access level does not allow writing to the Node."
675    BadOutOfRange,0x803C0000,"The value was out of range."
676    BadNotSupported,0x803D0000,"The requested operation is not supported."
677    BadNotFound,0x803E0000,"A requested item was not found or a search operation ended without success."
678    BadObjectDeleted,0x803F0000,"The object cannot be used because it has been deleted."
679    BadNotImplemented,0x80400000,"Requested operation is not implemented."
680    BadMonitoringModeInvalid,0x80410000,"The monitoring mode is invalid."
681    BadMonitoredItemIdInvalid,0x80420000,"The monitoring item id does not refer to a valid monitored item."
682    BadMonitoredItemFilterInvalid,0x80430000,"The monitored item filter parameter is not valid."
683    BadMonitoredItemFilterUnsupported,0x80440000,"The server does not support the requested monitored item filter."
684    BadFilterNotAllowed,0x80450000,"A monitoring filter cannot be used in combination with the attribute specified."
685    BadStructureMissing,0x80460000,"A mandatory structured parameter was missing or null."
686    BadEventFilterInvalid,0x80470000,"The event filter is not valid."
687    BadContentFilterInvalid,0x80480000,"The content filter is not valid."
688    BadFilterOperatorInvalid,0x80C10000,"An unrecognized operator was provided in a filter."
689    BadFilterOperatorUnsupported,0x80C20000,"A valid operator was provided, but the server does not provide support for this filter operator."
690    BadFilterOperandCountMismatch,0x80C30000,"The number of operands provided for the filter operator was less then expected for the operand provided."
691    BadFilterOperandInvalid,0x80490000,"The operand used in a content filter is not valid."
692    BadFilterElementInvalid,0x80C40000,"The referenced element is not a valid element in the content filter."
693    BadFilterLiteralInvalid,0x80C50000,"The referenced literal is not a valid value."
694    BadContinuationPointInvalid,0x804A0000,"The continuation point provide is longer valid."
695    BadNoContinuationPoints,0x804B0000,"The operation could not be processed because all continuation points have been allocated."
696    BadReferenceTypeIdInvalid,0x804C0000,"The reference type id does not refer to a valid reference type node."
697    BadBrowseDirectionInvalid,0x804D0000,"The browse direction is not valid."
698    BadNodeNotInView,0x804E0000,"The node is not part of the view."
699    BadNumericOverflow,0x81120000,"The number was not accepted because of a numeric overflow."
700    BadLocaleNotSupported,0x80ED0000,"The locale in the requested write operation is not supported."
701    BadNoValue,0x80F00000,"The variable has no default value and no initial value."
702    BadServerUriInvalid,0x804F0000,"The ServerUri is not a valid URI."
703    BadServerNameMissing,0x80500000,"No ServerName was specified."
704    BadDiscoveryUrlMissing,0x80510000,"No DiscoveryUrl was specified."
705    BadSempahoreFileMissing,0x80520000,"The semaphore file specified by the client is not valid."
706    BadRequestTypeInvalid,0x80530000,"The security token request type is not valid."
707    BadSecurityModeRejected,0x80540000,"The security mode does not meet the requirements set by the server."
708    BadSecurityPolicyRejected,0x80550000,"The security policy does not meet the requirements set by the server."
709    BadTooManySessions,0x80560000,"The server has reached its maximum number of sessions."
710    BadUserSignatureInvalid,0x80570000,"The user token signature is missing or invalid."
711    BadApplicationSignatureInvalid,0x80580000,"The signature generated with the client certificate is missing or invalid."
712    BadNoValidCertificates,0x80590000,"The client did not provide at least one software certificate that is valid and meets the profile requirements for the server."
713    BadIdentityChangeNotSupported,0x80C60000,"The server does not support changing the user identity assigned to the session."
714    BadRequestCancelledByRequest,0x805A0000,"The request was cancelled by the client with the Cancel service."
715    BadParentNodeIdInvalid,0x805B0000,"The parent node id does not to refer to a valid node."
716    BadReferenceNotAllowed,0x805C0000,"The reference could not be created because it violates constraints imposed by the data model."
717    BadNodeIdRejected,0x805D0000,"The requested node id was reject because it was either invalid or server does not allow node ids to be specified by the client."
718    BadNodeIdExists,0x805E0000,"The requested node id is already used by another node."
719    BadNodeClassInvalid,0x805F0000,"The node class is not valid."
720    BadBrowseNameInvalid,0x80600000,"The browse name is invalid."
721    BadBrowseNameDuplicated,0x80610000,"The browse name is not unique among nodes that share the same relationship with the parent."
722    BadNodeAttributesInvalid,0x80620000,"The node attributes are not valid for the node class."
723    BadTypeDefinitionInvalid,0x80630000,"The type definition node id does not reference an appropriate type node."
724    BadSourceNodeIdInvalid,0x80640000,"The source node id does not reference a valid node."
725    BadTargetNodeIdInvalid,0x80650000,"The target node id does not reference a valid node."
726    BadDuplicateReferenceNotAllowed,0x80660000,"The reference type between the nodes is already defined."
727    BadInvalidSelfReference,0x80670000,"The server does not allow this type of self reference on this node."
728    BadReferenceLocalOnly,0x80680000,"The reference type is not valid for a reference to a remote server."
729    BadNoDeleteRights,0x80690000,"The server will not allow the node to be deleted."
730    UncertainReferenceNotDeleted,0x40BC0000,"The server was not able to delete all target references."
731    BadServerIndexInvalid,0x806A0000,"The server index is not valid."
732    BadViewIdUnknown,0x806B0000,"The view id does not refer to a valid view node."
733    BadViewTimestampInvalid,0x80C90000,"The view timestamp is not available or not supported."
734    BadViewParameterMismatch,0x80CA0000,"The view parameters are not consistent with each other."
735    BadViewVersionInvalid,0x80CB0000,"The view version is not available or not supported."
736    UncertainNotAllNodesAvailable,0x40C00000,"The list of references may not be complete because the underlying system is not available."
737    GoodResultsMayBeIncomplete,0x00BA0000,"The server should have followed a reference to a node in a remote server but did not. The result set may be incomplete."
738    BadNotTypeDefinition,0x80C80000,"The provided Nodeid was not a type definition nodeid."
739    UncertainReferenceOutOfServer,0x406C0000,"One of the references to follow in the relative path references to a node in the address space in another server."
740    BadTooManyMatches,0x806D0000,"The requested operation has too many matches to return."
741    BadQueryTooComplex,0x806E0000,"The requested operation requires too many resources in the server."
742    BadNoMatch,0x806F0000,"The requested operation has no match to return."
743    BadMaxAgeInvalid,0x80700000,"The max age parameter is invalid."
744    BadSecurityModeInsufficient,0x80E60000,"The operation is not permitted over the current secure channel."
745    BadHistoryOperationInvalid,0x80710000,"The history details parameter is not valid."
746    BadHistoryOperationUnsupported,0x80720000,"The server does not support the requested operation."
747    BadInvalidTimestampArgument,0x80BD0000,"The defined timestamp to return was invalid."
748    BadWriteNotSupported,0x80730000,"The server does not support writing the combination of value, status and timestamps provided."
749    BadTypeMismatch,0x80740000,"The value supplied for the attribute is not of the same type as the attribute's value."
750    BadMethodInvalid,0x80750000,"The method id does not refer to a method for the specified object."
751    BadArgumentsMissing,0x80760000,"The client did not specify all of the input arguments for the method."
752    BadNotExecutable,0x81110000,"The executable attribute does not allow the execution of the method."
753    BadTooManySubscriptions,0x80770000,"The server has reached its maximum number of subscriptions."
754    BadTooManyPublishRequests,0x80780000,"The server has reached the maximum number of queued publish requests."
755    BadNoSubscription,0x80790000,"There is no subscription available for this session."
756    BadSequenceNumberUnknown,0x807A0000,"The sequence number is unknown to the server."
757    GoodRetransmissionQueueNotSupported,0x00DF0000,"The Server does not support retransmission queue and acknowledgement of sequence numbers is not available."
758    BadMessageNotAvailable,0x807B0000,"The requested notification message is no longer available."
759    BadInsufficientClientProfile,0x807C0000,"The client of the current session does not support one or more Profiles that are necessary for the subscription."
760    BadStateNotActive,0x80BF0000,"The sub-state machine is not currently active."
761    BadAlreadyExists,0x81150000,"An equivalent rule already exists."
762    BadTcpServerTooBusy,0x807D0000,"The server cannot process the request because it is too busy."
763    BadTcpMessageTypeInvalid,0x807E0000,"The type of the message specified in the header invalid."
764    BadTcpSecureChannelUnknown,0x807F0000,"The SecureChannelId and/or TokenId are not currently in use."
765    BadTcpMessageTooLarge,0x80800000,"The size of the message chunk specified in the header is too large."
766    BadTcpNotEnoughResources,0x80810000,"There are not enough resources to process the request."
767    BadTcpInternalError,0x80820000,"An internal error occurred."
768    BadTcpEndpointUrlInvalid,0x80830000,"The server does not recognize the QueryString specified."
769    BadRequestInterrupted,0x80840000,"The request could not be sent because of a network interruption."
770    BadRequestTimeout,0x80850000,"Timeout occurred while processing the request."
771    BadSecureChannelClosed,0x80860000,"The secure channel has been closed."
772    BadSecureChannelTokenUnknown,0x80870000,"The token has expired or is not recognized."
773    BadSequenceNumberInvalid,0x80880000,"The sequence number is not valid."
774    BadProtocolVersionUnsupported,0x80BE0000,"The applications do not have compatible protocol versions."
775    BadConfigurationError,0x80890000,"There is a problem with the configuration that affects the usefulness of the value."
776    BadNotConnected,0x808A0000,"The variable should receive its value from another variable, but has never been configured to do so."
777    BadDeviceFailure,0x808B0000,"There has been a failure in the device/data source that generates the value that has affected the value."
778    BadSensorFailure,0x808C0000,"There has been a failure in the sensor from which the value is derived by the device/data source."
779    BadOutOfService,0x808D0000,"The source of the data is not operational."
780    BadDeadbandFilterInvalid,0x808E0000,"The deadband filter is not valid."
781    UncertainNoCommunicationLastUsableValue,0x408F0000,"Communication to the data source has failed. The variable value is the last value that had a good quality."
782    UncertainLastUsableValue,0x40900000,"Whatever was updating this value has stopped doing so."
783    UncertainSubstituteValue,0x40910000,"The value is an operational value that was manually overwritten."
784    UncertainInitialValue,0x40920000,"The value is an initial value for a variable that normally receives its value from another variable."
785    UncertainSensorNotAccurate,0x40930000,"The value is at one of the sensor limits."
786    UncertainEngineeringUnitsExceeded,0x40940000,"The value is outside of the range of values defined for this parameter."
787    UncertainSubNormal,0x40950000,"The data value is derived from multiple sources and has less than the required number of Good sources."
788    GoodLocalOverride,0x00960000,"The value has been overridden."
789    GoodSubNormal,0x00EB0000,"The value is derived from multiple sources and has the required number of Good sources, but less than the full number of Good sources."
790    BadRefreshInProgress,0x80970000,"This Condition refresh failed, a Condition refresh operation is already in progress."
791    BadConditionAlreadyDisabled,0x80980000,"This condition has already been disabled."
792    BadConditionAlreadyEnabled,0x80CC0000,"This condition has already been enabled."
793    BadConditionDisabled,0x80990000,"Property not available, this condition is disabled."
794    BadEventIdUnknown,0x809A0000,"The specified event id is not recognized."
795    BadEventNotAcknowledgeable,0x80BB0000,"The event cannot be acknowledged."
796    BadDialogNotActive,0x80CD0000,"The dialog condition is not active."
797    BadDialogResponseInvalid,0x80CE0000,"The response is not valid for the dialog."
798    BadConditionBranchAlreadyAcked,0x80CF0000,"The condition branch has already been acknowledged."
799    BadConditionBranchAlreadyConfirmed,0x80D00000,"The condition branch has already been confirmed."
800    BadConditionAlreadyShelved,0x80D10000,"The condition has already been shelved."
801    BadConditionNotShelved,0x80D20000,"The condition is not currently shelved."
802    BadShelvingTimeOutOfRange,0x80D30000,"The shelving time not within an acceptable range."
803    BadNoData,0x809B0000,"No data exists for the requested time range or event filter."
804    BadBoundNotFound,0x80D70000,"No data found to provide upper or lower bound value."
805    BadBoundNotSupported,0x80D80000,"The server cannot retrieve a bound for the variable."
806    BadDataLost,0x809D0000,"Data is missing due to collection started/stopped/lost."
807    BadDataUnavailable,0x809E0000,"Expected data is unavailable for the requested time range due to an un-mounted volume, an off-line archive or tape, or similar reason for temporary unavailability."
808    BadEntryExists,0x809F0000,"The data or event was not successfully inserted because a matching entry exists."
809    BadNoEntryExists,0x80A00000,"The data or event was not successfully updated because no matching entry exists."
810    BadTimestampNotSupported,0x80A10000,"The Client requested history using a TimestampsToReturn the Server does not support."
811    GoodEntryInserted,0x00A20000,"The data or event was successfully inserted into the historical database."
812    GoodEntryReplaced,0x00A30000,"The data or event field was successfully replaced in the historical database."
813    UncertainDataSubNormal,0x40A40000,"The aggregate value is derived from multiple values and has less than the required number of Good values."
814    GoodNoData,0x00A50000,"No data exists for the requested time range or event filter."
815    GoodMoreData,0x00A60000,"More data is available in the time range beyond the number of values requested."
816    BadAggregateListMismatch,0x80D40000,"The requested number of Aggregates does not match the requested number of NodeIds."
817    BadAggregateNotSupported,0x80D50000,"The requested Aggregate is not support by the server."
818    BadAggregateInvalidInputs,0x80D60000,"The aggregate value could not be derived due to invalid data inputs."
819    BadAggregateConfigurationRejected,0x80DA0000,"The aggregate configuration is not valid for specified node."
820    GoodDataIgnored,0x00D90000,"The request specifies fields which are not valid for the EventType or cannot be saved by the historian."
821    BadRequestNotAllowed,0x80E40000,"The request was rejected by the server because it did not meet the criteria set by the server."
822    BadRequestNotComplete,0x81130000,"The request has not been processed by the server yet."
823    BadTransactionPending,0x80E80000,"The operation is not allowed because a transaction is in progress."
824    BadTicketRequired,0x811F0000,"The device identity needs a ticket before it can be accepted."
825    BadTicketInvalid,0x81200000,"The device identity needs a ticket before it can be accepted."
826    BadLocked,0x80E90000,"The requested operation is not allowed, because the Node is locked by a different application."
827    BadRequiresLock,0x80EC0000,"The requested operation is not allowed, because the Node is not locked by the application."
828    GoodEdited,0x00DC0000,"The value does not come from the real source and has been edited by the server."
829    GoodPostActionFailed,0x00DD0000,"There was an error in execution of these post-actions."
830    UncertainDominantValueChanged,0x40DE0000,"The related EngineeringUnit has been changed but the Variable Value is still provided based on the previous unit."
831    GoodDependentValueChanged,0x00E00000,"A dependent value has been changed but the change has not been applied to the device."
832    BadDominantValueChanged,0x80E10000,"The related EngineeringUnit has been changed but this change has not been applied to the device. The Variable Value is still dependent on the previous unit but its status is currently Bad."
833    UncertainDependentValueChanged,0x40E20000,"A dependent value has been changed but the change has not been applied to the device. The quality of the dominant variable is uncertain."
834    BadDependentValueChanged,0x80E30000,"A dependent value has been changed but the change has not been applied to the device. The quality of the dominant variable is Bad."
835    GoodEdited_DependentValueChanged,0x01160000,"It is delivered with a dominant Variable value when a dependent Variable has changed but the change has not been applied."
836    GoodEdited_DominantValueChanged,0x01170000,"It is delivered with a dependent Variable value when a dominant Variable has changed but the change has not been applied."
837    GoodEdited_DominantValueChanged_DependentValueChanged,0x01180000,"It is delivered with a dependent Variable value when a dominant or dependent Variable has changed but change has not been applied."
838    BadEdited_OutOfRange,0x81190000,"It is delivered with a Variable value when Variable has changed but the value is not legal."
839    BadInitialValue_OutOfRange,0x811A0000,"It is delivered with a Variable value when a source Variable has changed but the value is not legal."
840    BadOutOfRange_DominantValueChanged,0x811B0000,"It is delivered with a dependent Variable value when a dominant Variable has changed and the value is not legal."
841    BadEdited_OutOfRange_DominantValueChanged,0x811C0000,"It is delivered with a dependent Variable value when a dominant Variable has changed, the value is not legal and the change has not been applied."
842    BadOutOfRange_DominantValueChanged_DependentValueChanged,0x811D0000,"It is delivered with a dependent Variable value when a dominant or dependent Variable has changed and the value is not legal."
843    BadEdited_OutOfRange_DominantValueChanged_DependentValueChanged,0x811E0000,"It is delivered with a dependent Variable value when a dominant or dependent Variable has changed, the value is not legal and the change has not been applied."
844    GoodCommunicationEvent,0x00A70000,"The communication layer has raised an event."
845    GoodShutdownEvent,0x00A80000,"The system is shutting down."
846    GoodCallAgain,0x00A90000,"The operation is not finished and needs to be called again."
847    GoodNonCriticalTimeout,0x00AA0000,"A non-critical timeout occurred."
848    BadInvalidArgument,0x80AB0000,"One or more arguments are invalid."
849    BadConnectionRejected,0x80AC0000,"Could not establish a network connection to remote server."
850    BadDisconnect,0x80AD0000,"The server has disconnected from the client."
851    BadConnectionClosed,0x80AE0000,"The network connection has been closed."
852    BadInvalidState,0x80AF0000,"The operation cannot be completed because the object is closed, uninitialized or in some other invalid state."
853    BadEndOfStream,0x80B00000,"Cannot move beyond end of the stream."
854    BadNoDataAvailable,0x80B10000,"No data is currently available for reading from a non-blocking stream."
855    BadWaitingForResponse,0x80B20000,"The asynchronous operation is waiting for a response."
856    BadOperationAbandoned,0x80B30000,"The asynchronous operation was abandoned by the caller."
857    BadExpectedStreamToBlock,0x80B40000,"The stream did not return all data requested (possibly because it is a non-blocking stream)."
858    BadWouldBlock,0x80B50000,"Non blocking behaviour is required and the operation would block."
859    BadSyntaxError,0x80B60000,"A value had an invalid syntax."
860    BadMaxConnectionsReached,0x80B70000,"The operation could not be finished because all available connections are in use."
861    UncertainTransducerInManual,0x42080000,"The value may not be accurate because the transducer is in manual mode."
862    UncertainSimulatedValue,0x42090000,"The value is simulated."
863    UncertainSensorCalibration,0x420A0000,"The value may not be accurate due to a sensor calibration fault."
864    UncertainConfigurationError,0x420F0000,"The value may not be accurate due to a configuration issue."
865    GoodCascadeInitializationAcknowledged,0x04010000,"The value source supports cascade handshaking and the value has been Initialized based on an initialization request from a cascade secondary."
866    GoodCascadeInitializationRequest,0x04020000,"The value source supports cascade handshaking and is requesting initialization of a cascade primary."
867    GoodCascadeNotInvited,0x04030000,"The value source supports cascade handshaking, however, the source’s current state does not allow for cascade."
868    GoodCascadeNotSelected,0x04040000,"The value source supports cascade handshaking, however, the source has not selected the corresponding cascade primary for use."
869    GoodFaultStateActive,0x04070000,"There is a fault state condition active in the value source."
870    GoodInitiateFaultState,0x04080000,"A fault state condition is being requested of the destination."
871    GoodCascade,0x04090000,"The value is accurate, and the signal source supports cascade handshaking."
872    BadDataSetIdInvalid,0x80E70000,"The DataSet specified for the DataSetWriter creation is invalid."
873
874    Invalid,0xFFFFFFFF,"Invalid status code"
875}
876// Note that the invalid status code is impossible to get normally.
877
878#[cfg(test)]
879mod tests {
880    use crate::time_series::ParseStatusCodeError;
881
882    use super::{
883        StatusCode, StatusCodeInfoType, StatusCodeLimit, StatusCodeSeverity,
884        StatusCodeValidationError, StatusCodeValueType, SubStatusCode,
885    };
886
887    #[test]
888    fn test_from_sub_code() {
889        assert_eq!("Good", StatusCode::Good.to_string());
890        assert_eq!(
891            "BadBrowseDirectionInvalid",
892            StatusCode::BadBrowseDirectionInvalid.to_string()
893        );
894        assert_eq!(
895            "UncertainDependentValueChanged",
896            StatusCode::UncertainDependentValueChanged.to_string()
897        );
898    }
899
900    #[test]
901    fn test_modify() {
902        let code = StatusCode::from(0);
903        assert_eq!(code, StatusCode::Good);
904        let code = code.set_severity(StatusCodeSeverity::Uncertain);
905        assert_eq!(code.severity(), StatusCodeSeverity::Uncertain);
906        let code = code.set_severity(StatusCodeSeverity::Bad);
907        assert_eq!(code.severity(), StatusCodeSeverity::Bad);
908
909        code.validate().unwrap();
910
911        assert!(!code.structure_changed());
912        let code = code.set_structure_changed(true);
913        code.validate().unwrap();
914        assert!(code.structure_changed());
915        let code = code.set_structure_changed(false);
916        assert!(!code.structure_changed());
917        let code = code.set_structure_changed(true);
918
919        assert!(!code.semantics_changed());
920        let code = code.set_semantics_changed(true);
921        code.validate().unwrap();
922        assert!(code.semantics_changed());
923        let code = code.set_semantics_changed(false);
924        assert!(!code.semantics_changed());
925        let code = code.set_semantics_changed(true);
926
927        assert_eq!(code.sub_code(), SubStatusCode::Bad);
928        let code = code.set_sub_code(SubStatusCode::BadAggregateConfigurationRejected);
929        assert_eq!(
930            code.sub_code(),
931            SubStatusCode::BadAggregateConfigurationRejected
932        );
933        let code = code.set_sub_code(SubStatusCode::UncertainNotAllNodesAvailable);
934        assert_eq!(
935            code.sub_code(),
936            SubStatusCode::UncertainNotAllNodesAvailable
937        );
938
939        assert_eq!(code.info_type(), StatusCodeInfoType::NotUsed);
940        let code = code.set_info_type(StatusCodeInfoType::DataValue);
941        assert_eq!(code.info_type(), StatusCodeInfoType::DataValue);
942        code.validate().unwrap();
943        let code = code.set_info_type(StatusCodeInfoType::NotUsed);
944        assert_eq!(code.info_type(), StatusCodeInfoType::NotUsed);
945
946        assert_eq!(code.limit(), StatusCodeLimit::None);
947        let code = code.set_limit(StatusCodeLimit::High);
948        assert_eq!(code.limit(), StatusCodeLimit::High);
949        let code = code.set_limit(StatusCodeLimit::Constant);
950        assert_eq!(code.limit(), StatusCodeLimit::Constant);
951
952        assert!(matches!(
953            code.validate(),
954            Err(StatusCodeValidationError::InvalidInfoBits)
955        ));
956        let code = code.set_info_type(StatusCodeInfoType::DataValue);
957        code.validate().unwrap();
958
959        assert!(!code.overflow());
960        let code = code.set_overflow(true);
961        code.validate().unwrap();
962        assert!(code.overflow());
963        let code = code.set_overflow(false);
964        assert!(!code.overflow());
965        let code = code.set_overflow(true);
966
967        assert!(!code.multi_value());
968        let code = code.set_multi_value(true);
969        code.validate().unwrap();
970        assert!(code.multi_value());
971        let code = code.set_multi_value(false);
972        assert!(!code.multi_value());
973        let code = code.set_multi_value(true);
974
975        assert!(!code.extra_data());
976        let code = code.set_extra_data(true);
977        code.validate().unwrap();
978        assert!(code.extra_data());
979        let code = code.set_extra_data(false);
980        assert!(!code.extra_data());
981        let code = code.set_extra_data(true);
982
983        assert!(!code.partial());
984        let code = code.set_partial(true);
985        code.validate().unwrap();
986        assert!(code.partial());
987        let code = code.set_partial(false);
988        assert!(!code.partial());
989        let code = code.set_partial(true);
990
991        assert_eq!(code.value_type(), StatusCodeValueType::Raw);
992        let code = code.set_value_type(StatusCodeValueType::Calculated);
993        assert_eq!(code.value_type(), StatusCodeValueType::Calculated);
994        let code = code.set_value_type(StatusCodeValueType::Interpolated);
995        assert_eq!(code.value_type(), StatusCodeValueType::Interpolated);
996
997        assert_eq!(StatusCodeSeverity::Uncertain, code.severity());
998        assert!(code.structure_changed());
999        assert!(code.semantics_changed());
1000        assert_eq!(code.info_type(), StatusCodeInfoType::DataValue);
1001        assert_eq!(
1002            code.sub_code(),
1003            SubStatusCode::UncertainNotAllNodesAvailable
1004        );
1005        assert!(code.overflow());
1006        assert!(code.multi_value());
1007        assert!(code.extra_data());
1008        assert!(code.partial());
1009        assert_eq!(code.value_type(), StatusCodeValueType::Interpolated);
1010
1011        code.validate().unwrap();
1012    }
1013
1014    #[test]
1015    fn test_parse() {
1016        assert_eq!(
1017            "Good, StructureChanged, Calculated",
1018            StatusCode::try_parse("Good, StructureChanged, Calculated")
1019                .unwrap()
1020                .to_string()
1021        );
1022        assert_eq!(
1023            "UncertainSensorCalibration, Overflow, ExtraData",
1024            StatusCode::try_parse("Uncertain_SensorCalibration, Overflow, ExtraData")
1025                .unwrap()
1026                .to_string()
1027        );
1028        assert_eq!(
1029            "Good, Low, Calculated",
1030            StatusCode::try_parse("Good, Low, Calculated")
1031                .unwrap()
1032                .to_string()
1033        );
1034
1035        assert!(matches!(
1036            StatusCode::try_parse("Bad_SomethingWrong").unwrap_err(),
1037            ParseStatusCodeError::UnknownSubStatusCode(_)
1038        ));
1039
1040        assert!(matches!(
1041            StatusCode::try_parse("Bad, Low, High").unwrap_err(),
1042            ParseStatusCodeError::TooManyLimits
1043        ));
1044
1045        assert!(matches!(
1046            StatusCode::try_parse("Good, Calculated, Interpolated").unwrap_err(),
1047            ParseStatusCodeError::Invalid(StatusCodeValidationError::UndefinedValueType)
1048        ));
1049
1050        assert!(matches!(
1051            StatusCode::try_parse("Good, Something, Foo").unwrap_err(),
1052            ParseStatusCodeError::UnsupportedFlag(_)
1053        ));
1054    }
1055}