1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// OPCUA for Rust
// SPDX-License-Identifier: MPL-2.0
// Copyright (C) 2017-2020 Adam Lock

//! Provides callback traits and concrete implementations that the client can use to register for notifications
//! with the client api.
//!
//! For example, the client must supply an [`OnSubscriptionNotification`] implementation when it calls `Session::create_subscription`.
//! It could implement this trait for itself, or it can use the concrete implementations in [`DataChangeCallback`] and [`EventCallback`].
//!
//! [`DataChangeCallback`]: ./struct.DataChangeCallback.html
//! [`EventCallback`]: ./struct.EventCallback.html


use std::fmt;

use opcua_types::{
    service_types::EventNotificationList,
    status_code::StatusCode,
};

use crate::subscription::MonitoredItem;

/// The `OnSubscriptionNotification` trait is the callback registered along with a new subscription to
/// receive subscription notification callbacks.
///
/// Unless your subscription contains a mix of items which are monitoring data and events
/// you probably only need to implement either `data_change()`, or `event()` and leave the default,
/// no-op implementation for the other.
///
/// There are concrete implementations of this trait in [`DataChangeCallback`] and [`EventCallback`].
///
/// [`DataChangeCallback`]: ./struct.DataChangeCallback.html
/// [`EventCallback`]: ./struct.EventCallback.html
///
pub trait OnSubscriptionNotification {
    /// Called by the subscription after a `DataChangeNotification`. The default implementation
    /// does nothing.
    fn on_data_change(&mut self, _data_change_items: Vec<&MonitoredItem>) {}

    /// Called by the subscription after a `EventNotificationList`. The notifications contained within
    /// are individual `EventFieldList` structs filled from the select clause criteria from when the
    /// event was constructed. The default implementation does nothing.
    fn on_event(&mut self, _events: &EventNotificationList) {}
}

/// The `OnConnectionStatusChange` trait can be used to register on the session to be notified
/// of connection status change notifications.
pub trait OnConnectionStatusChange {
    /// Called when the connection status changes from connected to disconnected or vice versa
    fn on_connection_status_change(&mut self, connected: bool);
}


/// The `OnSessionClosed` trait can be used to register on a session and called to notify the client
/// that the session has closed.
pub trait OnSessionClosed {
    /// Called when the connection closed (in addition to a status change event). The status
    /// code should be checked to see if the closure was a graceful terminate (`Good`), or the result
    /// of a network or protocol error.
    ///
    /// If no session retry policy has been created for the client session, the server implementation
    /// might choose to reconnect in response to a bad status code by itself, however it should
    /// avoid retrying too quickly or indefinitely in case the error is permanent.
    fn on_session_closed(&mut self, status_code: StatusCode);
}

/// This is a concrete implementation of [`OnSubscriptionNotification`] that calls a function when
/// a data change occurs.
pub struct DataChangeCallback {
    /// The actual call back
    cb: Box<dyn Fn(Vec<&MonitoredItem>) + Send + Sync + 'static>
}

impl OnSubscriptionNotification for DataChangeCallback {
    fn on_data_change(&mut self, data_change_items: Vec<&MonitoredItem>) {
        (self.cb)(data_change_items);
    }
}

impl DataChangeCallback {
    /// Constructs a callback from the supplied function
    pub fn new<CB>(cb: CB) -> Self where CB: Fn(Vec<&MonitoredItem>) + Send + Sync + 'static {
        Self {
            cb: Box::new(cb)
        }
    }
}

/// This is a concrete implementation of [`OnSubscriptionNotification`] that calls a function
/// when an event occurs.
pub struct EventCallback {
    /// The actual call back
    cb: Box<dyn Fn(&EventNotificationList) + Send + Sync + 'static>
}

impl OnSubscriptionNotification for EventCallback {
    fn on_event(&mut self, events: &EventNotificationList) {
        (self.cb)(events);
    }
}

impl EventCallback {
    /// Constructs a callback from the supplied function
    pub fn new<CB>(cb: CB) -> Self where CB: Fn(&EventNotificationList) + Send + Sync + 'static {
        Self {
            cb: Box::new(cb)
        }
    }
}

/// This is a concrete implementation of [`OnConnectionStatusChange`] that calls the supplied function.
pub struct ConnectionStatusCallback {
    cb: Box<dyn FnMut(bool) + Send + Sync + 'static>,
}

impl fmt::Debug for ConnectionStatusCallback {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[callback]")
    }
}

impl OnConnectionStatusChange for ConnectionStatusCallback {
    fn on_connection_status_change(&mut self, connected: bool) {
        if connected {
            debug!("Received OPC UA connected event");
        } else {
            debug!("Received OPC UA disconnected event");
        }
        (self.cb)(connected);
    }
}

impl ConnectionStatusCallback {
    // Constructor
    pub fn new<CB>(cb: CB) -> Self where CB: FnMut(bool) + Send + Sync + 'static {
        Self {
            cb: Box::new(cb)
        }
    }
}

/// This is a concrete implementation of `OnSessionClosed` that will call the supplied
/// function.
pub struct SessionClosedCallback {
    cb: Box<dyn FnMut(StatusCode) + Send + Sync + 'static>,
}

impl OnSessionClosed for SessionClosedCallback {
    fn on_session_closed(&mut self, status_code: StatusCode) {
        (self.cb)(status_code);
    }
}

impl SessionClosedCallback {
    // Constructor
    pub fn new<CB>(cb: CB) -> Self where CB: FnMut(StatusCode) + Send + Sync + 'static {
        Self {
            cb: Box::new(cb)
        }
    }
}