diff --git a/lib/client.js b/lib/client.js index eebc2d46..bd030ada 100644 --- a/lib/client.js +++ b/lib/client.js @@ -911,6 +911,117 @@ class Client extends EventEmitter { }); } + /** + * The unconfirmedCOVNotification command sends an unconfirmed COV notification to a device + * @function bacstack.unconfirmedCOVNotification + * @param {string} address - IP address of the target device. + * @param {number} subscriberProcessId - The process id which was used by a target device in the subscription. + * @param {number} initiatingDeviceId - The id of this device. + * @param {object} monitoredObjectId - Specifies about which object the notification is. + * @param {number} monitoredObjectId.type - The BACNET object type of the notification. + * @param {number} monitoredObjectId.instance - The BACNET object instance of the notification. + * @param {number} timeRemaining - How long the subscription is still active in seconds. + * @param {object[]} values - List of properties with updated values. + * @param {object} values.property - Property specifications. + * @param {number} values.property.id - The updated BACNET property id. + * @param {number} values.property.index - The array index of the updated property. + * @param {object[]} values.value - A list of updated values. + * @param {ApplicationTags} values.value.type - The data-type of the updated value. + * @param {object} values.value.value - The actual updated value. + * @param {number} values.priority - The priority of the updated property. + * @example + * const bacnet = require('bacstack'); + * const client = new bacnet(); + * + * client.unconfirmedCOVNotification( + * '127.0.0.1', + * 3, + * 433, + * {type: 2, instance: 122}, + * 120, + * [ + * { + * property: {id: 85}, + * value: [{type: baEnum.ApplicationTags.REAL, value: 12.3}] + * }, + * { + * property: {id: 111}, + * value: [{type: baEnum.ApplicationTags.BIT_STRING, value: 0xFFFF}] + * } + * ]); + */ + unconfirmedCOVNotification(address, subscriberProcessId, initiatingDeviceId, monitoredObjectId, timeRemaining, values) { + const buffer = this._getBuffer(); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, address); + baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.UNCONFIRMED_COV_NOTIFICATION); + baServices.covNotify.encode(buffer, subscriberProcessId, initiatingDeviceId, monitoredObjectId, timeRemaining, values); + baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); + this._transport.send(buffer.buffer, buffer.offset, address); + } + + /** + * The unconfirmedCOVNotification command sends an unconfirmed COV notification to a device + * @function bacstack.unconfirmedCOVNotification + * @param {string} address - IP address of the target device. + * @param {number} subscriberProcessId - The process id which was used by a target device in the subscription. + * @param {number} initiatingDeviceId - The id of this device. + * @param {object} monitoredObjectId - Specifies about which object the notification is. + * @param {number} monitoredObjectId.type - The BACNET object type of the notification. + * @param {number} monitoredObjectId.instance - The BACNET object instance of the notification. + * @param {number} timeRemaining - How long the subscription is still active in seconds. + * @param {object[]} values - List of properties with updated values. + * @param {object} values.property - Property specifications. + * @param {number} values.property.id - The updated BACNET property id. + * @param {number} values.property.index - The array index of the updated property. + * @param {object[]} values.value - A list of updated values. + * @param {ApplicationTags} values.value.type - The data-type of the updated value. + * @param {object} values.value.value - The actual updated value. + * @param {number} values.priority - The priority of the updated property. + * @param {function} next - The callback containing an error, in case of a failure and value object in case of success. + * @example + * const bacnet = require('bacstack'); + * const client = new bacnet(); + * + * client.confirmedCOVNotification( + * '127.0.0.1', + * 3, + * 433, + * {type: 2, instance: 122}, + * 120, + * [ + * { + * property: {id: 85}, + * value: [{type: baEnum.ApplicationTags.REAL, value: 12.3}] + * }, + * { + * property: {id: 111}, + * value: [{type: baEnum.ApplicationTags.BIT_STRING, value: 0xFFFF}] + * } + * ], + * (err, value) => { + * console.log('value: ', value); + * next(); + * }); + */ + confirmedCOVNotification(address, subscriberProcessId, initiatingDeviceId, monitoredObjectId, timeRemaining, values, options, next) { + next = next || options; + const settings = { + maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65, + maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476, + invokeId: options.invokeId || this._getInvokeId() + }; + const buffer = this._getBuffer(); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CONFIRMED_COV_NOTIFICATION, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); + baServices.covNotify.encode(buffer, subscriberProcessId, initiatingDeviceId, monitoredObjectId, timeRemaining, values); + baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); + this._transport.send(buffer.buffer, buffer.offset, address); + this._addCallback(settings.invokeId, (err, data) => { + if (err) return next(err); + next(); + }); + } + createObject(address, objectId, values, options, next) { next = next || options; const settings = { diff --git a/test/integration/confirmed-cov-notification.spec.js b/test/integration/confirmed-cov-notification.spec.js new file mode 100644 index 00000000..66112eb5 --- /dev/null +++ b/test/integration/confirmed-cov-notification.spec.js @@ -0,0 +1,29 @@ +'use strict'; + +const expect = require('chai').expect; +const utils = require('./utils'); +const baEnum = require('../../lib/enum'); + +describe('bacstack - confirmedCOVNotification integration', () => { + it('should return a timeout error if no device is available', (next) => { + const client = new utils.bacnetClient({apduTimeout: 200}); + client.confirmedCOVNotification( + '127.0.0.1', 3, 433, {type: 2, instance: 122}, 120, + [ + { + property: {id: 85}, + value: [{type: baEnum.ApplicationTags.REAL, value: 12.3}] + }, + { + property: {id: 111}, + value: [{type: baEnum.ApplicationTags.BIT_STRING, value: 0xFFFF}] + } + ], + (err, value) => { + expect(err.message).to.eql('ERR_TIMEOUT'); + expect(value).to.eql(undefined); + client.close(); + next(); + }); + }); +}); diff --git a/test/integration/unconfirmed-cov-notification.spec.js b/test/integration/unconfirmed-cov-notification.spec.js new file mode 100644 index 00000000..c52937b4 --- /dev/null +++ b/test/integration/unconfirmed-cov-notification.spec.js @@ -0,0 +1,24 @@ +'use strict'; + +const expect = require('chai').expect; +const utils = require('./utils'); +const baEnum = require('../../lib/enum'); + +describe('bacstack - unconfirmedCOVNotification integration', () => { + it('should correctly send a telegram', () => { + const client = new utils.bacnetClient({apduTimeout: 200}); + client.unconfirmedCOVNotification( + '127.0.0.1', 3, 433, {type: 2, instance: 122}, 120, [ + { + property: {id: 85}, + value: [{type: baEnum.ApplicationTags.REAL, value: 12.3}] + }, + { + property: {id: 111}, + value: + [{type: baEnum.ApplicationTags.BIT_STRING, value: 0xFFFF}] + } + ]); + client.close(); + }); +});