Skip to content

Commit

Permalink
feat: implemented more BACNET functions
Browse files Browse the repository at this point in the history
  • Loading branch information
fh1ch committed Aug 16, 2017
1 parent 1e60d5f commit 2721232
Show file tree
Hide file tree
Showing 13 changed files with 335 additions and 30 deletions.
36 changes: 36 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,42 @@ module.exports = function(settings) {
client.reinitializeDevice(address, state, password, next);
};

self.writeFile = function(address, objectId, position, count, fileBuffer, next) {
client.writeFile(address, objectId, position, count, fileBuffer, next);
};

self.readFile = function(address, objectId, position, count, next) {
client.readFile(address, objectId, position, count, next);
};

self.readRange = function(address, objectId, idxBegin, quantity, next) {
client.readRange(address, objectId, idxBegin, quantity, next);
};

self.subscribeCOV = function(address, objectId, subscribeId, cancel, issueConfirmedNotifications, lifetime, next) {
client.subscribeCOV(address, objectId, subscribeId, cancel, issueConfirmedNotifications, lifetime, next);
};

self.subscribeProperty = function(address, objectId, monitoredProperty, subscribeId, cancel, issueConfirmedNotifications, next) {
client.subscribeProperty(address, objectId, monitoredProperty, subscribeId, cancel, issueConfirmedNotifications, next);
};

self.createObject = function(address, objectId, valueList, next) {
client.createObject(address, objectId, valueList, next);
};

self.deleteObject = function(address, objectId, next) {
client.deleteObject(address, objectId, next);
};

self.removeListElement = function(address, objectId, reference, valueList, next) {
client.removeListElement(address, objectId, reference, valueList, next);
};

self.addListElement = function(address, objectId, reference, valueList, next) {
client.addListElement(address, objectId, reference, valueList, next);
};

