Skip to content

Commit

Permalink
feat(bacnet-services): implement writePropertyMultiple service
Browse files Browse the repository at this point in the history
  • Loading branch information
fh1ch committed Jul 9, 2017
1 parent f06e3c5 commit ea9332c
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 11 deletions.
4 changes: 2 additions & 2 deletions lib/bacnet-asn1.js
Original file line number Diff line number Diff line change
Expand Up @@ -628,11 +628,11 @@ var decode_is_closing_tag_number = module.exports.decode_is_closing_tag_number =
return IS_CLOSING_TAG(buffer[offset]) && result.tagNumber === tagNumber;
};

var decode_is_closing_tag = function(buffer, offset) {
var decode_is_closing_tag = module.exports.decode_is_closing_tag = function(buffer, offset) {
return (buffer[offset] & 0x07) === 7;
};

var decode_is_opening_tag = function(buffer, offset) {
var decode_is_opening_tag = module.exports.decode_is_opening_tag = function(buffer, offset) {
return (buffer[offset] & 0x07) === 6;
};

Expand Down
82 changes: 76 additions & 6 deletions lib/bacnet-services.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ module.exports.EncodeIhaveBroadcast = function(buffer, deviceId, objectId, objec

module.exports.EncodeAlarmAcknowledge = function(buffer, ackProcessIdentifier, eventObjectIdentifier, eventStateAcked, ackSource, eventTimeStamp, ackTimeStamp) {
baAsn1.encodeContextUnsigned(buffer, 0, ackProcessIdentifier);
baAsn1.encode_context_objectId(buffer, 1, eventObjectIdentifier.type, eventObjectIdentifier.instance);
baAsn1.encode_context_object_id(buffer, 1, eventObjectIdentifier.type, eventObjectIdentifier.instance);
baAsn1.encodeContextEnumerated(buffer, 2, eventStateAcked);
baAsn1.bacapp_encode_context_timestamp(buffer, 3, eventTimeStamp);
baAsn1.encode_context_character_string(buffer, 4, ackSource);
Expand Down Expand Up @@ -267,7 +267,7 @@ module.exports.EncodeAlarmSummary = function(buffer, objectIdentifier, alarmStat

module.exports.EncodeGetEventInformation = function(buffer, sendLast, lastReceivedObjectIdentifier) {
if (sendLast) {
baAsn1.encode_context_objectId(buffer, 0, lastReceivedObjectIdentifier.type, lastReceivedObjectIdentifier.instance);
baAsn1.encode_context_object_id(buffer, 0, lastReceivedObjectIdentifier.type, lastReceivedObjectIdentifier.instance);
}
};

Expand Down Expand Up @@ -299,7 +299,7 @@ module.exports.EncodeLifeSafetyOperation = function(buffer, processId, requestin
baAsn1.encodeContextUnsigned(buffer, 0, processId);
baAsn1.encode_context_character_string(buffer, 1, requestingSrc);
baAsn1.encodeContextEnumerated(buffer, 2, operation);
baAsn1.encode_context_objectId(buffer, 3, targetObject.type, targetObject.instance);
baAsn1.encode_context_object_id(buffer, 3, targetObject.type, targetObject.instance);
};

module.exports.EncodePrivateTransferConfirmed = function(buffer, vendorID, serviceNumber, data) {
Expand Down Expand Up @@ -396,7 +396,7 @@ module.exports.DecodeReinitializeDevice = function(buffer, offset, apduLen) {
};

module.exports.EncodeReadRange = function(buffer, objectId, propertyId, arrayIndex, requestType, position, time, count) {
baAsn1.encode_context_objectId(buffer, 0, objectId.type, objectId.instance);
baAsn1.encode_context_object_id(buffer, 0, objectId.type, objectId.instance);
baAsn1.encodeContextEnumerated(buffer, 1, propertyId);
if (arrayIndex !== baAsn1.BACNET_ARRAY_ALL) {
baAsn1.encodeContextUnsigned(buffer, 2, arrayIndex);
Expand Down Expand Up @@ -549,7 +549,7 @@ module.exports.DecodeWriteProperty = function(buffer, offset, apduLen) {
len++;
decodedValue = baAsn1.decode_object_id(buffer, offset + len);
var objectId = {
type: decodedValue.instance,
type: decodedValue.objectType,
instance: decodedValue.instance
};
len += decodedValue.len;
Expand Down Expand Up @@ -602,7 +602,7 @@ module.exports.DecodeWriteProperty = function(buffer, offset, apduLen) {
};

var EncodeWritePropertyMultiple = module.exports.EncodeWritePropertyMultiple = function(buffer, objectId, valueList) {
baAsn1.encode_context_objectId(buffer, 0, objectId.type, objectId.instance);
baAsn1.encode_context_object_id(buffer, 0, objectId.type, objectId.instance);
baAsn1.encode_opening_tag(buffer, 1);
valueList.forEach(function(pValue) {
baAsn1.encodeContextEnumerated(buffer, 0, pValue.property.propertyIdentifier);
Expand All @@ -621,6 +621,76 @@ var EncodeWritePropertyMultiple = module.exports.EncodeWritePropertyMultiple = f
baAsn1.encode_closing_tag(buffer, 1);
};

module.exports.DecodeWritePropertyMultiple = function(buffer, offset, apduLen) {
var len = 0;
var result;
var decodedValue;
var objectId;
result = baAsn1.decode_tag_number_and_value(buffer, offset + len);
len += result.len;
if ((result.tagNumber !== 0) || (apduLen <= len)) return;
apduLen -= len;
if (apduLen < 4) return;
decodedValue = baAsn1.decode_object_id(buffer, offset + len);
len += decodedValue.len;
objectId = {
type: decodedValue.objectType,
instance: decodedValue.instance
};
if (!baAsn1.decode_is_opening_tag_number(buffer, offset + len, 1)) return;
len++;
var _values = [];
while ((apduLen - len) > 1) {
var newEntry = {};
result = baAsn1.decode_tag_number_and_value(buffer, offset + len);
len += result.len;
if (result.tagNumber !== 0) return;
decodedValue = baAsn1.decode_enumerated(buffer, offset + len, result.value);
len += decodedValue.len;
var propertyId = decodedValue.value;
var arrayIndex = baAsn1.BACNET_ARRAY_ALL;
result = baAsn1.decode_tag_number_and_value(buffer, offset + len);
len += result.len;
if (result.tagNumber === 1) {
decodedValue = baAsn1.decode_unsigned(buffer, offset + len, result.value);
len += decodedValue.len;
arrayIndex = decodedValue.value;
result = baAsn1.decode_tag_number_and_value(buffer, offset + len);
len += result.len;
}
newEntry.property = {propertyId: propertyId, arrayIndex: arrayIndex};
if ((result.tagNumber !== 2) || (!baAsn1.decode_is_opening_tag(buffer, offset + len - 1))) return;
var values = [];
while ((len + offset) <= buffer.length && !baAsn1.decode_is_closing_tag(buffer, offset + len)) {
var value = baAsn1.bacapp_decode_application_data(buffer, offset + len, apduLen + offset, objectId.type, propertyId);
if (!value) return;
len += value.len;
values.push(value);
}
len++;
newEntry.value = values;
var priority = baAsn1.BACNET_NO_PRIORITY;
result = baAsn1.decode_tag_number_and_value(buffer, offset + len);
len += result.len;
if (result.tagNumber === 3) {
decodedValue = baAsn1.decode_unsigned(buffer, offset + len, result.value);
len += decodedValue.len;
priority = decodedValue.value;
} else {
len--;
}
newEntry.priority = priority;
_values.push(newEntry);
}
if (!baAsn1.decode_is_closing_tag_number(buffer, offset + len, 1)) return;
len++;
return {
len: len,
objectId: objectId,
valuesRefs: _values
};
};

module.exports.EncodeWriteObjectMultiple = function(buffer, valueList) {
valueList.forEach(function(rValue) {
EncodeWritePropertyMultiple(buffer, rValue.objectIdentifier, rValue.values);
Expand Down
147 changes: 144 additions & 3 deletions test/unit/bacnet-services.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ describe('bacstack - Services layer', function() {
expect(result).to.deep.equal({
objectId: {
instance: 12,
type: 12
type: 31
},
value: {
priority: 16,
Expand Down Expand Up @@ -605,7 +605,7 @@ describe('bacstack - Services layer', function() {
expect(result).to.deep.equal({
objectId: {
instance: 12,
type: 12
type: 31
},
value: {
priority: 8,
Expand Down Expand Up @@ -665,7 +665,7 @@ describe('bacstack - Services layer', function() {
expect(result).to.deep.equal({
objectId: {
instance: 12,
type: 12
type: 31
},
value: {
priority: 16,
Expand Down Expand Up @@ -697,6 +697,147 @@ describe('bacstack - Services layer', function() {
});
});

describe('WritePropertyMultiple', function() {
it('should successfully encode and decode', function() {
var buffer = utils.getBuffer();
var date = new Date(1, 1, 1);
var time = new Date(1, 1, 1);
time.setMilliseconds(990);
baServices.EncodeWritePropertyMultiple(buffer, {type: 39, instance: 2400}, [
{property: {propertyIdentifier: 81, propertyArrayIndex: 0xFFFFFFFF}, value: [
{Tag: 1, Value: true},
{Tag: 1, Value: false},
{Tag: 2, Value: 1},
{Tag: 2, Value: 1000},
{Tag: 2, Value: 1000000},
{Tag: 2, Value: 1000000000},
{Tag: 3, Value: -1},
{Tag: 3, Value: -1000},
{Tag: 3, Value: -1000000},
{Tag: 3, Value: -1000000000},
{Tag: 4, Value: 0.1},
{Tag: 5, Value: 100.121212},
// FIXME: correct octet-string implementation
// {Tag: 6, Value: [1, 2, 100, 200]},
{Tag: 7, Value: 'Test1234$'},
// FIXME: correct bit-string implementation
// {Tag: 8, Value: {bits_used: 0, value: []}},
// {Tag: 8, Value: {bits_used: 24, value: [0xAA, 0xAA, 0xAA]}},
{Tag: 9, Value: 4},
{Tag: 10, Value: date},
{Tag: 11, Value: time},
{Tag: 12, Value: {type: 3, instance: 0}}
], priority: 0}
]);
var result = baServices.DecodeWritePropertyMultiple(buffer.buffer, 0, buffer.offset);
delete result.len;
result.valuesRefs[0].value[10].value = Math.floor(result.valuesRefs[0].value[10].value * 1000) / 1000;
expect(result).to.deep.equal({
objectId: {
type: 39,
instance: 2400
},
valuesRefs: [
{
priority: 0,
property: {
arrayIndex: 0xFFFFFFFF,
propertyId: 81
},
value: [
{type: 1, value: true, len: 1},
{type: 1, value: false, len: 1},
{type: 2, value: 1, len: 2},
{type: 2, value: 1000, len: 3},
{type: 2, value: 1000000, len: 4},
{type: 2, value: 1000000000, len: 5},
{type: 3, value: -1, len: 2},
{type: 3, value: -1000, len: 3},
{type: 3, value: -1000000, len: 4},
{type: 3, value: -1000000000, len: 5},
{type: 4, value: 0.1, len: 5},
{type: 5, value: 100.121212, len: 10},
// FIXME: correct octet-string implementation
// {type: 6, value: [1, 2, 100, 200]},
{type: 7, value: 'Test1234$', len: 12},
// FIXME: correct bit-string implementation
// {type: 8, value: {bits_used: 0, value: []}},
// {type: 8, value: {bits_used: 24, value: [0xAA, 0xAA, 0xAA]}},
{type: 9, value: 4, len: 2},
{type: 10, value: date, len: 5},
{type: 11, value: time, len: 5},
{type: 12, value: {type: 3, instance: 0}, len: 5}
]
}
]
});
});

it('should successfully encode and decode with defined priority', function() {
var buffer = utils.getBuffer();
var date = new Date(1, 1, 1);
var time = new Date(1, 1, 1);
time.setMilliseconds(990);
baServices.EncodeWritePropertyMultiple(buffer, {type: 39, instance: 2400}, [
{property: {propertyIdentifier: 81, propertyArrayIndex: 0xFFFFFFFF}, value: [
{Tag: 7, Value: 'Test1234$'}
], priority: 12}
]);
var result = baServices.DecodeWritePropertyMultiple(buffer.buffer, 0, buffer.offset);
delete result.len;
expect(result).to.deep.equal({
objectId: {
type: 39,
instance: 2400
},
valuesRefs: [
{
priority: 12,
property: {
arrayIndex: 0xFFFFFFFF,
propertyId: 81
},
value: [
{type: 7, value: 'Test1234$', len: 12}
]
}
]
});
});

it('should successfully encode and decode with defined array index', function() {
var buffer = utils.getBuffer();
var date = new Date(1, 1, 1);
var time = new Date(1, 1, 1);
time.setMilliseconds(990);
baServices.EncodeWritePropertyMultiple(buffer, {type: 39, instance: 2400}, [
{property: {propertyIdentifier: 81, propertyArrayIndex: 414141}, value: [
{Tag: 7, Value: 'Test1234$'}
], priority: 0}
]);
var result = baServices.DecodeWritePropertyMultiple(buffer.buffer, 0, buffer.offset);
delete result.len;
expect(result).to.deep.equal({
objectId: {
type: 39,
instance: 2400
},
valuesRefs: [
{
priority: 0,
property: {
arrayIndex: 414141,
propertyId: 81
},
value: [
{type: 7, value: 'Test1234$', len: 12}
]
}
]
});
});
});

describe('DeviceCommunicationControl', function() {
it('should successfully encode and decode', function() {
var buffer = utils.getBuffer();
Expand Down

0 comments on commit ea9332c

Please sign in to comment.