APIs

Show:
"use strict";
var _ = require("underscore");
var util = require("util");
var EventEmitter = require("events").EventEmitter;
var assert = require("node-opcua-assert");

function WatchDog() {
    this._subscriber = {};
    this._counter = 0;
    this._current_time = new Date();
    this._visit_subscriber_b = this._visit_subscriber.bind(this);
    this._timer = null;
}
util.inherits(WatchDog, EventEmitter);

/**
 * returns the number of subscribers using the WatchDog object.
 * @property subscriberCount
 * @type {Number}
 */
WatchDog.prototype.__defineGetter__("subscriberCount", function () {
    return Object.keys(this._subscriber).length;
});

function has_expired(watchDogData, currentTime) {
    var elapsed_time = currentTime - watchDogData.last_seen;
    return elapsed_time > watchDogData.timeout;
}

function _start_timer() {
    assert(this._timer === null, "start timer  called ?");
    this._timer = setInterval(this._visit_subscriber_b, 1000);
}
function _stop_timer() {
    assert(this._timer !== null, "_stop_timer already called ?");
    clearInterval(this._timer);
    this._timer = null;
}

WatchDog.prototype._visit_subscriber = function () {

    var self = this;
    self._current_time = new Date();

    var expired_subscribers = _.filter(self._subscriber, function (watchDogData) {
        watchDogData.visitCount += 1;
        return has_expired(watchDogData, self._current_time);
    });

    //xx console.log("_visit_subscriber", _.map(expired_subscribers, _.property("key")));
    if (expired_subscribers.length) {
        self.emit("timeout", expired_subscribers);
    }
    expired_subscribers.forEach(function (subscriber) {
        self.removeSubscriber(subscriber.subscriber);
        subscriber.subscriber.watchdogReset();
    });
    self._current_time = new Date();
};

function keepAliveFunc() {
    var self = this;
    assert(self._watchDog instanceof WatchDog);
    assert(_.isNumber(self._watchDogData.key));
    self._watchDogData.last_seen = new Date();
    if (self.onClientSeen) {
        self.onClientSeen(self._watchDogData .last_seen);
    }
}

/**
 * add a subscriber to the WatchDog.
 * @method addSubscriber
 *
 * add a subscriber to the WatchDog.
 *
 * This method modifies the subscriber be adding a
 * new method to it called 'keepAlive'
 * The subscriber must also provide a "watchdogReset". watchdogReset will be called
 * if the subscriber failed to call keepAlive withing the timeout period.
 * @param subscriber
 * @param timeout
 * @return {number}
 */
WatchDog.prototype.addSubscriber = function (subscriber, timeout) {

    var self = this;

    self._current_time = new Date();

    timeout = timeout || 1000;
    assert(_.isNumber(timeout), " invalid timeout ");
    assert(_.isFunction(subscriber.watchdogReset), " the subscriber must provide a watchdogReset method ");
    assert(!_.isFunction(subscriber.keepAlive));

    self._counter += 1;
    var key = self._counter;

    subscriber._watchDog = self;

    subscriber._watchDogData = {
        key: key,
        subscriber: subscriber,
        timeout: timeout,
        last_seen: self._current_time,
        visitCount: 0
    };

    self._subscriber[key] = subscriber._watchDogData;

    if (subscriber.onClientSeen) {
        subscriber.onClientSeen(subscriber._watchDogData .last_seen);
    }

    subscriber.keepAlive = keepAliveFunc.bind(subscriber);

    // start timer when the first subscriber comes in
    if (self.subscriberCount === 1) {
        assert(self._timer === null);
        _start_timer.call(self);
    }

    return key;

};

WatchDog.prototype.removeSubscriber = function (subscriber) {
    if (!subscriber._watchDog) {
        return; // already removed !!!
    }
    assert(subscriber._watchDog instanceof WatchDog);
    assert(_.isNumber(subscriber._watchDogData.key));
    assert(_.isFunction(subscriber.keepAlive));
    assert(this._subscriber.hasOwnProperty(subscriber._watchDogData.key));

    delete this._subscriber[subscriber._watchDogData.key];
    delete subscriber._watchDog;
    delete subscriber._watchDogData;
    delete subscriber.keepAlive;

    // delete timer when the last subscriber comes out
    //xx console.log("xxxx WatchDog.prototype.removeSubscriber ",this.subscriberCount );
    if (this.subscriberCount === 0) {
        _stop_timer.call(this);
    }

};


WatchDog.prototype.shutdown = function () {
    assert(this._timer === null && Object.keys(this._subscriber).length === 0, " leaking subscriber in watchdog");
};

exports.WatchDog = WatchDog;