opcua_types/
impls.rs

1use std::{self, fmt};
2
3use tracing::error;
4
5use crate::{
6    argument::Argument,
7    attribute::AttributeId,
8    byte_string::ByteString,
9    constants,
10    localized_text::LocalizedText,
11    node_id::NodeId,
12    profiles,
13    qualified_name::QualifiedName,
14    response_header::{AsRequestHandle, ResponseHeader},
15    status_code::StatusCode,
16    string::UAString,
17    variant::Variant,
18    AnonymousIdentityToken, ApplicationDescription, CallMethodRequest, DataTypeId, DataValue,
19    EndpointDescription, Error, ExpandedNodeId, HistoryUpdateType, IdentityCriteriaType,
20    MessageSecurityMode, MonitoredItemCreateRequest, MonitoringMode, MonitoringParameters,
21    NumericRange, ObjectId, ReadValueId, ServiceCounterDataType, ServiceFault, SignatureData,
22    UserNameIdentityToken, UserTokenPolicy, UserTokenType, WriteValue,
23};
24
25use super::PerformUpdateType;
26
27/// Implemented by messages
28pub trait MessageInfo {
29    /// The binary type id associated with the message
30    fn type_id(&self) -> ObjectId;
31    /// The JSON type id associated with the message.
32    fn json_type_id(&self) -> ObjectId;
33    /// The XML type id associated with the message.
34    fn xml_type_id(&self) -> ObjectId;
35    /// The data type id associated with the message.
36    fn data_type_id(&self) -> DataTypeId;
37}
38
39/// Trait implemented by all messages, allowing for custom message types.
40pub trait ExpandedMessageInfo {
41    /// The binary type id associated with the message.
42    fn full_type_id(&self) -> ExpandedNodeId;
43    /// The JSON type id associated with the message.
44    fn full_json_type_id(&self) -> ExpandedNodeId;
45    /// The XML type id associated with the message.
46    fn full_xml_type_id(&self) -> ExpandedNodeId;
47    /// The data type ID associated with the message.
48    fn full_data_type_id(&self) -> ExpandedNodeId;
49}
50
51impl<T> ExpandedMessageInfo for T
52where
53    T: MessageInfo,
54{
55    fn full_type_id(&self) -> ExpandedNodeId {
56        self.type_id().into()
57    }
58
59    fn full_json_type_id(&self) -> ExpandedNodeId {
60        self.json_type_id().into()
61    }
62
63    fn full_xml_type_id(&self) -> ExpandedNodeId {
64        self.xml_type_id().into()
65    }
66
67    fn full_data_type_id(&self) -> ExpandedNodeId {
68        self.data_type_id().into()
69    }
70}
71
72impl ServiceFault {
73    /// Create a new ServiceFault from a request handle and a status code.
74    pub fn new(request_header: impl AsRequestHandle, service_result: StatusCode) -> ServiceFault {
75        ServiceFault {
76            response_header: ResponseHeader::new_service_result(request_header, service_result),
77        }
78    }
79}
80
81impl UserTokenPolicy {
82    /// Return the anonymous user token policy.
83    pub fn anonymous() -> UserTokenPolicy {
84        UserTokenPolicy {
85            policy_id: UAString::from("anonymous"),
86            token_type: UserTokenType::Anonymous,
87            issued_token_type: UAString::null(),
88            issuer_endpoint_url: UAString::null(),
89            security_policy_uri: UAString::null(),
90        }
91    }
92}
93
94impl EndpointDescription {
95    /// Returns a reference to a policy that matches the supplied token type, otherwise None
96    pub fn find_policy(&self, token_type: UserTokenType) -> Option<&UserTokenPolicy> {
97        if let Some(ref policies) = self.user_identity_tokens {
98            policies.iter().find(|t| t.token_type == token_type)
99        } else {
100            None
101        }
102    }
103
104    /// Returns a reference to a policy that matches the supplied policy id
105    pub fn find_policy_by_id(&self, policy_id: &str) -> Option<&UserTokenPolicy> {
106        if let Some(ref policies) = self.user_identity_tokens {
107            policies.iter().find(|t| t.policy_id.as_ref() == policy_id)
108        } else {
109            None
110        }
111    }
112}
113
114impl UserNameIdentityToken {
115    /// Ensures the token is valid
116    pub fn is_valid(&self) -> bool {
117        !self.user_name.is_null() && !self.password.is_null()
118    }
119
120    /// Get the plaintext password as a string, if possible.
121    pub fn plaintext_password(&self) -> Result<String, Error> {
122        if !self.encryption_algorithm.is_empty() {
123            // Should not be calling this function at all encryption is applied
124            return Err(Error::new(
125                StatusCode::BadSecurityChecksFailed,
126                "Password is encrypted",
127            ));
128        }
129        String::from_utf8(self.password.as_ref().to_vec())
130            .map_err(|e| Error::new(StatusCode::BadSecurityChecksFailed, e))
131    }
132}
133
134impl<'a> From<&'a NodeId> for ReadValueId {
135    fn from(node_id: &'a NodeId) -> Self {
136        Self::from(node_id.clone())
137    }
138}
139
140impl From<NodeId> for ReadValueId {
141    fn from(node_id: NodeId) -> Self {
142        ReadValueId {
143            node_id,
144            attribute_id: AttributeId::Value as u32,
145            index_range: NumericRange::None,
146            data_encoding: QualifiedName::null(),
147        }
148    }
149}
150
151impl<'a> From<(u16, &'a str)> for ReadValueId {
152    fn from(v: (u16, &'a str)) -> Self {
153        Self::from(NodeId::from(v))
154    }
155}
156
157impl ReadValueId {
158    /// Create a new simple read value ID.
159    pub fn new(node_id: NodeId, attribute_id: AttributeId) -> Self {
160        Self {
161            node_id,
162            attribute_id: attribute_id as u32,
163            ..Default::default()
164        }
165    }
166
167    /// Create a new read value ID for values.
168    pub fn new_value(node_id: NodeId) -> Self {
169        Self {
170            node_id,
171            attribute_id: AttributeId::Value as u32,
172            ..Default::default()
173        }
174    }
175}
176
177impl Default for AnonymousIdentityToken {
178    fn default() -> Self {
179        AnonymousIdentityToken {
180            policy_id: UAString::from(profiles::SECURITY_USER_TOKEN_POLICY_ANONYMOUS),
181        }
182    }
183}
184
185impl SignatureData {
186    /// Return an empty SignatureData.
187    pub fn null() -> SignatureData {
188        SignatureData {
189            algorithm: UAString::null(),
190            signature: ByteString::null(),
191        }
192    }
193}
194
195impl From<NodeId> for MonitoredItemCreateRequest {
196    fn from(value: NodeId) -> Self {
197        Self::new(
198            value.into(),
199            MonitoringMode::Reporting,
200            MonitoringParameters::default(),
201        )
202    }
203}
204
205impl MonitoredItemCreateRequest {
206    /// Adds an item to monitor to the subscription
207    pub fn new(
208        item_to_monitor: ReadValueId,
209        monitoring_mode: MonitoringMode,
210        requested_parameters: MonitoringParameters,
211    ) -> MonitoredItemCreateRequest {
212        MonitoredItemCreateRequest {
213            item_to_monitor,
214            monitoring_mode,
215            requested_parameters,
216        }
217    }
218}
219
220impl From<(NodeId, NodeId, Option<Vec<Variant>>)> for CallMethodRequest {
221    fn from(value: (NodeId, NodeId, Option<Vec<Variant>>)) -> Self {
222        Self {
223            object_id: value.0,
224            method_id: value.1,
225            input_arguments: value.2,
226        }
227    }
228}
229
230impl<'a> From<&'a str> for EndpointDescription {
231    fn from(v: &'a str) -> Self {
232        EndpointDescription::from((
233            v,
234            constants::SECURITY_POLICY_NONE_URI,
235            MessageSecurityMode::None,
236        ))
237    }
238}
239
240impl<'a> From<(&'a str, &'a str, MessageSecurityMode)> for EndpointDescription {
241    fn from(v: (&'a str, &'a str, MessageSecurityMode)) -> Self {
242        EndpointDescription::from((v.0, v.1, v.2, None))
243    }
244}
245
246impl<'a> From<(&'a str, &'a str, MessageSecurityMode, UserTokenPolicy)> for EndpointDescription {
247    fn from(v: (&'a str, &'a str, MessageSecurityMode, UserTokenPolicy)) -> Self {
248        EndpointDescription::from((v.0, v.1, v.2, Some(vec![v.3])))
249    }
250}
251
252impl<'a> From<(&'a str, &'a str, MessageSecurityMode, Vec<UserTokenPolicy>)>
253    for EndpointDescription
254{
255    fn from(v: (&'a str, &'a str, MessageSecurityMode, Vec<UserTokenPolicy>)) -> Self {
256        EndpointDescription::from((v.0, v.1, v.2, Some(v.3)))
257    }
258}
259
260impl<'a>
261    From<(
262        &'a str,
263        &'a str,
264        MessageSecurityMode,
265        Option<Vec<UserTokenPolicy>>,
266    )> for EndpointDescription
267{
268    fn from(
269        v: (
270            &'a str,
271            &'a str,
272            MessageSecurityMode,
273            Option<Vec<UserTokenPolicy>>,
274        ),
275    ) -> Self {
276        EndpointDescription {
277            endpoint_url: UAString::from(v.0),
278            security_policy_uri: UAString::from(v.1),
279            security_mode: v.2,
280            server: ApplicationDescription::default(),
281            security_level: 0,
282            server_certificate: ByteString::null(),
283            transport_profile_uri: UAString::null(),
284            user_identity_tokens: v.3,
285        }
286    }
287}
288
289impl From<String> for EndpointDescription {
290    fn from(v: String) -> Self {
291        EndpointDescription::from(v.as_str())
292    }
293}
294
295const MESSAGE_SECURITY_MODE_NONE: &str = "None";
296const MESSAGE_SECURITY_MODE_SIGN: &str = "Sign";
297const MESSAGE_SECURITY_MODE_SIGN_AND_ENCRYPT: &str = "SignAndEncrypt";
298
299impl fmt::Display for MessageSecurityMode {
300    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301        let name = match self {
302            MessageSecurityMode::None => MESSAGE_SECURITY_MODE_NONE,
303            MessageSecurityMode::Sign => MESSAGE_SECURITY_MODE_SIGN,
304            MessageSecurityMode::SignAndEncrypt => MESSAGE_SECURITY_MODE_SIGN_AND_ENCRYPT,
305            _ => "",
306        };
307        write!(f, "{name}")
308    }
309}
310
311impl From<MessageSecurityMode> for String {
312    fn from(security_mode: MessageSecurityMode) -> Self {
313        security_mode.to_string()
314    }
315}
316
317impl<'a> From<&'a str> for MessageSecurityMode {
318    fn from(str: &'a str) -> Self {
319        match str {
320            MESSAGE_SECURITY_MODE_NONE => MessageSecurityMode::None,
321            MESSAGE_SECURITY_MODE_SIGN => MessageSecurityMode::Sign,
322            MESSAGE_SECURITY_MODE_SIGN_AND_ENCRYPT => MessageSecurityMode::SignAndEncrypt,
323            _ => {
324                error!("Specified security mode \"{}\" is not recognized", str);
325                MessageSecurityMode::Invalid
326            }
327        }
328    }
329}
330
331impl From<(&str, DataTypeId)> for Argument {
332    fn from(v: (&str, DataTypeId)) -> Self {
333        Argument {
334            name: UAString::from(v.0),
335            data_type: v.1.into(),
336            value_rank: -1,
337            array_dimensions: None,
338            description: LocalizedText::null(),
339        }
340    }
341}
342
343impl ServiceCounterDataType {
344    /// Register a successful entry.
345    pub fn success(&mut self) {
346        self.total_count += 1;
347    }
348
349    /// Register an error.
350    pub fn error(&mut self) {
351        self.total_count += 1;
352        self.error_count += 1;
353    }
354}
355
356#[allow(clippy::derivable_impls, reason = "This is for generated code")]
357impl Default for PerformUpdateType {
358    fn default() -> Self {
359        Self::Insert
360    }
361}
362
363#[allow(clippy::derivable_impls, reason = "This is for generated code")]
364impl Default for HistoryUpdateType {
365    fn default() -> Self {
366        Self::Insert
367    }
368}
369
370#[allow(clippy::derivable_impls, reason = "This is for generated code")]
371impl Default for IdentityCriteriaType {
372    fn default() -> Self {
373        Self::Anonymous
374    }
375}
376
377impl WriteValue {
378    /// default constructor with all struct members
379    pub fn new(
380        node_id: NodeId,
381        attribute_id: AttributeId,
382        index_range: NumericRange,
383        value: DataValue,
384    ) -> Self {
385        Self {
386            node_id,
387            attribute_id: attribute_id as u32,
388            index_range,
389            value,
390        }
391    }
392
393    /// return a WriteValue with AttributeId::Value and no index rane,
394    ///  which is the most common case
395    pub fn value_attr(node_id: NodeId, val: Variant) -> Self {
396        Self {
397            node_id,
398            attribute_id: AttributeId::Value as u32,
399            index_range: NumericRange::None,
400            value: val.into(),
401        }
402    }
403}