/**
* Unloads the current BACstack instance and closes the underlying UDP socket.
* @function bacstack.close
Expand Down
141 changes: 141 additions & 0 deletions lib/bacnet-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,147 @@ module.exports = function(settings) {
});
};

self.writeFile = function(address, objectId, position, count, fileBuffer, next) {
var maxSegments = baEnum.BacnetMaxSegments.MAX_SEG65;
var buffer = getBuffer();
var invokeId = getInvokeId();
baNpdu.encode(buffer, baEnum.BacnetNpduControls.PRIORITY_NORMAL_MESSAGE | baEnum.BacnetNpduControls.EXPECTING_REPLY, address);
baAdpu.encodeConfirmedServiceRequest(buffer, baEnum.BacnetPduTypes.PDU_TYPE_CONFIRMED_SERVICE_REQUEST, baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, maxSegments, baEnum.BacnetMaxAdpu.MAX_APDU1476, invokeId, 0, 0);
baServices.encodeAtomicWriteFile(buffer, true, objectId, position, 1, fileBuffer, count);
baBvlc.encode(buffer.buffer, baEnum.BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU, buffer.offset);
transport.send(buffer.buffer, buffer.offset, address);
addCallback(invokeId, function(err, data) {
if (err) return next(err);
var result = baServices.decodeAtomicWriteFileAcknowledge(data.buffer, data.offset, data.length);
if (!result) return next(new Error('INVALID_DECODING'));
next(null, result);
});
};

self.readFile = function(address, objectId, position, count, next) {
var maxSegments = baEnum.BacnetMaxSegments.MAX_SEG65;
var buffer = getBuffer();
var invokeId = getInvokeId();
baNpdu.encode(buffer, baEnum.BacnetNpduControls.PRIORITY_NORMAL_MESSAGE | baEnum.BacnetNpduControls.EXPECTING_REPLY, address);
baAdpu.encodeConfirmedServiceRequest(buffer, baEnum.BacnetPduTypes.PDU_TYPE_CONFIRMED_SERVICE_REQUEST, baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_ATOMIC_READ_FILE, maxSegments, baEnum.BacnetMaxAdpu.MAX_APDU1476, invokeId, 0, 0);
baServices.encodeAtomicReadFile(buffer, true, objectId, position, count);
baBvlc.encode(buffer.buffer, baEnum.BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU, buffer.offset);
transport.send(buffer.buffer, buffer.offset, address);
addCallback(invokeId, function(err, data) {
if (err) return next(err);
var result = baServices.decodeAtomicReadFileAcknowledge(data.buffer, data.offset, data.length);
if (!result) return next(new Error('INVALID_DECODING'));
next(null, result);
});
};

self.readRange = function(address, objectId, idxBegin, quantity, next) {
var maxSegments = baEnum.BacnetMaxSegments.MAX_SEG65;
var buffer = getBuffer();
var invokeId = getInvokeId();
baNpdu.encode(buffer, baEnum.BacnetNpduControls.PRIORITY_NORMAL_MESSAGE | baEnum.BacnetNpduControls.EXPECTING_REPLY, address);
baAdpu.encodeConfirmedServiceRequest(buffer, baEnum.BacnetPduTypes.PDU_TYPE_CONFIRMED_SERVICE_REQUEST, baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_READ_RANGE, maxSegments, baEnum.BacnetMaxAdpu.MAX_APDU1476, invokeId, 0, 0);
baServices.encodeReadRange(buffer, objectId, baEnum.BacnetPropertyIds.PROP_LOG_BUFFER, baAsn1.BACNET_ARRAY_ALL, baEnum.BacnetReadRangeRequestTypes.RR_BY_POSITION, idxBegin, new Date(), quantity);
baBvlc.encode(buffer.buffer, baEnum.BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU, buffer.offset);
transport.send(buffer.buffer, buffer.offset, address);
addCallback(invokeId, function(err, data) {
if (err) return next(err);
var result = baServices.decodeReadRangeAcknowledge(data.buffer, data.offset, data.length);
if (!result) return next(new Error('INVALID_DECODING'));
next(null, result);
});
};

self.subscribeCOV = function(address, objectId, subscribeId, cancel, issueConfirmedNotifications, lifetime, next) {
var maxSegments = baEnum.BacnetMaxSegments.MAX_SEG65;
var buffer = getBuffer();
var invokeId = getInvokeId();
baNpdu.encode(buffer, baEnum.BacnetNpduControls.PRIORITY_NORMAL_MESSAGE | baEnum.BacnetNpduControls.EXPECTING_REPLY, address);
baAdpu.encodeConfirmedServiceRequest(buffer, baEnum.BacnetPduTypes.PDU_TYPE_CONFIRMED_SERVICE_REQUEST, baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_SUBSCRIBE_COV, maxSegments, baEnum.BacnetMaxAdpu.MAX_APDU1476, invokeId, 0, 0);
baServices.encodeSubscribeCOV(buffer, subscribeId, objectId, cancel, issueConfirmedNotifications, lifetime);
baBvlc.encode(buffer.buffer, baEnum.BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU, buffer.offset);
transport.send(buffer.buffer, buffer.offset, address);
addCallback(invokeId, function(err, data) {
if (err) return next(err);
next();
});
};

self.subscribeProperty = function(address, objectId, monitoredProperty, subscribeId, cancel, issueConfirmedNotifications, next) {
var maxSegments = baEnum.BacnetMaxSegments.MAX_SEG65;
var buffer = getBuffer();
var invokeId = getInvokeId();
baNpdu.encode(buffer, baEnum.BacnetNpduControls.PRIORITY_NORMAL_MESSAGE | baEnum.BacnetNpduControls.EXPECTING_REPLY, address);
baAdpu.encodeConfirmedServiceRequest(buffer, baEnum.BacnetPduTypes.PDU_TYPE_CONFIRMED_SERVICE_REQUEST, baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY, maxSegments, baEnum.BacnetMaxAdpu.MAX_APDU1476, invokeId, 0, 0);
baServices.encodeSubscribeProperty(buffer, subscribeId, objectId, cancel, issueConfirmedNotifications, 0, monitoredProperty, false, 0x0f);
baBvlc.encode(buffer.buffer, baEnum.BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU, buffer.offset);
transport.send(buffer.buffer, buffer.offset, address);
addCallback(invokeId, function(err, data) {
if (err) return next(err);
next();
});
};

self.createObject = function(address, objectId, valueList, next) {
var maxSegments = baEnum.BacnetMaxSegments.MAX_SEG65;
var buffer = getBuffer();
var invokeId = getInvokeId();
baNpdu.encode(buffer, baEnum.BacnetNpduControls.PRIORITY_NORMAL_MESSAGE | baEnum.BacnetNpduControls.EXPECTING_REPLY, address);
baAdpu.encodeConfirmedServiceRequest(buffer, baEnum.BacnetPduTypes.PDU_TYPE_CONFIRMED_SERVICE_REQUEST, baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_CREATE_OBJECT, maxSegments, baEnum.BacnetMaxAdpu.MAX_APDU1476, invokeId, 0, 0);
baServices.encodeCreateProperty(buffer, objectId, valueList);
baBvlc.encode(buffer.buffer, baEnum.BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU, buffer.offset);
transport.send(buffer.buffer, buffer.offset, address);
addCallback(invokeId, function(err, data) {
if (err) return next(err);
next();
});
};

self.deleteObject = function(address, objectId, next) {
var maxSegments = baEnum.BacnetMaxSegments.MAX_SEG65;
var buffer = getBuffer();
var invokeId = getInvokeId();
baNpdu.encode(buffer, baEnum.BacnetNpduControls.PRIORITY_NORMAL_MESSAGE | baEnum.BacnetNpduControls.EXPECTING_REPLY, address);
baAdpu.encodeConfirmedServiceRequest(buffer, baEnum.BacnetPduTypes.PDU_TYPE_CONFIRMED_SERVICE_REQUEST, baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_DELETE_OBJECT, maxSegments, baEnum.BacnetMaxAdpu.MAX_APDU1476, invokeId, 0, 0);
baServices.encodeDeleteObject(buffer, objectId);
baBvlc.encode(buffer.buffer, baEnum.BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU, buffer.offset);
transport.send(buffer.buffer, buffer.offset, address);
addCallback(invokeId, function(err, data) {
if (err) return next(err);
next();
});
};

self.removeListElement = function(address, objectId, reference, valueList, next) {
var maxSegments = baEnum.BacnetMaxSegments.MAX_SEG65;
var buffer = getBuffer();
var invokeId = getInvokeId();
baNpdu.encode(buffer, baEnum.BacnetNpduControls.PRIORITY_NORMAL_MESSAGE | baEnum.BacnetNpduControls.EXPECTING_REPLY, address);
baAdpu.encodeConfirmedServiceRequest(buffer, baEnum.BacnetPduTypes.PDU_TYPE_CONFIRMED_SERVICE_REQUEST, baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT, maxSegments, baEnum.BacnetMaxAdpu.MAX_APDU1476, invokeId, 0, 0);
baServices.encodeAddListElement(buffer, objectId, reference.propertyIdentifier, reference.propertyArrayIndex, valueList);
baBvlc.encode(buffer.buffer, baEnum.BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU, buffer.offset);
transport.send(buffer.buffer, buffer.offset, address);
addCallback(invokeId, function(err, data) {
if (err) return next(err);
next();
});
};

self.addListElement = function(address, objectId, reference, valueList, next) {
var maxSegments = baEnum.BacnetMaxSegments.MAX_SEG65;
var buffer = getBuffer();
var invokeId = getInvokeId();
baNpdu.encode(buffer, baEnum.BacnetNpduControls.PRIORITY_NORMAL_MESSAGE | baEnum.BacnetNpduControls.EXPECTING_REPLY, address);
baAdpu.encodeConfirmedServiceRequest(buffer, baEnum.BacnetPduTypes.PDU_TYPE_CONFIRMED_SERVICE_REQUEST, baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_ADD_LIST_ELEMENT, maxSegments, baEnum.BacnetMaxAdpu.MAX_APDU1476, invokeId, 0, 0);
baServices.encodeAddListElement(buffer, objectId, reference.propertyIdentifier, reference.propertyArrayIndex, valueList);
baBvlc.encode(buffer.buffer, baEnum.BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU, buffer.offset);
transport.send(buffer.buffer, buffer.offset, address);
addCallback(invokeId, function(err, data) {
if (err) return next(err);
next();
});
};

self.close = function() {
transport.close();
};
Expand Down
43 changes: 13 additions & 30 deletions lib/bacnet-services.js
Original file line number Diff line number Diff line change
Expand Up @@ -920,17 +920,6 @@ module.exports.encodeWriteObjectMultiple = function(buffer, valueList) {
});
};

module.exports.decodeDeleteObject = function(buffer, offset, apduLen) {
var result = baAsn1.decodeTagNumberAndValue(buffer, offset);
if (result.tagNumber === 12) return;
var len = 1;
var value = baAsn1.decodeObjectId(buffer, offset + len);
len += value.len;
if (len !== apduLen) return;
value.len = len;
return value;
};

module.exports.encodeCreateObjectAcknowledge = function(buffer, objectId) {
baAsn1.encodeApplicationObjectId(buffer, objectId.type, objectId.instance);
};
Expand Down Expand Up @@ -1379,10 +1368,19 @@ module.exports.decodeReadRangeAcknowledge = function(buffer, offset, apduLen) {
};
};

module.exports.encodeWriteObjectMultiple = function(buffer, valueList) {
valueList.forEach(function(rValue) {
encodeWritePropertyMultiple(buffer, rValue.objectIdentifier, rValue.values);
});
module.exports.decodeDeleteObject = function(buffer, offset, apduLen) {
var result = baAsn1.decodeTagNumberAndValue(buffer, offset);
if (result.tagNumber !== 12) return;
var len = 1;
var value = baAsn1.decodeObjectId(buffer, offset + len);
len += value.len;
if (len !== apduLen) return;
value.len = len;
return value;
};

module.exports.encodeDeleteObject = function(buffer, objectId) {
baAsn1.encodeApplicationObjectId(buffer, objectId.type, objectId.instance);
};

// TODO: Implement decoding for following functions
Expand Down Expand Up @@ -1503,21 +1501,6 @@ module.exports.encodePrivateTransferAcknowledge = function(buffer, vendorID, ser
baAsn1.encodeClosingTag(buffer, 2);
};

module.exports.decodeDeleteObject = function(buffer, offset, apduLen) {
var result = baAsn1.decodeTagNumberAndValue(buffer, offset);
if (result.tagNumber === 12) return;
var len = 1;
var value = baAsn1.decodeObjectId(buffer, offset + len);
len += value.len;
if (len !== apduLen) return;
value.len = len;
return value;
};

module.exports.encodeCreateObjectAcknowledge = function(buffer, objectId) {
baAsn1.encodeApplicationObjectId(buffer, objectId.type, objectId.instance);
};

module.exports.encodeCOVNotify = function(buffer, subscriberProcessIdentifier, initiatingDeviceIdentifier, monitoredObjectIdentifier, timeRemaining, values) {
baAsn1.encodeContextUnsigned(buffer, 0, subscriberProcessIdentifier);
baAsn1.encodeContextObjectId(buffer, 1, baEnum.BacnetObjectTypes.OBJECT_DEVICE, initiatingDeviceIdentifier);
Expand Down
16 changes: 16 additions & 0 deletions test/integration/add-list-element.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var expect = require('chai').expect;
var utils = require('./utils');

describe('bacstack - addListElement integration', function() {
it('should return a timeout error if no device is available', function(next) {
var client = new utils.bacnetClient({adpuTimeout: 200});
client.addListElement('127.0.0.1', {type: 19, instance: 101}, {propertyIdentifier: 80, propertyArrayIndex: 0}, [
{type: 1, value: true}
], function(err, value) {
expect(err).to.eql(new Error('ERR_TIMEOUT'));
expect(value).to.eql(undefined);
client.close();
next();
});
});
});
16 changes: 16 additions & 0 deletions test/integration/create-object.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var expect = require('chai').expect;
var utils = require('./utils');

describe('bacstack - createObject integration', function() {
it('should return a timeout error if no device is available', function(next) {
var client = new utils.bacnetClient({adpuTimeout: 200});
client.createObject('127.0.0.1', {type: 2, instance: 300}, [
{property: {propertyIdentifier: 85, propertyArrayIndex: 1}, value: [{type: 1, value: true}]}
], function(err, value) {
expect(err).to.eql(new Error('ERR_TIMEOUT'));
expect(value).to.eql(undefined);
client.close();
next();
});
});
});
14 changes: 14 additions & 0 deletions test/integration/delete-object.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var expect = require('chai').expect;
var utils = require('./utils');

describe('bacstack - deleteObject integration', function() {
it('should return a timeout error if no device is available', function(next) {
var client = new utils.bacnetClient({adpuTimeout: 200});
client.deleteObject('127.0.0.1', {type: 2, instance: 15}, function(err, value) {
expect(err).to.eql(new Error('ERR_TIMEOUT'));
expect(value).to.eql(undefined);
client.close();
next();
});
});
});
14 changes: 14 additions & 0 deletions test/integration/read-file.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var expect = require('chai').expect;
var utils = require('./utils');

describe('bacstack - readFile integration', function() {
it('should return a timeout error if no device is available', function(next) {
var client = new utils.bacnetClient({adpuTimeout: 200});
client.readFile('127.0.0.1', {type: 10, instance: 100}, 0, 100, function(err, value) {
expect(err).to.eql(new Error('ERR_TIMEOUT'));
expect(value).to.eql(undefined);
client.close();
next();
});
});
});
14 changes: 14 additions & 0 deletions test/integration/read-range.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var expect = require('chai').expect;
var utils = require('./utils');

describe('bacstack - readRange integration', function() {
it('should return a timeout error if no device is available', function(next) {
var client = new utils.bacnetClient({adpuTimeout: 200});
client.readRange('127.0.0.1', {type: 20, instance: 0}, 0, 200, function(err, value) {
expect(err).to.eql(new Error('ERR_TIMEOUT'));
expect(value).to.eql(undefined);
client.close();
next();
});
});
});
16 changes: 16 additions & 0 deletions test/integration/remove-list-element.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var expect = require('chai').expect;
var utils = require('./utils');

describe('bacstack - removeListElement integration', function() {
it('should return a timeout error if no device is available', function(next) {
var client = new utils.bacnetClient({adpuTimeout: 200});
client.removeListElement('127.0.0.1', {type: 19, instance: 100}, {propertyIdentifier: 80, propertyArrayIndex: 0}, [
{type: 1, value: true}
], function(err, value) {
expect(err).to.eql(new Error('ERR_TIMEOUT'));
expect(value).to.eql(undefined);
client.close();
next();
});
});
});
14 changes: 14 additions & 0 deletions test/integration/subscribe-cov.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var expect = require('chai').expect;
var utils = require('./utils');

describe('bacstack - subscribeCOV integration', function() {
it('should return a timeout error if no device is available', function(next) {
var client = new utils.bacnetClient({adpuTimeout: 200});
client.subscribeCOV('127.0.0.1', {type: 5, instance: 3}, 7, false, false, 0, function(err, value) {
expect(err).to.eql(new Error('ERR_TIMEOUT'));
expect(value).to.eql(undefined);
client.close();
next();
});
});
});
14 changes: 14 additions & 0 deletions test/integration/subscribe-property.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var expect = require('chai').expect;
var utils = require('./utils');

describe('bacstack - subscribeProperty integration', function() {
it('should return a timeout error if no device is available', function(next) {
var client = new utils.bacnetClient({adpuTimeout: 200});
client.subscribeProperty('127.0.0.1', {type: 5, instance: 33}, {propertyIdentifier: 80, propertyArrayIndex: 0}, 8, false, false, function(err, value) {
expect(err).to.eql(new Error('ERR_TIMEOUT'));
expect(value).to.eql(undefined);
client.close();
next();
});
});
});
14 changes: 14 additions & 0 deletions test/integration/write-file.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var expect = require('chai').expect;
var utils = require('./utils');

describe('bacstack - writeFile integration', function() {
it('should return a timeout error if no device is available', function(next) {
var client = new utils.bacnetClient({adpuTimeout: 200});
client.writeFile('127.0.0.1', {type: 10, instance: 2}, 0, 4, [5, 6, 7 , 8], function(err, value) {
expect(err).to.eql(new Error('ERR_TIMEOUT'));
expect(value).to.eql(undefined);
client.close();
next();
});
});
});
Loading

0 comments on commit 2721232

Please sign in to comment.