Skip to main content

zerodds_opcua_server/
services.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! OPC-UA service messages (Part 4 §5): Session lifecycle (CreateSession /
4//! ActivateSession / CloseSession) plus the Read and Call service sets, with
5//! the [`ServiceRequest`] / [`ServiceResponse`] dispatch enums.
6//!
7//! Each message body is `[TypeId:NodeId][RequestHeader|ResponseHeader][fields]`
8//! per Part 6 §5; the common headers come from [`zerodds_opcua_uacp`].
9
10use alloc::string::String;
11use alloc::vec::Vec;
12
13use zerodds_opcua_gateway::data_value::{DataValue, ExtensionObject, ExtensionObjectBody, Variant};
14use zerodds_opcua_gateway::node_id::{ExpandedNodeId, NodeId};
15use zerodds_opcua_gateway::types::{LocalizedText, QualifiedName};
16use zerodds_opcua_pubsub::uadp::datatypes::{ApplicationDescription, EndpointDescription};
17use zerodds_opcua_pubsub::{DecodeError, EncodeError, UaDecode, UaEncode, UaReader, UaWriter};
18
19use zerodds_opcua_uacp::securechannel::node_ids;
20use zerodds_opcua_uacp::securechannel::{RequestHeader, ResponseHeader, null_extension_object};
21
22use crate::wire::{
23    read_array, read_byte_string, read_string, read_string_array, read_u32_array,
24    skip_diagnostic_info_array, write_array, write_byte_string, write_empty_diagnostic_info_array,
25    write_string, write_string_array, write_u32_array,
26};
27
28/// The `Value` attribute id (Part 4 §A.1).
29pub const ATTRIBUTE_VALUE: u32 = 13;
30
31// ---------------------------------------------------------------------------
32// Shared sub-types.
33// ---------------------------------------------------------------------------
34
35/// `SignatureData` (Part 4 §7.36) — empty for SecurityMode `None`.
36#[derive(Debug, Clone, PartialEq, Eq, Default)]
37pub struct SignatureData {
38    /// Signature algorithm URI.
39    pub algorithm: String,
40    /// Signature bytes.
41    pub signature: Vec<u8>,
42}
43
44impl UaEncode for SignatureData {
45    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
46        write_string(w, &self.algorithm)?;
47        write_byte_string(w, &self.signature)?;
48        Ok(())
49    }
50}
51
52impl UaDecode for SignatureData {
53    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
54        Ok(Self {
55            algorithm: read_string(r)?,
56            signature: read_byte_string(r)?,
57        })
58    }
59}
60
61/// `ReadValueId` (Part 4 §7.28).
62#[derive(Debug, Clone, PartialEq)]
63pub struct ReadValueId {
64    /// Node to read.
65    pub node_id: NodeId,
66    /// Attribute id (e.g. [`ATTRIBUTE_VALUE`]).
67    pub attribute_id: u32,
68    /// Index range (empty = whole value).
69    pub index_range: String,
70    /// Requested data encoding (empty QualifiedName = default).
71    pub data_encoding: QualifiedName,
72}
73
74impl UaEncode for ReadValueId {
75    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
76        self.node_id.encode(w)?;
77        w.write_u32(self.attribute_id);
78        write_string(w, &self.index_range)?;
79        self.data_encoding.encode(w)?;
80        Ok(())
81    }
82}
83
84impl UaDecode for ReadValueId {
85    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
86        Ok(Self {
87            node_id: NodeId::decode(r)?,
88            attribute_id: r.read_u32()?,
89            index_range: read_string(r)?,
90            data_encoding: QualifiedName::decode(r)?,
91        })
92    }
93}
94
95/// `WriteValue` (Part 4 §5.11.4) — a single node/attribute write.
96#[derive(Debug, Clone, PartialEq)]
97pub struct WriteValue {
98    /// Node to write.
99    pub node_id: NodeId,
100    /// Attribute id (e.g. [`ATTRIBUTE_VALUE`]).
101    pub attribute_id: u32,
102    /// Index range (empty = whole value).
103    pub index_range: String,
104    /// The value to write.
105    pub value: DataValue,
106}
107
108impl UaEncode for WriteValue {
109    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
110        self.node_id.encode(w)?;
111        w.write_u32(self.attribute_id);
112        write_string(w, &self.index_range)?;
113        self.value.encode(w)?;
114        Ok(())
115    }
116}
117
118impl UaDecode for WriteValue {
119    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
120        Ok(Self {
121            node_id: NodeId::decode(r)?,
122            attribute_id: r.read_u32()?,
123            index_range: read_string(r)?,
124            value: DataValue::decode(r)?,
125        })
126    }
127}
128
129/// `CallMethodRequest` (Part 4 §5.12.2).
130#[derive(Debug, Clone, PartialEq)]
131pub struct CallMethodRequest {
132    /// Object the method belongs to.
133    pub object_id: NodeId,
134    /// Method to call.
135    pub method_id: NodeId,
136    /// Input argument values.
137    pub input_arguments: Vec<Variant>,
138}
139
140impl UaEncode for CallMethodRequest {
141    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
142        self.object_id.encode(w)?;
143        self.method_id.encode(w)?;
144        write_array(w, &self.input_arguments, "InputArguments")?;
145        Ok(())
146    }
147}
148
149impl UaDecode for CallMethodRequest {
150    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
151        Ok(Self {
152            object_id: NodeId::decode(r)?,
153            method_id: NodeId::decode(r)?,
154            input_arguments: read_array::<Variant>(r)?,
155        })
156    }
157}
158
159/// `CallMethodResult` (Part 4 §5.12.2).
160#[derive(Debug, Clone, PartialEq)]
161pub struct CallMethodResult {
162    /// Method call status.
163    pub status_code: u32,
164    /// Per-input-argument status codes.
165    pub input_argument_results: Vec<u32>,
166    /// Output argument values.
167    pub output_arguments: Vec<Variant>,
168}
169
170impl UaEncode for CallMethodResult {
171    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
172        w.write_u32(self.status_code);
173        write_u32_array(w, &self.input_argument_results)?;
174        write_empty_diagnostic_info_array(w); // inputArgumentDiagnosticInfos
175        write_array(w, &self.output_arguments, "OutputArguments")?;
176        Ok(())
177    }
178}
179
180impl UaDecode for CallMethodResult {
181    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
182        let status_code = r.read_u32()?;
183        let input_argument_results = read_u32_array(r)?;
184        skip_diagnostic_info_array(r)?;
185        let output_arguments = read_array::<Variant>(r)?;
186        Ok(Self {
187            status_code,
188            input_argument_results,
189            output_arguments,
190        })
191    }
192}
193
194// ---------------------------------------------------------------------------
195// CreateSession (Part 4 §5.7.2).
196// ---------------------------------------------------------------------------
197
198/// `CreateSessionRequest`.
199#[derive(Debug, Clone, PartialEq)]
200pub struct CreateSessionRequest {
201    /// Common request header.
202    pub request_header: RequestHeader,
203    /// Client application description.
204    pub client_description: ApplicationDescription,
205    /// Server URI.
206    pub server_uri: String,
207    /// Endpoint URL.
208    pub endpoint_url: String,
209    /// Human-readable session name.
210    pub session_name: String,
211    /// Client nonce.
212    pub client_nonce: Vec<u8>,
213    /// Client certificate (DER).
214    pub client_certificate: Vec<u8>,
215    /// Requested session timeout in milliseconds.
216    pub requested_session_timeout: f64,
217    /// Maximum response message size.
218    pub max_response_message_size: u32,
219}
220
221impl CreateSessionRequest {
222    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
223        Ok(Self {
224            request_header: decode_request_header(r)?,
225            client_description: ApplicationDescription::decode(r)?,
226            server_uri: read_string(r)?,
227            endpoint_url: read_string(r)?,
228            session_name: read_string(r)?,
229            client_nonce: read_byte_string(r)?,
230            client_certificate: read_byte_string(r)?,
231            requested_session_timeout: r.read_f64()?,
232            max_response_message_size: r.read_u32()?,
233        })
234    }
235
236    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
237        node_ids::CREATE_SESSION_REQUEST.encode(w)?;
238        encode_request_header(&self.request_header, w)?;
239        self.client_description.encode(w)?;
240        write_string(w, &self.server_uri)?;
241        write_string(w, &self.endpoint_url)?;
242        write_string(w, &self.session_name)?;
243        write_byte_string(w, &self.client_nonce)?;
244        write_byte_string(w, &self.client_certificate)?;
245        w.write_f64(self.requested_session_timeout);
246        w.write_u32(self.max_response_message_size);
247        Ok(())
248    }
249}
250
251/// `CreateSessionResponse`.
252#[derive(Debug, Clone, PartialEq)]
253pub struct CreateSessionResponse {
254    /// Common response header.
255    pub response_header: ResponseHeader,
256    /// Session identifier.
257    pub session_id: NodeId,
258    /// Authentication token for subsequent requests.
259    pub authentication_token: NodeId,
260    /// Revised session timeout in milliseconds.
261    pub revised_session_timeout: f64,
262    /// Server nonce.
263    pub server_nonce: Vec<u8>,
264    /// Server certificate (DER).
265    pub server_certificate: Vec<u8>,
266    /// Endpoints the server offers.
267    pub server_endpoints: Vec<EndpointDescription>,
268    /// Server signature over the client nonce+cert (empty for `None`).
269    pub server_signature: SignatureData,
270    /// Maximum request message size.
271    pub max_request_message_size: u32,
272}
273
274impl CreateSessionResponse {
275    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
276        let response_header = decode_response_header(r)?;
277        let session_id = NodeId::decode(r)?;
278        let authentication_token = NodeId::decode(r)?;
279        let revised_session_timeout = r.read_f64()?;
280        let server_nonce = read_byte_string(r)?;
281        let server_certificate = read_byte_string(r)?;
282        let server_endpoints = read_array::<EndpointDescription>(r)?;
283        // serverSoftwareCertificates (empty array).
284        let _swc = r.read_i32()?;
285        let server_signature = SignatureData::decode(r)?;
286        let max_request_message_size = r.read_u32()?;
287        Ok(Self {
288            response_header,
289            session_id,
290            authentication_token,
291            revised_session_timeout,
292            server_nonce,
293            server_certificate,
294            server_endpoints,
295            server_signature,
296            max_request_message_size,
297        })
298    }
299
300    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
301        node_ids::CREATE_SESSION_RESPONSE.encode(w)?;
302        encode_response_header(&self.response_header, w)?;
303        self.session_id.encode(w)?;
304        self.authentication_token.encode(w)?;
305        w.write_f64(self.revised_session_timeout);
306        write_byte_string(w, &self.server_nonce)?;
307        write_byte_string(w, &self.server_certificate)?;
308        write_array(w, &self.server_endpoints, "ServerEndpoints")?;
309        w.write_i32(0); // serverSoftwareCertificates: empty
310        self.server_signature.encode(w)?;
311        w.write_u32(self.max_request_message_size);
312        Ok(())
313    }
314}
315
316// ---------------------------------------------------------------------------
317// ActivateSession (Part 4 §5.7.3).
318// ---------------------------------------------------------------------------
319
320/// `ActivateSessionRequest`.
321#[derive(Debug, Clone, PartialEq)]
322pub struct ActivateSessionRequest {
323    /// Common request header (carries the authentication token).
324    pub request_header: RequestHeader,
325    /// Client signature (empty for `None`).
326    pub client_signature: SignatureData,
327    /// Preferred locales.
328    pub locale_ids: Vec<String>,
329    /// User identity token (an ExtensionObject; anonymous for the demo).
330    pub user_identity_token: zerodds_opcua_gateway::data_value::ExtensionObject,
331    /// User token signature (empty for `None`).
332    pub user_token_signature: SignatureData,
333}
334
335impl ActivateSessionRequest {
336    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
337        let request_header = decode_request_header(r)?;
338        let client_signature = SignatureData::decode(r)?;
339        // clientSoftwareCertificates (empty array).
340        let _swc = r.read_i32()?;
341        let locale_ids = read_string_array(r)?;
342        let user_identity_token = zerodds_opcua_gateway::data_value::ExtensionObject::decode(r)?;
343        let user_token_signature = SignatureData::decode(r)?;
344        Ok(Self {
345            request_header,
346            client_signature,
347            locale_ids,
348            user_identity_token,
349            user_token_signature,
350        })
351    }
352
353    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
354        node_ids::ACTIVATE_SESSION_REQUEST.encode(w)?;
355        encode_request_header(&self.request_header, w)?;
356        self.client_signature.encode(w)?;
357        w.write_i32(0); // clientSoftwareCertificates: empty
358        write_string_array(w, &self.locale_ids)?;
359        self.user_identity_token.encode(w)?;
360        self.user_token_signature.encode(w)?;
361        Ok(())
362    }
363}
364
365/// `ActivateSessionResponse`.
366#[derive(Debug, Clone, PartialEq)]
367pub struct ActivateSessionResponse {
368    /// Common response header.
369    pub response_header: ResponseHeader,
370    /// New server nonce.
371    pub server_nonce: Vec<u8>,
372    /// Per-certificate results.
373    pub results: Vec<u32>,
374}
375
376impl ActivateSessionResponse {
377    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
378        let response_header = decode_response_header(r)?;
379        let server_nonce = read_byte_string(r)?;
380        let results = read_u32_array(r)?;
381        skip_diagnostic_info_array(r)?;
382        Ok(Self {
383            response_header,
384            server_nonce,
385            results,
386        })
387    }
388
389    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
390        node_ids::ACTIVATE_SESSION_RESPONSE.encode(w)?;
391        encode_response_header(&self.response_header, w)?;
392        write_byte_string(w, &self.server_nonce)?;
393        write_u32_array(w, &self.results)?;
394        write_empty_diagnostic_info_array(w);
395        Ok(())
396    }
397}
398
399// ---------------------------------------------------------------------------
400// CloseSession (Part 4 §5.7.4).
401// ---------------------------------------------------------------------------
402
403/// `CloseSessionRequest`.
404#[derive(Debug, Clone, PartialEq)]
405pub struct CloseSessionRequest {
406    /// Common request header.
407    pub request_header: RequestHeader,
408    /// Whether to delete the session's subscriptions.
409    pub delete_subscriptions: bool,
410}
411
412impl CloseSessionRequest {
413    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
414        Ok(Self {
415            request_header: decode_request_header(r)?,
416            delete_subscriptions: r.read_u8()? != 0,
417        })
418    }
419
420    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
421        node_ids::CLOSE_SESSION_REQUEST.encode(w)?;
422        encode_request_header(&self.request_header, w)?;
423        w.write_u8(u8::from(self.delete_subscriptions));
424        Ok(())
425    }
426}
427
428/// `CloseSessionResponse`.
429#[derive(Debug, Clone, PartialEq)]
430pub struct CloseSessionResponse {
431    /// Common response header.
432    pub response_header: ResponseHeader,
433}
434
435impl CloseSessionResponse {
436    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
437        Ok(Self {
438            response_header: decode_response_header(r)?,
439        })
440    }
441
442    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
443        node_ids::CLOSE_SESSION_RESPONSE.encode(w)?;
444        encode_response_header(&self.response_header, w)?;
445        Ok(())
446    }
447}
448
449// ---------------------------------------------------------------------------
450// Read (Part 4 §5.11.2).
451// ---------------------------------------------------------------------------
452
453/// `ReadRequest`.
454#[derive(Debug, Clone, PartialEq)]
455pub struct ReadRequest {
456    /// Common request header.
457    pub request_header: RequestHeader,
458    /// Maximum acceptable value age in milliseconds.
459    pub max_age: f64,
460    /// TimestampsToReturn (0 Source / 1 Server / 2 Both / 3 Neither).
461    pub timestamps_to_return: i32,
462    /// Nodes/attributes to read.
463    pub nodes_to_read: Vec<ReadValueId>,
464}
465
466impl ReadRequest {
467    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
468        Ok(Self {
469            request_header: decode_request_header(r)?,
470            max_age: r.read_f64()?,
471            timestamps_to_return: r.read_i32()?,
472            nodes_to_read: read_array::<ReadValueId>(r)?,
473        })
474    }
475
476    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
477        node_ids::READ_REQUEST.encode(w)?;
478        encode_request_header(&self.request_header, w)?;
479        w.write_f64(self.max_age);
480        w.write_i32(self.timestamps_to_return);
481        write_array(w, &self.nodes_to_read, "NodesToRead")?;
482        Ok(())
483    }
484}
485
486/// `ReadResponse`.
487#[derive(Debug, Clone, PartialEq)]
488pub struct ReadResponse {
489    /// Common response header.
490    pub response_header: ResponseHeader,
491    /// One DataValue per requested node.
492    pub results: Vec<DataValue>,
493}
494
495impl ReadResponse {
496    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
497        let response_header = decode_response_header(r)?;
498        let results = read_array::<DataValue>(r)?;
499        skip_diagnostic_info_array(r)?;
500        Ok(Self {
501            response_header,
502            results,
503        })
504    }
505
506    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
507        node_ids::READ_RESPONSE.encode(w)?;
508        encode_response_header(&self.response_header, w)?;
509        write_array(w, &self.results, "Results")?;
510        write_empty_diagnostic_info_array(w);
511        Ok(())
512    }
513}
514
515// ---------------------------------------------------------------------------
516// Write (Part 4 §5.11.4).
517// ---------------------------------------------------------------------------
518
519/// `WriteRequest`.
520#[derive(Debug, Clone, PartialEq)]
521pub struct WriteRequest {
522    /// Common request header.
523    pub request_header: RequestHeader,
524    /// Nodes/attributes to write.
525    pub nodes_to_write: Vec<WriteValue>,
526}
527
528impl WriteRequest {
529    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
530        Ok(Self {
531            request_header: decode_request_header(r)?,
532            nodes_to_write: read_array::<WriteValue>(r)?,
533        })
534    }
535
536    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
537        node_ids::WRITE_REQUEST.encode(w)?;
538        encode_request_header(&self.request_header, w)?;
539        write_array(w, &self.nodes_to_write, "NodesToWrite")?;
540        Ok(())
541    }
542}
543
544/// `WriteResponse`.
545#[derive(Debug, Clone, PartialEq)]
546pub struct WriteResponse {
547    /// Common response header.
548    pub response_header: ResponseHeader,
549    /// One StatusCode per requested write.
550    pub results: Vec<u32>,
551}
552
553impl WriteResponse {
554    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
555        let response_header = decode_response_header(r)?;
556        let results = read_u32_array(r)?;
557        skip_diagnostic_info_array(r)?;
558        Ok(Self {
559            response_header,
560            results,
561        })
562    }
563
564    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
565        node_ids::WRITE_RESPONSE.encode(w)?;
566        encode_response_header(&self.response_header, w)?;
567        write_u32_array(w, &self.results)?;
568        write_empty_diagnostic_info_array(w);
569        Ok(())
570    }
571}
572
573// ---------------------------------------------------------------------------
574// Call (Part 4 §5.12.2).
575// ---------------------------------------------------------------------------
576
577/// `CallRequest`.
578#[derive(Debug, Clone, PartialEq)]
579pub struct CallRequest {
580    /// Common request header.
581    pub request_header: RequestHeader,
582    /// Methods to call.
583    pub methods_to_call: Vec<CallMethodRequest>,
584}
585
586impl CallRequest {
587    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
588        Ok(Self {
589            request_header: decode_request_header(r)?,
590            methods_to_call: read_array::<CallMethodRequest>(r)?,
591        })
592    }
593
594    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
595        node_ids::CALL_REQUEST.encode(w)?;
596        encode_request_header(&self.request_header, w)?;
597        write_array(w, &self.methods_to_call, "MethodsToCall")?;
598        Ok(())
599    }
600}
601
602/// `CallResponse`.
603#[derive(Debug, Clone, PartialEq)]
604pub struct CallResponse {
605    /// Common response header.
606    pub response_header: ResponseHeader,
607    /// One result per method call.
608    pub results: Vec<CallMethodResult>,
609}
610
611impl CallResponse {
612    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
613        let response_header = decode_response_header(r)?;
614        let results = read_array::<CallMethodResult>(r)?;
615        skip_diagnostic_info_array(r)?;
616        Ok(Self {
617            response_header,
618            results,
619        })
620    }
621
622    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
623        node_ids::CALL_RESPONSE.encode(w)?;
624        encode_response_header(&self.response_header, w)?;
625        write_array(w, &self.results, "Results")?;
626        write_empty_diagnostic_info_array(w);
627        Ok(())
628    }
629}
630
631// ---------------------------------------------------------------------------
632// Browse (Part 4 §5.9.2).
633// ---------------------------------------------------------------------------
634
635/// `ViewDescription` (Part 4 §7.44) — a null view browses the whole address
636/// space.
637#[derive(Debug, Clone, PartialEq)]
638pub struct ViewDescription {
639    /// View NodeId (null = no view).
640    pub view_id: NodeId,
641    /// Timestamp (DateTime ticks; `0` = any).
642    pub timestamp: i64,
643    /// View version (`0` = any).
644    pub view_version: u32,
645}
646
647impl Default for ViewDescription {
648    fn default() -> Self {
649        Self {
650            view_id: NodeId::numeric(0, 0),
651            timestamp: 0,
652            view_version: 0,
653        }
654    }
655}
656
657impl UaEncode for ViewDescription {
658    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
659        self.view_id.encode(w)?;
660        w.write_i64(self.timestamp);
661        w.write_u32(self.view_version);
662        Ok(())
663    }
664}
665
666impl UaDecode for ViewDescription {
667    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
668        Ok(Self {
669            view_id: NodeId::decode(r)?,
670            timestamp: r.read_i64()?,
671            view_version: r.read_u32()?,
672        })
673    }
674}
675
676/// `BrowseDescription` (Part 4 §7) — one node to browse.
677#[derive(Debug, Clone, PartialEq)]
678pub struct BrowseDescription {
679    /// Node to browse.
680    pub node_id: NodeId,
681    /// BrowseDirection (0 Forward / 1 Inverse / 2 Both).
682    pub browse_direction: i32,
683    /// ReferenceType filter (null = all).
684    pub reference_type_id: NodeId,
685    /// Include subtypes of the reference type.
686    pub include_subtypes: bool,
687    /// NodeClass mask (0 = all).
688    pub node_class_mask: u32,
689    /// Result mask (which `ReferenceDescription` fields to fill).
690    pub result_mask: u32,
691}
692
693impl UaEncode for BrowseDescription {
694    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
695        self.node_id.encode(w)?;
696        w.write_i32(self.browse_direction);
697        self.reference_type_id.encode(w)?;
698        w.write_u8(u8::from(self.include_subtypes));
699        w.write_u32(self.node_class_mask);
700        w.write_u32(self.result_mask);
701        Ok(())
702    }
703}
704
705impl UaDecode for BrowseDescription {
706    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
707        Ok(Self {
708            node_id: NodeId::decode(r)?,
709            browse_direction: r.read_i32()?,
710            reference_type_id: NodeId::decode(r)?,
711            include_subtypes: r.read_u8()? != 0,
712            node_class_mask: r.read_u32()?,
713            result_mask: r.read_u32()?,
714        })
715    }
716}
717
718/// `ReferenceDescription` (Part 4 §7.29) — one browse result entry.
719#[derive(Debug, Clone, PartialEq)]
720pub struct ReferenceDescription {
721    /// ReferenceType traversed.
722    pub reference_type_id: NodeId,
723    /// `true` for a forward reference.
724    pub is_forward: bool,
725    /// Target node.
726    pub node_id: ExpandedNodeId,
727    /// Target BrowseName.
728    pub browse_name: QualifiedName,
729    /// Target DisplayName.
730    pub display_name: LocalizedText,
731    /// Target NodeClass (enum value).
732    pub node_class: i32,
733    /// Target TypeDefinition.
734    pub type_definition: ExpandedNodeId,
735}
736
737impl UaEncode for ReferenceDescription {
738    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
739        self.reference_type_id.encode(w)?;
740        w.write_u8(u8::from(self.is_forward));
741        self.node_id.encode(w)?;
742        self.browse_name.encode(w)?;
743        self.display_name.encode(w)?;
744        w.write_i32(self.node_class);
745        self.type_definition.encode(w)?;
746        Ok(())
747    }
748}
749
750impl UaDecode for ReferenceDescription {
751    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
752        Ok(Self {
753            reference_type_id: NodeId::decode(r)?,
754            is_forward: r.read_u8()? != 0,
755            node_id: ExpandedNodeId::decode(r)?,
756            browse_name: QualifiedName::decode(r)?,
757            display_name: LocalizedText::decode(r)?,
758            node_class: r.read_i32()?,
759            type_definition: ExpandedNodeId::decode(r)?,
760        })
761    }
762}
763
764/// `BrowseResult` (Part 4 §7.6) — the references found for one browsed node.
765#[derive(Debug, Clone, PartialEq)]
766pub struct BrowseResult {
767    /// Per-node browse status.
768    pub status_code: u32,
769    /// Continuation point (empty = none).
770    pub continuation_point: Vec<u8>,
771    /// The references found.
772    pub references: Vec<ReferenceDescription>,
773}
774
775impl UaEncode for BrowseResult {
776    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
777        w.write_u32(self.status_code);
778        write_byte_string(w, &self.continuation_point)?;
779        write_array(w, &self.references, "References")?;
780        Ok(())
781    }
782}
783
784impl UaDecode for BrowseResult {
785    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
786        Ok(Self {
787            status_code: r.read_u32()?,
788            continuation_point: read_byte_string(r)?,
789            references: read_array::<ReferenceDescription>(r)?,
790        })
791    }
792}
793
794/// `BrowseRequest`.
795#[derive(Debug, Clone, PartialEq)]
796pub struct BrowseRequest {
797    /// Common request header.
798    pub request_header: RequestHeader,
799    /// View to browse (null = whole address space).
800    pub view: ViewDescription,
801    /// Max references per node (`0` = no limit).
802    pub requested_max_references_per_node: u32,
803    /// Nodes to browse.
804    pub nodes_to_browse: Vec<BrowseDescription>,
805}
806
807impl BrowseRequest {
808    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
809        Ok(Self {
810            request_header: decode_request_header(r)?,
811            view: ViewDescription::decode(r)?,
812            requested_max_references_per_node: r.read_u32()?,
813            nodes_to_browse: read_array::<BrowseDescription>(r)?,
814        })
815    }
816
817    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
818        node_ids::BROWSE_REQUEST.encode(w)?;
819        encode_request_header(&self.request_header, w)?;
820        self.view.encode(w)?;
821        w.write_u32(self.requested_max_references_per_node);
822        write_array(w, &self.nodes_to_browse, "NodesToBrowse")?;
823        Ok(())
824    }
825}
826
827/// `BrowseResponse`.
828#[derive(Debug, Clone, PartialEq)]
829pub struct BrowseResponse {
830    /// Common response header.
831    pub response_header: ResponseHeader,
832    /// One result per browsed node.
833    pub results: Vec<BrowseResult>,
834}
835
836impl BrowseResponse {
837    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
838        let response_header = decode_response_header(r)?;
839        let results = read_array::<BrowseResult>(r)?;
840        skip_diagnostic_info_array(r)?;
841        Ok(Self {
842            response_header,
843            results,
844        })
845    }
846
847    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
848        node_ids::BROWSE_RESPONSE.encode(w)?;
849        encode_response_header(&self.response_header, w)?;
850        write_array(w, &self.results, "Results")?;
851        write_empty_diagnostic_info_array(w);
852        Ok(())
853    }
854}
855
856// ---------------------------------------------------------------------------
857// Discovery: GetEndpoints (Part 4 §5.5.4) + FindServers (§5.5.2).
858// ---------------------------------------------------------------------------
859
860/// `GetEndpointsRequest`.
861#[derive(Debug, Clone, PartialEq)]
862pub struct GetEndpointsRequest {
863    /// Common request header.
864    pub request_header: RequestHeader,
865    /// Endpoint URL the client used.
866    pub endpoint_url: String,
867    /// Preferred locales.
868    pub locale_ids: Vec<String>,
869    /// Transport profile URIs filter (empty = all).
870    pub profile_uris: Vec<String>,
871}
872
873impl GetEndpointsRequest {
874    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
875        Ok(Self {
876            request_header: decode_request_header(r)?,
877            endpoint_url: read_string(r)?,
878            locale_ids: read_string_array(r)?,
879            profile_uris: read_string_array(r)?,
880        })
881    }
882
883    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
884        node_ids::GET_ENDPOINTS_REQUEST.encode(w)?;
885        encode_request_header(&self.request_header, w)?;
886        write_string(w, &self.endpoint_url)?;
887        write_string_array(w, &self.locale_ids)?;
888        write_string_array(w, &self.profile_uris)?;
889        Ok(())
890    }
891}
892
893/// `GetEndpointsResponse`.
894#[derive(Debug, Clone, PartialEq)]
895pub struct GetEndpointsResponse {
896    /// Common response header.
897    pub response_header: ResponseHeader,
898    /// The endpoints the server offers.
899    pub endpoints: Vec<EndpointDescription>,
900}
901
902impl GetEndpointsResponse {
903    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
904        Ok(Self {
905            response_header: decode_response_header(r)?,
906            endpoints: read_array::<EndpointDescription>(r)?,
907        })
908    }
909
910    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
911        node_ids::GET_ENDPOINTS_RESPONSE.encode(w)?;
912        encode_response_header(&self.response_header, w)?;
913        write_array(w, &self.endpoints, "Endpoints")?;
914        Ok(())
915    }
916}
917
918/// `FindServersRequest`.
919#[derive(Debug, Clone, PartialEq)]
920pub struct FindServersRequest {
921    /// Common request header.
922    pub request_header: RequestHeader,
923    /// Endpoint URL the client used.
924    pub endpoint_url: String,
925    /// Preferred locales.
926    pub locale_ids: Vec<String>,
927    /// Server URIs filter (empty = all).
928    pub server_uris: Vec<String>,
929}
930
931impl FindServersRequest {
932    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
933        Ok(Self {
934            request_header: decode_request_header(r)?,
935            endpoint_url: read_string(r)?,
936            locale_ids: read_string_array(r)?,
937            server_uris: read_string_array(r)?,
938        })
939    }
940
941    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
942        node_ids::FIND_SERVERS_REQUEST.encode(w)?;
943        encode_request_header(&self.request_header, w)?;
944        write_string(w, &self.endpoint_url)?;
945        write_string_array(w, &self.locale_ids)?;
946        write_string_array(w, &self.server_uris)?;
947        Ok(())
948    }
949}
950
951/// `FindServersResponse`.
952#[derive(Debug, Clone, PartialEq)]
953pub struct FindServersResponse {
954    /// Common response header.
955    pub response_header: ResponseHeader,
956    /// The servers known at this endpoint.
957    pub servers: Vec<ApplicationDescription>,
958}
959
960impl FindServersResponse {
961    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
962        Ok(Self {
963            response_header: decode_response_header(r)?,
964            servers: read_array::<ApplicationDescription>(r)?,
965        })
966    }
967
968    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
969        node_ids::FIND_SERVERS_RESPONSE.encode(w)?;
970        encode_response_header(&self.response_header, w)?;
971        write_array(w, &self.servers, "Servers")?;
972        Ok(())
973    }
974}
975
976// ---------------------------------------------------------------------------
977// Subscription / MonitoredItem (Part 4 §5.13 / §5.14).
978// ---------------------------------------------------------------------------
979
980/// `CreateSubscriptionRequest`.
981#[derive(Debug, Clone, PartialEq)]
982pub struct CreateSubscriptionRequest {
983    /// Common request header.
984    pub request_header: RequestHeader,
985    /// Requested publishing interval in milliseconds.
986    pub requested_publishing_interval: f64,
987    /// Requested lifetime count.
988    pub requested_lifetime_count: u32,
989    /// Requested max keep-alive count.
990    pub requested_max_keep_alive_count: u32,
991    /// Max notifications per publish (`0` = unlimited).
992    pub max_notifications_per_publish: u32,
993    /// Whether publishing is initially enabled.
994    pub publishing_enabled: bool,
995    /// Subscription priority.
996    pub priority: u8,
997}
998
999impl CreateSubscriptionRequest {
1000    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1001        Ok(Self {
1002            request_header: decode_request_header(r)?,
1003            requested_publishing_interval: r.read_f64()?,
1004            requested_lifetime_count: r.read_u32()?,
1005            requested_max_keep_alive_count: r.read_u32()?,
1006            max_notifications_per_publish: r.read_u32()?,
1007            publishing_enabled: r.read_u8()? != 0,
1008            priority: r.read_u8()?,
1009        })
1010    }
1011
1012    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1013        node_ids::CREATE_SUBSCRIPTION_REQUEST.encode(w)?;
1014        encode_request_header(&self.request_header, w)?;
1015        w.write_f64(self.requested_publishing_interval);
1016        w.write_u32(self.requested_lifetime_count);
1017        w.write_u32(self.requested_max_keep_alive_count);
1018        w.write_u32(self.max_notifications_per_publish);
1019        w.write_u8(u8::from(self.publishing_enabled));
1020        w.write_u8(self.priority);
1021        Ok(())
1022    }
1023}
1024
1025/// `CreateSubscriptionResponse`.
1026#[derive(Debug, Clone, PartialEq)]
1027pub struct CreateSubscriptionResponse {
1028    /// Common response header.
1029    pub response_header: ResponseHeader,
1030    /// Assigned subscription id.
1031    pub subscription_id: u32,
1032    /// Revised publishing interval.
1033    pub revised_publishing_interval: f64,
1034    /// Revised lifetime count.
1035    pub revised_lifetime_count: u32,
1036    /// Revised max keep-alive count.
1037    pub revised_max_keep_alive_count: u32,
1038}
1039
1040impl CreateSubscriptionResponse {
1041    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1042        Ok(Self {
1043            response_header: decode_response_header(r)?,
1044            subscription_id: r.read_u32()?,
1045            revised_publishing_interval: r.read_f64()?,
1046            revised_lifetime_count: r.read_u32()?,
1047            revised_max_keep_alive_count: r.read_u32()?,
1048        })
1049    }
1050
1051    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1052        node_ids::CREATE_SUBSCRIPTION_RESPONSE.encode(w)?;
1053        encode_response_header(&self.response_header, w)?;
1054        w.write_u32(self.subscription_id);
1055        w.write_f64(self.revised_publishing_interval);
1056        w.write_u32(self.revised_lifetime_count);
1057        w.write_u32(self.revised_max_keep_alive_count);
1058        Ok(())
1059    }
1060}
1061
1062/// `SetPublishingModeRequest`.
1063#[derive(Debug, Clone, PartialEq)]
1064pub struct SetPublishingModeRequest {
1065    /// Common request header.
1066    pub request_header: RequestHeader,
1067    /// New publishing-enabled flag.
1068    pub publishing_enabled: bool,
1069    /// Subscriptions to apply it to.
1070    pub subscription_ids: Vec<u32>,
1071}
1072
1073impl SetPublishingModeRequest {
1074    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1075        Ok(Self {
1076            request_header: decode_request_header(r)?,
1077            publishing_enabled: r.read_u8()? != 0,
1078            subscription_ids: read_u32_array(r)?,
1079        })
1080    }
1081
1082    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1083        node_ids::SET_PUBLISHING_MODE_REQUEST.encode(w)?;
1084        encode_request_header(&self.request_header, w)?;
1085        w.write_u8(u8::from(self.publishing_enabled));
1086        write_u32_array(w, &self.subscription_ids)?;
1087        Ok(())
1088    }
1089}
1090
1091/// `SetPublishingModeResponse`.
1092#[derive(Debug, Clone, PartialEq)]
1093pub struct SetPublishingModeResponse {
1094    /// Common response header.
1095    pub response_header: ResponseHeader,
1096    /// Per-subscription status.
1097    pub results: Vec<u32>,
1098}
1099
1100impl SetPublishingModeResponse {
1101    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1102        let response_header = decode_response_header(r)?;
1103        let results = read_u32_array(r)?;
1104        skip_diagnostic_info_array(r)?;
1105        Ok(Self {
1106            response_header,
1107            results,
1108        })
1109    }
1110
1111    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1112        node_ids::SET_PUBLISHING_MODE_RESPONSE.encode(w)?;
1113        encode_response_header(&self.response_header, w)?;
1114        write_u32_array(w, &self.results)?;
1115        write_empty_diagnostic_info_array(w);
1116        Ok(())
1117    }
1118}
1119
1120/// `MonitoringParameters` (Part 4 §7.21).
1121#[derive(Debug, Clone, PartialEq)]
1122pub struct MonitoringParameters {
1123    /// Client-assigned handle echoed in notifications.
1124    pub client_handle: u32,
1125    /// Requested sampling interval in milliseconds.
1126    pub sampling_interval: f64,
1127    /// Monitoring filter (null ExtensionObject = none).
1128    pub filter: ExtensionObject,
1129    /// Notification queue size.
1130    pub queue_size: u32,
1131    /// Discard oldest on overflow.
1132    pub discard_oldest: bool,
1133}
1134
1135impl UaEncode for MonitoringParameters {
1136    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1137        w.write_u32(self.client_handle);
1138        w.write_f64(self.sampling_interval);
1139        self.filter.encode(w)?;
1140        w.write_u32(self.queue_size);
1141        w.write_u8(u8::from(self.discard_oldest));
1142        Ok(())
1143    }
1144}
1145
1146impl UaDecode for MonitoringParameters {
1147    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1148        Ok(Self {
1149            client_handle: r.read_u32()?,
1150            sampling_interval: r.read_f64()?,
1151            filter: ExtensionObject::decode(r)?,
1152            queue_size: r.read_u32()?,
1153            discard_oldest: r.read_u8()? != 0,
1154        })
1155    }
1156}
1157
1158/// `MonitoredItemCreateRequest`.
1159#[derive(Debug, Clone, PartialEq)]
1160pub struct MonitoredItemCreateRequest {
1161    /// What to monitor.
1162    pub item_to_monitor: ReadValueId,
1163    /// MonitoringMode (0 Disabled / 1 Sampling / 2 Reporting).
1164    pub monitoring_mode: i32,
1165    /// Requested parameters.
1166    pub requested_parameters: MonitoringParameters,
1167}
1168
1169impl UaEncode for MonitoredItemCreateRequest {
1170    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1171        self.item_to_monitor.encode(w)?;
1172        w.write_i32(self.monitoring_mode);
1173        self.requested_parameters.encode(w)?;
1174        Ok(())
1175    }
1176}
1177
1178impl UaDecode for MonitoredItemCreateRequest {
1179    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1180        Ok(Self {
1181            item_to_monitor: ReadValueId::decode(r)?,
1182            monitoring_mode: r.read_i32()?,
1183            requested_parameters: MonitoringParameters::decode(r)?,
1184        })
1185    }
1186}
1187
1188/// `MonitoredItemCreateResult`.
1189#[derive(Debug, Clone, PartialEq)]
1190pub struct MonitoredItemCreateResult {
1191    /// Per-item status.
1192    pub status_code: u32,
1193    /// Assigned monitored item id.
1194    pub monitored_item_id: u32,
1195    /// Revised sampling interval.
1196    pub revised_sampling_interval: f64,
1197    /// Revised queue size.
1198    pub revised_queue_size: u32,
1199    /// Filter result (null ExtensionObject = none).
1200    pub filter_result: ExtensionObject,
1201}
1202
1203impl UaEncode for MonitoredItemCreateResult {
1204    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1205        w.write_u32(self.status_code);
1206        w.write_u32(self.monitored_item_id);
1207        w.write_f64(self.revised_sampling_interval);
1208        w.write_u32(self.revised_queue_size);
1209        self.filter_result.encode(w)?;
1210        Ok(())
1211    }
1212}
1213
1214impl UaDecode for MonitoredItemCreateResult {
1215    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1216        Ok(Self {
1217            status_code: r.read_u32()?,
1218            monitored_item_id: r.read_u32()?,
1219            revised_sampling_interval: r.read_f64()?,
1220            revised_queue_size: r.read_u32()?,
1221            filter_result: ExtensionObject::decode(r)?,
1222        })
1223    }
1224}
1225
1226/// A null monitoring filter / filter result.
1227#[must_use]
1228pub fn null_filter() -> ExtensionObject {
1229    null_extension_object()
1230}
1231
1232/// `CreateMonitoredItemsRequest`.
1233#[derive(Debug, Clone, PartialEq)]
1234pub struct CreateMonitoredItemsRequest {
1235    /// Common request header.
1236    pub request_header: RequestHeader,
1237    /// Target subscription.
1238    pub subscription_id: u32,
1239    /// TimestampsToReturn.
1240    pub timestamps_to_return: i32,
1241    /// Items to create.
1242    pub items_to_create: Vec<MonitoredItemCreateRequest>,
1243}
1244
1245impl CreateMonitoredItemsRequest {
1246    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1247        Ok(Self {
1248            request_header: decode_request_header(r)?,
1249            subscription_id: r.read_u32()?,
1250            timestamps_to_return: r.read_i32()?,
1251            items_to_create: read_array::<MonitoredItemCreateRequest>(r)?,
1252        })
1253    }
1254
1255    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1256        node_ids::CREATE_MONITORED_ITEMS_REQUEST.encode(w)?;
1257        encode_request_header(&self.request_header, w)?;
1258        w.write_u32(self.subscription_id);
1259        w.write_i32(self.timestamps_to_return);
1260        write_array(w, &self.items_to_create, "ItemsToCreate")?;
1261        Ok(())
1262    }
1263}
1264
1265/// `CreateMonitoredItemsResponse`.
1266#[derive(Debug, Clone, PartialEq)]
1267pub struct CreateMonitoredItemsResponse {
1268    /// Common response header.
1269    pub response_header: ResponseHeader,
1270    /// Per-item results.
1271    pub results: Vec<MonitoredItemCreateResult>,
1272}
1273
1274impl CreateMonitoredItemsResponse {
1275    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1276        let response_header = decode_response_header(r)?;
1277        let results = read_array::<MonitoredItemCreateResult>(r)?;
1278        skip_diagnostic_info_array(r)?;
1279        Ok(Self {
1280            response_header,
1281            results,
1282        })
1283    }
1284
1285    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1286        node_ids::CREATE_MONITORED_ITEMS_RESPONSE.encode(w)?;
1287        encode_response_header(&self.response_header, w)?;
1288        write_array(w, &self.results, "Results")?;
1289        write_empty_diagnostic_info_array(w);
1290        Ok(())
1291    }
1292}
1293
1294/// `SubscriptionAcknowledgement`.
1295#[derive(Debug, Clone, PartialEq, Eq)]
1296pub struct SubscriptionAcknowledgement {
1297    /// Subscription id.
1298    pub subscription_id: u32,
1299    /// Acknowledged sequence number.
1300    pub sequence_number: u32,
1301}
1302
1303impl UaEncode for SubscriptionAcknowledgement {
1304    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1305        w.write_u32(self.subscription_id);
1306        w.write_u32(self.sequence_number);
1307        Ok(())
1308    }
1309}
1310
1311impl UaDecode for SubscriptionAcknowledgement {
1312    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1313        Ok(Self {
1314            subscription_id: r.read_u32()?,
1315            sequence_number: r.read_u32()?,
1316        })
1317    }
1318}
1319
1320/// `MonitoredItemNotification` — one changed value in a `DataChangeNotification`.
1321#[derive(Debug, Clone, PartialEq)]
1322pub struct MonitoredItemNotification {
1323    /// The monitored item's client handle.
1324    pub client_handle: u32,
1325    /// The new value.
1326    pub value: DataValue,
1327}
1328
1329impl UaEncode for MonitoredItemNotification {
1330    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1331        w.write_u32(self.client_handle);
1332        self.value.encode(w)?;
1333        Ok(())
1334    }
1335}
1336
1337impl UaDecode for MonitoredItemNotification {
1338    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1339        Ok(Self {
1340            client_handle: r.read_u32()?,
1341            value: DataValue::decode(r)?,
1342        })
1343    }
1344}
1345
1346/// `DataChangeNotification` (Part 4 §7.25.2) — carried inside an
1347/// `ExtensionObject` in a [`NotificationMessage`].
1348#[derive(Debug, Clone, PartialEq)]
1349pub struct DataChangeNotification {
1350    /// The changed monitored items.
1351    pub monitored_items: Vec<MonitoredItemNotification>,
1352}
1353
1354impl DataChangeNotification {
1355    /// Wraps this notification in its `ExtensionObject` (TypeId 811).
1356    ///
1357    /// # Errors
1358    /// [`EncodeError`] on an oversized field.
1359    pub fn to_extension_object(&self) -> Result<ExtensionObject, EncodeError> {
1360        let mut w = UaWriter::new();
1361        write_array(&mut w, &self.monitored_items, "MonitoredItems")?;
1362        write_empty_diagnostic_info_array(&mut w);
1363        Ok(ExtensionObject {
1364            type_id: node_ids::DATA_CHANGE_NOTIFICATION,
1365            body: ExtensionObjectBody::ByteString(w.into_vec()),
1366        })
1367    }
1368
1369    /// Recovers a `DataChangeNotification` from its `ExtensionObject`.
1370    ///
1371    /// # Errors
1372    /// [`DecodeError`] if the body is not a matching ByteString.
1373    pub fn from_extension_object(eo: &ExtensionObject) -> Result<Self, DecodeError> {
1374        match &eo.body {
1375            ExtensionObjectBody::ByteString(b) => {
1376                let mut r = UaReader::new(b);
1377                let monitored_items = read_array::<MonitoredItemNotification>(&mut r)?;
1378                skip_diagnostic_info_array(&mut r)?;
1379                Ok(Self { monitored_items })
1380            }
1381            _ => Err(DecodeError::MalformedMessage {
1382                message: "DataChangeNotification ExtensionObject is not a ByteString body",
1383            }),
1384        }
1385    }
1386}
1387
1388/// `NotificationMessage` (Part 4 §7.26).
1389#[derive(Debug, Clone, PartialEq)]
1390pub struct NotificationMessage {
1391    /// Sequence number.
1392    pub sequence_number: u32,
1393    /// Publish time (DateTime ticks).
1394    pub publish_time: i64,
1395    /// Notification data (each an `ExtensionObject`, e.g. DataChangeNotification).
1396    pub notification_data: Vec<ExtensionObject>,
1397}
1398
1399impl UaEncode for NotificationMessage {
1400    fn encode(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1401        w.write_u32(self.sequence_number);
1402        w.write_i64(self.publish_time);
1403        write_array(w, &self.notification_data, "NotificationData")?;
1404        Ok(())
1405    }
1406}
1407
1408impl UaDecode for NotificationMessage {
1409    fn decode(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1410        Ok(Self {
1411            sequence_number: r.read_u32()?,
1412            publish_time: r.read_i64()?,
1413            notification_data: read_array::<ExtensionObject>(r)?,
1414        })
1415    }
1416}
1417
1418/// `PublishRequest`.
1419#[derive(Debug, Clone, PartialEq)]
1420pub struct PublishRequest {
1421    /// Common request header.
1422    pub request_header: RequestHeader,
1423    /// Acknowledgements of previously received notifications.
1424    pub subscription_acknowledgements: Vec<SubscriptionAcknowledgement>,
1425}
1426
1427impl PublishRequest {
1428    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1429        Ok(Self {
1430            request_header: decode_request_header(r)?,
1431            subscription_acknowledgements: read_array::<SubscriptionAcknowledgement>(r)?,
1432        })
1433    }
1434
1435    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1436        node_ids::PUBLISH_REQUEST.encode(w)?;
1437        encode_request_header(&self.request_header, w)?;
1438        write_array(w, &self.subscription_acknowledgements, "Acks")?;
1439        Ok(())
1440    }
1441}
1442
1443/// `PublishResponse`.
1444#[derive(Debug, Clone, PartialEq)]
1445pub struct PublishResponse {
1446    /// Common response header.
1447    pub response_header: ResponseHeader,
1448    /// Subscription the notification belongs to.
1449    pub subscription_id: u32,
1450    /// Sequence numbers still available for Republish.
1451    pub available_sequence_numbers: Vec<u32>,
1452    /// Whether more notifications are queued.
1453    pub more_notifications: bool,
1454    /// The notification message.
1455    pub notification_message: NotificationMessage,
1456    /// Acknowledgement results.
1457    pub results: Vec<u32>,
1458}
1459
1460impl PublishResponse {
1461    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1462        let response_header = decode_response_header(r)?;
1463        let subscription_id = r.read_u32()?;
1464        let available_sequence_numbers = read_u32_array(r)?;
1465        let more_notifications = r.read_u8()? != 0;
1466        let notification_message = NotificationMessage::decode(r)?;
1467        let results = read_u32_array(r)?;
1468        skip_diagnostic_info_array(r)?;
1469        Ok(Self {
1470            response_header,
1471            subscription_id,
1472            available_sequence_numbers,
1473            more_notifications,
1474            notification_message,
1475            results,
1476        })
1477    }
1478
1479    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1480        node_ids::PUBLISH_RESPONSE.encode(w)?;
1481        encode_response_header(&self.response_header, w)?;
1482        w.write_u32(self.subscription_id);
1483        write_u32_array(w, &self.available_sequence_numbers)?;
1484        w.write_u8(u8::from(self.more_notifications));
1485        self.notification_message.encode(w)?;
1486        write_u32_array(w, &self.results)?;
1487        write_empty_diagnostic_info_array(w);
1488        Ok(())
1489    }
1490}
1491
1492/// `DeleteSubscriptionsRequest`.
1493#[derive(Debug, Clone, PartialEq)]
1494pub struct DeleteSubscriptionsRequest {
1495    /// Common request header.
1496    pub request_header: RequestHeader,
1497    /// Subscriptions to delete.
1498    pub subscription_ids: Vec<u32>,
1499}
1500
1501impl DeleteSubscriptionsRequest {
1502    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1503        Ok(Self {
1504            request_header: decode_request_header(r)?,
1505            subscription_ids: read_u32_array(r)?,
1506        })
1507    }
1508
1509    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1510        node_ids::DELETE_SUBSCRIPTIONS_REQUEST.encode(w)?;
1511        encode_request_header(&self.request_header, w)?;
1512        write_u32_array(w, &self.subscription_ids)?;
1513        Ok(())
1514    }
1515}
1516
1517/// `DeleteSubscriptionsResponse`.
1518#[derive(Debug, Clone, PartialEq)]
1519pub struct DeleteSubscriptionsResponse {
1520    /// Common response header.
1521    pub response_header: ResponseHeader,
1522    /// Per-subscription status.
1523    pub results: Vec<u32>,
1524}
1525
1526impl DeleteSubscriptionsResponse {
1527    fn decode_body(r: &mut UaReader<'_>) -> Result<Self, DecodeError> {
1528        let response_header = decode_response_header(r)?;
1529        let results = read_u32_array(r)?;
1530        skip_diagnostic_info_array(r)?;
1531        Ok(Self {
1532            response_header,
1533            results,
1534        })
1535    }
1536
1537    fn encode_into(&self, w: &mut UaWriter) -> Result<(), EncodeError> {
1538        node_ids::DELETE_SUBSCRIPTIONS_RESPONSE.encode(w)?;
1539        encode_response_header(&self.response_header, w)?;
1540        write_u32_array(w, &self.results)?;
1541        write_empty_diagnostic_info_array(w);
1542        Ok(())
1543    }
1544}
1545
1546// ---------------------------------------------------------------------------
1547// Dispatch enums.
1548// ---------------------------------------------------------------------------
1549
1550/// Any decodable service request.
1551#[derive(Debug, Clone, PartialEq)]
1552pub enum ServiceRequest {
1553    /// CreateSession.
1554    CreateSession(CreateSessionRequest),
1555    /// ActivateSession.
1556    ActivateSession(ActivateSessionRequest),
1557    /// CloseSession.
1558    CloseSession(CloseSessionRequest),
1559    /// Read.
1560    Read(ReadRequest),
1561    /// Write.
1562    Write(WriteRequest),
1563    /// Browse.
1564    Browse(BrowseRequest),
1565    /// GetEndpoints.
1566    GetEndpoints(GetEndpointsRequest),
1567    /// FindServers.
1568    FindServers(FindServersRequest),
1569    /// CreateSubscription.
1570    CreateSubscription(CreateSubscriptionRequest),
1571    /// SetPublishingMode.
1572    SetPublishingMode(SetPublishingModeRequest),
1573    /// CreateMonitoredItems.
1574    CreateMonitoredItems(CreateMonitoredItemsRequest),
1575    /// Publish.
1576    Publish(PublishRequest),
1577    /// DeleteSubscriptions.
1578    DeleteSubscriptions(DeleteSubscriptionsRequest),
1579    /// Call.
1580    Call(CallRequest),
1581}
1582
1583impl ServiceRequest {
1584    /// Encodes the full body (`TypeId` + fields).
1585    ///
1586    /// # Errors
1587    /// [`EncodeError`] on an oversized field.
1588    pub fn encode(&self) -> Result<Vec<u8>, EncodeError> {
1589        let mut w = UaWriter::new();
1590        match self {
1591            Self::CreateSession(m) => m.encode_into(&mut w)?,
1592            Self::ActivateSession(m) => m.encode_into(&mut w)?,
1593            Self::CloseSession(m) => m.encode_into(&mut w)?,
1594            Self::Read(m) => m.encode_into(&mut w)?,
1595            Self::Write(m) => m.encode_into(&mut w)?,
1596            Self::Browse(m) => m.encode_into(&mut w)?,
1597            Self::GetEndpoints(m) => m.encode_into(&mut w)?,
1598            Self::FindServers(m) => m.encode_into(&mut w)?,
1599            Self::CreateSubscription(m) => m.encode_into(&mut w)?,
1600            Self::SetPublishingMode(m) => m.encode_into(&mut w)?,
1601            Self::CreateMonitoredItems(m) => m.encode_into(&mut w)?,
1602            Self::Publish(m) => m.encode_into(&mut w)?,
1603            Self::DeleteSubscriptions(m) => m.encode_into(&mut w)?,
1604            Self::Call(m) => m.encode_into(&mut w)?,
1605        }
1606        Ok(w.into_vec())
1607    }
1608
1609    /// Decodes a request body, dispatching on the leading `TypeId`.
1610    ///
1611    /// # Errors
1612    /// [`DecodeError`] on truncation or an unsupported service type.
1613    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {
1614        let mut r = UaReader::new(body);
1615        let type_id = NodeId::decode(&mut r)?;
1616        Ok(if type_id == node_ids::CREATE_SESSION_REQUEST {
1617            Self::CreateSession(CreateSessionRequest::decode_body(&mut r)?)
1618        } else if type_id == node_ids::ACTIVATE_SESSION_REQUEST {
1619            Self::ActivateSession(ActivateSessionRequest::decode_body(&mut r)?)
1620        } else if type_id == node_ids::CLOSE_SESSION_REQUEST {
1621            Self::CloseSession(CloseSessionRequest::decode_body(&mut r)?)
1622        } else if type_id == node_ids::READ_REQUEST {
1623            Self::Read(ReadRequest::decode_body(&mut r)?)
1624        } else if type_id == node_ids::WRITE_REQUEST {
1625            Self::Write(WriteRequest::decode_body(&mut r)?)
1626        } else if type_id == node_ids::BROWSE_REQUEST {
1627            Self::Browse(BrowseRequest::decode_body(&mut r)?)
1628        } else if type_id == node_ids::GET_ENDPOINTS_REQUEST {
1629            Self::GetEndpoints(GetEndpointsRequest::decode_body(&mut r)?)
1630        } else if type_id == node_ids::FIND_SERVERS_REQUEST {
1631            Self::FindServers(FindServersRequest::decode_body(&mut r)?)
1632        } else if type_id == node_ids::CREATE_SUBSCRIPTION_REQUEST {
1633            Self::CreateSubscription(CreateSubscriptionRequest::decode_body(&mut r)?)
1634        } else if type_id == node_ids::SET_PUBLISHING_MODE_REQUEST {
1635            Self::SetPublishingMode(SetPublishingModeRequest::decode_body(&mut r)?)
1636        } else if type_id == node_ids::CREATE_MONITORED_ITEMS_REQUEST {
1637            Self::CreateMonitoredItems(CreateMonitoredItemsRequest::decode_body(&mut r)?)
1638        } else if type_id == node_ids::PUBLISH_REQUEST {
1639            Self::Publish(PublishRequest::decode_body(&mut r)?)
1640        } else if type_id == node_ids::DELETE_SUBSCRIPTIONS_REQUEST {
1641            Self::DeleteSubscriptions(DeleteSubscriptionsRequest::decode_body(&mut r)?)
1642        } else if type_id == node_ids::CALL_REQUEST {
1643            Self::Call(CallRequest::decode_body(&mut r)?)
1644        } else {
1645            return Err(DecodeError::MalformedMessage {
1646                message: "unsupported OPC-UA service request type",
1647            });
1648        })
1649    }
1650}
1651
1652/// Any decodable service response.
1653#[derive(Debug, Clone, PartialEq)]
1654pub enum ServiceResponse {
1655    /// CreateSession.
1656    CreateSession(CreateSessionResponse),
1657    /// ActivateSession.
1658    ActivateSession(ActivateSessionResponse),
1659    /// CloseSession.
1660    CloseSession(CloseSessionResponse),
1661    /// Read.
1662    Read(ReadResponse),
1663    /// Write.
1664    Write(WriteResponse),
1665    /// Browse.
1666    Browse(BrowseResponse),
1667    /// GetEndpoints.
1668    GetEndpoints(GetEndpointsResponse),
1669    /// FindServers.
1670    FindServers(FindServersResponse),
1671    /// CreateSubscription.
1672    CreateSubscription(CreateSubscriptionResponse),
1673    /// SetPublishingMode.
1674    SetPublishingMode(SetPublishingModeResponse),
1675    /// CreateMonitoredItems.
1676    CreateMonitoredItems(CreateMonitoredItemsResponse),
1677    /// Publish.
1678    Publish(PublishResponse),
1679    /// DeleteSubscriptions.
1680    DeleteSubscriptions(DeleteSubscriptionsResponse),
1681    /// Call.
1682    Call(CallResponse),
1683}
1684
1685impl ServiceResponse {
1686    /// Encodes the full body (`TypeId` + fields).
1687    ///
1688    /// # Errors
1689    /// [`EncodeError`] on an oversized field.
1690    pub fn encode(&self) -> Result<Vec<u8>, EncodeError> {
1691        let mut w = UaWriter::new();
1692        match self {
1693            Self::CreateSession(m) => m.encode_into(&mut w)?,
1694            Self::ActivateSession(m) => m.encode_into(&mut w)?,
1695            Self::CloseSession(m) => m.encode_into(&mut w)?,
1696            Self::Read(m) => m.encode_into(&mut w)?,
1697            Self::Write(m) => m.encode_into(&mut w)?,
1698            Self::Browse(m) => m.encode_into(&mut w)?,
1699            Self::GetEndpoints(m) => m.encode_into(&mut w)?,
1700            Self::FindServers(m) => m.encode_into(&mut w)?,
1701            Self::CreateSubscription(m) => m.encode_into(&mut w)?,
1702            Self::SetPublishingMode(m) => m.encode_into(&mut w)?,
1703            Self::CreateMonitoredItems(m) => m.encode_into(&mut w)?,
1704            Self::Publish(m) => m.encode_into(&mut w)?,
1705            Self::DeleteSubscriptions(m) => m.encode_into(&mut w)?,
1706            Self::Call(m) => m.encode_into(&mut w)?,
1707        }
1708        Ok(w.into_vec())
1709    }
1710
1711    /// Decodes a response body, dispatching on the leading `TypeId`.
1712    ///
1713    /// # Errors
1714    /// [`DecodeError`] on truncation or an unsupported response type.
1715    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {
1716        let mut r = UaReader::new(body);
1717        let type_id = NodeId::decode(&mut r)?;
1718        Ok(if type_id == node_ids::CREATE_SESSION_RESPONSE {
1719            Self::CreateSession(CreateSessionResponse::decode_body(&mut r)?)
1720        } else if type_id == node_ids::ACTIVATE_SESSION_RESPONSE {
1721            Self::ActivateSession(ActivateSessionResponse::decode_body(&mut r)?)
1722        } else if type_id == node_ids::CLOSE_SESSION_RESPONSE {
1723            Self::CloseSession(CloseSessionResponse::decode_body(&mut r)?)
1724        } else if type_id == node_ids::READ_RESPONSE {
1725            Self::Read(ReadResponse::decode_body(&mut r)?)
1726        } else if type_id == node_ids::WRITE_RESPONSE {
1727            Self::Write(WriteResponse::decode_body(&mut r)?)
1728        } else if type_id == node_ids::BROWSE_RESPONSE {
1729            Self::Browse(BrowseResponse::decode_body(&mut r)?)
1730        } else if type_id == node_ids::GET_ENDPOINTS_RESPONSE {
1731            Self::GetEndpoints(GetEndpointsResponse::decode_body(&mut r)?)
1732        } else if type_id == node_ids::FIND_SERVERS_RESPONSE {
1733            Self::FindServers(FindServersResponse::decode_body(&mut r)?)
1734        } else if type_id == node_ids::CREATE_SUBSCRIPTION_RESPONSE {
1735            Self::CreateSubscription(CreateSubscriptionResponse::decode_body(&mut r)?)
1736        } else if type_id == node_ids::SET_PUBLISHING_MODE_RESPONSE {
1737            Self::SetPublishingMode(SetPublishingModeResponse::decode_body(&mut r)?)
1738        } else if type_id == node_ids::CREATE_MONITORED_ITEMS_RESPONSE {
1739            Self::CreateMonitoredItems(CreateMonitoredItemsResponse::decode_body(&mut r)?)
1740        } else if type_id == node_ids::PUBLISH_RESPONSE {
1741            Self::Publish(PublishResponse::decode_body(&mut r)?)
1742        } else if type_id == node_ids::DELETE_SUBSCRIPTIONS_RESPONSE {
1743            Self::DeleteSubscriptions(DeleteSubscriptionsResponse::decode_body(&mut r)?)
1744        } else if type_id == node_ids::CALL_RESPONSE {
1745            Self::Call(CallResponse::decode_body(&mut r)?)
1746        } else {
1747            return Err(DecodeError::MalformedMessage {
1748                message: "unsupported OPC-UA service response type",
1749            });
1750        })
1751    }
1752}
1753
1754// RequestHeader/ResponseHeader encode/decode are crate-private in opcua-uacp,
1755// so re-do the field order here against the public structs.
1756fn encode_request_header(h: &RequestHeader, w: &mut UaWriter) -> Result<(), EncodeError> {
1757    h.authentication_token.encode(w)?;
1758    w.write_i64(h.timestamp);
1759    w.write_u32(h.request_handle);
1760    w.write_u32(h.return_diagnostics);
1761    write_string(w, &h.audit_entry_id)?;
1762    w.write_u32(h.timeout_hint);
1763    h.additional_header.encode(w)?;
1764    Ok(())
1765}
1766
1767fn decode_request_header(r: &mut UaReader<'_>) -> Result<RequestHeader, DecodeError> {
1768    Ok(RequestHeader {
1769        authentication_token: NodeId::decode(r)?,
1770        timestamp: r.read_i64()?,
1771        request_handle: r.read_u32()?,
1772        return_diagnostics: r.read_u32()?,
1773        audit_entry_id: read_string(r)?,
1774        timeout_hint: r.read_u32()?,
1775        additional_header: zerodds_opcua_gateway::data_value::ExtensionObject::decode(r)?,
1776    })
1777}
1778
1779fn encode_response_header(h: &ResponseHeader, w: &mut UaWriter) -> Result<(), EncodeError> {
1780    w.write_i64(h.timestamp);
1781    w.write_u32(h.request_handle);
1782    w.write_u32(h.service_result);
1783    w.write_u8(0); // empty DiagnosticInfo
1784    write_string_array(w, &h.string_table)?;
1785    h.additional_header.encode(w)?;
1786    Ok(())
1787}
1788
1789fn decode_response_header(r: &mut UaReader<'_>) -> Result<ResponseHeader, DecodeError> {
1790    let timestamp = r.read_i64()?;
1791    let request_handle = r.read_u32()?;
1792    let service_result = r.read_u32()?;
1793    // empty DiagnosticInfo (we only ever emit mask 0; tolerate present fields).
1794    crate::wire::skip_one_diagnostic_info(r)?;
1795    let string_table = read_string_array(r)?;
1796    let additional_header = zerodds_opcua_gateway::data_value::ExtensionObject::decode(r)?;
1797    Ok(ResponseHeader {
1798        timestamp,
1799        request_handle,
1800        service_result,
1801        string_table,
1802        additional_header,
1803    })
1804}
1805
1806#[cfg(test)]
1807mod tests {
1808    use super::*;
1809    use zerodds_opcua_gateway::data_value::VariantValue;
1810    use zerodds_opcua_uacp::securechannel::null_extension_object;
1811
1812    fn req_header() -> RequestHeader {
1813        RequestHeader::new(NodeId::numeric(0, 0), 1)
1814    }
1815
1816    #[test]
1817    fn read_request_round_trips() {
1818        let req = ServiceRequest::Read(ReadRequest {
1819            request_header: req_header(),
1820            max_age: 0.0,
1821            timestamps_to_return: 0,
1822            nodes_to_read: alloc::vec![ReadValueId {
1823                node_id: NodeId::numeric(1, 42),
1824                attribute_id: ATTRIBUTE_VALUE,
1825                index_range: String::new(),
1826                data_encoding: QualifiedName {
1827                    namespace_index: 0,
1828                    name: String::new(),
1829                },
1830            }],
1831        });
1832        let body = req.encode().expect("encode");
1833        assert_eq!(ServiceRequest::decode(&body).expect("decode"), req);
1834    }
1835
1836    #[test]
1837    fn read_response_round_trips() {
1838        let resp = ServiceResponse::Read(ReadResponse {
1839            response_header: ResponseHeader::new(1, 0),
1840            results: alloc::vec![DataValue::new_value(
1841                Variant::scalar(VariantValue::Int32(99)),
1842                0,
1843                0,
1844            )],
1845        });
1846        let body = resp.encode().expect("encode");
1847        assert_eq!(ServiceResponse::decode(&body).expect("decode"), resp);
1848    }
1849
1850    #[test]
1851    fn write_round_trips() {
1852        let req = ServiceRequest::Write(WriteRequest {
1853            request_header: req_header(),
1854            nodes_to_write: alloc::vec![WriteValue {
1855                node_id: NodeId::numeric(1, 7),
1856                attribute_id: ATTRIBUTE_VALUE,
1857                index_range: String::new(),
1858                value: DataValue::new_value(Variant::scalar(VariantValue::Int32(5)), 0, 0),
1859            }],
1860        });
1861        let body = req.encode().expect("encode");
1862        assert_eq!(ServiceRequest::decode(&body).expect("decode"), req);
1863
1864        let resp = ServiceResponse::Write(WriteResponse {
1865            response_header: ResponseHeader::new(1, 0),
1866            results: alloc::vec![0],
1867        });
1868        let rb = resp.encode().expect("encode");
1869        assert_eq!(ServiceResponse::decode(&rb).expect("decode"), resp);
1870    }
1871
1872    #[test]
1873    fn browse_round_trips() {
1874        let req = ServiceRequest::Browse(BrowseRequest {
1875            request_header: req_header(),
1876            view: ViewDescription::default(),
1877            requested_max_references_per_node: 100,
1878            nodes_to_browse: alloc::vec![BrowseDescription {
1879                node_id: NodeId::numeric(0, 85),
1880                browse_direction: 2,
1881                reference_type_id: NodeId::numeric(0, 33),
1882                include_subtypes: true,
1883                node_class_mask: 0,
1884                result_mask: 0x3F,
1885            }],
1886        });
1887        let body = req.encode().expect("encode");
1888        assert_eq!(ServiceRequest::decode(&body).expect("decode"), req);
1889
1890        let resp = ServiceResponse::Browse(BrowseResponse {
1891            response_header: ResponseHeader::new(1, 0),
1892            results: alloc::vec![BrowseResult {
1893                status_code: 0,
1894                continuation_point: Vec::new(),
1895                references: alloc::vec![ReferenceDescription {
1896                    reference_type_id: NodeId::numeric(0, 35),
1897                    is_forward: true,
1898                    node_id: ExpandedNodeId {
1899                        node_id: NodeId::numeric(1, 1),
1900                        namespace_uri: String::new(),
1901                        server_index: 0,
1902                    },
1903                    browse_name: QualifiedName {
1904                        namespace_index: 1,
1905                        name: String::from("Boiler"),
1906                    },
1907                    display_name: LocalizedText {
1908                        locale: None,
1909                        text: Some(String::from("Boiler")),
1910                    },
1911                    node_class: 1,
1912                    type_definition: ExpandedNodeId {
1913                        node_id: NodeId::numeric(0, 58),
1914                        namespace_uri: String::new(),
1915                        server_index: 0,
1916                    },
1917                }],
1918            }],
1919        });
1920        let rb = resp.encode().expect("encode");
1921        assert_eq!(ServiceResponse::decode(&rb).expect("decode"), resp);
1922    }
1923
1924    #[test]
1925    fn discovery_requests_round_trip() {
1926        let ge = ServiceRequest::GetEndpoints(GetEndpointsRequest {
1927            request_header: req_header(),
1928            endpoint_url: String::from("opc.tcp://x:4840"),
1929            locale_ids: alloc::vec![String::from("en")],
1930            profile_uris: Vec::new(),
1931        });
1932        assert_eq!(
1933            ServiceRequest::decode(&ge.encode().expect("e")).expect("d"),
1934            ge
1935        );
1936
1937        let fs = ServiceRequest::FindServers(FindServersRequest {
1938            request_header: req_header(),
1939            endpoint_url: String::from("opc.tcp://x:4840"),
1940            locale_ids: Vec::new(),
1941            server_uris: alloc::vec![String::from("urn:server")],
1942        });
1943        assert_eq!(
1944            ServiceRequest::decode(&fs.encode().expect("e")).expect("d"),
1945            fs
1946        );
1947
1948        // Empty responses round-trip through the dispatch too.
1949        let ger = ServiceResponse::GetEndpoints(GetEndpointsResponse {
1950            response_header: ResponseHeader::new(1, 0),
1951            endpoints: Vec::new(),
1952        });
1953        assert_eq!(
1954            ServiceResponse::decode(&ger.encode().expect("e")).expect("d"),
1955            ger
1956        );
1957    }
1958
1959    #[test]
1960    fn subscription_round_trips() {
1961        let cs = ServiceRequest::CreateSubscription(CreateSubscriptionRequest {
1962            request_header: req_header(),
1963            requested_publishing_interval: 500.0,
1964            requested_lifetime_count: 6000,
1965            requested_max_keep_alive_count: 20,
1966            max_notifications_per_publish: 0,
1967            publishing_enabled: true,
1968            priority: 5,
1969        });
1970        assert_eq!(
1971            ServiceRequest::decode(&cs.encode().expect("e")).expect("d"),
1972            cs
1973        );
1974
1975        let cm = ServiceRequest::CreateMonitoredItems(CreateMonitoredItemsRequest {
1976            request_header: req_header(),
1977            subscription_id: 1,
1978            timestamps_to_return: 0,
1979            items_to_create: alloc::vec![MonitoredItemCreateRequest {
1980                item_to_monitor: ReadValueId {
1981                    node_id: NodeId::numeric(1, 1),
1982                    attribute_id: ATTRIBUTE_VALUE,
1983                    index_range: String::new(),
1984                    data_encoding: QualifiedName {
1985                        namespace_index: 0,
1986                        name: String::new(),
1987                    },
1988                },
1989                monitoring_mode: 2,
1990                requested_parameters: MonitoringParameters {
1991                    client_handle: 7,
1992                    sampling_interval: 250.0,
1993                    filter: null_filter(),
1994                    queue_size: 4,
1995                    discard_oldest: true,
1996                },
1997            }],
1998        });
1999        assert_eq!(
2000            ServiceRequest::decode(&cm.encode().expect("e")).expect("d"),
2001            cm
2002        );
2003    }
2004
2005    #[test]
2006    fn publish_data_change_notification_round_trips() {
2007        // The DataChangeNotification survives the ExtensionObject wrapping.
2008        let dcn = DataChangeNotification {
2009            monitored_items: alloc::vec![MonitoredItemNotification {
2010                client_handle: 7,
2011                value: DataValue::new_value(Variant::scalar(VariantValue::Int32(99)), 0, 0),
2012            }],
2013        };
2014        let eo = dcn.to_extension_object().expect("wrap");
2015        assert_eq!(
2016            DataChangeNotification::from_extension_object(&eo).expect("unwrap"),
2017            dcn
2018        );
2019
2020        // And the whole PublishResponse round-trips through the dispatch.
2021        let resp = ServiceResponse::Publish(PublishResponse {
2022            response_header: ResponseHeader::new(1, 0),
2023            subscription_id: 3,
2024            available_sequence_numbers: alloc::vec![1],
2025            more_notifications: false,
2026            notification_message: NotificationMessage {
2027                sequence_number: 1,
2028                publish_time: 0,
2029                notification_data: alloc::vec![eo],
2030            },
2031            results: Vec::new(),
2032        });
2033        assert_eq!(
2034            ServiceResponse::decode(&resp.encode().expect("e")).expect("d"),
2035            resp
2036        );
2037    }
2038
2039    #[test]
2040    fn call_round_trips() {
2041        let req = ServiceRequest::Call(CallRequest {
2042            request_header: req_header(),
2043            methods_to_call: alloc::vec![CallMethodRequest {
2044                object_id: NodeId::numeric(0, 0),
2045                method_id: NodeId::numeric(1, 100),
2046                input_arguments: alloc::vec![Variant::scalar(VariantValue::String(String::from(
2047                    "group-1"
2048                )))],
2049            }],
2050        });
2051        let body = req.encode().expect("encode");
2052        assert_eq!(ServiceRequest::decode(&body).expect("decode"), req);
2053
2054        let resp = ServiceResponse::Call(CallResponse {
2055            response_header: ResponseHeader::new(1, 0),
2056            results: alloc::vec![CallMethodResult {
2057                status_code: 0,
2058                input_argument_results: alloc::vec![0],
2059                output_arguments: alloc::vec![Variant::scalar(VariantValue::UInt32(7))],
2060            }],
2061        });
2062        let rb = resp.encode().expect("encode");
2063        assert_eq!(ServiceResponse::decode(&rb).expect("decode"), resp);
2064    }
2065
2066    #[test]
2067    fn session_lifecycle_round_trips() {
2068        let close = ServiceRequest::CloseSession(CloseSessionRequest {
2069            request_header: req_header(),
2070            delete_subscriptions: true,
2071        });
2072        assert_eq!(
2073            ServiceRequest::decode(&close.encode().expect("e")).expect("d"),
2074            close
2075        );
2076
2077        let act = ServiceResponse::ActivateSession(ActivateSessionResponse {
2078            response_header: ResponseHeader::new(1, 0),
2079            server_nonce: alloc::vec![1, 2, 3],
2080            results: alloc::vec![0],
2081        });
2082        let _ = null_extension_object();
2083        assert_eq!(
2084            ServiceResponse::decode(&act.encode().expect("e")).expect("d"),
2085            act
2086        );
2087    }
2088}