agsh / onvif

ONVIF node.js implementation
http://agsh.github.io/onvif/
MIT License
693 stars 236 forks source link

Events do not work correctly if more than one camera. (Неправильно работают события при наличии более одной камеры) #162

Closed vvvait closed 4 years ago

vvvait commented 4 years ago

Events from the first camera are duplicated to all others, events from other cameras do not come

reason here:

Cam.prototype.on('newListener', function(name) {
        // if there is the first listener, start pulling
        console.info(this.hostname, 'newListener', this.listeners(name), this.listenerCount(name));
        if (name === 'event' && this.listeners(name).length === 0) {
            this._eventRequest();
        }
    });

in log:

cam1 newListener [] 0
cam2 newListener [ [Function (anonymous)] ] 1

for some reason this.listeners(name) is global here

for test this issue:

let Cam = require('./lib/onvif').Cam;
let flow = require('nimble');

const callback = function CamFunc(err) {
    if (err) {
        console.log(err);
        return;
    }

    console.log('Connected to ONVIF Device');

    let cam_obj = this;

    let hasEvents = false;
    let hasTopics = false;
    let name = "";

    // Use Nimble's flow to execute ONVIF commands in sequence
    flow.series([
        function(callback) {
            cam_obj.getDeviceInformation(function(err, info, xml) {
                if (!err) console.log('Manufacturer  ' + info.manufacturer);
                if (!err) console.log('Model         ' + info.model);
                if (!err) console.log('Firmware      ' + info.firmwareVersion);
                if (!err) console.log('Serial Number ' + info.serialNumber);
                name = info.serialNumber;
                callback();
            });
        },
        function(callback) {
            cam_obj.getCapabilities(function(err, data, xml) {
                if (err) {
                    console.log(err);
                }
                if (!err && data.events && data.events.WSPullPointSupport && data.events.WSPullPointSupport == true) {
                    console.log('Camera supports WSPullPoint');
                    hasEvents = true;
                } else {
                    console.log('Camera does not show WSPullPoint support, but trying anyway');
                    // Have an Axis cameras that says False to WSPullPointSuppor but supports it anyway
                    hasEvents = true; // Hack for Axis cameras
                }

                if (hasEvents == false) {
                    console.log('This camera/NVT does not support PullPoint Events')
                }
                callback();
            });
        },
        function(callback) {
            if (hasEvents) {
                cam_obj.getEventProperties(function(err, data, xml) {
                    if (err) {
                        console.log(err);
                    }
                    else {
                        // Display the available Topics
                        let parseNode = function(node, topicPath) {
                            // loop over all the child nodes in this node
                            for (const child in node) {
                                if (child == "$") continue;
                                else if (child == "messageDescription") {
                                    // we have found the details that go with an event
                                    // examine the messageDescription
                                    let IsProperty = false;
                                    let source = '';
                                    let data = '';
                                    if (node[child].$ && node[child].$.IsProperty) IsProperty = node[child].$.IsProperty;
                                    if (node[child].source) source = JSON.stringify(node[child].source)
                                    if (node[child].data) data = JSON.stringify(node[child].data)
                                    console.log('Found Event - ' + topicPath.toUpperCase())
                                    //console.log('  IsProperty=' + IsProperty);
                                    //if (source.length > 0) console.log('  Source=' + source);
                                    //if (data.length > 0) console.log('  Data=' + data);
                                    hasTopics = true;
                                    return;
                                }
                                else {
                                    // decend into the child node, looking for the messageDescription
                                    parseNode(node[child], topicPath + '/' + child)
                                }
                            }
                        }
                        parseNode(data.topicSet, '');
                    }
                    callback();
                });
            } else {
                callback();
            }
        },
        function(callback) {
            if (hasEvents && hasTopics) {
                cam_obj.on('event', camMessage => {

                    // Extract Event Details
                    let eventTime = camMessage.message.message.$.UtcTime
                    let eventTopic = camMessage.topic._;
                    let eventProperty = camMessage.message.message.$.PropertyOperation
                    // there can be more than one SimpleItem in the 'data' part of the XML
                    let eventType;
                    let eventValue;

                    if (Array.isArray(camMessage.message.message.data)) {
                        eventType = camMessage.message.message.data.simpleItem.$.Name
                        eventValue = camMessage.message.message.data.simpleItem.$.Value
                    } else {
                        eventType = camMessage.message.message.data.simpleItem.$.Name
                        eventValue = camMessage.message.message.data.simpleItem.$.Value
                    }

                    console.log(`${name} EVENT TRIGGERED: ${eventTime.toJSON()} ${eventTopic} ${eventProperty} ${eventType} ${eventValue}`);
                })
            }
            callback();
        }
    ]);

    // Code completes here but the applictions remains running as there is a OnEvent listener that is active
    // To stop we must remove the event listener
    //setTimeout(()=>{cam_obj.removeAllListeners('event');},5000);

}

new Cam({
    hostname : "cam1",
    username : "admin",
    password : "admin",
    port : 80,
    timeout : 10000,
    preserveAddress : true   // Enables NAT support and re-writes for PullPointSubscription URL
}, callback);

new Cam({
    hostname : "cam2",
    username : "admin",
    password : "admin",
    port : 80,
    timeout : 10000,
    preserveAddress : true   // Enables NAT support and re-writes for PullPointSubscription URL
}, callback);