ts-bacnet
Version:
The BACnet protocol library written in pure JavaScript.
1,296 lines • 67.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeApplicationTime = exports.decodeBacnetTime = exports.decodeApplicationDate = exports.decodeDate = exports.decodeBitstring = exports.decodeCharacterString = exports.decodeOctetString = exports.decodeReal = exports.decodeSigned = exports.decodeReadAccessResult = exports.encodeReadAccessResult = exports.bacappDecodeApplicationData = exports.decodeTagNumberAndValue = exports.decodeObjectId = exports.decodeIsOpeningTag = exports.decodeIsClosingTag = exports.decodeIsClosingTagNumber = exports.decodeIsOpeningTagNumber = exports.decodeIsContextTag = exports.decodeTagNumber = exports.encodeContextSigned = exports.encodeContextBitstring = exports.bacappEncodePropertyState = exports.bacappEncodeContextDeviceObjPropertyRef = exports.bacappEncodeApplicationData = exports.encodeContextBoolean = exports.encodeReadAccessSpecification = exports.encodeClosingTag = exports.encodeOpeningTag = exports.encodeContextObjectId = exports.encodeApplicationTime = exports.encodeApplicationDate = exports.encodeBacnetDate = exports.encodeApplicationBitstring = exports.encodeApplicationSigned = exports.encodeApplicationEnumerated = exports.encodeApplicationUnsigned = exports.encodeApplicationObjectId = exports.encodeApplicationBoolean = exports.encodeApplicationOctetString = exports.encodeContextEnumerated = exports.encodeContextUnsigned = exports.encodeContextReal = exports.encodeTag = exports.encodeBacnetObjectId = exports.decodeEnumerated = exports.decodeUnsigned = exports.encodeUnsigned = exports.MAX_YEARS = exports.START_YEAR = void 0;
exports.encodeContextCharacterString = exports.encodeApplicationCharacterString = exports.decodeContextObjectId = exports.decodeContextCharacterString = exports.bacappEncodeContextTimestamp = exports.bacappEncodeTimestamp = exports.decodeReadAccessSpecification = void 0;
//@ts-nocheck
const iconv_lite_1 = __importDefault(require("iconv-lite"));
const enums_1 = __importDefault(require("./enums"));
exports.START_YEAR = 1900;
exports.MAX_YEARS = 256;
const getBuffer = () => {
return {
buffer: Buffer.alloc(1472),
offset: 0,
};
};
const getSignedLength = (value) => {
if (value >= -128 && value < 128)
return 1;
else if (value >= -32768 && value < 32768)
return 2;
else if (value > -8388608 && value < 8388608)
return 3;
else
return 4;
};
const getUnsignedLength = (value) => {
if (value < 0x100)
return 1;
else if (value < 0x10000)
return 2;
else if (value < 0x1000000)
return 3;
else
return 4;
};
const getEncodingType = (encoding, decodingBuffer, decodingOffset) => {
switch (encoding) {
case enums_1.default.CharacterStringEncoding.UCS_2:
if (decodingBuffer &&
decodingBuffer[decodingOffset] === 0xff &&
decodingBuffer[decodingOffset + 1] === 0xfe) {
return "ucs2";
}
return "UTF-16BE"; // Default to big-endian
case enums_1.default.CharacterStringEncoding.ISO_8859_1:
return "latin1";
case enums_1.default.CharacterStringEncoding.MICROSOFT_DBCS:
return "cp850";
case enums_1.default.CharacterStringEncoding.JIS_X_0208:
return "Shift_JIS";
default:
return "utf8";
}
};
const encodeUnsigned = (buffer, value, length) => {
buffer.buffer.writeUIntBE(value, buffer.offset, length, true);
buffer.offset += length;
};
exports.encodeUnsigned = encodeUnsigned;
const encodeBacnetUnsigned = (buffer, value) => {
(0, exports.encodeUnsigned)(buffer, value, getUnsignedLength(value));
};
const encodeSigned = (buffer, value, length) => {
buffer.buffer.writeIntBE(value, buffer.offset, length, true);
buffer.offset += length;
};
const encodeBacnetSigned = (buffer, value) => {
encodeSigned(buffer, value, getSignedLength(value));
};
const encodeBacnetReal = (buffer, value) => {
buffer.buffer.writeFloatBE(value, buffer.offset, true);
buffer.offset += 4;
};
const encodeBacnetDouble = (buffer, value) => {
buffer.buffer.writeDoubleBE(value, buffer.offset, true);
buffer.offset += 8;
};
const decodeUnsigned = (buffer, offset, length) => {
if (length === 0) {
return {
len: 0,
value: 0,
};
}
return {
len: length,
//@ts-ignore
value: buffer.readUIntBE(offset, length),
};
};
exports.decodeUnsigned = decodeUnsigned;
const decodeEnumerated = (buffer, offset, length) => {
return (0, exports.decodeUnsigned)(buffer, offset, length);
};
exports.decodeEnumerated = decodeEnumerated;
const encodeBacnetObjectId = (buffer, objectType, instance) => {
const value = (((objectType & enums_1.default.ASN1_MAX_OBJECT) << enums_1.default.ASN1_INSTANCE_BITS) |
(instance & enums_1.default.ASN1_MAX_INSTANCE)) >>>
0;
(0, exports.encodeUnsigned)(buffer, value, 4);
};
exports.encodeBacnetObjectId = encodeBacnetObjectId;
const encodeTag = (buffer, tagNumber, contextSpecific, lenValueType) => {
let len = 1;
const tmp = new Array(3);
tmp[0] = 0;
if (contextSpecific) {
tmp[0] |= 0x8;
}
if (tagNumber <= 14) {
tmp[0] |= tagNumber << 4;
}
else {
tmp[0] |= 0xf0;
tmp[1] = tagNumber;
len++;
}
if (lenValueType <= 4) {
tmp[0] |= lenValueType;
Buffer.from(tmp).copy(buffer.buffer, buffer.offset, 0, len);
buffer.offset += len;
}
else {
tmp[0] |= 5;
if (lenValueType <= 253) {
tmp[len++] = lenValueType;
Buffer.from(tmp).copy(buffer.buffer, buffer.offset, 0, len);
buffer.offset += len;
}
else if (lenValueType <= 65535) {
tmp[len++] = 254;
Buffer.from(tmp).copy(buffer.buffer, buffer.offset, 0, len);
buffer.offset += len;
(0, exports.encodeUnsigned)(buffer, lenValueType, 2);
}
else {
tmp[len++] = 255;
Buffer.from(tmp).copy(buffer.buffer, buffer.offset, 0, len);
buffer.offset += len;
(0, exports.encodeUnsigned)(buffer, lenValueType, 4);
}
}
};
exports.encodeTag = encodeTag;
const encodeBacnetEnumerated = (buffer, value) => {
encodeBacnetUnsigned(buffer, value);
};
const isExtendedTagNumber = (x) => {
return (x & 0xf0) === 0xf0;
};
const isExtendedValue = (x) => {
return (x & 0x07) === 5;
};
const isContextSpecific = (x) => {
return (x & 0x8) === 0x8;
};
const isOpeningTag = (x) => {
return (x & 0x07) === 6;
};
const isClosingTag = (x) => {
return (x & 0x07) === 7;
};
const encodeContextReal = (buffer, tagNumber, value) => {
(0, exports.encodeTag)(buffer, tagNumber, true, 4);
encodeBacnetReal(buffer, value);
};
exports.encodeContextReal = encodeContextReal;
const encodeContextUnsigned = (buffer, tagNumber, value) => {
(0, exports.encodeTag)(buffer, tagNumber, true, getUnsignedLength(value));
encodeBacnetUnsigned(buffer, value);
};
exports.encodeContextUnsigned = encodeContextUnsigned;
const encodeContextEnumerated = (buffer, tagNumber, value) => {
(0, exports.encodeContextUnsigned)(buffer, tagNumber, value);
};
exports.encodeContextEnumerated = encodeContextEnumerated;
const encodeOctetString = (buffer, octetString, octetOffset, octetCount) => {
if (octetString) {
for (let i = octetOffset; i < octetOffset + octetCount; i++) {
buffer.buffer[buffer.offset++] = octetString[i];
}
}
};
const encodeApplicationOctetString = (buffer, octetString, octetOffset, octetCount) => {
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.OCTET_STRING, false, octetCount);
encodeOctetString(buffer, octetString, octetOffset, octetCount);
};
exports.encodeApplicationOctetString = encodeApplicationOctetString;
const encodeApplicationNull = (buffer) => {
buffer.buffer[buffer.offset++] = enums_1.default.ApplicationTag.NULL;
};
const encodeApplicationBoolean = (buffer, booleanValue) => {
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.BOOLEAN, false, booleanValue ? 1 : 0);
};
exports.encodeApplicationBoolean = encodeApplicationBoolean;
const encodeApplicationReal = (buffer, value) => {
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.REAL, false, 4);
encodeBacnetReal(buffer, value);
};
const encodeApplicationDouble = (buffer, value) => {
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.DOUBLE, false, 8);
encodeBacnetDouble(buffer, value);
};
const bitstringBytesUsed = (bitString) => {
let len = 0;
if (bitString.bitsUsed > 0) {
const lastBit = bitString.bitsUsed - 1;
const usedBytes = lastBit / 8 + 1;
len = Math.floor(usedBytes);
}
return len;
};
const encodeApplicationObjectId = (buffer, objectType, instance) => {
const tmp = getBuffer();
(0, exports.encodeBacnetObjectId)(tmp, objectType, instance);
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.OBJECTIDENTIFIER, false, tmp.offset);
tmp.buffer.copy(buffer.buffer, buffer.offset, 0, tmp.offset);
buffer.offset += tmp.offset;
};
exports.encodeApplicationObjectId = encodeApplicationObjectId;
const encodeApplicationUnsigned = (buffer, value) => {
const tmp = getBuffer();
encodeBacnetUnsigned(tmp, value);
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.UNSIGNED_INTEGER, false, tmp.offset);
tmp.buffer.copy(buffer.buffer, buffer.offset, 0, tmp.offset);
buffer.offset += tmp.offset;
};
exports.encodeApplicationUnsigned = encodeApplicationUnsigned;
const encodeApplicationEnumerated = (buffer, value) => {
const tmp = getBuffer();
encodeBacnetEnumerated(tmp, value);
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.ENUMERATED, false, tmp.offset);
tmp.buffer.copy(buffer.buffer, buffer.offset, 0, tmp.offset);
buffer.offset += tmp.offset;
};
exports.encodeApplicationEnumerated = encodeApplicationEnumerated;
const encodeApplicationSigned = (buffer, value) => {
const tmp = getBuffer();
encodeBacnetSigned(tmp, value);
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.SIGNED_INTEGER, false, tmp.offset);
tmp.buffer.copy(buffer.buffer, buffer.offset, 0, tmp.offset);
buffer.offset += tmp.offset;
};
exports.encodeApplicationSigned = encodeApplicationSigned;
const byteReverseBits = (inByte) => {
let outByte = 0;
if ((inByte & 1) > 0) {
outByte |= 0x80;
}
if ((inByte & 2) > 0) {
outByte |= 0x40;
}
if ((inByte & 4) > 0) {
outByte |= 0x20;
}
if ((inByte & 8) > 0) {
outByte |= 0x10;
}
if ((inByte & 16) > 0) {
outByte |= 0x8;
}
if ((inByte & 32) > 0) {
outByte |= 0x4;
}
if ((inByte & 64) > 0) {
outByte |= 0x2;
}
if ((inByte & 128) > 0) {
outByte |= 1;
}
return outByte;
};
const bitstringOctet = (bitString, octetIndex) => {
let octet = 0;
if (bitString.value && octetIndex < enums_1.default.ASN1_MAX_BITSTRING_BYTES) {
octet = bitString.value[octetIndex];
}
return octet;
};
const encodeBitstring = (buffer, bitString) => {
if (bitString.bitsUsed === 0) {
buffer.buffer[buffer.offset++] = 0;
}
else {
const usedBytes = bitstringBytesUsed(bitString);
const remainingUsedBits = bitString.bitsUsed - (usedBytes - 1) * 8;
buffer.buffer[buffer.offset++] = 8 - remainingUsedBits;
for (let i = 0; i < usedBytes; i++) {
buffer.buffer[buffer.offset++] = byteReverseBits(bitstringOctet(bitString, i));
}
}
};
const encodeApplicationBitstring = (buffer, bitString) => {
let bitStringEncodedLength = 1;
bitStringEncodedLength += bitstringBytesUsed(bitString);
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.BIT_STRING, false, bitStringEncodedLength);
encodeBitstring(buffer, bitString);
};
exports.encodeApplicationBitstring = encodeApplicationBitstring;
const encodeBacnetDate = (buffer, value) => {
if (value === new Date(1, 1, 1)) {
buffer.buffer[buffer.offset++] = 0xff;
buffer.buffer[buffer.offset++] = 0xff;
buffer.buffer[buffer.offset++] = 0xff;
buffer.buffer[buffer.offset++] = 0xff;
return;
}
if (value.getFullYear() >= exports.START_YEAR) {
buffer.buffer[buffer.offset++] = value.getFullYear() - exports.START_YEAR;
}
else if (value.getFullYear() < exports.MAX_YEARS /* 1900 + 255 max */) {
buffer.buffer[buffer.offset++] = value.getFullYear();
}
else {
throw new Error("invaide year: " + value.getFullYear());
}
buffer.buffer[buffer.offset++] = value.getMonth();
buffer.buffer[buffer.offset++] = value.getDate();
buffer.buffer[buffer.offset++] = value.getDay() === 0 ? 7 : value.getDay();
};
exports.encodeBacnetDate = encodeBacnetDate;
const encodeApplicationDate = (buffer, value) => {
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.DATE, false, 4);
(0, exports.encodeBacnetDate)(buffer, value);
};
exports.encodeApplicationDate = encodeApplicationDate;
const encodeBacnetTime = (buffer, value) => {
buffer.buffer[buffer.offset++] = value.getHours();
buffer.buffer[buffer.offset++] = value.getMinutes();
buffer.buffer[buffer.offset++] = value.getSeconds();
buffer.buffer[buffer.offset++] = value.getMilliseconds() / 10;
};
const encodeApplicationTime = (buffer, value) => {
(0, exports.encodeTag)(buffer, enums_1.default.ApplicationTag.TIME, false, 4);
encodeBacnetTime(buffer, value);
};
exports.encodeApplicationTime = encodeApplicationTime;
const bacappEncodeDatetime = (buffer, value) => {
if (value !== new Date(1, 1, 1)) {
(0, exports.encodeApplicationDate)(buffer, value);
(0, exports.encodeApplicationTime)(buffer, value);
}
};
const encodeContextObjectId = (buffer, tagNumber, objectType, instance) => {
(0, exports.encodeTag)(buffer, tagNumber, true, 4);
(0, exports.encodeBacnetObjectId)(buffer, objectType, instance);
};
exports.encodeContextObjectId = encodeContextObjectId;
const encodeOpeningTag = (buffer, tagNumber) => {
let len = 1;
const tmp = new Array(2);
tmp[0] = 0x8;
if (tagNumber <= 14) {
tmp[0] |= tagNumber << 4;
}
else {
tmp[0] |= 0xf0;
tmp[1] = tagNumber;
len++;
}
tmp[0] |= 6;
Buffer.from(tmp).copy(buffer.buffer, buffer.offset, 0, len);
buffer.offset += len;
};
exports.encodeOpeningTag = encodeOpeningTag;
const encodeClosingTag = (buffer, tagNumber) => {
let len = 1;
const tmp = new Array(2);
tmp[0] = 0x8;
if (tagNumber <= 14) {
tmp[0] |= tagNumber << 4;
}
else {
tmp[0] |= 0xf0;
tmp[1] = tagNumber;
len++;
}
tmp[0] |= 7;
Buffer.from(tmp).copy(buffer.buffer, buffer.offset, 0, len);
buffer.offset += len;
};
exports.encodeClosingTag = encodeClosingTag;
const encodeReadAccessSpecification = (buffer, value) => {
(0, exports.encodeContextObjectId)(buffer, 0, value.objectId.type, value.objectId.instance);
(0, exports.encodeOpeningTag)(buffer, 1);
value.properties.forEach((p) => {
(0, exports.encodeContextEnumerated)(buffer, 0, p.id);
if (p.index && p.index !== enums_1.default.ASN1_ARRAY_ALL) {
(0, exports.encodeContextUnsigned)(buffer, 1, p.index);
}
});
(0, exports.encodeClosingTag)(buffer, 1);
};
exports.encodeReadAccessSpecification = encodeReadAccessSpecification;
const encodeContextBoolean = (buffer, tagNumber, booleanValue) => {
(0, exports.encodeTag)(buffer, tagNumber, true, 1);
buffer.buffer.writeUInt8(booleanValue ? 1 : 0, buffer.offset, true);
buffer.offset += 1;
};
exports.encodeContextBoolean = encodeContextBoolean;
const encodeCovSubscription = (buffer, value) => {
(0, exports.encodeOpeningTag)(buffer, 0);
(0, exports.encodeOpeningTag)(buffer, 0);
(0, exports.encodeOpeningTag)(buffer, 1);
(0, exports.encodeApplicationUnsigned)(buffer, value.recipient.network);
if (value.recipient.network === 0xffff) {
(0, exports.encodeApplicationOctetString)(buffer, 0, 0, 0);
}
else {
(0, exports.encodeApplicationOctetString)(buffer, value.recipient.address, 0, value.recipient.address.length);
}
(0, exports.encodeClosingTag)(buffer, 1);
(0, exports.encodeClosingTag)(buffer, 0);
(0, exports.encodeContextUnsigned)(buffer, 1, value.subscriptionProcessId);
(0, exports.encodeClosingTag)(buffer, 0);
(0, exports.encodeOpeningTag)(buffer, 1);
(0, exports.encodeContextObjectId)(buffer, 0, value.monitoredObjectId.type, value.monitoredObjectId.instance);
(0, exports.encodeContextEnumerated)(buffer, 1, value.monitoredProperty.id);
if (value.monitoredProperty.index !== enums_1.default.ASN1_ARRAY_ALL) {
(0, exports.encodeContextUnsigned)(buffer, 2, value.monitoredProperty.index);
}
(0, exports.encodeClosingTag)(buffer, 1);
(0, exports.encodeContextBoolean)(buffer, 2, value.issueConfirmedNotifications);
(0, exports.encodeContextUnsigned)(buffer, 3, value.timeRemaining);
if (value.covIncrement > 0) {
(0, exports.encodeContextReal)(buffer, 4, value.covIncrement);
}
};
const bacappEncodeApplicationData = (buffer, value) => {
if (value.value === null) {
value.type = enums_1.default.ApplicationTag.NULL;
}
switch (value.type) {
case enums_1.default.ApplicationTag.NULL:
encodeApplicationNull(buffer);
break;
case enums_1.default.ApplicationTag.BOOLEAN:
(0, exports.encodeApplicationBoolean)(buffer, value.value);
break;
case enums_1.default.ApplicationTag.UNSIGNED_INTEGER:
(0, exports.encodeApplicationUnsigned)(buffer, value.value);
break;
case enums_1.default.ApplicationTag.SIGNED_INTEGER:
(0, exports.encodeApplicationSigned)(buffer, value.value);
break;
case enums_1.default.ApplicationTag.REAL:
encodeApplicationReal(buffer, value.value);
break;
case enums_1.default.ApplicationTag.DOUBLE:
encodeApplicationDouble(buffer, value.value);
break;
case enums_1.default.ApplicationTag.OCTET_STRING:
(0, exports.encodeApplicationOctetString)(buffer, value.value, 0, value.value.length);
break;
case enums_1.default.ApplicationTag.CHARACTER_STRING:
(0, exports.encodeApplicationCharacterString)(buffer, value.value, value.encoding);
break;
case enums_1.default.ApplicationTag.BIT_STRING:
(0, exports.encodeApplicationBitstring)(buffer, value.value);
break;
case enums_1.default.ApplicationTag.ENUMERATED:
(0, exports.encodeApplicationEnumerated)(buffer, value.value);
break;
case enums_1.default.ApplicationTag.DATE:
(0, exports.encodeApplicationDate)(buffer, value.value);
break;
case enums_1.default.ApplicationTag.TIME:
(0, exports.encodeApplicationTime)(buffer, value.value);
break;
case enums_1.default.ApplicationTag.TIMESTAMP:
(0, exports.bacappEncodeTimestamp)(buffer, value.value);
break;
case enums_1.default.ApplicationTag.DATETIME:
bacappEncodeDatetime(buffer, value.value);
break;
case enums_1.default.ApplicationTag.OBJECTIDENTIFIER:
(0, exports.encodeApplicationObjectId)(buffer, value.value.type, value.value.instance);
break;
case enums_1.default.ApplicationTag.COV_SUBSCRIPTION:
encodeCovSubscription(buffer, value.value);
break;
case enums_1.default.ApplicationTag.READ_ACCESS_RESULT:
(0, exports.encodeReadAccessResult)(buffer, value.value);
break;
case enums_1.default.ApplicationTag.READ_ACCESS_SPECIFICATION:
(0, exports.encodeReadAccessSpecification)(buffer, value.value);
break;
case undefined:
throw new Error("Cannot encode a value if the type has not been specified");
default:
throw ("No encode for ApplicationTag type: " +
enums_1.default.getEnumName(enums_1.default.ApplicationTag, value.type));
}
};
exports.bacappEncodeApplicationData = bacappEncodeApplicationData;
const bacappEncodeDeviceObjPropertyRef = (buffer, value) => {
(0, exports.encodeContextObjectId)(buffer, 0, value.objectId.type, value.objectId.instance);
(0, exports.encodeContextEnumerated)(buffer, 1, value.id);
if (value.arrayIndex !== enums_1.default.ASN1_ARRAY_ALL) {
(0, exports.encodeContextUnsigned)(buffer, 2, value.arrayIndex);
}
if (value.deviceIndentifier.type === enums_1.default.ObjectType.DEVICE) {
(0, exports.encodeContextObjectId)(buffer, 3, value.deviceIndentifier.type, value.deviceIndentifier.instance);
}
};
const bacappEncodeContextDeviceObjPropertyRef = (buffer, tagNumber, value) => {
(0, exports.encodeOpeningTag)(buffer, tagNumber);
bacappEncodeDeviceObjPropertyRef(buffer, value);
(0, exports.encodeClosingTag)(buffer, tagNumber);
};
exports.bacappEncodeContextDeviceObjPropertyRef = bacappEncodeContextDeviceObjPropertyRef;
const bacappEncodePropertyState = (buffer, value) => {
switch (value.type) {
case enums_1.default.PropertyStates.BOOLEAN_VALUE:
(0, exports.encodeContextBoolean)(buffer, 0, value.state === 1);
break;
case enums_1.default.PropertyStates.BINARY_VALUE:
(0, exports.encodeContextEnumerated)(buffer, 1, value.state);
break;
case enums_1.default.PropertyStates.EVENT_TYPE:
(0, exports.encodeContextEnumerated)(buffer, 2, value.state);
break;
case enums_1.default.PropertyStates.POLARITY:
(0, exports.encodeContextEnumerated)(buffer, 3, value.state);
break;
case enums_1.default.PropertyStates.PROGRAM_CHANGE:
(0, exports.encodeContextEnumerated)(buffer, 4, value.state);
break;
case enums_1.default.PropertyStates.PROGRAM_STATE:
(0, exports.encodeContextEnumerated)(buffer, 5, value.state);
break;
case enums_1.default.PropertyStates.REASON_FOR_HALT:
(0, exports.encodeContextEnumerated)(buffer, 6, value.state);
break;
case enums_1.default.PropertyStates.RELIABILITY:
(0, exports.encodeContextEnumerated)(buffer, 7, value.state);
break;
case enums_1.default.PropertyStates.STATE:
(0, exports.encodeContextEnumerated)(buffer, 8, value.state);
break;
case enums_1.default.PropertyStates.SYSTEM_STATUS:
(0, exports.encodeContextEnumerated)(buffer, 9, value.state);
break;
case enums_1.default.PropertyStates.UNITS:
(0, exports.encodeContextEnumerated)(buffer, 10, value.state);
break;
case enums_1.default.PropertyStates.UNSIGNED_VALUE:
(0, exports.encodeContextUnsigned)(buffer, 11, value.state);
break;
case enums_1.default.PropertyStates.LIFE_SAFETY_MODE:
(0, exports.encodeContextEnumerated)(buffer, 12, value.state);
break;
case enums_1.default.PropertyStates.LIFE_SAFETY_STATE:
(0, exports.encodeContextEnumerated)(buffer, 13, value.state);
break;
default:
break;
}
};
exports.bacappEncodePropertyState = bacappEncodePropertyState;
const encodeContextBitstring = (buffer, tagNumber, bitString) => {
let bitStringEncodedLength = 1;
bitStringEncodedLength += bitstringBytesUsed(bitString);
(0, exports.encodeTag)(buffer, tagNumber, true, bitStringEncodedLength);
encodeBitstring(buffer, bitString);
};
exports.encodeContextBitstring = encodeContextBitstring;
const encodeContextSigned = (buffer, tagNumber, value) => {
(0, exports.encodeTag)(buffer, tagNumber, true, getSignedLength(value));
encodeBacnetSigned(buffer, value);
};
exports.encodeContextSigned = encodeContextSigned;
const encodeContextTime = (buffer, tagNumber, value) => {
(0, exports.encodeTag)(buffer, tagNumber, true, 4);
encodeBacnetTime(buffer, value);
};
const bacappEncodeContextDatetime = (buffer, tagNumber, value) => {
if (value !== new Date(1, 1, 1)) {
(0, exports.encodeOpeningTag)(buffer, tagNumber);
bacappEncodeDatetime(buffer, value);
(0, exports.encodeClosingTag)(buffer, tagNumber);
}
else {
throw new Error("wrong Datetime while bacapp encoding context");
}
};
const decodeTagNumber = (buffer, offset) => {
let len = 1;
let tagNumber;
if (isExtendedTagNumber(buffer[offset])) {
tagNumber = buffer[offset + 1];
len++;
}
else {
tagNumber = buffer[offset] >> 4;
}
return {
len,
tagNumber,
};
};
exports.decodeTagNumber = decodeTagNumber;
const decodeIsContextTag = (buffer, offset, tagNumber) => {
const result = (0, exports.decodeTagNumber)(buffer, offset);
return isContextSpecific(buffer[offset]) && result.tagNumber === tagNumber;
};
exports.decodeIsContextTag = decodeIsContextTag;
const decodeIsOpeningTagNumber = (buffer, offset, tagNumber) => {
const result = (0, exports.decodeTagNumber)(buffer, offset);
return isOpeningTag(buffer[offset]) && result.tagNumber === tagNumber;
};
exports.decodeIsOpeningTagNumber = decodeIsOpeningTagNumber;
const decodeIsClosingTagNumber = (buffer, offset, tagNumber) => {
const result = (0, exports.decodeTagNumber)(buffer, offset);
return isClosingTag(buffer[offset]) && result.tagNumber === tagNumber;
};
exports.decodeIsClosingTagNumber = decodeIsClosingTagNumber;
const decodeIsClosingTag = (buffer, offset) => {
return (buffer[offset] & 0x07) === 7;
};
exports.decodeIsClosingTag = decodeIsClosingTag;
const decodeIsOpeningTag = (buffer, offset) => {
return (buffer[offset] & 0x07) === 6;
};
exports.decodeIsOpeningTag = decodeIsOpeningTag;
const decodeObjectId = (buffer, offset, NOT_USED_NUMBER = 0) => {
const result = (0, exports.decodeUnsigned)(buffer, offset, 4);
const objectType = (result.value >> enums_1.default.ASN1_INSTANCE_BITS) & enums_1.default.ASN1_MAX_OBJECT;
const instance = result.value & enums_1.default.ASN1_MAX_INSTANCE;
return {
len: result.len,
objectType,
instance,
};
};
exports.decodeObjectId = decodeObjectId;
const decodeObjectIdSafe = (buffer, offset, lenValue) => {
if (lenValue !== 4) {
return {
len: 0,
objectType: 0,
instance: 0,
};
}
else {
return (0, exports.decodeObjectId)(buffer, offset);
}
};
const decodeTagNumberAndValue = (buffer, offset) => {
let value;
let result = (0, exports.decodeTagNumber)(buffer, offset);
let len = result.len;
if (isExtendedValue(buffer[offset])) {
if (buffer[offset + len] === 255) {
len++;
result = (0, exports.decodeUnsigned)(buffer, offset + len, 4);
len += result.len;
value = result.value;
}
else if (buffer[offset + len] === 254) {
len++;
result = (0, exports.decodeUnsigned)(buffer, offset + len, 2);
len += result.len;
value = result.value;
}
else {
value = buffer[offset + len];
len++;
}
}
else if (isOpeningTag(buffer[offset])) {
value = 0;
}
else if (isClosingTag(buffer[offset])) {
value = 0;
}
else {
value = buffer[offset] & 0x07;
}
return {
len,
tagNumber: result.tagNumber,
value,
};
};
exports.decodeTagNumberAndValue = decodeTagNumberAndValue;
const bacappDecodeApplicationData = (buffer, offset, maxOffset, objectType, propertyId) => {
if (!isContextSpecific(buffer[offset])) {
let result = (0, exports.decodeTagNumberAndValue)(buffer, offset);
if (result) {
let len = result.len;
result = bacappDecodeData(buffer, offset + len, maxOffset, result.tagNumber, result.value);
if (!result) {
return undefined;
}
let resObj = {
len: len + result.len,
type: result.type,
value: result.value,
};
// HACK: Drop string specific handling ASAP
if (result.encoding !== undefined)
resObj.encoding = result.encoding;
return resObj;
}
}
else {
return bacappDecodeContextApplicationData(buffer, offset, maxOffset, objectType, propertyId);
}
};
exports.bacappDecodeApplicationData = bacappDecodeApplicationData;
const encodeReadAccessResult = (buffer, value) => {
(0, exports.encodeContextObjectId)(buffer, 0, value.objectId.type, value.objectId.instance);
(0, exports.encodeOpeningTag)(buffer, 1);
value.values.forEach((item) => {
(0, exports.encodeContextEnumerated)(buffer, 2, item.property.id);
if (item.property.index !== enums_1.default.ASN1_ARRAY_ALL) {
(0, exports.encodeContextUnsigned)(buffer, 3, item.property.index);
}
if (item.value &&
item.value[0] &&
item.value[0].value &&
item.value[0].value.type === "BacnetError") {
(0, exports.encodeOpeningTag)(buffer, 5);
(0, exports.encodeApplicationEnumerated)(buffer, item.value[0].value.errorClass);
(0, exports.encodeApplicationEnumerated)(buffer, item.value[0].value.errorCode);
(0, exports.encodeClosingTag)(buffer, 5);
}
else {
if (!item.value)
throw new Error("No value in item");
(0, exports.encodeOpeningTag)(buffer, 4);
item.value.forEach((subItem) => {
(0, exports.bacappEncodeApplicationData)(buffer, subItem);
});
(0, exports.encodeClosingTag)(buffer, 4);
}
});
(0, exports.encodeClosingTag)(buffer, 1);
};
exports.encodeReadAccessResult = encodeReadAccessResult;
const decodeReadAccessResult = (buffer, offset, apduLen) => {
let len = 0;
let value = {};
if (!(0, exports.decodeIsContextTag)(buffer, offset + len, 0)) {
return undefined;
}
len++;
let result = (0, exports.decodeObjectId)(buffer, offset + len);
value.objectId = {
type: result.objectType,
instance: result.instance,
};
len += result.len;
if (!(0, exports.decodeIsOpeningTagNumber)(buffer, offset + len, 1)) {
return -1; // TODO: why?
}
len++;
const values = [];
while (apduLen - len > 0) {
let newEntry = {};
if ((0, exports.decodeIsClosingTagNumber)(buffer, offset + len, 1)) {
len++;
break;
}
result = (0, exports.decodeTagNumberAndValue)(buffer, offset + len);
len += result.len;
if (result.tagNumber !== 2) {
return undefined;
}
result = (0, exports.decodeEnumerated)(buffer, offset + len, result.value);
newEntry.id = result.value;
len += result.len;
result = (0, exports.decodeTagNumberAndValue)(buffer, offset + len);
if (result.tagNumber === 3) {
len += result.len;
result = (0, exports.decodeUnsigned)(buffer, offset + len, result.value);
newEntry.index = result.value;
len += result.len;
}
else {
newEntry.index = enums_1.default.ASN1_ARRAY_ALL;
}
result = (0, exports.decodeTagNumberAndValue)(buffer, offset + len);
len += result.len;
if (result.tagNumber === 4) {
const localValues = [];
while (len + offset <= buffer.length &&
!(0, exports.decodeIsClosingTagNumber)(buffer, offset + len, 4)) {
let localResult = (0, exports.bacappDecodeApplicationData)(buffer, offset + len, apduLen + offset - 1, value.objectId.type, newEntry.id);
if (!localResult) {
return undefined;
}
len += localResult.len;
let resObj = {
value: localResult.value,
type: localResult.type,
};
// HACK: Drop string specific handling ASAP
if (localResult.encoding !== undefined)
resObj.encoding = localResult.encoding;
localValues.push(resObj);
}
if (!(0, exports.decodeIsClosingTagNumber)(buffer, offset + len, 4)) {
return undefined;
}
if (localValues.count === 2 &&
localValues[0].type === enums_1.default.ApplicationTag.DATE &&
localValues[1].type === enums_1.default.ApplicationTag.TIME) {
const date = localValues[0].value;
const time = localValues[1].value;
const bdatetime = new Date(date.year, date.month, date.day, time.hour, time.minute, time.second, time.millisecond);
newEntry.value = [
{ type: enums_1.default.ApplicationTag.DATETIME, value: bdatetime },
];
}
else {
newEntry.value = localValues;
}
len++;
}
else if (result.tagNumber === 5) {
let err = {};
result = (0, exports.decodeTagNumberAndValue)(buffer, offset + len);
len += result.len;
result = (0, exports.decodeEnumerated)(buffer, offset + len, result.value);
len += result.len;
err.errorClass = result.value;
result = (0, exports.decodeTagNumberAndValue)(buffer, offset + len);
len += result.len;
result = (0, exports.decodeEnumerated)(buffer, offset + len, result.value);
len += result.len;
err.errorCode = result.value;
if (!(0, exports.decodeIsClosingTagNumber)(buffer, offset + len, 5)) {
return undefined;
}
len++;
newEntry.value = [
{
type: enums_1.default.ApplicationTag.ERROR,
value: err,
},
];
}
values.push(newEntry);
}
value.values = values;
return {
len,
value,
};
};
exports.decodeReadAccessResult = decodeReadAccessResult;
const decodeSigned = (buffer, offset, length) => {
return {
len: length,
value: buffer.readIntBE(offset, length, true),
};
};
exports.decodeSigned = decodeSigned;
const decodeReal = (buffer, offset) => {
return {
len: 4,
value: buffer.readFloatBE(offset, true),
};
};
exports.decodeReal = decodeReal;
const decodeRealSafe = (buffer, offset, lenValue) => {
if (lenValue !== 4) {
return {
len: lenValue,
value: 0,
};
}
else {
return (0, exports.decodeReal)(buffer, offset);
}
};
const decodeDouble = (buffer, offset) => {
return {
len: 8,
value: buffer.readDoubleBE(offset, true),
};
};
const decodeDoubleSafe = (buffer, offset, lenValue) => {
if (lenValue !== 8) {
return {
len: lenValue,
value: 0,
};
}
else {
return decodeDouble(buffer, offset);
}
};
const decodeOctetString = (buffer, offset, maxLength, octetStringOffset, octetStringLength) => {
const octetString = [];
for (let i = octetStringOffset; i < octetStringOffset + octetStringLength; i++) {
octetString.push(buffer[offset + i]);
}
return {
len: octetStringLength,
value: octetString,
};
};
exports.decodeOctetString = decodeOctetString;
const multiCharsetCharacterstringDecode = (buffer, offset, maxLength, encoding, length) => {
const stringBuf = Buffer.alloc(length);
buffer.copy(stringBuf, 0, offset, offset + length);
return {
value: iconv_lite_1.default.decode(stringBuf, getEncodingType(encoding, buffer, offset)),
len: length + 1,
encoding,
};
};
const decodeCharacterString = (buffer, offset, maxLength, lenValue) => {
return multiCharsetCharacterstringDecode(buffer, offset + 1, maxLength, buffer[offset], lenValue - 1);
};
exports.decodeCharacterString = decodeCharacterString;
const bitstringSetBitsUsed = (bitString, bytesUsed, unusedBits) => {
bitString.bitsUsed = bytesUsed * 8;
bitString.bitsUsed -= unusedBits;
};
const decodeBitstring = (buffer, offset, lenValue) => {
let len = 0;
let bitString = {};
bitString.value = [];
if (lenValue > 0) {
const bytesUsed = lenValue - 1;
if (bytesUsed <= enums_1.default.ASN1_MAX_BITSTRING_BYTES) {
len = 1;
for (let i = 0; i < bytesUsed; i++) {
bitString.value.push(byteReverseBits(buffer[offset + len++]));
}
const unusedBits = buffer[offset] & 0x07;
bitstringSetBitsUsed(bitString, bytesUsed, unusedBits);
}
}
return {
len,
value: bitString,
};
};
exports.decodeBitstring = decodeBitstring;
const decodeDate = (buffer, offset) => {
let date;
const year = buffer[offset] + 1900;
const month = buffer[offset + 1];
const day = buffer[offset + 2];
const wday = buffer[offset + 3];
if (month === 0xff && day === 0xff && wday === 0xff && year - 1900 === 0xff) {
date = new Date(1, 1, 1);
}
else {
date = new Date(year, month, day);
}
return {
len: 4,
value: date,
};
};
exports.decodeDate = decodeDate;
const decodeDateSafe = (buffer, offset, lenValue) => {
if (lenValue !== 4) {
return {
len: lenValue,
value: new Date(1, 1, 1),
};
}
else {
return (0, exports.decodeDate)(buffer, offset);
}
};
const decodeApplicationDate = (buffer, offset) => {
const result = (0, exports.decodeTagNumber)(buffer, offset);
if (result.tagNumber === enums_1.default.ApplicationTag.DATE) {
const value = (0, exports.decodeDate)(buffer, offset + 1);
return {
len: value.len + 1,
value,
};
}
return undefined;
};
exports.decodeApplicationDate = decodeApplicationDate;
const decodeBacnetTime = (buffer, offset, value = new Date(1, 1, 1)) => {
const hour = buffer[offset + 0];
const min = buffer[offset + 1];
const sec = buffer[offset + 2];
let hundredths = buffer[offset + 3];
if (hour === 0xff && min === 0xff && sec === 0xff && hundredths === 0xff) {
value = new Date(1, 1, 1);
}
else {
if (hundredths > 100)
hundredths = 0;
value = new Date(1, 1, 1, hour, min, sec, hundredths * 10);
}
return {
len: 4,
value,
};
};
exports.decodeBacnetTime = decodeBacnetTime;
const decodeBacnetTimeSafe = (buffer, offset, lenValue) => {
if (lenValue !== 4) {
return {
len: lenValue,
value: new Date(1, 1, 1),
};
}
else {
return (0, exports.decodeBacnetTime)(buffer, offset);
}
};
const decodeApplicationTime = (buffer, offset) => {
const result = (0, exports.decodeTagNumber)(buffer, offset);
if (result.tagNumber === enums_1.default.ApplicationTag.TIME) {
const value = (0, exports.decodeBacnetTime)(buffer, offset + 1);
return {
len: value.len + 1,
value,
};
}
return undefined;
};
exports.decodeApplicationTime = decodeApplicationTime;
const decodeBacnetDatetime = (buffer, offset) => {
let len = 0;
const date = (0, exports.decodeApplicationDate)(buffer, offset + len);
len += date.len;
const time = (0, exports.decodeApplicationTime)(buffer, offset + len);
len += time.len;
return {
len,
value: new Date(date.year, date.month, date.day, time.hour, time.minute, time.second, time.millisecond),
};
};
const bacappDecodeData = (buffer, offset, maxLength, tagDataType, lenValueType) => {
let result;
let value = {
len: 0,
type: tagDataType,
};
switch (tagDataType) {
case enums_1.default.ApplicationTag.NULL:
value.value = null;
break;
case enums_1.default.ApplicationTag.BOOLEAN:
value.value = lenValueType > 0;
break;
case enums_1.default.ApplicationTag.UNSIGNED_INTEGER:
result = (0, exports.decodeUnsigned)(buffer, offset, lenValueType);
value.len += result.len;
value.value = result.value;
break;
case enums_1.default.ApplicationTag.SIGNED_INTEGER:
result = (0, exports.decodeSigned)(buffer, offset, lenValueType);
value.len += result.len;
value.value = result.value;
break;
case enums_1.default.ApplicationTag.REAL:
result = decodeRealSafe(buffer, offset, lenValueType);
value.len += result.len;
value.value = result.value;
break;
case enums_1.default.ApplicationTag.DOUBLE:
result = decodeDoubleSafe(buffer, offset, lenValueType);
value.len += result.len;
value.value = result.value;
break;
case enums_1.default.ApplicationTag.OCTET_STRING:
result = (0, exports.decodeOctetString)(buffer, offset, maxLength, 0, lenValueType);
value.len += result.len;
value.value = result.value;
break;
case enums_1.default.ApplicationTag.CHARACTER_STRING:
result = (0, exports.decodeCharacterString)(buffer, offset, maxLength, lenValueType);
value.len += result.len;
value.value = result.value;
value.encoding = result.encoding;
break;
case enums_1.default.ApplicationTag.BIT_STRING:
result = (0, exports.decodeBitstring)(buffer, offset, lenValueType);
value.len += result.len;
value.value = result.value;
break;
case enums_1.default.ApplicationTag.ENUMERATED:
result = (0, exports.decodeEnumerated)(buffer, offset, lenValueType);
value.len += result.len;
value.value = result.value;
break;
case enums_1.default.ApplicationTag.DATE:
result = decodeDateSafe(buffer, offset, lenValueType);
value.len += result.len;
value.value = result.value;
break;
case enums_1.default.ApplicationTag.TIME:
result = decodeBacnetTimeSafe(buffer, offset, lenValueType);
value.len += result.len;
value.value = result.value;
break;
case enums_1.default.ApplicationTag.OBJECTIDENTIFIER:
result = decodeObjectIdSafe(buffer, offset, lenValueType);
value.len += result.len;
value.value = { type: result.objectType, instance: result.instance };
break;
default:
break;
}
return value;
};
const bacappContextTagType = (property, tagNumber) => {
let tag = 0;
switch (property) {
case enums_1.default.PropertyIdentifier.ACTUAL_SHED_LEVEL:
case enums_1.default.PropertyIdentifier.REQUESTED_SHED_LEVEL:
case enums_1.default.PropertyIdentifier.EXPECTED_SHED_LEVEL:
switch (tagNumber) {
case 0:
case 1:
tag = enums_1.default.ApplicationTag.UNSIGNED_INTEGER;
break;
case 2:
tag = enums_1.default.ApplicationTag.REAL;
break;
default:
break;
}
break;
case enums_1.default.PropertyIdentifier.ACTION:
switch (tagNumber) {
case 0:
case 1:
tag = enums_1.default.ApplicationTag.OBJECTIDENTIFIER;
break;
case 2:
tag = enums_1.default.ApplicationTag.ENUMERATED;
break;
case 3:
case 5:
case 6:
tag = enums_1.default.ApplicationTag.UNSIGNED_INTEGER;
break;
case 7:
case 8:
tag = enums_1.default.ApplicationTag.BOOLEAN;
break;
default:
break;
}
break;
case enums_1.default.PropertyIdentifier.LIST_OF_GROUP_MEMBERS:
switch (tagNumber) {
case 0:
tag = enums_1.default.ApplicationTag.OBJECTIDENTIFIER;
break;
default:
break;
}
break;
case enums_1.default.PropertyIdentifier.EXCEPTION_SCHEDULE:
switch (tagNumber) {
case 1:
tag = enums_1.default.ApplicationTag.OBJECTIDENTIFIER;
break;
case 3:
tag = enums_1.default.ApplicationTag.UNSIGNED_INTEGER;
break;
default:
break;
}
break;
case enums_1.default.PropertyIdentifier.LOG_DEVICE_OBJECT_PROPERTY:
switch (tagNumber) {
case 0:
case 3:
tag = enums_1.default.ApplicationTag.OBJECTIDENTIFIER;
break;
case 1:
tag = enums_1.default.ApplicationTag.ENUMERATED;
break;
case 2:
tag = enums_1.default.ApplicationTag.UNSIGNED_INTEGER;
break;
default:
break;
}
break;
case enums_1.default.PropertyIdentifier.SUBORDINATE_LIST:
switch (tagNumber) {
case 0:
case 1:
tag = enums_1.default.ApplicationTag.OBJECTIDENTIFIER;
break;
default:
break;
}
break;
case enums_1.default.PropertyIdentifier.RECIPIENT_LIST:
switch (tagNumber) {
case 0:
tag = enums_1.default.ApplicationTag.OBJECTIDENTIFIER;
break;
default:
break;
}
break;
case enums_1.default.PropertyIdentifier.ACTIVE_COV_SUBSCRIPTIONS:
switch (tagNumber) {
case 0:
case 1:
break;
case 2:
tag = enums_1.default.ApplicationTag.BOOLEAN;
break;
case 3:
tag = enums_1.default.ApplicationTag.UNSIGNED_INTEGER;
break;
case 4:
tag = enums_1.default.ApplicationTag.REAL;
break;
default:
break;
}
break;
default:
break;
}
return tag;
};
const decodeDeviceObjPropertyRef = (buffer, offset, NOTUSEDARG = 0) => {
let len = 0;
let arrayIndex = enums_1.default.ASN1_ARRAY_ALL;
if (!(0, exports.decodeIsContextTag)(buffer, offset + len, 0)) {
return undefined;
}
len++;
let objectId = (0, exports.decodeObjectId)(buffer, offset + len);
len += objectId.len;
let result = (0, exports.decodeTagNumberAndValue)(buffer, offset + len);
len += result.len;
if (result.tagNumber !== 1) {
return undefined;
}
let id = (0, exports.decodeEnumerated)(buffer, offset + len, result.value);
len += id.len;
result = (0, exports.decodeTagNumberAndValue)(buffer, offset + len);
if (result.tagNumber === 2) {
len += result.len;
arrayIndex = (0, exports.decodeUnsigned)(buffer, offset + len, result.value);
len += arrayIndex.len;
}
if ((0, exports.decodeIsContextTag)(buffer, offset + len, 3)) {
if (!isClosingTag(buffer[offset + len])) {
len++;
objectId = (0, exports.decodeObjectId)(buffer, offset + len);
len += objectId.len;
}
}
return {
len,
value: {
objectId: objectId,
id: id,
},
};
};
const decodeReadAccessSpecification = (buffer, offset, apduLen) => {
let len = 0;
let value = {};
if (!(0, exports.decodeIsContextTag)(buffer, offset + len, 0)) {
return undefined;
}
len++;
let decodedValue = (0, exports.decodeObjectId)(buffer, offset + len);
value.objectId = {
type: decodedValue.objectType,
instance: decodedValue.instance,
};
len += decodedValue.len;
if (!(0, exports.decodeIsOpeningTagNumber)(buffer, offset + len, 1)) {
return undefined;
}
len++;
const propertyIdAndArrayIndex = [];
while (apduLen - len > 1 &&
!(0, exports.decodeIsClosingTagNumber)(buffer, offset + len, 1)) {
let propertyRef = {};
if (!isContextSpecific(buffer[offset + len])) {
return undefined;
}
let result = (0, exports.decodeTagN