Skip to main content

mabi_opcua/types/
status_code.rs

1//! OPC UA Status Code implementation.
2//!
3//! Status codes are 32-bit values that indicate the result of an operation.
4//! The structure follows the OPC UA specification.
5
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9/// OPC UA Status Code.
10///
11/// A status code is a 32-bit value composed of:
12/// - Bits 31-30: Severity (00=Good, 01=Uncertain, 10=Bad)
13/// - Bits 29-16: Sub-code
14/// - Bits 15-0: Info bits
15///
16/// # Examples
17///
18/// ```
19/// use mabi_opcua::types::StatusCode;
20///
21/// let status = StatusCode::GOOD;
22/// assert!(status.is_good());
23///
24/// let status = StatusCode::BAD_NODE_ID_UNKNOWN;
25/// assert!(status.is_bad());
26/// ```
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
28#[repr(transparent)]
29pub struct StatusCode(u32);
30
31impl StatusCode {
32    // ==========================================================================
33    // Good Status Codes
34    // ==========================================================================
35
36    /// The operation was successful.
37    pub const GOOD: Self = Self(0x00000000);
38    /// The data source is a local value.
39    pub const GOOD_LOCAL_OVERRIDE: Self = Self(0x00960000);
40    /// The operation completed but resulted in clamped values.
41    pub const GOOD_CLAMPED: Self = Self(0x00300000);
42    /// No data exists for the requested time range.
43    pub const GOOD_NO_DATA: Self = Self(0x00A50000);
44    /// More data is available for the requested bounds.
45    pub const GOOD_MORE_DATA: Self = Self(0x00A60000);
46    /// The subscription was transferred.
47    pub const GOOD_SUBSCRIPTION_TRANSFERRED: Self = Self(0x002D0000);
48    /// Refresh in progress.
49    pub const GOOD_REFRESH_IN_PROGRESS: Self = Self(0x006A0000);
50    /// Refresh finished.
51    pub const GOOD_REFRESH_FINISHED: Self = Self(0x006B0000);
52
53    // ==========================================================================
54    // Uncertain Status Codes
55    // ==========================================================================
56
57    /// The value is uncertain.
58    pub const UNCERTAIN: Self = Self(0x40000000);
59    /// The data value is from a different source.
60    pub const UNCERTAIN_LAST_USABLE_VALUE: Self = Self(0x408F0000);
61    /// Sensor failure.
62    pub const UNCERTAIN_SENSOR_NOT_ACCURATE: Self = Self(0x40930000);
63    /// Sub-normal operation.
64    pub const UNCERTAIN_SUB_NORMAL: Self = Self(0x40A40000);
65    /// Initial value.
66    pub const UNCERTAIN_INITIAL_VALUE: Self = Self(0x40920000);
67
68    // ==========================================================================
69    // Bad Status Codes
70    // ==========================================================================
71
72    /// An unexpected error occurred.
73    pub const BAD_UNEXPECTED_ERROR: Self = Self(0x80010000);
74    /// An internal error occurred.
75    pub const BAD_INTERNAL_ERROR: Self = Self(0x80020000);
76    /// Not enough memory.
77    pub const BAD_OUT_OF_MEMORY: Self = Self(0x80030000);
78    /// Invalid argument.
79    pub const BAD_INVALID_ARGUMENT: Self = Self(0x80AB0000);
80    /// Resource not available.
81    pub const BAD_RESOURCE_UNAVAILABLE: Self = Self(0x80040000);
82    /// Communication error.
83    pub const BAD_COMMUNICATION_ERROR: Self = Self(0x80050000);
84    /// Encoding error.
85    pub const BAD_ENCODING_ERROR: Self = Self(0x80060000);
86    /// Decoding error.
87    pub const BAD_DECODING_ERROR: Self = Self(0x80070000);
88    /// Timeout occurred.
89    pub const BAD_TIMEOUT: Self = Self(0x800A0000);
90    /// Service not supported.
91    pub const BAD_SERVICE_UNSUPPORTED: Self = Self(0x800B0000);
92    /// Operation was shut down.
93    pub const BAD_SHUTDOWN: Self = Self(0x800C0000);
94    /// Server not connected.
95    pub const BAD_SERVER_NOT_CONNECTED: Self = Self(0x800D0000);
96    /// Server halted.
97    pub const BAD_SERVER_HALTED: Self = Self(0x800E0000);
98    /// Nothing to do.
99    pub const BAD_NOTHING_TO_DO: Self = Self(0x800F0000);
100    /// Too many operations.
101    pub const BAD_TOO_MANY_OPERATIONS: Self = Self(0x80100000);
102    /// Data type mismatch.
103    pub const BAD_DATA_TYPE_MISMATCH: Self = Self(0x80490000);
104    /// Node ID unknown.
105    pub const BAD_NODE_ID_UNKNOWN: Self = Self(0x80340000);
106    /// Node ID invalid.
107    pub const BAD_NODE_ID_INVALID: Self = Self(0x80330000);
108    /// Attribute ID invalid.
109    pub const BAD_ATTRIBUTE_ID_INVALID: Self = Self(0x80350000);
110    /// Index range invalid.
111    pub const BAD_INDEX_RANGE_INVALID: Self = Self(0x80360000);
112    /// Index range out of bounds.
113    pub const BAD_INDEX_RANGE_NO_DATA: Self = Self(0x80370000);
114    /// Data encoding invalid.
115    pub const BAD_DATA_ENCODING_INVALID: Self = Self(0x80380000);
116    /// Data encoding unsupported.
117    pub const BAD_DATA_ENCODING_UNSUPPORTED: Self = Self(0x80390000);
118    /// Not readable.
119    pub const BAD_NOT_READABLE: Self = Self(0x803A0000);
120    /// Not writable.
121    pub const BAD_NOT_WRITABLE: Self = Self(0x803B0000);
122    /// Out of range.
123    pub const BAD_OUT_OF_RANGE: Self = Self(0x803C0000);
124    /// Not supported.
125    pub const BAD_NOT_SUPPORTED: Self = Self(0x803D0000);
126    /// Not found.
127    pub const BAD_NOT_FOUND: Self = Self(0x803E0000);
128    /// Object deleted.
129    pub const BAD_OBJECT_DELETED: Self = Self(0x803F0000);
130    /// Not implemented.
131    pub const BAD_NOT_IMPLEMENTED: Self = Self(0x80400000);
132    /// Monitoring mode invalid.
133    pub const BAD_MONITORING_MODE_INVALID: Self = Self(0x80410000);
134    /// Monitored item ID invalid.
135    pub const BAD_MONITORED_ITEM_ID_INVALID: Self = Self(0x80420000);
136    /// Monitored item filter invalid.
137    pub const BAD_MONITORED_ITEM_FILTER_INVALID: Self = Self(0x80430000);
138    /// Monitored item filter unsupported.
139    pub const BAD_MONITORED_ITEM_FILTER_UNSUPPORTED: Self = Self(0x80440000);
140    /// Filter not allowed.
141    pub const BAD_FILTER_NOT_ALLOWED: Self = Self(0x80450000);
142    /// Structure missing.
143    pub const BAD_STRUCTURE_MISSING: Self = Self(0x80460000);
144    /// Event filter invalid.
145    pub const BAD_EVENT_FILTER_INVALID: Self = Self(0x80470000);
146    /// Content filter invalid.
147    pub const BAD_CONTENT_FILTER_INVALID: Self = Self(0x80480000);
148    /// Subscription ID invalid.
149    pub const BAD_SUBSCRIPTION_ID_INVALID: Self = Self(0x80280000);
150    /// Sequence number unknown.
151    pub const BAD_SEQUENCE_NUMBER_UNKNOWN: Self = Self(0x80290000);
152    /// Message not available.
153    pub const BAD_MESSAGE_NOT_AVAILABLE: Self = Self(0x802A0000);
154    /// Insufficient client profile.
155    pub const BAD_INSUFFICIENT_CLIENT_PROFILE: Self = Self(0x802B0000);
156    /// Session ID invalid.
157    pub const BAD_SESSION_ID_INVALID: Self = Self(0x80250000);
158    /// Session closed.
159    pub const BAD_SESSION_CLOSED: Self = Self(0x80260000);
160    /// Session not activated.
161    pub const BAD_SESSION_NOT_ACTIVATED: Self = Self(0x80270000);
162    /// Secure channel ID invalid.
163    pub const BAD_SECURE_CHANNEL_ID_INVALID: Self = Self(0x80220000);
164    /// Secure channel closed.
165    pub const BAD_SECURE_CHANNEL_CLOSED: Self = Self(0x80860000);
166    /// Request timeout.
167    pub const BAD_REQUEST_TIMEOUT: Self = Self(0x80A10000);
168    /// Security checks failed.
169    pub const BAD_SECURITY_CHECKS_FAILED: Self = Self(0x80130000);
170    /// User access denied.
171    pub const BAD_USER_ACCESS_DENIED: Self = Self(0x801F0000);
172    /// Identity token invalid.
173    pub const BAD_IDENTITY_TOKEN_INVALID: Self = Self(0x80200000);
174    /// Identity token rejected.
175    pub const BAD_IDENTITY_TOKEN_REJECTED: Self = Self(0x80210000);
176    /// No match.
177    pub const BAD_NO_MATCH: Self = Self(0x806F0000);
178    /// Browse direction invalid.
179    pub const BAD_BROWSE_DIRECTION_INVALID: Self = Self(0x804D0000);
180    /// Node not browsable.
181    pub const BAD_NODE_NOT_BROWSABLE: Self = Self(0x804E0000);
182    /// Reference type ID invalid.
183    pub const BAD_REFERENCE_TYPE_ID_INVALID: Self = Self(0x804C0000);
184    /// Continuation point invalid.
185    pub const BAD_CONTINUATION_POINT_INVALID: Self = Self(0x804F0000);
186    /// No continuation points.
187    pub const BAD_NO_CONTINUATION_POINTS: Self = Self(0x80500000);
188    /// Too many subscriptions.
189    pub const BAD_TOO_MANY_SUBSCRIPTIONS: Self = Self(0x80770000);
190    /// Too many monitored items.
191    pub const BAD_TOO_MANY_MONITORED_ITEMS: Self = Self(0x80780000);
192    /// Write not supported.
193    pub const BAD_WRITE_NOT_SUPPORTED: Self = Self(0x80730000);
194    /// History operation invalid.
195    pub const BAD_HISTORY_OPERATION_INVALID: Self = Self(0x80710000);
196    /// History operation unsupported.
197    pub const BAD_HISTORY_OPERATION_UNSUPPORTED: Self = Self(0x80720000);
198
199    // ==========================================================================
200    // Methods
201    // ==========================================================================
202
203    /// Create a status code from a raw value.
204    pub const fn from_raw(value: u32) -> Self {
205        Self(value)
206    }
207
208    /// Get the raw value.
209    pub const fn raw(&self) -> u32 {
210        self.0
211    }
212
213    /// Check if the status is good.
214    pub const fn is_good(&self) -> bool {
215        self.severity() == 0
216    }
217
218    /// Check if the status is uncertain.
219    pub const fn is_uncertain(&self) -> bool {
220        self.severity() == 1
221    }
222
223    /// Check if the status is bad.
224    pub const fn is_bad(&self) -> bool {
225        self.severity() >= 2
226    }
227
228    /// Get the severity (0=Good, 1=Uncertain, 2=Bad).
229    pub const fn severity(&self) -> u8 {
230        ((self.0 >> 30) & 0x03) as u8
231    }
232
233    /// Get the sub-code.
234    pub const fn sub_code(&self) -> u16 {
235        ((self.0 >> 16) & 0x3FFF) as u16
236    }
237
238    /// Get the info bits.
239    pub const fn info_bits(&self) -> u16 {
240        (self.0 & 0xFFFF) as u16
241    }
242
243    /// Get a description of this status code.
244    pub fn description(&self) -> &'static str {
245        match *self {
246            Self::GOOD => "Good",
247            Self::GOOD_LOCAL_OVERRIDE => "Good (local override)",
248            Self::GOOD_CLAMPED => "Good (clamped)",
249            Self::GOOD_NO_DATA => "Good (no data)",
250            Self::GOOD_MORE_DATA => "Good (more data available)",
251            Self::UNCERTAIN => "Uncertain",
252            Self::UNCERTAIN_LAST_USABLE_VALUE => "Uncertain (last usable value)",
253            Self::UNCERTAIN_SENSOR_NOT_ACCURATE => "Uncertain (sensor not accurate)",
254            Self::UNCERTAIN_INITIAL_VALUE => "Uncertain (initial value)",
255            Self::BAD_UNEXPECTED_ERROR => "Bad (unexpected error)",
256            Self::BAD_INTERNAL_ERROR => "Bad (internal error)",
257            Self::BAD_OUT_OF_MEMORY => "Bad (out of memory)",
258            Self::BAD_INVALID_ARGUMENT => "Bad (invalid argument)",
259            Self::BAD_TIMEOUT => "Bad (timeout)",
260            Self::BAD_NODE_ID_UNKNOWN => "Bad (node ID unknown)",
261            Self::BAD_NODE_ID_INVALID => "Bad (node ID invalid)",
262            Self::BAD_ATTRIBUTE_ID_INVALID => "Bad (attribute ID invalid)",
263            Self::BAD_NOT_READABLE => "Bad (not readable)",
264            Self::BAD_NOT_WRITABLE => "Bad (not writable)",
265            Self::BAD_NOT_FOUND => "Bad (not found)",
266            Self::BAD_NOT_SUPPORTED => "Bad (not supported)",
267            Self::BAD_NOT_IMPLEMENTED => "Bad (not implemented)",
268            Self::BAD_SUBSCRIPTION_ID_INVALID => "Bad (subscription ID invalid)",
269            Self::BAD_SESSION_ID_INVALID => "Bad (session ID invalid)",
270            Self::BAD_SESSION_CLOSED => "Bad (session closed)",
271            Self::BAD_TOO_MANY_SUBSCRIPTIONS => "Bad (too many subscriptions)",
272            Self::BAD_TOO_MANY_MONITORED_ITEMS => "Bad (too many monitored items)",
273            _ => "Unknown status code",
274        }
275    }
276}
277
278impl Default for StatusCode {
279    fn default() -> Self {
280        Self::GOOD
281    }
282}
283
284impl fmt::Display for StatusCode {
285    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286        write!(f, "{} (0x{:08X})", self.description(), self.0)
287    }
288}
289
290impl From<u32> for StatusCode {
291    fn from(value: u32) -> Self {
292        Self(value)
293    }
294}
295
296impl From<StatusCode> for u32 {
297    fn from(status: StatusCode) -> Self {
298        status.0
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    use super::*;
305
306    #[test]
307    fn test_good_status() {
308        assert!(StatusCode::GOOD.is_good());
309        assert!(!StatusCode::GOOD.is_uncertain());
310        assert!(!StatusCode::GOOD.is_bad());
311        assert_eq!(StatusCode::GOOD.severity(), 0);
312    }
313
314    #[test]
315    fn test_uncertain_status() {
316        assert!(!StatusCode::UNCERTAIN.is_good());
317        assert!(StatusCode::UNCERTAIN.is_uncertain());
318        assert!(!StatusCode::UNCERTAIN.is_bad());
319        assert_eq!(StatusCode::UNCERTAIN.severity(), 1);
320    }
321
322    #[test]
323    fn test_bad_status() {
324        assert!(!StatusCode::BAD_UNEXPECTED_ERROR.is_good());
325        assert!(!StatusCode::BAD_UNEXPECTED_ERROR.is_uncertain());
326        assert!(StatusCode::BAD_UNEXPECTED_ERROR.is_bad());
327        assert_eq!(StatusCode::BAD_UNEXPECTED_ERROR.severity(), 2);
328    }
329
330    #[test]
331    fn test_status_code_display() {
332        let status = StatusCode::GOOD;
333        assert!(status.to_string().contains("Good"));
334
335        let status = StatusCode::BAD_NODE_ID_UNKNOWN;
336        assert!(status.to_string().contains("node ID unknown"));
337    }
338}