opcua_types/
status_code.rs

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