diff --git a/client/src/session.rs b/client/src/session.rs index e5d97e180..a581117fc 100644 --- a/client/src/session.rs +++ b/client/src/session.rs @@ -1444,7 +1444,7 @@ impl Session { /// [`CallMethodRequest`]: ./struct.CallMethodRequest.html /// [`CallMethodResult`]: ./struct.CallMethodResult.html /// - pub fn call_method(&mut self, method: T) -> Result where T: Into { + pub fn call(&mut self, method: T) -> Result where T: Into { debug!("call_method"); let methods_to_call = Some(vec![method.into()]); let request = CallRequest { @@ -1485,7 +1485,7 @@ impl Session { let object_id: NodeId = ObjectId::Server.into(); let method_id: NodeId = MethodId::Server_GetMonitoredItems.into(); let request: CallMethodRequest = (object_id, method_id, args).into(); - let response = self.call_method(request)?; + let response = self.call(request)?; if let Some(mut result) = response.output_arguments { if result.len() == 2 { let server_handles = result.remove(0).as_u32_array().map_err(|_| StatusCode::BadUnexpectedError)?; diff --git a/client/src/session_retry.rs b/client/src/session_retry.rs index b843d5fbd..3dc9e4161 100644 --- a/client/src/session_retry.rs +++ b/client/src/session_retry.rs @@ -10,7 +10,7 @@ pub enum Answer { /// Give up reconnecting GiveUp, } - + /// The session retry policy determines what to if the connection fails. In these circumstances, /// the client needs to re-establish a connection and the policy says how many times to try between /// failure and at what interval. diff --git a/server/src/address_space/address_space.rs b/server/src/address_space/address_space.rs index 0ec26f71a..6f3baba45 100644 --- a/server/src/address_space/address_space.rs +++ b/server/src/address_space/address_space.rs @@ -21,6 +21,7 @@ use crate::{ diagnostics::ServerDiagnostics, state::ServerState, session::Session, + callbacks, constants, }; @@ -83,7 +84,7 @@ macro_rules! server_diagnostics_summary { } } -type MethodCallback = Box Result + Send + Sync + 'static>; +type MethodCallback = Box; #[derive(PartialEq, Eq, Clone, Debug, Hash)] struct MethodKey { @@ -329,8 +330,8 @@ impl AddressSpace { // Server method handlers use crate::address_space::method_impls; - self.register_method_handler(ObjectId::Server, MethodId::Server_GetMonitoredItems, Box::new(method_impls::handle_get_monitored_items)); - self.register_method_handler(ObjectId::Server, MethodId::Server_ResendData, Box::new(method_impls::handle_resend_data)); + self.register_method_handler(ObjectId::Server, MethodId::Server_ResendData, Box::new(method_impls::ServerResendDataMethod)); + self.register_method_handler(ObjectId::Server, MethodId::Server_GetMonitoredItems, Box::new(method_impls::ServerGetMonitoredItemsMethod)); } } @@ -668,7 +669,7 @@ impl AddressSpace { /// /// Calls require a registered handler to handle the method. If there is no handler, or if /// the request refers to a non existent object / method, the function will return an error. - pub fn call_method(&self, server_state: &ServerState, session: &mut Session, request: &CallMethodRequest) -> Result { + pub fn call_method(&mut self, _server_state: &ServerState, session: &mut Session, request: &CallMethodRequest) -> Result { let (object_id, method_id) = (&request.object_id, &request.method_id); // Handle the call @@ -689,10 +690,10 @@ impl AddressSpace { object_id: object_id.clone(), method_id: method_id.clone(), }; - if let Some(handler) = self.method_handlers.get(&key) { + if let Some(handler) = self.method_handlers.get_mut(&key) { // Call the handler trace!("Method call to {:?} on {:?} being handled by a registered handler", method_id, object_id); - handler(self, server_state, session, request) + handler.call(session, request) } else { // TODO we could do a secondary search on a (NodeId::null(), method_id) here // so that method handler is reusable for multiple objects diff --git a/server/src/address_space/method_impls.rs b/server/src/address_space/method_impls.rs index f9e0e12e5..e3fbf00cf 100644 --- a/server/src/address_space/method_impls.rs +++ b/server/src/address_space/method_impls.rs @@ -3,9 +3,8 @@ use opcua_types::status_code::StatusCode; use opcua_types::service_types::{CallMethodRequest, CallMethodResult}; use crate::{ - address_space::AddressSpace, - state::ServerState, - session::Session + session::Session, + callbacks::Method, }; /// Count the number of provided input arguments, comparing them to the expected number. @@ -47,70 +46,78 @@ macro_rules! get_input_argument { } /// This is the handler for Server.ResendData method call. -pub fn handle_resend_data(_: &AddressSpace, _: &ServerState, session: &mut Session, request: &CallMethodRequest) -> Result { - debug!("Method handler for ResendData"); +pub struct ServerResendDataMethod; - // OPC UA part 5 - ResendData([in] UInt32 subscriptionId); - // - // subscriptionId - Identifier of the subscription to refresh - // - // Return codes - // - // BadSubscriptionIdInvalid - // BadUserAccessDenied +impl Method for ServerResendDataMethod { + fn call(&mut self, session: &mut Session, request: &CallMethodRequest) -> Result { + debug!("Method handler for ResendData"); - ensure_input_argument_count(request, 1)?; + // OPC UA part 5 - ResendData([in] UInt32 subscriptionId); + // + // subscriptionId - Identifier of the subscription to refresh + // + // Return codes + // + // BadSubscriptionIdInvalid + // BadUserAccessDenied - let subscription_id = get_input_argument!(request, 0, UInt32)?; + ensure_input_argument_count(request, 1)?; - if let Some(subscription) = session.subscriptions.get_mut(*subscription_id) { - subscription.set_resend_data(); - Ok(CallMethodResult { - status_code: StatusCode::Good, - input_argument_results: Some(vec![StatusCode::Good]), - input_argument_diagnostic_infos: None, - output_arguments: None, - }) - } else { - // Subscription id does not exist - // Note we could check other sessions for a matching id and return BadUserAccessDenied in that case - Err(StatusCode::BadSubscriptionIdInvalid) + let subscription_id = get_input_argument!(request, 0, UInt32)?; + + if let Some(subscription) = session.subscriptions.get_mut(*subscription_id) { + subscription.set_resend_data(); + Ok(CallMethodResult { + status_code: StatusCode::Good, + input_argument_results: Some(vec![StatusCode::Good]), + input_argument_diagnostic_infos: None, + output_arguments: None, + }) + } else { + // Subscription id does not exist + // Note we could check other sessions for a matching id and return BadUserAccessDenied in that case + Err(StatusCode::BadSubscriptionIdInvalid) + } } } /// This is the handler for the Server.GetMonitoredItems method call. -pub fn handle_get_monitored_items(_: &AddressSpace, _: &ServerState, session: &mut Session, request: &CallMethodRequest) -> Result { - debug!("Method handler for GetMonitoredItems"); +pub struct ServerGetMonitoredItemsMethod; - // OPC UA part 5 - GetMonitoredItems([in] UInt32 subscriptionId, [out] UInt32[] serverHandles, [out] UInt32[] clientHandles); - // - // subscriptionId - Identifier of the subscription - // serverHandles - Array of serverHandles for all MonitoredItems of the Subscription identified by subscriptionId - // clientHandles - Array of clientHandles for all MonitoredItems of the Subscription identified by subscriptionId - // - // Return codes - // - // BadSubscriptionIdInvalid - // BadUserAccessDenied +impl Method for ServerGetMonitoredItemsMethod { + fn call(&mut self, session: &mut Session, request: &CallMethodRequest) -> Result { + debug!("Method handler for GetMonitoredItems"); - ensure_input_argument_count(request, 1)?; + // OPC UA part 5 - GetMonitoredItems([in] UInt32 subscriptionId, [out] UInt32[] serverHandles, [out] UInt32[] clientHandles); + // + // subscriptionId - Identifier of the subscription + // serverHandles - Array of serverHandles for all MonitoredItems of the Subscription identified by subscriptionId + // clientHandles - Array of clientHandles for all MonitoredItems of the Subscription identified by subscriptionId + // + // Return codes + // + // BadSubscriptionIdInvalid + // BadUserAccessDenied - let subscription_id = get_input_argument!(request, 0, UInt32)?; + ensure_input_argument_count(request, 1)?; - if let Some(subscription) = session.subscriptions.subscriptions().get(&subscription_id) { - // Response - // serverHandles: Vec - // clientHandles: Vec - let (server_handles, client_handles) = subscription.get_handles(); - Ok(CallMethodResult { - status_code: StatusCode::Good, - input_argument_results: Some(vec![StatusCode::Good]), - input_argument_diagnostic_infos: None, - output_arguments: Some(vec![server_handles.into(), client_handles.into()]), - }) - } else { - // Subscription id does not exist - // Note we could check other sessions for a matching id and return BadUserAccessDenied in that case - Err(StatusCode::BadSubscriptionIdInvalid) + let subscription_id = get_input_argument!(request, 0, UInt32)?; + + if let Some(subscription) = session.subscriptions.subscriptions().get(&subscription_id) { + // Response + // serverHandles: Vec + // clientHandles: Vec + let (server_handles, client_handles) = subscription.get_handles(); + Ok(CallMethodResult { + status_code: StatusCode::Good, + input_argument_results: Some(vec![StatusCode::Good]), + input_argument_diagnostic_infos: None, + output_arguments: Some(vec![server_handles.into(), client_handles.into()]), + }) + } else { + // Subscription id does not exist + // Note we could check other sessions for a matching id and return BadUserAccessDenied in that case + Err(StatusCode::BadSubscriptionIdInvalid) + } } -} +} \ No newline at end of file diff --git a/server/src/callbacks.rs b/server/src/callbacks.rs index 594f90cc3..7bd322732 100644 --- a/server/src/callbacks.rs +++ b/server/src/callbacks.rs @@ -5,12 +5,13 @@ use std::sync::{Arc, RwLock}; use opcua_types::{ NodeId, status_code::StatusCode, + service_types::{CallMethodRequest, CallMethodResult}, }; use crate::session::Session; /// Called by RegisterNodes service -pub trait OnRegisterNodes { +pub trait RegisterNodes { /// Called when a client calls the RegisterNodes service. This implementation should return a list /// of the same size and order containing node ids corresponding to the input, or aliases. The implementation /// should return `BadNodeIdInvalid` if any of the node ids in the input are invalid. @@ -20,16 +21,24 @@ pub trait OnRegisterNodes { /// /// There is no guarantee that the corresponding `OnUnregisterNodes` will be called by the client, /// therefore use the weak session references and a periodic check to perform any housekeeping. - fn on_register_nodes(&mut self, session: Arc>, nodes_to_register: &[NodeId]) -> Result, StatusCode>; + fn register_nodes(&mut self, session: Arc>, nodes_to_register: &[NodeId]) -> Result, StatusCode>; } /// Called by UnregisterNodes service -pub trait OnUnregisterNodes { +pub trait UnregisterNodes { /// Called when a client calls the UnregisterNodes service. See `OnRegisterNodes` trait for more /// information. A client may not call this function, e.g. if connection breaks so do not /// count on receiving this to perform any housekeeping. /// /// The function should not validate the nodes in the request and should just ignore any /// unregistered nodes. - fn on_unregister_nodes(&mut self, session: Arc>, nodes_to_unregister: &[NodeId]) -> Result<(), StatusCode>; + fn unregister_nodes(&mut self, session: Arc>, nodes_to_unregister: &[NodeId]) -> Result<(), StatusCode>; +} + +/// Called by the Method service when it invokes a method +pub trait Method { + /// A method is registered via the address space to a method id and optionally an object id. + /// When a client sends a CallRequest / CallMethod request, the registered object will + /// be invoked to handle the call. + fn call(&mut self, session: &mut Session, request: &CallMethodRequest) -> Result; } diff --git a/server/src/services/message_handler.rs b/server/src/services/message_handler.rs index ea367a306..5f91ae872 100644 --- a/server/src/services/message_handler.rs +++ b/server/src/services/message_handler.rs @@ -213,7 +213,7 @@ impl MessageHandler { SupportedMessage::CallRequest(ref request) => { validated_request!(self, request, &mut session, { - self.method_service.call(&address_space, &server_state, &mut session, request) + self.method_service.call(&mut address_space, &server_state, &mut session, request) }) } diff --git a/server/src/services/method.rs b/server/src/services/method.rs index 8a4e766d2..e9d771554 100644 --- a/server/src/services/method.rs +++ b/server/src/services/method.rs @@ -23,7 +23,7 @@ impl MethodService { MethodService {} } - pub fn call(&self, address_space: &AddressSpace, server_state: &ServerState, session: &mut Session, request: &CallRequest) -> Result { + pub fn call(&self, address_space: &mut AddressSpace, server_state: &ServerState, session: &mut Session, request: &CallRequest) -> Result { if let Some(ref calls) = request.methods_to_call { if calls.len() >= server_state.max_method_calls() { Ok(self.service_fault(&request.request_header, StatusCode::BadTooManyOperations)) diff --git a/server/src/services/view.rs b/server/src/services/view.rs index 498154705..94e675469 100644 --- a/server/src/services/view.rs +++ b/server/src/services/view.rs @@ -153,17 +153,19 @@ impl ViewService { if is_empty_option_vec!(request.nodes_to_register) { Ok(self.service_fault(&request.request_header, StatusCode::BadNothingToDo)) } else { - let nodes_to_register = request.nodes_to_register.as_ref().unwrap(); if let Some(ref mut callback) = server_state.register_nodes_callback { - let result = callback.on_register_nodes(session, &nodes_to_register[..]); - if let Ok(registered_node_ids) = result { - let response = RegisterNodesResponse { - response_header: ResponseHeader::new_good(&request.request_header), - registered_node_ids: Some(registered_node_ids), - }; - Ok(response.into()) - } else { - Ok(self.service_fault(&request.request_header, result.unwrap_err())) + let nodes_to_register = request.nodes_to_register.as_ref().unwrap(); + match callback.register_nodes(session, &nodes_to_register[..]) { + Ok(registered_node_ids) => { + let response = RegisterNodesResponse { + response_header: ResponseHeader::new_good(&request.request_header), + registered_node_ids: Some(registered_node_ids), + }; + Ok(response.into()) + } + Err(err) => { + Ok(self.service_fault(&request.request_header, err)) + } } } else { Ok(self.service_fault(&request.request_header, StatusCode::BadNodeIdInvalid)) @@ -175,16 +177,18 @@ impl ViewService { if is_empty_option_vec!(request.nodes_to_unregister) { Ok(self.service_fault(&request.request_header, StatusCode::BadNothingToDo)) } else { - let nodes_to_unregister = request.nodes_to_unregister.as_ref().unwrap(); if let Some(ref mut callback) = server_state.unregister_nodes_callback { - let result = callback.on_unregister_nodes(session, &nodes_to_unregister[..]); - if let Ok(_) = result { - let response = UnregisterNodesResponse { - response_header: ResponseHeader::new_good(&request.request_header), - }; - Ok(response.into()) - } else { - Ok(self.service_fault(&request.request_header, result.unwrap_err())) + let nodes_to_unregister = request.nodes_to_unregister.as_ref().unwrap(); + match callback.unregister_nodes(session, &nodes_to_unregister[..]) { + Ok(_) => { + let response = UnregisterNodesResponse { + response_header: ResponseHeader::new_good(&request.request_header), + }; + Ok(response.into()) + } + Err(err) => { + Ok(self.service_fault(&request.request_header, err)) + } } } else { Ok(UnregisterNodesResponse { diff --git a/server/src/state.rs b/server/src/state.rs index e7ceddd81..0f8436ac3 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -17,7 +17,7 @@ use opcua_types::{ use crate::config::{ServerConfig, ServerEndpoint}; use crate::diagnostics::ServerDiagnostics; -use crate::callbacks::*; +use crate::callbacks::{RegisterNodes, UnregisterNodes}; const TOKEN_POLICY_ANONYMOUS: &str = "anonymous"; const TOKEN_POLICY_USER_PASS_PLAINTEXT: &str = "userpass_plaintext"; @@ -71,9 +71,9 @@ pub struct ServerState { /// Diagnostic information pub diagnostics: Arc>, /// Callback for register nodes - pub(crate) register_nodes_callback: Option>, + pub(crate) register_nodes_callback: Option>, /// Callback for unregister nodes - pub(crate) unregister_nodes_callback: Option>, + pub(crate) unregister_nodes_callback: Option>, } @@ -317,7 +317,7 @@ impl ServerState { } } - pub fn set_register_nodes_callbacks(&mut self, register_nodes_callback: Box, unregister_nodes_callback: Box) { + pub fn set_register_nodes_callbacks(&mut self, register_nodes_callback: Box, unregister_nodes_callback: Box) { self.register_nodes_callback = Some(register_nodes_callback); self.unregister_nodes_callback = Some(unregister_nodes_callback); } diff --git a/server/src/tests/services/method.rs b/server/src/tests/services/method.rs index df945bcbe..51ac7adf1 100644 --- a/server/src/tests/services/method.rs +++ b/server/src/tests/services/method.rs @@ -12,6 +12,19 @@ use crate::services::{ monitored_item::MonitoredItemService, }; +fn do_method_service_test(f: F) + where F: FnOnce(&mut super::ServerState, &mut Session, &mut AddressSpace, &MethodService) +{ + let st = ServiceTest::new(); + + let s = MethodService::new(); + + let (mut server_state, mut session) = st.get_server_state_and_session(); + let mut address_space = st.address_space.write().unwrap(); + + f(&mut server_state, &mut session, &mut address_space, &s); +} + fn new_call_method_request(object_id: S, method_id: T, input_arguments: Option>) -> CallMethodRequest where S: Into, T: Into { CallMethodRequest { @@ -58,7 +71,7 @@ fn create_monitored_items_request(subscription_id: u32, client_handle: u32, n } /// This is a convenience for tests -fn call_single(s: &MethodService, address_space: &AddressSpace, server_state: &ServerState, session: &mut Session, request: CallMethodRequest) -> Result { +fn call_single(s: &MethodService, address_space: &mut AddressSpace, server_state: &ServerState, session: &mut Session, request: CallMethodRequest) -> Result { let response = s.call(address_space, server_state, session, &CallRequest { request_header: RequestHeader::new(&NodeId::null(), &DateTime::now(), 1), methods_to_call: Some(vec![request]), @@ -69,146 +82,136 @@ fn call_single(s: &MethodService, address_space: &AddressSpace, server_state: &S #[test] fn call_getmonitoreditems() { - let st = ServiceTest::new(); - - let s = MethodService::new(); - - let (mut server_state, mut session) = st.get_server_state_and_session(); - let address_space = st.address_space.write().unwrap(); - - // Call without a valid object id - { - let request = new_call_method_request(NodeId::null(), MethodId::Server_GetMonitoredItems, None); - let response = call_single(&s, &address_space, &server_state, &mut session, request).unwrap(); - assert_eq!(response.status_code, StatusCode::BadNodeIdUnknown); - } - - // Call without a valid method id - { - let request = new_call_method_request(ObjectId::Server, NodeId::null(), None); - let response = call_single(&s, &address_space, &server_state, &mut session, request).unwrap(); - assert_eq!(response.status_code, StatusCode::BadMethodInvalid); - } + do_method_service_test(|server_state, session, address_space, s| { + // Call without a valid object id + { + let request = new_call_method_request(NodeId::null(), MethodId::Server_GetMonitoredItems, None); + let response = call_single(s, address_space, &server_state, session, request).unwrap(); + assert_eq!(response.status_code, StatusCode::BadNodeIdUnknown); + } - // Call without args - { - let request = new_call_method_request(ObjectId::Server, MethodId::Server_GetMonitoredItems, None); - let response = call_single(&s, &address_space, &server_state, &mut session, request).unwrap(); - assert_eq!(response.status_code, StatusCode::BadArgumentsMissing); - } + // Call without a valid method id + { + let request = new_call_method_request(ObjectId::Server, NodeId::null(), None); + let response = call_single(s, address_space, &server_state, session, request).unwrap(); + assert_eq!(response.status_code, StatusCode::BadMethodInvalid); + } - // Call with too many args - { - let args: Vec = vec![100.into(), 100.into()]; - let request = new_call_method_request(ObjectId::Server, MethodId::Server_GetMonitoredItems, Some(args)); - let response = call_single(&s, &address_space, &server_state, &mut session, request).unwrap(); - assert_eq!(response.status_code, StatusCode::BadTooManyArguments); - } + // Call without args + { + let request = new_call_method_request(ObjectId::Server, MethodId::Server_GetMonitoredItems, None); + let response = call_single(s, address_space, &server_state, session, request).unwrap(); + assert_eq!(response.status_code, StatusCode::BadArgumentsMissing); + } - // Call with incorrect arg - { - let args: Vec = vec![100u8.into()]; - let request = new_call_method_request(ObjectId::Server, MethodId::Server_GetMonitoredItems, Some(args)); - let response = call_single(&s, &address_space, &server_state, &mut session, request).unwrap(); - assert_eq!(response.status_code, StatusCode::BadInvalidArgument); - } + // Call with too many args + { + let args: Vec = vec![100.into(), 100.into()]; + let request = new_call_method_request(ObjectId::Server, MethodId::Server_GetMonitoredItems, Some(args)); + let response = call_single(s, address_space, &server_state, session, request).unwrap(); + assert_eq!(response.status_code, StatusCode::BadTooManyArguments); + } - // Call with invalid subscription id - { - let args: Vec = vec![100u32.into()]; - let request = new_call_method_request(ObjectId::Server, MethodId::Server_GetMonitoredItems, Some(args)); - let response = call_single(&s, &address_space, &server_state, &mut session, request).unwrap(); - assert_eq!(response.status_code, StatusCode::BadSubscriptionIdInvalid); - } + // Call with incorrect arg + { + let args: Vec = vec![100u8.into()]; + let request = new_call_method_request(ObjectId::Server, MethodId::Server_GetMonitoredItems, Some(args)); + let response = call_single(s, address_space, &server_state, session, request).unwrap(); + assert_eq!(response.status_code, StatusCode::BadInvalidArgument); + } - // Call with valid subscription id - { - let ss = SubscriptionService::new(); - let mis = MonitoredItemService::new(); - - // Create a subscription with some monitored items where client handle is distinct - let subscription_id = { - let request = create_subscription_request(); - let response: CreateSubscriptionResponse = supported_message_as!(ss.create_subscription(&mut server_state, &mut session, &request).unwrap(), CreateSubscriptionResponse); - response.subscription_id - }; - - // Create a monitored item - let monitored_item_id = { - let request = create_monitored_items_request(subscription_id, 999, VariableId::Server_ServerStatus_CurrentTime); - let response: CreateMonitoredItemsResponse = supported_message_as!(mis.create_monitored_items(&mut session, &request).unwrap(), CreateMonitoredItemsResponse); - response.results.unwrap()[0].monitored_item_id - }; - - // Call to get monitored items and verify handles - let args: Vec = vec![subscription_id.into()]; - let request = new_call_method_request(ObjectId::Server, MethodId::Server_GetMonitoredItems, Some(args)); - let response = call_single(&s, &address_space, &server_state, &mut session, request).unwrap(); - assert_eq!(response.status_code, StatusCode::Good); - - // There should be two output args, each a vector of u32 - let mut result = response.output_arguments.unwrap(); - let server_handles = result.remove(0); - let client_handles = result.remove(0); - - if let Variant::Array(mut v) = server_handles { - assert_eq!(v.len(), 1); - assert_eq!(Variant::from(monitored_item_id), v.pop().unwrap()); - } else { - assert!(false); + // Call with invalid subscription id + { + let args: Vec = vec![100u32.into()]; + let request = new_call_method_request(ObjectId::Server, MethodId::Server_GetMonitoredItems, Some(args)); + let response = call_single(s, address_space, &server_state, session, request).unwrap(); + assert_eq!(response.status_code, StatusCode::BadSubscriptionIdInvalid); } - if let Variant::Array(mut v) = client_handles { - assert_eq!(v.len(), 1); - assert_eq!(Variant::from(999u32), v.pop().unwrap()); - } else { - assert!(false); + // Call with valid subscription id + { + let ss = SubscriptionService::new(); + let mis = MonitoredItemService::new(); + + // Create a subscription with some monitored items where client handle is distinct + let subscription_id = { + let request = create_subscription_request(); + let response: CreateSubscriptionResponse = supported_message_as!(ss.create_subscription(server_state, session, &request).unwrap(), CreateSubscriptionResponse); + response.subscription_id + }; + + // Create a monitored item + let monitored_item_id = { + let request = create_monitored_items_request(subscription_id, 999, VariableId::Server_ServerStatus_CurrentTime); + let response: CreateMonitoredItemsResponse = supported_message_as!(mis.create_monitored_items(session, &request).unwrap(), CreateMonitoredItemsResponse); + response.results.unwrap()[0].monitored_item_id + }; + + // Call to get monitored items and verify handles + let args: Vec = vec![subscription_id.into()]; + let request = new_call_method_request(ObjectId::Server, MethodId::Server_GetMonitoredItems, Some(args)); + let response = call_single(s, address_space, &server_state, session, request).unwrap(); + assert_eq!(response.status_code, StatusCode::Good); + + // There should be two output args, each a vector of u32 + let mut result = response.output_arguments.unwrap(); + let server_handles = result.remove(0); + let client_handles = result.remove(0); + + if let Variant::Array(mut v) = server_handles { + assert_eq!(v.len(), 1); + assert_eq!(Variant::from(monitored_item_id), v.pop().unwrap()); + } else { + assert!(false); + } + + if let Variant::Array(mut v) = client_handles { + assert_eq!(v.len(), 1); + assert_eq!(Variant::from(999u32), v.pop().unwrap()); + } else { + assert!(false); + } } - } + }); } #[test] fn call_resend_data() { - let st = ServiceTest::new(); - - let s = MethodService::new(); - - let (mut server_state, mut session) = st.get_server_state_and_session(); - let address_space = st.address_space.write().unwrap(); - - // Call without a valid object id - { - let request = new_call_method_request(NodeId::null(), MethodId::Server_ResendData, None); - let response = call_single(&s, &address_space, &server_state, &mut session, request).unwrap(); - assert_eq!(response.status_code, StatusCode::BadNodeIdUnknown); - } + do_method_service_test(|server_state, session, address_space, s| { + // Call without a valid object id + { + let request = new_call_method_request(NodeId::null(), MethodId::Server_ResendData, None); + let response = call_single(s, address_space, &server_state, session, request).unwrap(); + assert_eq!(response.status_code, StatusCode::BadNodeIdUnknown); + } - // Call with invalid subscription id - { - let args: Vec = vec![100u32.into()]; - let request = new_call_method_request(ObjectId::Server, MethodId::Server_ResendData, Some(args)); - let response = call_single(&s, &address_space, &server_state, &mut session, request).unwrap(); - assert_eq!(response.status_code, StatusCode::BadSubscriptionIdInvalid); - } + // Call with invalid subscription id + { + let args: Vec = vec![100u32.into()]; + let request = new_call_method_request(ObjectId::Server, MethodId::Server_ResendData, Some(args)); + let response = call_single(s, address_space, &server_state, session, request).unwrap(); + assert_eq!(response.status_code, StatusCode::BadSubscriptionIdInvalid); + } - // Call with valid subscription id - { - let ss = SubscriptionService::new(); - let _mis = MonitoredItemService::new(); - - // Create a subscription with some monitored items where client handle is distinct - let subscription_id = { - let request = create_subscription_request(); - let response: CreateSubscriptionResponse = supported_message_as!(ss.create_subscription(&mut server_state, &mut session, &request).unwrap(), CreateSubscriptionResponse); - response.subscription_id - }; - - // Call to get monitored items and verify handles - let args: Vec = vec![subscription_id.into()]; - let request = new_call_method_request(ObjectId::Server, MethodId::Server_ResendData, Some(args)); - let response = call_single(&s, &address_space, &server_state, &mut session, request).unwrap(); - assert_eq!(response.status_code, StatusCode::Good); - } + // Call with valid subscription id + { + let ss = SubscriptionService::new(); + let _mis = MonitoredItemService::new(); + + // Create a subscription with some monitored items where client handle is distinct + let subscription_id = { + let request = create_subscription_request(); + let response: CreateSubscriptionResponse = supported_message_as!(ss.create_subscription(server_state, session, &request).unwrap(), CreateSubscriptionResponse); + response.subscription_id + }; + + // Call to get monitored items and verify handles + let args: Vec = vec![subscription_id.into()]; + let request = new_call_method_request(ObjectId::Server, MethodId::Server_ResendData, Some(args)); + let response = call_single(s, address_space, &server_state, session, request).unwrap(); + assert_eq!(response.status_code, StatusCode::Good); + } + }); } diff --git a/server/src/tests/services/view.rs b/server/src/tests/services/view.rs index 1e331ccdd..8a4d010cf 100644 --- a/server/src/tests/services/view.rs +++ b/server/src/tests/services/view.rs @@ -371,8 +371,8 @@ struct RegisterNodesImpl { pub session: Weak> } -impl OnRegisterNodes for RegisterNodesImpl { - fn on_register_nodes(&mut self, session: Arc>, nodes_to_register: &[NodeId]) -> Result, StatusCode> { +impl RegisterNodes for RegisterNodesImpl { + fn register_nodes(&mut self, session: Arc>, nodes_to_register: &[NodeId]) -> Result, StatusCode> { let bad_node = ObjectId::ObjectsFolder.into(); let good_node = NodeId::new(1, 100); let alias_node = NodeId::new(1, 200); @@ -399,8 +399,8 @@ impl OnRegisterNodes for RegisterNodesImpl { struct UnregisterNodesImpl; -impl OnUnregisterNodes for UnregisterNodesImpl { - fn on_unregister_nodes(&mut self, _session: Arc>, _nodes_to_unregister: &[NodeId]) -> Result<(), StatusCode> +impl UnregisterNodes for UnregisterNodesImpl { + fn unregister_nodes(&mut self, _session: Arc>, _nodes_to_unregister: &[NodeId]) -> Result<(), StatusCode> { Ok(()) }