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
484#[allow(clippy::derivable_impls)]
485impl Default for StatusCodeLimit {
486    fn default() -> Self {
487        Self::None
488    }
489}
490
491value_enum_impl!(
492    /// Severity of a status code.
493    StatusCodeSeverity,
494    ///Status code is good.
495    Good = 0b00,
496    ///Status code is uncertain.
497    Uncertain = 0b01,
498    ///Status code is bad.
499    Bad = 0b10,
500);
501
502#[allow(clippy::derivable_impls)]
503impl Default for StatusCodeSeverity {
504    fn default() -> Self {
505        Self::Good
506    }
507}
508
509value_enum_impl!(
510    /// The type of data value
511    StatusCodeValueType,
512    /// Value is raw
513    Raw = 0b00,
514    /// Value is calculated
515    Calculated = 0b01,
516    /// Value is interpolated
517    Interpolated = 0b10,
518    /// Undefined value type
519    Undefined = 0b11
520);
521
522value_enum_impl!(
523    /// Whether the status code represents a data value or something else.
524    StatusCodeInfoType,
525    /// Info bits are not used and shall be zero.
526    NotUsed = 0,
527    /// Status code is associated with a data value.
528    DataValue = 1,
529);
530
531/// This macro accepts the OPC-UA status codes CSV verbatim, and converts it to
532/// an enum, SubStatusCode, and constants under `StatusCode` for convenience.
533///
534/// Conveniently, this CSV is a valid rust token tree, though newlines cannot be significant in rust.
535macro_rules! sub_code_impl {
536    ($($code:ident,$val:literal,$doc:literal)*) => {
537        value_enum_impl!(
538            /// The category of a status code.
539            SubStatusCode,
540            $(#[doc = $doc] $code = $val),*
541        );
542        sub_code_impl!(_code_consts $($doc $code = $val),*);
543    };
544
545    (_code_consts $($comment:literal $code:ident = $val:literal),*) => {
546        #[allow(non_upper_case_globals)]
547        impl StatusCode {
548            $(#[doc = $comment] pub const $code: StatusCode = StatusCode($val);)*
549        }
550    };
551}
552
553sub_code_impl! {
554    Good,0x00000000,"The operation succeeded."
555    Uncertain,0x40000000,"The operation was uncertain."
556    Bad,0x80000000,"The operation failed."
557    BadUnexpectedError,0x80010000,"An unexpected error occurred."
558    BadInternalError,0x80020000,"An internal error occurred as a result of a programming or configuration error."
559    BadOutOfMemory,0x80030000,"Not enough memory to complete the operation."
560    BadResourceUnavailable,0x80040000,"An operating system resource is not available."
561    BadCommunicationError,0x80050000,"A low level communication error occurred."
562    BadEncodingError,0x80060000,"Encoding halted because of invalid data in the objects being serialized."
563    BadDecodingError,0x80070000,"Decoding halted because of invalid data in the stream."
564    BadEncodingLimitsExceeded,0x80080000,"The message encoding/decoding limits imposed by the stack have been exceeded."
565    BadRequestTooLarge,0x80B80000,"The request message size exceeds limits set by the server."
566    BadResponseTooLarge,0x80B90000,"The response message size exceeds limits set by the client or server."
567    BadUnknownResponse,0x80090000,"An unrecognized response was received from the server."
568    BadTimeout,0x800A0000,"The operation timed out."
569    BadServiceUnsupported,0x800B0000,"The server does not support the requested service."
570    BadShutdown,0x800C0000,"The operation was cancelled because the application is shutting down."
571    BadServerNotConnected,0x800D0000,"The operation could not complete because the client is not connected to the server."
572    BadServerHalted,0x800E0000,"The server has stopped and cannot process any requests."
573    BadNothingToDo,0x800F0000,"No processing could be done because there was nothing to do."
574    BadTooManyOperations,0x80100000,"The request could not be processed because it specified too many operations."
575    BadTooManyMonitoredItems,0x80DB0000,"The request could not be processed because there are too many monitored items in the subscription."
576    BadDataTypeIdUnknown,0x80110000,"The extension object cannot be (de)serialized because the data type id is not recognized."
577    BadCertificateInvalid,0x80120000,"The certificate provided as a parameter is not valid."
578    BadSecurityChecksFailed,0x80130000,"An error occurred verifying security."
579    BadCertificatePolicyCheckFailed,0x81140000,"The certificate does not meet the requirements of the security policy."
580    BadCertificateTimeInvalid,0x80140000,"The certificate has expired or is not yet valid."
581    BadCertificateIssuerTimeInvalid,0x80150000,"An issuer certificate has expired or is not yet valid."
582    BadCertificateHostNameInvalid,0x80160000,"The HostName used to connect to a server does not match a HostName in the certificate."
583    BadCertificateUriInvalid,0x80170000,"The URI specified in the ApplicationDescription does not match the URI in the certificate."
584    BadCertificateUseNotAllowed,0x80180000,"The certificate may not be used for the requested operation."
585    BadCertificateIssuerUseNotAllowed,0x80190000,"The issuer certificate may not be used for the requested operation."
586    BadCertificateUntrusted,0x801A0000,"The certificate is not trusted."
587    BadCertificateRevocationUnknown,0x801B0000,"It was not possible to determine if the certificate has been revoked."
588    BadCertificateIssuerRevocationUnknown,0x801C0000,"It was not possible to determine if the issuer certificate has been revoked."
589    BadCertificateRevoked,0x801D0000,"The certificate has been revoked."
590    BadCertificateIssuerRevoked,0x801E0000,"The issuer certificate has been revoked."
591    BadCertificateChainIncomplete,0x810D0000,"The certificate chain is incomplete."
592    BadUserAccessDenied,0x801F0000,"User does not have permission to perform the requested operation."
593    BadIdentityTokenInvalid,0x80200000,"The user identity token is not valid."
594    BadIdentityTokenRejected,0x80210000,"The user identity token is valid but the server has rejected it."
595    BadSecureChannelIdInvalid,0x80220000,"The specified secure channel is no longer valid."
596    BadInvalidTimestamp,0x80230000,"The timestamp is outside the range allowed by the server."
597    BadNonceInvalid,0x80240000,"The nonce does appear to be not a random value or it is not the correct length."
598    BadSessionIdInvalid,0x80250000,"The session id is not valid."
599    BadSessionClosed,0x80260000,"The session was closed by the client."
600    BadSessionNotActivated,0x80270000,"The session cannot be used because ActivateSession has not been called."
601    BadSubscriptionIdInvalid,0x80280000,"The subscription id is not valid."
602    BadRequestHeaderInvalid,0x802A0000,"The header for the request is missing or invalid."
603    BadTimestampsToReturnInvalid,0x802B0000,"The timestamps to return parameter is invalid."
604    BadRequestCancelledByClient,0x802C0000,"The request was cancelled by the client."
605    BadTooManyArguments,0x80E50000,"Too many arguments were provided."
606    BadLicenseExpired,0x810E0000,"The server requires a license to operate in general or to perform a service or operation, but existing license is expired."
607    BadLicenseLimitsExceeded,0x810F0000,"The server has limits on number of allowed operations / objects, based on installed licenses, and these limits where exceeded."
608    BadLicenseNotAvailable,0x81100000,"The server does not have a license which is required to operate in general or to perform a service or operation."
609    BadServerTooBusy,0x80EE0000,"The Server does not have the resources to process the request at this time."
610    GoodPasswordChangeRequired,0x00EF0000,"The log-on for the user succeeded but the user is required to change the password."
611    GoodSubscriptionTransferred,0x002D0000,"The subscription was transferred to another session."
612    GoodCompletesAsynchronously,0x002E0000,"The processing will complete asynchronously."
613    GoodOverload,0x002F0000,"Sampling has slowed down due to resource limitations."
614    GoodClamped,0x00300000,"The value written was accepted but was clamped."
615    BadNoCommunication,0x80310000,"Communication with the data source is defined, but not established, and there is no last known value available."
616    BadWaitingForInitialData,0x80320000,"Waiting for the server to obtain values from the underlying data source."
617    BadNodeIdInvalid,0x80330000,"The syntax the node id is not valid or refers to a node that is not valid for the operation."
618    BadNodeIdUnknown,0x80340000,"The node id refers to a node that does not exist in the server address space."
619    BadAttributeIdInvalid,0x80350000,"The attribute is not supported for the specified Node."
620    BadIndexRangeInvalid,0x80360000,"The syntax of the index range parameter is invalid."
621    BadIndexRangeNoData,0x80370000,"No data exists within the range of indexes specified."
622    BadIndexRangeDataMismatch,0x80EA0000,"The written data does not match the IndexRange specified."
623    BadDataEncodingInvalid,0x80380000,"The data encoding is invalid."
624    BadDataEncodingUnsupported,0x80390000,"The server does not support the requested data encoding for the node."
625    BadNotReadable,0x803A0000,"The access level does not allow reading or subscribing to the Node."
626    BadNotWritable,0x803B0000,"The access level does not allow writing to the Node."
627    BadOutOfRange,0x803C0000,"The value was out of range."
628    BadNotSupported,0x803D0000,"The requested operation is not supported."
629    BadNotFound,0x803E0000,"A requested item was not found or a search operation ended without success."
630    BadObjectDeleted,0x803F0000,"The object cannot be used because it has been deleted."
631    BadNotImplemented,0x80400000,"Requested operation is not implemented."
632    BadMonitoringModeInvalid,0x80410000,"The monitoring mode is invalid."
633    BadMonitoredItemIdInvalid,0x80420000,"The monitoring item id does not refer to a valid monitored item."
634    BadMonitoredItemFilterInvalid,0x80430000,"The monitored item filter parameter is not valid."
635    BadMonitoredItemFilterUnsupported,0x80440000,"The server does not support the requested monitored item filter."
636    BadFilterNotAllowed,0x80450000,"A monitoring filter cannot be used in combination with the attribute specified."
637    BadStructureMissing,0x80460000,"A mandatory structured parameter was missing or null."
638    BadEventFilterInvalid,0x80470000,"The event filter is not valid."
639    BadContentFilterInvalid,0x80480000,"The content filter is not valid."
640    BadFilterOperatorInvalid,0x80C10000,"An unrecognized operator was provided in a filter."
641    BadFilterOperatorUnsupported,0x80C20000,"A valid operator was provided, but the server does not provide support for this filter operator."
642    BadFilterOperandCountMismatch,0x80C30000,"The number of operands provided for the filter operator was less then expected for the operand provided."
643    BadFilterOperandInvalid,0x80490000,"The operand used in a content filter is not valid."
644    BadFilterElementInvalid,0x80C40000,"The referenced element is not a valid element in the content filter."
645    BadFilterLiteralInvalid,0x80C50000,"The referenced literal is not a valid value."
646    BadContinuationPointInvalid,0x804A0000,"The continuation point provide is longer valid."
647    BadNoContinuationPoints,0x804B0000,"The operation could not be processed because all continuation points have been allocated."
648    BadReferenceTypeIdInvalid,0x804C0000,"The reference type id does not refer to a valid reference type node."
649    BadBrowseDirectionInvalid,0x804D0000,"The browse direction is not valid."
650    BadNodeNotInView,0x804E0000,"The node is not part of the view."
651    BadNumericOverflow,0x81120000,"The number was not accepted because of a numeric overflow."
652    BadLocaleNotSupported,0x80ED0000,"The locale in the requested write operation is not supported."
653    BadNoValue,0x80F00000,"The variable has no default value and no initial value."
654    BadServerUriInvalid,0x804F0000,"The ServerUri is not a valid URI."
655    BadServerNameMissing,0x80500000,"No ServerName was specified."
656    BadDiscoveryUrlMissing,0x80510000,"No DiscoveryUrl was specified."
657    BadSempahoreFileMissing,0x80520000,"The semaphore file specified by the client is not valid."
658    BadRequestTypeInvalid,0x80530000,"The security token request type is not valid."
659    BadSecurityModeRejected,0x80540000,"The security mode does not meet the requirements set by the server."
660    BadSecurityPolicyRejected,0x80550000,"The security policy does not meet the requirements set by the server."
661    BadTooManySessions,0x80560000,"The server has reached its maximum number of sessions."
662    BadUserSignatureInvalid,0x80570000,"The user token signature is missing or invalid."
663    BadApplicationSignatureInvalid,0x80580000,"The signature generated with the client certificate is missing or invalid."
664    BadNoValidCertificates,0x80590000,"The client did not provide at least one software certificate that is valid and meets the profile requirements for the server."
665    BadIdentityChangeNotSupported,0x80C60000,"The server does not support changing the user identity assigned to the session."
666    BadRequestCancelledByRequest,0x805A0000,"The request was cancelled by the client with the Cancel service."
667    BadParentNodeIdInvalid,0x805B0000,"The parent node id does not to refer to a valid node."
668    BadReferenceNotAllowed,0x805C0000,"The reference could not be created because it violates constraints imposed by the data model."
669    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."
670    BadNodeIdExists,0x805E0000,"The requested node id is already used by another node."
671    BadNodeClassInvalid,0x805F0000,"The node class is not valid."
672    BadBrowseNameInvalid,0x80600000,"The browse name is invalid."
673    BadBrowseNameDuplicated,0x80610000,"The browse name is not unique among nodes that share the same relationship with the parent."
674    BadNodeAttributesInvalid,0x80620000,"The node attributes are not valid for the node class."
675    BadTypeDefinitionInvalid,0x80630000,"The type definition node id does not reference an appropriate type node."
676    BadSourceNodeIdInvalid,0x80640000,"The source node id does not reference a valid node."
677    BadTargetNodeIdInvalid,0x80650000,"The target node id does not reference a valid node."
678    BadDuplicateReferenceNotAllowed,0x80660000,"The reference type between the nodes is already defined."
679    BadInvalidSelfReference,0x80670000,"The server does not allow this type of self reference on this node."
680    BadReferenceLocalOnly,0x80680000,"The reference type is not valid for a reference to a remote server."
681    BadNoDeleteRights,0x80690000,"The server will not allow the node to be deleted."
682    UncertainReferenceNotDeleted,0x40BC0000,"The server was not able to delete all target references."
683    BadServerIndexInvalid,0x806A0000,"The server index is not valid."
684    BadViewIdUnknown,0x806B0000,"The view id does not refer to a valid view node."
685    BadViewTimestampInvalid,0x80C90000,"The view timestamp is not available or not supported."
686    BadViewParameterMismatch,0x80CA0000,"The view parameters are not consistent with each other."
687    BadViewVersionInvalid,0x80CB0000,"The view version is not available or not supported."
688    UncertainNotAllNodesAvailable,0x40C00000,"The list of references may not be complete because the underlying system is not available."
689    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."
690    BadNotTypeDefinition,0x80C80000,"The provided Nodeid was not a type definition nodeid."
691    UncertainReferenceOutOfServer,0x406C0000,"One of the references to follow in the relative path references to a node in the address space in another server."
692    BadTooManyMatches,0x806D0000,"The requested operation has too many matches to return."
693    BadQueryTooComplex,0x806E0000,"The requested operation requires too many resources in the server."
694    BadNoMatch,0x806F0000,"The requested operation has no match to return."
695    BadMaxAgeInvalid,0x80700000,"The max age parameter is invalid."
696    BadSecurityModeInsufficient,0x80E60000,"The operation is not permitted over the current secure channel."
697    BadHistoryOperationInvalid,0x80710000,"The history details parameter is not valid."
698    BadHistoryOperationUnsupported,0x80720000,"The server does not support the requested operation."
699    BadInvalidTimestampArgument,0x80BD0000,"The defined timestamp to return was invalid."
700    BadWriteNotSupported,0x80730000,"The server does not support writing the combination of value, status and timestamps provided."
701    BadTypeMismatch,0x80740000,"The value supplied for the attribute is not of the same type as the attribute's value."
702    BadMethodInvalid,0x80750000,"The method id does not refer to a method for the specified object."
703    BadArgumentsMissing,0x80760000,"The client did not specify all of the input arguments for the method."
704    BadNotExecutable,0x81110000,"The executable attribute does not allow the execution of the method."
705    BadTooManySubscriptions,0x80770000,"The server has reached its maximum number of subscriptions."
706    BadTooManyPublishRequests,0x80780000,"The server has reached the maximum number of queued publish requests."
707    BadNoSubscription,0x80790000,"There is no subscription available for this session."
708    BadSequenceNumberUnknown,0x807A0000,"The sequence number is unknown to the server."
709    GoodRetransmissionQueueNotSupported,0x00DF0000,"The Server does not support retransmission queue and acknowledgement of sequence numbers is not available."
710    BadMessageNotAvailable,0x807B0000,"The requested notification message is no longer available."
711    BadInsufficientClientProfile,0x807C0000,"The client of the current session does not support one or more Profiles that are necessary for the subscription."
712    BadStateNotActive,0x80BF0000,"The sub-state machine is not currently active."
713    BadAlreadyExists,0x81150000,"An equivalent rule already exists."
714    BadTcpServerTooBusy,0x807D0000,"The server cannot process the request because it is too busy."
715    BadTcpMessageTypeInvalid,0x807E0000,"The type of the message specified in the header invalid."
716    BadTcpSecureChannelUnknown,0x807F0000,"The SecureChannelId and/or TokenId are not currently in use."
717    BadTcpMessageTooLarge,0x80800000,"The size of the message chunk specified in the header is too large."
718    BadTcpNotEnoughResources,0x80810000,"There are not enough resources to process the request."
719    BadTcpInternalError,0x80820000,"An internal error occurred."
720    BadTcpEndpointUrlInvalid,0x80830000,"The server does not recognize the QueryString specified."
721    BadRequestInterrupted,0x80840000,"The request could not be sent because of a network interruption."
722    BadRequestTimeout,0x80850000,"Timeout occurred while processing the request."
723    BadSecureChannelClosed,0x80860000,"The secure channel has been closed."
724    BadSecureChannelTokenUnknown,0x80870000,"The token has expired or is not recognized."
725    BadSequenceNumberInvalid,0x80880000,"The sequence number is not valid."
726    BadProtocolVersionUnsupported,0x80BE0000,"The applications do not have compatible protocol versions."
727    BadConfigurationError,0x80890000,"There is a problem with the configuration that affects the usefulness of the value."
728    BadNotConnected,0x808A0000,"The variable should receive its value from another variable, but has never been configured to do so."
729    BadDeviceFailure,0x808B0000,"There has been a failure in the device/data source that generates the value that has affected the value."
730    BadSensorFailure,0x808C0000,"There has been a failure in the sensor from which the value is derived by the device/data source."
731    BadOutOfService,0x808D0000,"The source of the data is not operational."
732    BadDeadbandFilterInvalid,0x808E0000,"The deadband filter is not valid."
733    UncertainNoCommunicationLastUsableValue,0x408F0000,"Communication to the data source has failed. The variable value is the last value that had a good quality."
734    UncertainLastUsableValue,0x40900000,"Whatever was updating this value has stopped doing so."
735    UncertainSubstituteValue,0x40910000,"The value is an operational value that was manually overwritten."
736    UncertainInitialValue,0x40920000,"The value is an initial value for a variable that normally receives its value from another variable."
737    UncertainSensorNotAccurate,0x40930000,"The value is at one of the sensor limits."
738    UncertainEngineeringUnitsExceeded,0x40940000,"The value is outside of the range of values defined for this parameter."
739    UncertainSubNormal,0x40950000,"The data value is derived from multiple sources and has less than the required number of Good sources."
740    GoodLocalOverride,0x00960000,"The value has been overridden."
741    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."
742    BadRefreshInProgress,0x80970000,"This Condition refresh failed, a Condition refresh operation is already in progress."
743    BadConditionAlreadyDisabled,0x80980000,"This condition has already been disabled."
744    BadConditionAlreadyEnabled,0x80CC0000,"This condition has already been enabled."
745    BadConditionDisabled,0x80990000,"Property not available, this condition is disabled."
746    BadEventIdUnknown,0x809A0000,"The specified event id is not recognized."
747    BadEventNotAcknowledgeable,0x80BB0000,"The event cannot be acknowledged."
748    BadDialogNotActive,0x80CD0000,"The dialog condition is not active."
749    BadDialogResponseInvalid,0x80CE0000,"The response is not valid for the dialog."
750    BadConditionBranchAlreadyAcked,0x80CF0000,"The condition branch has already been acknowledged."
751    BadConditionBranchAlreadyConfirmed,0x80D00000,"The condition branch has already been confirmed."
752    BadConditionAlreadyShelved,0x80D10000,"The condition has already been shelved."
753    BadConditionNotShelved,0x80D20000,"The condition is not currently shelved."
754    BadShelvingTimeOutOfRange,0x80D30000,"The shelving time not within an acceptable range."
755    BadNoData,0x809B0000,"No data exists for the requested time range or event filter."
756    BadBoundNotFound,0x80D70000,"No data found to provide upper or lower bound value."
757    BadBoundNotSupported,0x80D80000,"The server cannot retrieve a bound for the variable."
758    BadDataLost,0x809D0000,"Data is missing due to collection started/stopped/lost."
759    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."
760    BadEntryExists,0x809F0000,"The data or event was not successfully inserted because a matching entry exists."
761    BadNoEntryExists,0x80A00000,"The data or event was not successfully updated because no matching entry exists."
762    BadTimestampNotSupported,0x80A10000,"The Client requested history using a TimestampsToReturn the Server does not support."
763    GoodEntryInserted,0x00A20000,"The data or event was successfully inserted into the historical database."
764    GoodEntryReplaced,0x00A30000,"The data or event field was successfully replaced in the historical database."
765    UncertainDataSubNormal,0x40A40000,"The aggregate value is derived from multiple values and has less than the required number of Good values."
766    GoodNoData,0x00A50000,"No data exists for the requested time range or event filter."
767    GoodMoreData,0x00A60000,"More data is available in the time range beyond the number of values requested."
768    BadAggregateListMismatch,0x80D40000,"The requested number of Aggregates does not match the requested number of NodeIds."
769    BadAggregateNotSupported,0x80D50000,"The requested Aggregate is not support by the server."
770    BadAggregateInvalidInputs,0x80D60000,"The aggregate value could not be derived due to invalid data inputs."
771    BadAggregateConfigurationRejected,0x80DA0000,"The aggregate configuration is not valid for specified node."
772    GoodDataIgnored,0x00D90000,"The request specifies fields which are not valid for the EventType or cannot be saved by the historian."
773    BadRequestNotAllowed,0x80E40000,"The request was rejected by the server because it did not meet the criteria set by the server."
774    BadRequestNotComplete,0x81130000,"The request has not been processed by the server yet."
775    BadTransactionPending,0x80E80000,"The operation is not allowed because a transaction is in progress."
776    BadTicketRequired,0x811F0000,"The device identity needs a ticket before it can be accepted."
777    BadTicketInvalid,0x81200000,"The device identity needs a ticket before it can be accepted."
778    BadLocked,0x80E90000,"The requested operation is not allowed, because the Node is locked by a different application."
779    BadRequiresLock,0x80EC0000,"The requested operation is not allowed, because the Node is not locked by the application."
780    GoodEdited,0x00DC0000,"The value does not come from the real source and has been edited by the server."
781    GoodPostActionFailed,0x00DD0000,"There was an error in execution of these post-actions."
782    UncertainDominantValueChanged,0x40DE0000,"The related EngineeringUnit has been changed but the Variable Value is still provided based on the previous unit."
783    GoodDependentValueChanged,0x00E00000,"A dependent value has been changed but the change has not been applied to the device."
784    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."
785    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."
786    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."
787    GoodEdited_DependentValueChanged,0x01160000,"It is delivered with a dominant Variable value when a dependent Variable has changed but the change has not been applied."
788    GoodEdited_DominantValueChanged,0x01170000,"It is delivered with a dependent Variable value when a dominant Variable has changed but the change has not been applied."
789    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."
790    BadEdited_OutOfRange,0x81190000,"It is delivered with a Variable value when Variable has changed but the value is not legal."
791    BadInitialValue_OutOfRange,0x811A0000,"It is delivered with a Variable value when a source Variable has changed but the value is not legal."
792    BadOutOfRange_DominantValueChanged,0x811B0000,"It is delivered with a dependent Variable value when a dominant Variable has changed and the value is not legal."
793    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."
794    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."
795    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."
796    GoodCommunicationEvent,0x00A70000,"The communication layer has raised an event."
797    GoodShutdownEvent,0x00A80000,"The system is shutting down."
798    GoodCallAgain,0x00A90000,"The operation is not finished and needs to be called again."
799    GoodNonCriticalTimeout,0x00AA0000,"A non-critical timeout occurred."
800    BadInvalidArgument,0x80AB0000,"One or more arguments are invalid."
801    BadConnectionRejected,0x80AC0000,"Could not establish a network connection to remote server."
802    BadDisconnect,0x80AD0000,"The server has disconnected from the client."
803    BadConnectionClosed,0x80AE0000,"The network connection has been closed."
804    BadInvalidState,0x80AF0000,"The operation cannot be completed because the object is closed, uninitialized or in some other invalid state."
805    BadEndOfStream,0x80B00000,"Cannot move beyond end of the stream."
806    BadNoDataAvailable,0x80B10000,"No data is currently available for reading from a non-blocking stream."
807    BadWaitingForResponse,0x80B20000,"The asynchronous operation is waiting for a response."
808    BadOperationAbandoned,0x80B30000,"The asynchronous operation was abandoned by the caller."
809    BadExpectedStreamToBlock,0x80B40000,"The stream did not return all data requested (possibly because it is a non-blocking stream)."
810    BadWouldBlock,0x80B50000,"Non blocking behaviour is required and the operation would block."
811    BadSyntaxError,0x80B60000,"A value had an invalid syntax."
812    BadMaxConnectionsReached,0x80B70000,"The operation could not be finished because all available connections are in use."
813    UncertainTransducerInManual,0x42080000,"The value may not be accurate because the transducer is in manual mode."
814    UncertainSimulatedValue,0x42090000,"The value is simulated."
815    UncertainSensorCalibration,0x420A0000,"The value may not be accurate due to a sensor calibration fault."
816    UncertainConfigurationError,0x420F0000,"The value may not be accurate due to a configuration issue."
817    GoodCascadeInitializationAcknowledged,0x04010000,"The value source supports cascade handshaking and the value has been Initialized based on an initialization request from a cascade secondary."
818    GoodCascadeInitializationRequest,0x04020000,"The value source supports cascade handshaking and is requesting initialization of a cascade primary."
819    GoodCascadeNotInvited,0x04030000,"The value source supports cascade handshaking, however, the source’s current state does not allow for cascade."
820    GoodCascadeNotSelected,0x04040000,"The value source supports cascade handshaking, however, the source has not selected the corresponding cascade primary for use."
821    GoodFaultStateActive,0x04070000,"There is a fault state condition active in the value source."
822    GoodInitiateFaultState,0x04080000,"A fault state condition is being requested of the destination."
823    GoodCascade,0x04090000,"The value is accurate, and the signal source supports cascade handshaking."
824    BadDataSetIdInvalid,0x80E70000,"The DataSet specified for the DataSetWriter creation is invalid."
825
826    Invalid,0xFFFFFFFF,"Invalid status code"
827}
828// Note that the invalid status code is impossible to get normally.
829
830#[cfg(test)]
831mod tests {
832    use super::{
833        StatusCode, StatusCodeInfoType, StatusCodeLimit, StatusCodeSeverity,
834        StatusCodeValidationError, StatusCodeValueType, SubStatusCode,
835    };
836
837    #[test]
838    fn test_from_sub_code() {
839        assert_eq!("Good", StatusCode::Good.to_string());
840        assert_eq!(
841            "BadBrowseDirectionInvalid",
842            StatusCode::BadBrowseDirectionInvalid.to_string()
843        );
844        assert_eq!(
845            "UncertainDependentValueChanged",
846            StatusCode::UncertainDependentValueChanged.to_string()
847        );
848    }
849
850    #[test]
851    fn test_modify() {
852        let code = StatusCode::from(0);
853        assert_eq!(code, StatusCode::Good);
854        let code = code.set_severity(StatusCodeSeverity::Uncertain);
855        assert_eq!(code.severity(), StatusCodeSeverity::Uncertain);
856        let code = code.set_severity(StatusCodeSeverity::Bad);
857        assert_eq!(code.severity(), StatusCodeSeverity::Bad);
858
859        code.validate().unwrap();
860
861        assert!(!code.structure_changed());
862        let code = code.set_structure_changed(true);
863        code.validate().unwrap();
864        assert!(code.structure_changed());
865        let code = code.set_structure_changed(false);
866        assert!(!code.structure_changed());
867        let code = code.set_structure_changed(true);
868
869        assert!(!code.semantics_changed());
870        let code = code.set_semantics_changed(true);
871        code.validate().unwrap();
872        assert!(code.semantics_changed());
873        let code = code.set_semantics_changed(false);
874        assert!(!code.semantics_changed());
875        let code = code.set_semantics_changed(true);
876
877        assert_eq!(code.sub_code(), SubStatusCode::Bad);
878        let code = code.set_sub_code(SubStatusCode::BadAggregateConfigurationRejected);
879        assert_eq!(
880            code.sub_code(),
881            SubStatusCode::BadAggregateConfigurationRejected
882        );
883        let code = code.set_sub_code(SubStatusCode::UncertainNotAllNodesAvailable);
884        assert_eq!(
885            code.sub_code(),
886            SubStatusCode::UncertainNotAllNodesAvailable
887        );
888
889        assert_eq!(code.info_type(), StatusCodeInfoType::NotUsed);
890        let code = code.set_info_type(StatusCodeInfoType::DataValue);
891        assert_eq!(code.info_type(), StatusCodeInfoType::DataValue);
892        code.validate().unwrap();
893        let code = code.set_info_type(StatusCodeInfoType::NotUsed);
894        assert_eq!(code.info_type(), StatusCodeInfoType::NotUsed);
895
896        assert_eq!(code.limit(), StatusCodeLimit::None);
897        let code = code.set_limit(StatusCodeLimit::High);
898        assert_eq!(code.limit(), StatusCodeLimit::High);
899        let code = code.set_limit(StatusCodeLimit::Constant);
900        assert_eq!(code.limit(), StatusCodeLimit::Constant);
901
902        assert!(matches!(
903            code.validate(),
904            Err(StatusCodeValidationError::InvalidInfoBits)
905        ));
906        let code = code.set_info_type(StatusCodeInfoType::DataValue);
907        code.validate().unwrap();
908
909        assert!(!code.overflow());
910        let code = code.set_overflow(true);
911        code.validate().unwrap();
912        assert!(code.overflow());
913        let code = code.set_overflow(false);
914        assert!(!code.overflow());
915        let code = code.set_overflow(true);
916
917        assert!(!code.multi_value());
918        let code = code.set_multi_value(true);
919        code.validate().unwrap();
920        assert!(code.multi_value());
921        let code = code.set_multi_value(false);
922        assert!(!code.multi_value());
923        let code = code.set_multi_value(true);
924
925        assert!(!code.extra_data());
926        let code = code.set_extra_data(true);
927        code.validate().unwrap();
928        assert!(code.extra_data());
929        let code = code.set_extra_data(false);
930        assert!(!code.extra_data());
931        let code = code.set_extra_data(true);
932
933        assert!(!code.partial());
934        let code = code.set_partial(true);
935        code.validate().unwrap();
936        assert!(code.partial());
937        let code = code.set_partial(false);
938        assert!(!code.partial());
939        let code = code.set_partial(true);
940
941        assert_eq!(code.value_type(), StatusCodeValueType::Raw);
942        let code = code.set_value_type(StatusCodeValueType::Calculated);
943        assert_eq!(code.value_type(), StatusCodeValueType::Calculated);
944        let code = code.set_value_type(StatusCodeValueType::Interpolated);
945        assert_eq!(code.value_type(), StatusCodeValueType::Interpolated);
946
947        assert_eq!(StatusCodeSeverity::Uncertain, code.severity());
948        assert!(code.structure_changed());
949        assert!(code.semantics_changed());
950        assert_eq!(code.info_type(), StatusCodeInfoType::DataValue);
951        assert_eq!(
952            code.sub_code(),
953            SubStatusCode::UncertainNotAllNodesAvailable
954        );
955        assert!(code.overflow());
956        assert!(code.multi_value());
957        assert!(code.extra_data());
958        assert!(code.partial());
959        assert_eq!(code.value_type(), StatusCodeValueType::Interpolated);
960
961        code.validate().unwrap();
962    }
963}