Skip to content

Commit

Permalink
Merge pull request #57 from fh1ch/feat/decoding-cov-create-object
Browse files Browse the repository at this point in the history
feat(bacnet-service): implement decoding functionality for COV and Cr…
  • Loading branch information
fh1ch committed Aug 21, 2017
2 parents 29eefef + 635e419 commit 00772d0
Show file tree
Hide file tree
Showing 4 changed files with 367 additions and 68 deletions.
8 changes: 3 additions & 5 deletions lib/bacnet-asn1.js
Original file line number Diff line number Diff line change
Expand Up @@ -1651,14 +1651,12 @@ var decodeContextDate = function(buffer, offset, tagNumber) {
};
};

var decodeContextObjectId = function(buffer, offset, tagNumber) {
module.exports.decodeContextObjectId = function(buffer, offset, tagNumber) {
var result = decodeIsContextTagWithLength(buffer, offset, tagNumber);
if (!result.value) return;
var decodedValue = decodeObjectId(buffer, offset + result.len);
return {
len: result.len + decodedValue.len,
value: decodedValue.value
};
decodedValue.len = decodedValue.len + result.len;
return decodedValue;
};

var bacappDecodeContextData = function(buffer, offset, maxApduLen, propertyTag) {
Expand Down
9 changes: 4 additions & 5 deletions lib/bacnet-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,9 @@ module.exports = function(options) {
if (!result) return debug('Received invalid readRange message');
self.emit('readRange', {address: address, invokeId: invokeId, request: result});
} else if (service === baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_CREATE_OBJECT) {
// TODO: Implement
// result = baServices.decodeCreateObject(buffer, offset, length);
// if (!result) return debug('Received invalid createObject message');
// self.emit('createObject', {address: address, invokeId: invokeId, request: result});
result = baServices.decodeCreateObject(buffer, offset, length);
if (!result) return debug('Received invalid createObject message');
self.emit('createObject', {address: address, invokeId: invokeId, request: result});
} else if (service === baEnum.BacnetConfirmedServices.SERVICE_CONFIRMED_DELETE_OBJECT) {
result = baServices.decodeDeleteObject(buffer, offset, length);
if (!result) return debug('Received invalid deleteObject message');
Expand Down Expand Up @@ -727,7 +726,7 @@ module.exports = function(options) {
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);
baServices.encodeCreateObject(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) {
Expand Down
246 changes: 188 additions & 58 deletions lib/bacnet-services.js
Original file line number Diff line number Diff line change
Expand Up @@ -1383,23 +1383,7 @@ module.exports.encodeDeleteObject = function(buffer, objectId) {
baAsn1.encodeApplicationObjectId(buffer, objectId.type, objectId.instance);
};

// TODO: Implement decoding for following functions
module.exports.encodeIhaveBroadcast = function(buffer, deviceId, objectId, objectName) {
baAsn1.encodeApplicationObjectId(buffer, deviceId.type, deviceId.instance);
baAsn1.encodeApplicationObjectId(buffer, objectId.type, objectId.instance);
baAsn1.encodeApplicationCharacterString(buffer, objectName);
};

module.exports.encodeAlarmAcknowledge = function(buffer, ackProcessIdentifier, eventObjectIdentifier, eventStateAcked, ackSource, eventTimeStamp, ackTimeStamp) {
baAsn1.encodeContextUnsigned(buffer, 0, ackProcessIdentifier);
baAsn1.encodeContextObjectId(buffer, 1, eventObjectIdentifier.type, eventObjectIdentifier.instance);
baAsn1.encodeContextEnumerated(buffer, 2, eventStateAcked);
baAsn1.bacappEncodeContextTimestamp(buffer, 3, eventTimeStamp);
baAsn1.encodeContextCharacterString(buffer, 4, ackSource);
baAsn1.bacappEncodeContextTimestamp(buffer, 5, ackTimeStamp);
};

module.exports.encodeCreateProperty = function(buffer, objectId, valueList) {
module.exports.encodeCreateObject = function(buffer, objectId, valueList) {
baAsn1.encodeOpeningTag(buffer, 0);
baAsn1.encodeContextObjectId(buffer, 1, objectId.type, objectId.instance);
baAsn1.encodeClosingTag(buffer, 0);
Expand All @@ -1421,6 +1405,192 @@ module.exports.encodeCreateProperty = function(buffer, objectId, valueList) {
baAsn1.encodeClosingTag(buffer, 1);
};

module.exports.decodeCreateObject = function(buffer, offset, apduLen) {
var len = 0;
var result;
var decodedValue;
var objectId;
var valuesRefs = [];
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
if ((result.tagNumber === 0) && (apduLen > len)) {
apduLen -= len;
if (apduLen < 4) return;
decodedValue = baAsn1.decodeContextObjectId(buffer, offset + len, 1);
len += decodedValue.len;
objectId = {type: decodedValue.objectType, instance: decodedValue.instance};
} else {
return;
}
if (baAsn1.decodeIsClosingTag(buffer, offset + len)) {
len++;
}
if (!baAsn1.decodeIsOpeningTagNumber(buffer, offset + len, 1)) return;
len++;
while ((apduLen - len) > 1) {
var newEntry = {};
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
if (result.tagNumber !== 0) return;
decodedValue = baAsn1.decodeEnumerated(buffer, offset + len, result.value);
len += decodedValue.len;
var propertyId = decodedValue.value;
var arraIndex = baAsn1.BACNET_ARRAY_ALL;
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
if (result.tagNumber === 1) {
decodedValue = baAsn1.decodeUnsigned(buffer, offset + len, result.value);
len += decodedValue.len;
arraIndex += decodedValue.value;
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
}
newEntry.property = {propertyId: propertyId, arrayIndex: arraIndex};
if ((result.tagNumber === 2) && (baAsn1.decodeIsOpeningTag(buffer, offset + len - 1))) {
var values = [];
while (!baAsn1.decodeIsClosingTag(buffer, offset + len)) {
decodedValue = baAsn1.bacappDecodeApplicationData(buffer, offset + len, apduLen + offset, objectId.type, propertyId);
if (!decodedValue) return;
len += decodedValue.len;
values.push(decodedValue);
}
len++;
newEntry.value = values;
} else {
return;
}
valuesRefs.push(newEntry);
}
if (!baAsn1.decodeIsClosingTagNumber(buffer, offset + len, 1)) return;
len++;
return {
len: len,
objectId: objectId,
values: valuesRefs
};
};

module.exports.encodeCOVNotify = function(buffer, subscriberProcessIdentifier, initiatingDeviceIdentifier, monitoredObjectIdentifier, timeRemaining, values) {
baAsn1.encodeContextUnsigned(buffer, 0, subscriberProcessIdentifier);
baAsn1.encodeContextObjectId(buffer, 1, baEnum.BacnetObjectTypes.OBJECT_DEVICE, initiatingDeviceIdentifier);
baAsn1.encodeContextObjectId(buffer, 2, monitoredObjectIdentifier.type, monitoredObjectIdentifier.instance);
baAsn1.encodeContextUnsigned(buffer, 3, timeRemaining);
baAsn1.encodeOpeningTag(buffer, 4);
values.forEach(function(value) {
baAsn1.encodeContextEnumerated(buffer, 0, value.property.propertyIdentifier);
if (value.property.propertyArrayIndex === baAsn1.BACNET_ARRAY_ALL) {
baAsn1.encodeContextUnsigned(buffer, 1, value.property.propertyArrayIndex);
}
baAsn1.encodeOpeningTag(buffer, 2);
value.value.forEach(function(v) {
baAsn1.bacappEncodeApplicationData(buffer, v);
});
baAsn1.encodeClosingTag(buffer, 2);
if (value.priority === baAsn1.BACNET_NO_PRIORITY) {
baAsn1.encodeContextUnsigned(buffer, 3, value.priority);
}
// TODO: Handle to too large telegrams -> ADPU limit
});
baAsn1.encodeClosingTag(buffer, 4);
};

module.exports.decodeCOVNotify = function(buffer, offset, apduLen) {
var len = 0;
var result;
var decodedValue;
if (!baAsn1.decodeIsContextTag(buffer, offset + len, 0)) return;
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
decodedValue = baAsn1.decodeUnsigned(buffer, offset + len, result.value);
len += decodedValue.len;
var subscriberProcessIdentifier = decodedValue.value;
if (!baAsn1.decodeIsContextTag(buffer, offset + len, 1)) return;
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
decodedValue = baAsn1.decodeObjectId(buffer, offset + len);
len += decodedValue.len;
var initiatingDeviceIdentifier = {type: decodedValue.objectType, instance: decodedValue.instance};
if (!baAsn1.decodeIsContextTag(buffer, offset + len, 2)) return;
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
decodedValue = baAsn1.decodeObjectId(buffer, offset + len);
len += decodedValue.len;
var monitoredObjectIdentifier = {type: decodedValue.objectType, instance: decodedValue.instance};
if (!baAsn1.decodeIsContextTag(buffer, offset + len, 3)) return;
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
decodedValue = baAsn1.decodeUnsigned(buffer, offset + len, result.value);
len += decodedValue.len;
var timeRemaining = decodedValue.value;
if (!baAsn1.decodeIsOpeningTagNumber(buffer, offset + len, 4)) return;
len++;
var values = [];
while ((apduLen - len) > 1 && !baAsn1.decodeIsClosingTagNumber(buffer, offset + len, 4)) {
var newEntry = {};
newEntry.property = {};
if (!baAsn1.decodeIsContextTag(buffer, offset + len, 0)) return;
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
decodedValue = baAsn1.decodeEnumerated(buffer, offset + len, result.value);
len += decodedValue.len;
newEntry.property.propertyId = decodedValue.value;
if (baAsn1.decodeIsContextTag(buffer, offset + len, 1)) {
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
decodedValue = baAsn1.decodeUnsigned(buffer, offset + len, result.value);
len += decodedValue.len;
newEntry.property.arrayIndex = decodedValue.value;
} else {
newEntry.property.arrayIndex = baAsn1.BACNET_ARRAY_ALL;
}
if (!baAsn1.decodeIsOpeningTagNumber(buffer, offset + len, 2)) return;
len++;
var properties = [];
while ((apduLen - len) > 1 && !baAsn1.decodeIsClosingTagNumber(buffer, offset + len, 2)) {
decodedValue = baAsn1.bacappDecodeApplicationData(buffer, offset + len, apduLen + offset, monitoredObjectIdentifier.type, newEntry.property.propertyIdentifier);
if (!decodedValue) return;
len += decodedValue.len;
properties.push(decodedValue);
}
newEntry.value = properties;
len++;
if (baAsn1.decodeIsContextTag(buffer, offset + len, 3)) {
result = baAsn1.decodeTagNumberAndValue(buffer, offset + len);
len += result.len;
decodedValue = baAsn1.decodeUnsigned(buffer, offset + len, result.value);
len += decodedValue.len;
newEntry.priority = decodedValue.value;
} else {
newEntry.priority = baAsn1.BACNET_NO_PRIORITY;
}
values.push(newEntry);
}
return {
len: len,
subscriberProcessIdentifier: subscriberProcessIdentifier,
initiatingDeviceIdentifier: initiatingDeviceIdentifier,
monitoredObjectIdentifier: monitoredObjectIdentifier,
timeRemaining: timeRemaining,
values: values
};
};

// TODO: Implement decoding for following functions
module.exports.encodeIhaveBroadcast = function(buffer, deviceId, objectId, objectName) {
baAsn1.encodeApplicationObjectId(buffer, deviceId.type, deviceId.instance);
baAsn1.encodeApplicationObjectId(buffer, objectId.type, objectId.instance);
baAsn1.encodeApplicationCharacterString(buffer, objectName);
};

module.exports.encodeAlarmAcknowledge = function(buffer, ackProcessIdentifier, eventObjectIdentifier, eventStateAcked, ackSource, eventTimeStamp, ackTimeStamp) {
baAsn1.encodeContextUnsigned(buffer, 0, ackProcessIdentifier);
baAsn1.encodeContextObjectId(buffer, 1, eventObjectIdentifier.type, eventObjectIdentifier.instance);
baAsn1.encodeContextEnumerated(buffer, 2, eventStateAcked);
baAsn1.bacappEncodeContextTimestamp(buffer, 3, eventTimeStamp);
baAsn1.encodeContextCharacterString(buffer, 4, ackSource);
baAsn1.bacappEncodeContextTimestamp(buffer, 5, ackTimeStamp);
};

module.exports.encodeAddListElement = function(buffer, objectId, propertyId, arrayIndex, valueList) {
baAsn1.encodeContextObjectId(buffer, 0, objectId.type, objectId.instance);
baAsn1.encodeContextEnumerated(buffer, 1, propertyId);
Expand Down Expand Up @@ -1477,50 +1647,10 @@ module.exports.encodeLifeSafetyOperation = function(buffer, processId, requestin
baAsn1.encodeContextObjectId(buffer, 3, targetObject.type, targetObject.instance);
};

module.exports.encodePrivateTransferConfirmed = function(buffer, vendorID, serviceNumber, data) {
baAsn1.encodeContextUnsigned(buffer, 0, vendorID);
baAsn1.encodeContextUnsigned(buffer, 1, serviceNumber);
baAsn1.encodeOpeningTag(buffer, 2);
buffer.Add(data, data.length);
baAsn1.encodeClosingTag(buffer, 2);
};

module.exports.encodePrivateTransferUnconfirmed = function(buffer, vendorID, serviceNumber, data) {
baAsn1.encodeContextUnsigned(buffer, 0, vendorID);
baAsn1.encodeContextUnsigned(buffer, 1, serviceNumber);
baAsn1.encodeOpeningTag(buffer, 2);
buffer.Add(data, data.length);
baAsn1.encodeClosingTag(buffer, 2);
};

module.exports.encodePrivateTransferAcknowledge = function(buffer, vendorID, serviceNumber, data) {
module.exports.encodePrivateTransfer = function(buffer, vendorID, serviceNumber, data) {
baAsn1.encodeContextUnsigned(buffer, 0, vendorID);
baAsn1.encodeContextUnsigned(buffer, 1, serviceNumber);
baAsn1.encodeOpeningTag(buffer, 2);
buffer.Add(data, data.length);
baAsn1.encodeClosingTag(buffer, 2);
};

module.exports.encodeCOVNotify = function(buffer, subscriberProcessIdentifier, initiatingDeviceIdentifier, monitoredObjectIdentifier, timeRemaining, values) {
baAsn1.encodeContextUnsigned(buffer, 0, subscriberProcessIdentifier);
baAsn1.encodeContextObjectId(buffer, 1, baEnum.BacnetObjectTypes.OBJECT_DEVICE, initiatingDeviceIdentifier);
baAsn1.encodeContextObjectId(buffer, 2, monitoredObjectIdentifier.type, monitoredObjectIdentifier.instance);
baAsn1.encodeContextUnsigned(buffer, 3, timeRemaining);
baAsn1.encodeOpeningTag(buffer, 4);
values.forEach(function(value) {
baAsn1.encodeContextEnumerated(buffer, 0, value.property.propertyIdentifier);
if (value.property.propertyArrayIndex === baAsn1.BACNET_ARRAY_ALL) {
baAsn1.encodeContextUnsigned(buffer, 1, value.property.propertyArrayIndex);
}
baAsn1.encodeOpeningTag(buffer, 2);
value.value.forEach(function(v) {
baAsn1.bacappEncodeApplicationData(buffer, v);
});
baAsn1.encodeClosingTag(buffer, 2);
if (value.priority === baAsn1.BACNET_NO_PRIORITY) {
baAsn1.encodeContextUnsigned(buffer, 3, value.priority);
}
// TODO: Handle to too large telegrams -> ADPU limit
});
baAsn1.encodeClosingTag(buffer, 4);
};
Loading

0 comments on commit 00772d0

Please sign in to comment.