@bitpoolos/edge-bacnet 1.0.6 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bacnet_client.js +650 -233
- package/bacnet_device.js +65 -16
- package/bacnet_gateway.html +242 -99
- package/bacnet_gateway.js +211 -27
- package/bacnet_object.js +1 -1
- package/bacnet_read.html +211 -133
- package/bacnet_read.js +24 -24
- package/bacnet_server.js +321 -0
- package/bacnet_write.html +24 -15
- package/bacnet_write.js +0 -2
- package/common.js +95 -9
- package/edge-bacnet-datastore.cfg +0 -0
- package/package.json +6 -4
- package/resources/confirmationservice.min.js +1 -0
- package/resources/confirmdialog.min.js +1 -0
- package/resources/fonts/primeicons.woff2 +0 -0
- package/resources/node-bacnet/CHANGELOG.md +481 -0
- package/resources/{bacstack → node-bacnet}/LICENSE.md +3 -1
- package/resources/node-bacnet/README.md +91 -0
- package/resources/node-bacnet/docs/Client.html +4422 -0
- package/resources/node-bacnet/docs/bacnet-icon-quad.png +0 -0
- package/resources/node-bacnet/docs/bacnet-icon-quad128.png +0 -0
- package/resources/node-bacnet/docs/bacnet-icon-quad64.png +0 -0
- package/resources/node-bacnet/docs/bacnet-icon-small.xcf +0 -0
- package/resources/node-bacnet/docs/bacnet-icon.xcf +0 -0
- package/resources/node-bacnet/docs/bacnet.html +7032 -0
- package/resources/node-bacnet/docs/client.js.html +1759 -0
- package/resources/node-bacnet/docs/enum.js.html +2530 -0
- package/resources/node-bacnet/docs/global.html +2068 -0
- package/resources/node-bacnet/docs/images/mocha-logo.svg +65 -0
- package/resources/node-bacnet/docs/index.html +283 -0
- package/resources/node-bacnet/docs/scripts/collapse.js +11 -0
- package/resources/node-bacnet/docs/scripts/jquery-3.1.1.min.js +4 -0
- package/resources/node-bacnet/docs/scripts/linenumber.js +26 -0
- package/resources/node-bacnet/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/resources/node-bacnet/docs/scripts/prettify/lang-css.js +2 -0
- package/resources/node-bacnet/docs/scripts/prettify/prettify.js +28 -0
- package/resources/node-bacnet/docs/scripts/search.js +47 -0
- package/resources/node-bacnet/docs/services_i-am.js.html +157 -0
- package/resources/node-bacnet/docs/services_time-sync.js.html +118 -0
- package/resources/node-bacnet/docs/services_who-is.js.html +138 -0
- package/resources/node-bacnet/docs/styles/jsdoc.css +683 -0
- package/resources/node-bacnet/docs/styles/prettify.css +82 -0
- package/resources/node-bacnet/examples/discover-devices.js +66 -0
- package/resources/node-bacnet/examples/read-device.js +510 -0
- package/resources/node-bacnet/examples/subscribe-cov.js +75 -0
- package/resources/{bacstack → node-bacnet}/index.js +3 -0
- package/resources/{bacstack → node-bacnet}/lib/apdu.js +56 -39
- package/resources/{bacstack → node-bacnet}/lib/asn1.js +550 -532
- package/resources/node-bacnet/lib/bvlc.js +90 -0
- package/resources/node-bacnet/lib/client.js +1695 -0
- package/resources/node-bacnet/lib/enum.js +2463 -0
- package/resources/node-bacnet/lib/npdu.js +123 -0
- package/resources/{bacstack → node-bacnet}/lib/services/add-list-element.js +12 -6
- package/resources/{bacstack → node-bacnet}/lib/services/alarm-acknowledge.js +3 -3
- package/resources/{bacstack → node-bacnet}/lib/services/alarm-summary.js +5 -4
- package/resources/{bacstack → node-bacnet}/lib/services/atomic-read-file.js +49 -26
- package/resources/{bacstack → node-bacnet}/lib/services/atomic-write-file.js +40 -23
- package/resources/{bacstack → node-bacnet}/lib/services/cov-notify.js +33 -17
- package/resources/{bacstack → node-bacnet}/lib/services/create-object.js +23 -13
- package/resources/{bacstack → node-bacnet}/lib/services/delete-object.js +7 -2
- package/resources/{bacstack → node-bacnet}/lib/services/device-communication-control.js +8 -3
- package/resources/{bacstack → node-bacnet}/lib/services/error.js +7 -0
- package/resources/{bacstack → node-bacnet}/lib/services/event-information.js +10 -9
- package/resources/{bacstack → node-bacnet}/lib/services/event-notify-data.js +38 -16
- package/resources/{bacstack → node-bacnet}/lib/services/get-enrollment-summary.js +24 -11
- package/resources/{bacstack → node-bacnet}/lib/services/get-event-information.js +28 -13
- package/resources/node-bacnet/lib/services/i-am.js +90 -0
- package/resources/{bacstack/lib/services/i-have-broadcast.js → node-bacnet/lib/services/i-have.js} +3 -2
- package/resources/{bacstack → node-bacnet}/lib/services/index.js +7 -4
- package/resources/{bacstack → node-bacnet}/lib/services/life-safety-operation.js +3 -2
- package/resources/{bacstack → node-bacnet}/lib/services/private-transfer.js +3 -2
- package/resources/{bacstack → node-bacnet}/lib/services/read-property-multiple.js +11 -6
- package/resources/{bacstack → node-bacnet}/lib/services/read-property.js +42 -24
- package/resources/{bacstack → node-bacnet}/lib/services/read-range.js +37 -27
- package/resources/node-bacnet/lib/services/register-foreign-device.js +18 -0
- package/resources/{bacstack → node-bacnet}/lib/services/reinitialize-device.js +9 -4
- package/resources/{bacstack → node-bacnet}/lib/services/subscribe-cov.js +9 -4
- package/resources/{bacstack → node-bacnet}/lib/services/subscribe-property.js +18 -8
- package/resources/{bacstack → node-bacnet}/lib/services/time-sync.js +28 -5
- package/resources/{bacstack → node-bacnet}/lib/services/who-has.js +3 -3
- package/resources/{bacstack → node-bacnet}/lib/services/who-is.js +42 -9
- package/resources/{bacstack → node-bacnet}/lib/services/write-property-multiple.js +33 -16
- package/resources/{bacstack → node-bacnet}/lib/services/write-property.js +23 -13
- package/resources/node-bacnet/lib/transport.js +82 -0
- package/resources/node-bacnet/package.json +92 -0
- package/resources/primeicons.css +90 -2
- package/resources/bacstack/.codeclimate.yml +0 -15
- package/resources/bacstack/.dockerignore +0 -5
- package/resources/bacstack/.editorconfig +0 -13
- package/resources/bacstack/.eslintrc.yml +0 -13
- package/resources/bacstack/.github/ISSUE_TEMPLATE.md +0 -26
- package/resources/bacstack/.github/PULL_REQUEST_TEMPLATE.md +0 -14
- package/resources/bacstack/.github/workflows/ci.yml +0 -39
- package/resources/bacstack/.jscsrc +0 -8
- package/resources/bacstack/.jshintrc +0 -50
- package/resources/bacstack/.travis.yml +0 -27
- package/resources/bacstack/CHANGELOG.md +0 -232
- package/resources/bacstack/CODE_OF_CONDUCT.md +0 -74
- package/resources/bacstack/CONTRIBUTING.md +0 -77
- package/resources/bacstack/Dockerfile +0 -15
- package/resources/bacstack/FAQ.md +0 -64
- package/resources/bacstack/README.md +0 -157
- package/resources/bacstack/docker-compose.yml +0 -9
- package/resources/bacstack/lib/adpu.js +0 -190
- package/resources/bacstack/lib/bvlc.js +0 -43
- package/resources/bacstack/lib/client.js +0 -1028
- package/resources/bacstack/lib/enum.js +0 -1314
- package/resources/bacstack/lib/npdu.js +0 -119
- package/resources/bacstack/lib/services/i-am-broadcast.js +0 -51
- package/resources/bacstack/lib/services.js +0 -1963
- package/resources/bacstack/lib/transport.js +0 -52
- package/resources/bacstack/package-lock.json +0 -7974
- package/resources/bacstack/package.json +0 -84
- package/resources/bacstack/test/compliance/who-is.spec.js +0 -37
- package/resources/bacstack/test/integration/acknowledge-alarm.spec.js +0 -14
- package/resources/bacstack/test/integration/add-list-element.spec.js +0 -16
- package/resources/bacstack/test/integration/confirmed-event-notification.spec.js +0 -30
- package/resources/bacstack/test/integration/confirmed-private-transfer.spec.js +0 -15
- package/resources/bacstack/test/integration/create-object.spec.js +0 -16
- package/resources/bacstack/test/integration/delete-object.spec.js +0 -14
- package/resources/bacstack/test/integration/device-communication-control.spec.js +0 -14
- package/resources/bacstack/test/integration/get-alarm-summary.spec.js +0 -14
- package/resources/bacstack/test/integration/get-enrollment-summary.spec.js +0 -15
- package/resources/bacstack/test/integration/get-event-information.spec.js +0 -14
- package/resources/bacstack/test/integration/read-file.spec.js +0 -14
- package/resources/bacstack/test/integration/read-property-multiple.spec.js +0 -110
- package/resources/bacstack/test/integration/read-property.spec.js +0 -14
- package/resources/bacstack/test/integration/read-range.spec.js +0 -14
- package/resources/bacstack/test/integration/reinitialize-sevice.spec.js +0 -14
- package/resources/bacstack/test/integration/remove-list-element.spec.js +0 -16
- package/resources/bacstack/test/integration/subscribe-cov.spec.js +0 -14
- package/resources/bacstack/test/integration/subscribe-property.spec.js +0 -14
- package/resources/bacstack/test/integration/time-sync-utc.spec.js +0 -10
- package/resources/bacstack/test/integration/time-sync.spec.js +0 -10
- package/resources/bacstack/test/integration/unconfirmed-event-notification.spec.js +0 -28
- package/resources/bacstack/test/integration/unconfirmed-private-transfer.spec.js +0 -11
- package/resources/bacstack/test/integration/utils.js +0 -30
- package/resources/bacstack/test/integration/who-is.spec.js +0 -17
- package/resources/bacstack/test/integration/write-file.spec.js +0 -14
- package/resources/bacstack/test/integration/write-property-multiple.spec.js +0 -19
- package/resources/bacstack/test/integration/write-property.spec.js +0 -14
- package/resources/bacstack/test/unit/apdu.spec.js +0 -162
- package/resources/bacstack/test/unit/asn1.spec.js +0 -39
- package/resources/bacstack/test/unit/bacnet-apdu.spec.js +0 -161
- package/resources/bacstack/test/unit/bacnet-asn1.spec.js +0 -32
- package/resources/bacstack/test/unit/bacnet-bvlc.spec.js +0 -57
- package/resources/bacstack/test/unit/bacnet-npdu.spec.js +0 -118
- package/resources/bacstack/test/unit/bacnet-services.spec.js +0 -2052
- package/resources/bacstack/test/unit/bvlc.spec.js +0 -58
- package/resources/bacstack/test/unit/npdu.spec.js +0 -119
- package/resources/bacstack/test/unit/service-add-list-element.spec.js +0 -24
- package/resources/bacstack/test/unit/service-alarm-acknowledge.spec.js +0 -71
- package/resources/bacstack/test/unit/service-alarm-summary.spec.js +0 -22
- package/resources/bacstack/test/unit/service-atomic-read-file.spec.js +0 -54
- package/resources/bacstack/test/unit/service-atomic-write-file.spec.js +0 -56
- package/resources/bacstack/test/unit/service-cov-notify.spec.js +0 -98
- package/resources/bacstack/test/unit/service-create-object.spec.js +0 -90
- package/resources/bacstack/test/unit/service-delete-object.spec.js +0 -17
- package/resources/bacstack/test/unit/service-device-communication-control.spec.js +0 -29
- package/resources/bacstack/test/unit/service-error.spec.js +0 -17
- package/resources/bacstack/test/unit/service-event-information.spec.js +0 -48
- package/resources/bacstack/test/unit/service-event-notify-data.spec.js +0 -310
- package/resources/bacstack/test/unit/service-get-enrollment-summary.spec.js +0 -45
- package/resources/bacstack/test/unit/service-get-event-information.spec.js +0 -62
- package/resources/bacstack/test/unit/service-i-am.spec.js +0 -19
- package/resources/bacstack/test/unit/service-i-have-broadcast.spec.js +0 -18
- package/resources/bacstack/test/unit/service-life-safety-operation.spec.js +0 -19
- package/resources/bacstack/test/unit/service-private-transfer.spec.js +0 -18
- package/resources/bacstack/test/unit/service-read-property-multiple.spec.js +0 -131
- package/resources/bacstack/test/unit/service-read-property.spec.js +0 -541
- package/resources/bacstack/test/unit/service-read-range.spec.js +0 -97
- package/resources/bacstack/test/unit/service-reinitialize-device.spec.js +0 -27
- package/resources/bacstack/test/unit/service-subscribe-cov.spec.js +0 -32
- package/resources/bacstack/test/unit/service-subscribe-property.spec.js +0 -50
- package/resources/bacstack/test/unit/service-time-sync.spec.js +0 -18
- package/resources/bacstack/test/unit/service-who-has.spec.js +0 -33
- package/resources/bacstack/test/unit/service-who-is.spec.js +0 -17
- package/resources/bacstack/test/unit/service-write-property-multiple.spec.js +0 -143
- package/resources/bacstack/test/unit/service-write-property.spec.js +0 -198
- package/resources/bacstack/test/unit/utils.js +0 -6
|
@@ -0,0 +1,1759 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>client.js - Documentation</title>
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
<script src="scripts/prettify/prettify.js"></script>
|
|
10
|
+
<script src="scripts/prettify/lang-css.js"></script>
|
|
11
|
+
|
|
12
|
+
<link type="text/css" rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat:400,700">
|
|
13
|
+
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
|
|
14
|
+
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
|
|
15
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
|
|
19
|
+
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
|
|
20
|
+
<label for="nav-trigger" class="navicon-button x">
|
|
21
|
+
<div class="navicon"></div>
|
|
22
|
+
</label>
|
|
23
|
+
|
|
24
|
+
<label for="nav-trigger" class="overlay"></label>
|
|
25
|
+
|
|
26
|
+
<nav >
|
|
27
|
+
|
|
28
|
+
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="bacnet.html">bacnet</a><ul class='methods'><li data-type='method'><a href="bacnet.html#.close">close</a></li><li data-type='method'><a href="bacnet.html#.confirmedCOVNotification">confirmedCOVNotification</a></li><li data-type='method'><a href="bacnet.html#.deviceCommunicationControl">deviceCommunicationControl</a></li><li data-type='method'><a href="bacnet.html#.iAmResponse">iAmResponse</a></li><li data-type='method'><a href="bacnet.html#.readProperty">readProperty</a></li><li data-type='method'><a href="bacnet.html#.readPropertyMultiple">readPropertyMultiple</a></li><li data-type='method'><a href="bacnet.html#.readPropertyResponse">readPropertyResponse</a></li><li data-type='method'><a href="bacnet.html#.reinitializeDevice">reinitializeDevice</a></li><li data-type='method'><a href="bacnet.html#.resultResponse">resultResponse</a></li><li data-type='method'><a href="bacnet.html#.timeSync">timeSync</a></li><li data-type='method'><a href="bacnet.html#.timeSyncUTC">timeSyncUTC</a></li><li data-type='method'><a href="bacnet.html#.unconfirmedCOVNotification">unconfirmedCOVNotification</a></li><li data-type='method'><a href="bacnet.html#.whoIs">whoIs</a></li><li data-type='method'><a href="bacnet.html#.writeProperty">writeProperty</a></li><li data-type='method'><a href="bacnet.html#.writePropertyMultiple">writePropertyMultiple</a></li></ul></li><li><a href="Client.html">Client</a><ul class='methods'><li data-type='method'><a href="Client.html#.createBitstring">createBitstring</a></li><li data-type='method'><a href="Client.html#acknowledgeAlarm">acknowledgeAlarm</a></li><li data-type='method'><a href="Client.html#addListElement">addListElement</a></li><li data-type='method'><a href="Client.html#confirmedEventNotification">confirmedEventNotification</a></li><li data-type='method'><a href="Client.html#confirmedPrivateTransfer">confirmedPrivateTransfer</a></li><li data-type='method'><a href="Client.html#createObject">createObject</a></li><li data-type='method'><a href="Client.html#deleteObject">deleteObject</a></li><li data-type='method'><a href="Client.html#errorResponse">errorResponse</a></li><li data-type='method'><a href="Client.html#getAlarmSummary">getAlarmSummary</a></li><li data-type='method'><a href="Client.html#getEnrollmentSummary">getEnrollmentSummary</a></li><li data-type='method'><a href="Client.html#getEventInformation">getEventInformation</a></li><li data-type='method'><a href="Client.html#iHaveResponse">iHaveResponse</a></li><li data-type='method'><a href="Client.html#readFile">readFile</a></li><li data-type='method'><a href="Client.html#readRange">readRange</a></li><li data-type='method'><a href="Client.html#removeListElement">removeListElement</a></li><li data-type='method'><a href="Client.html#sendBvlc">sendBvlc</a></li><li data-type='method'><a href="Client.html#simpleAckResponse">simpleAckResponse</a></li><li data-type='method'><a href="Client.html#subscribeCov">subscribeCov</a></li><li data-type='method'><a href="Client.html#subscribeProperty">subscribeProperty</a></li><li data-type='method'><a href="Client.html#unconfirmedEventNotification">unconfirmedEventNotification</a></li><li data-type='method'><a href="Client.html#unconfirmedPrivateTransfer">unconfirmedPrivateTransfer</a></li><li data-type='method'><a href="Client.html#writeFile">writeFile</a></li></ul></li></ul><h3>Events</h3><ul><li><a href="bacnet.html#.event:error">error</a></li><li><a href="bacnet.html#.event:iAm">iAm</a></li><li><a href="bacnet.html#.event:timeSync">timeSync</a></li><li><a href="bacnet.html#.event:whoIs">whoIs</a></li></ul><h3>Global</h3><ul><li><a href="global.html#ApplicationTag">ApplicationTag</a></li><li><a href="global.html#EnableDisable">EnableDisable</a></li><li><a href="global.html#MaxApduLengthAccepted">MaxApduLengthAccepted</a></li><li><a href="global.html#MaxSegmentsAccepted">MaxSegmentsAccepted</a></li><li><a href="global.html#ReinitializedState">ReinitializedState</a></li><li><a href="global.html#usc">usc</a></li></ul>
|
|
29
|
+
</nav>
|
|
30
|
+
|
|
31
|
+
<div id="main">
|
|
32
|
+
|
|
33
|
+
<h1 class="page-title">client.js</h1>
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
<section>
|
|
42
|
+
<article>
|
|
43
|
+
<pre class="prettyprint source linenums"><code>'use strict';
|
|
44
|
+
|
|
45
|
+
// Util Modules
|
|
46
|
+
const EventEmitter = require('events').EventEmitter;
|
|
47
|
+
const debug = require('debug')('bacnet:client:debug');
|
|
48
|
+
const trace = require('debug')('bacnet:client:trace');
|
|
49
|
+
const usc = require('underscore');
|
|
50
|
+
|
|
51
|
+
// Local Modules
|
|
52
|
+
const baTransport = require('./transport');
|
|
53
|
+
const baServices = require('./services');
|
|
54
|
+
const baAsn1 = require('./asn1');
|
|
55
|
+
const baApdu = require('./apdu');
|
|
56
|
+
const baNpdu = require('./npdu');
|
|
57
|
+
const baBvlc = require('./bvlc');
|
|
58
|
+
const baEnum = require('./enum');
|
|
59
|
+
|
|
60
|
+
const ALL_INTERFACES = '0.0.0.0';
|
|
61
|
+
const LOCALHOST_INTERFACES_IPV4 = '127.0.0.1';
|
|
62
|
+
const BROADCAST_ADDRESS = '255.255.255.255';
|
|
63
|
+
const DEFAULT_HOP_COUNT = 0xFF;
|
|
64
|
+
const BVLC_HEADER_LENGTH = 4;
|
|
65
|
+
const BVLC_FWD_HEADER_LENGTH = 10; // FORWARDED_NPDU
|
|
66
|
+
|
|
67
|
+
const beU = baEnum.UnconfirmedServiceChoice;
|
|
68
|
+
const unconfirmedServiceMap = {
|
|
69
|
+
[beU.I_AM]: 'iAm',
|
|
70
|
+
[beU.WHO_IS]: 'whoIs',
|
|
71
|
+
[beU.WHO_HAS]: 'whoHas',
|
|
72
|
+
[beU.UNCONFIRMED_COV_NOTIFICATION]: 'covNotifyUnconfirmed',
|
|
73
|
+
[beU.TIME_SYNCHRONIZATION]: 'timeSync',
|
|
74
|
+
[beU.UTC_TIME_SYNCHRONIZATION]: 'timeSyncUTC',
|
|
75
|
+
[beU.UNCONFIRMED_EVENT_NOTIFICATION]: 'eventNotify',
|
|
76
|
+
[beU.I_HAVE]: 'iHave',
|
|
77
|
+
[beU.UNCONFIRMED_PRIVATE_TRANSFER]: 'privateTransfer',
|
|
78
|
+
};
|
|
79
|
+
const beC = baEnum.ConfirmedServiceChoice;
|
|
80
|
+
const confirmedServiceMap = {
|
|
81
|
+
[beC.READ_PROPERTY]: 'readProperty',
|
|
82
|
+
[beC.WRITE_PROPERTY]: 'writeProperty',
|
|
83
|
+
[beC.READ_PROPERTY_MULTIPLE]: 'readPropertyMultiple',
|
|
84
|
+
[beC.WRITE_PROPERTY_MULTIPLE]: 'writePropertyMultiple',
|
|
85
|
+
[beC.CONFIRMED_COV_NOTIFICATION]: 'covNotify',
|
|
86
|
+
[beC.ATOMIC_WRITE_FILE]: 'atomicWriteFile',
|
|
87
|
+
[beC.ATOMIC_READ_FILE]: 'atomicReadFile',
|
|
88
|
+
[beC.SUBSCRIBE_COV]: 'subscribeCov',
|
|
89
|
+
[beC.SUBSCRIBE_COV_PROPERTY]: 'subscribeProperty',
|
|
90
|
+
[beC.DEVICE_COMMUNICATION_CONTROL]: 'deviceCommunicationControl',
|
|
91
|
+
[beC.REINITIALIZE_DEVICE]: 'reinitializeDevice',
|
|
92
|
+
[beC.CONFIRMED_EVENT_NOTIFICATION]: 'eventNotify',
|
|
93
|
+
[beC.READ_RANGE]: 'readRange',
|
|
94
|
+
[beC.CREATE_OBJECT]: 'createObject',
|
|
95
|
+
[beC.DELETE_OBJECT]: 'deleteObject',
|
|
96
|
+
[beC.ACKNOWLEDGE_ALARM]: 'alarmAcknowledge',
|
|
97
|
+
[beC.GET_ALARM_SUMMARY]: 'getAlarmSummary',
|
|
98
|
+
[beC.GET_ENROLLMENT_SUMMARY]: 'getEnrollmentSummary',
|
|
99
|
+
[beC.GET_EVENT_INFORMATION]: 'getEventInformation',
|
|
100
|
+
[beC.LIFE_SAFETY_OPERATION]: 'lifeSafetyOperation',
|
|
101
|
+
[beC.ADD_LIST_ELEMENT]: 'addListElement',
|
|
102
|
+
[beC.REMOVE_LIST_ELEMENT]: 'removeListElement',
|
|
103
|
+
[beC.CONFIRMED_PRIVATE_TRANSFER]: 'privateTransfer',
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* To be able to communicate to BACNET devices, you have to initialize a new bacnet instance.
|
|
108
|
+
* @class bacnet
|
|
109
|
+
* @param {object=} this._settings - The options object used for parameterizing the bacnet.
|
|
110
|
+
* @param {number=} [options.port=47808] - BACNET communication port for listening and sending.
|
|
111
|
+
* @param {string=} options.interface - Specific BACNET communication interface if different from primary one.
|
|
112
|
+
* @param {string=} [options.broadcastAddress=255.255.255.255] - The address used for broadcast messages.
|
|
113
|
+
* @param {number=} [options.apduTimeout=3000] - The timeout in milliseconds until a transaction should be interpreted as error.
|
|
114
|
+
* @example
|
|
115
|
+
* const bacnet = require('node-bacnet');
|
|
116
|
+
*
|
|
117
|
+
* const client = new bacnet({
|
|
118
|
+
* port: 47809, // Use BAC1 as communication port
|
|
119
|
+
* interface: '192.168.251.10', // Listen on a specific interface
|
|
120
|
+
* broadcastAddress: '192.168.251.255', // Use the subnet broadcast address
|
|
121
|
+
* apduTimeout: 6000 // Wait twice as long for response
|
|
122
|
+
* });
|
|
123
|
+
*/
|
|
124
|
+
class Client extends EventEmitter {
|
|
125
|
+
/**
|
|
126
|
+
*
|
|
127
|
+
* @param options
|
|
128
|
+
*/
|
|
129
|
+
constructor(options) {
|
|
130
|
+
super();
|
|
131
|
+
|
|
132
|
+
options = options || {};
|
|
133
|
+
|
|
134
|
+
this._invokeCounter = 1;
|
|
135
|
+
this._invokeStore = {};
|
|
136
|
+
|
|
137
|
+
this._lastSequenceNumber = 0;
|
|
138
|
+
this._segmentStore = [];
|
|
139
|
+
|
|
140
|
+
this._settings = {
|
|
141
|
+
port: options.port || 47808,
|
|
142
|
+
interface: options.interface || ALL_INTERFACES,
|
|
143
|
+
transport: options.transport,
|
|
144
|
+
broadcastAddress: options.broadcastAddress || BROADCAST_ADDRESS,
|
|
145
|
+
apduTimeout: options.apduTimeout || 3000
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
options.reuseAddr = options.reuseAddr === undefined ? true : !!options.reuseAddr;
|
|
149
|
+
|
|
150
|
+
this._transport = this._settings.transport || new baTransport({
|
|
151
|
+
port: this._settings.port,
|
|
152
|
+
interface: this._settings.interface,
|
|
153
|
+
broadcastAddress: this._settings.broadcastAddress,
|
|
154
|
+
reuseAddr: options.reuseAddr
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Setup code
|
|
158
|
+
this._transport.on('message', this._receiveData.bind(this));
|
|
159
|
+
this._transport.on('error', this._receiveError.bind(this));
|
|
160
|
+
this._transport.on('listening', () => this.emit('listening'));
|
|
161
|
+
this._transport.open();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
*
|
|
166
|
+
* @returns {number}
|
|
167
|
+
* @private
|
|
168
|
+
*/
|
|
169
|
+
_getInvokeId() {
|
|
170
|
+
const id = this._invokeCounter++;
|
|
171
|
+
if (id >= 256) this._invokeCounter = 1;
|
|
172
|
+
return id - 1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
*
|
|
177
|
+
* @param id
|
|
178
|
+
* @param err
|
|
179
|
+
* @param result
|
|
180
|
+
* @returns {*}
|
|
181
|
+
* @private
|
|
182
|
+
*/
|
|
183
|
+
_invokeCallback(id, err, result) {
|
|
184
|
+
const callback = this._invokeStore[id];
|
|
185
|
+
if (callback) {
|
|
186
|
+
return void callback(err, result);
|
|
187
|
+
}
|
|
188
|
+
debug('InvokeId', id, 'not found -> drop package');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
*
|
|
193
|
+
* @param id
|
|
194
|
+
* @param callback
|
|
195
|
+
* @private
|
|
196
|
+
*/
|
|
197
|
+
_addCallback(id, callback) {
|
|
198
|
+
const timeout = setTimeout(() => {
|
|
199
|
+
delete this._invokeStore[id];
|
|
200
|
+
callback(new Error('ERR_TIMEOUT'));
|
|
201
|
+
}, this._settings.apduTimeout);
|
|
202
|
+
this._invokeStore[id] = (err, data) => {
|
|
203
|
+
clearTimeout(timeout);
|
|
204
|
+
delete this._invokeStore[id];
|
|
205
|
+
callback(err, data);
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
*
|
|
211
|
+
* @param isForwarded
|
|
212
|
+
* @returns {{offset: (number), buffer: *}}
|
|
213
|
+
* @private
|
|
214
|
+
*/
|
|
215
|
+
_getBuffer(isForwarded) {
|
|
216
|
+
return Object.assign({}, {
|
|
217
|
+
buffer: Buffer.alloc(this._transport.getMaxPayload()),
|
|
218
|
+
offset: isForwarded ? BVLC_FWD_HEADER_LENGTH : BVLC_HEADER_LENGTH
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
*
|
|
224
|
+
* @param invokeId
|
|
225
|
+
* @param buffer
|
|
226
|
+
* @param offset
|
|
227
|
+
* @param length
|
|
228
|
+
* @returns {*}
|
|
229
|
+
* @private
|
|
230
|
+
*/
|
|
231
|
+
_processError(invokeId, buffer, offset, length) {
|
|
232
|
+
const result = baServices.error.decode(buffer, offset, length);
|
|
233
|
+
trace('Received error:', result);
|
|
234
|
+
if (!result) {
|
|
235
|
+
return debug('Couldn`t decode Error');
|
|
236
|
+
}
|
|
237
|
+
const err = new Error(baServices.error.buildMessage(result));
|
|
238
|
+
err.bacnetErrorClass = result.class;
|
|
239
|
+
err.bacnetErrorCode = result.code;
|
|
240
|
+
this._invokeCallback(invokeId, err);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
*
|
|
245
|
+
* @param invokeId
|
|
246
|
+
* @param reason
|
|
247
|
+
* @private
|
|
248
|
+
*/
|
|
249
|
+
_processAbort(invokeId, reason) {
|
|
250
|
+
const err = new Error('BacnetAbort - Reason:' + reason);
|
|
251
|
+
err.bacnetAbortReason = reason;
|
|
252
|
+
this._invokeCallback(invokeId, err);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
*
|
|
257
|
+
* @param receiver
|
|
258
|
+
* @param negative
|
|
259
|
+
* @param server
|
|
260
|
+
* @param originalInvokeId
|
|
261
|
+
* @param sequencenumber
|
|
262
|
+
* @param actualWindowSize
|
|
263
|
+
* @private
|
|
264
|
+
*/
|
|
265
|
+
_segmentAckResponse(receiver, negative, server, originalInvokeId, sequencenumber, actualWindowSize) {
|
|
266
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
267
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
|
|
268
|
+
baApdu.encodeSegmentAck(buffer, baEnum.PduType.SEGMENT_ACK | (negative ? baEnum.PduSegAckBit.NEGATIVE_ACK : 0) | (server ? baEnum.PduSegAckBit.SERVER : 0), originalInvokeId, sequencenumber, actualWindowSize);
|
|
269
|
+
this.sendBvlc(receiver, buffer);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
*
|
|
274
|
+
* @param msg
|
|
275
|
+
* @param first
|
|
276
|
+
* @param moreFollows
|
|
277
|
+
* @param buffer
|
|
278
|
+
* @param offset
|
|
279
|
+
* @param length
|
|
280
|
+
* @private
|
|
281
|
+
*/
|
|
282
|
+
_performDefaultSegmentHandling(msg, first, moreFollows, buffer, offset, length) {
|
|
283
|
+
if (first) {
|
|
284
|
+
this._segmentStore = [];
|
|
285
|
+
msg.type &= ~baEnum.PduConReqBit.SEGMENTED_MESSAGE;
|
|
286
|
+
let apduHeaderLen = 3;
|
|
287
|
+
if ((msg.type & baEnum.PDU_TYPE_MASK) === baEnum.PduType.CONFIRMED_REQUEST) {
|
|
288
|
+
apduHeaderLen = 4;
|
|
289
|
+
}
|
|
290
|
+
const apdubuffer = this._getBuffer();
|
|
291
|
+
apdubuffer.offset = 0;
|
|
292
|
+
buffer.copy(apdubuffer.buffer, apduHeaderLen, offset, offset + length);
|
|
293
|
+
if ((msg.type & baEnum.PDU_TYPE_MASK) === baEnum.PduType.CONFIRMED_REQUEST) {
|
|
294
|
+
baApdu.encodeConfirmedServiceRequest(
|
|
295
|
+
apdubuffer,
|
|
296
|
+
msg.type,
|
|
297
|
+
msg.service,
|
|
298
|
+
msg.maxSegments,
|
|
299
|
+
msg.maxApdu,
|
|
300
|
+
msg.invokeId,
|
|
301
|
+
0,
|
|
302
|
+
0
|
|
303
|
+
);
|
|
304
|
+
} else {
|
|
305
|
+
baApdu.encodeComplexAck(apdubuffer, msg.type, msg.service, msg.invokeId, 0, 0);
|
|
306
|
+
}
|
|
307
|
+
this._segmentStore.push(apdubuffer.buffer.slice(0, length + apduHeaderLen));
|
|
308
|
+
} else {
|
|
309
|
+
this._segmentStore.push(buffer.slice(offset, offset + length));
|
|
310
|
+
}
|
|
311
|
+
if (!moreFollows) {
|
|
312
|
+
const apduBuffer = Buffer.concat(this._segmentStore);
|
|
313
|
+
this._segmentStore = [];
|
|
314
|
+
msg.type &= ~baEnum.PduConReqBit.SEGMENTED_MESSAGE;
|
|
315
|
+
this._handlePdu(apduBuffer, 0, apduBuffer.length, msg.header);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
*
|
|
321
|
+
* @param msg
|
|
322
|
+
* @param server
|
|
323
|
+
* @param buffer
|
|
324
|
+
* @param offset
|
|
325
|
+
* @param length
|
|
326
|
+
* @private
|
|
327
|
+
*/
|
|
328
|
+
_processSegment(msg, server, buffer, offset, length) {
|
|
329
|
+
let first = false;
|
|
330
|
+
if (msg.sequencenumber === 0 && this._lastSequenceNumber === 0) {
|
|
331
|
+
first = true;
|
|
332
|
+
} else {
|
|
333
|
+
if (msg.sequencenumber !== this._lastSequenceNumber + 1) {
|
|
334
|
+
return this._segmentAckResponse(msg.header.address, true, server, msg.invokeId, this._lastSequenceNumber, msg.proposedWindowNumber);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
this._lastSequenceNumber = msg.sequencenumber;
|
|
338
|
+
const moreFollows = msg.type & baEnum.PduConReqBit.MORE_FOLLOWS;
|
|
339
|
+
if (!moreFollows) {
|
|
340
|
+
this._lastSequenceNumber = 0;
|
|
341
|
+
}
|
|
342
|
+
if ((msg.sequencenumber % msg.proposedWindowNumber) === 0 || !moreFollows) {
|
|
343
|
+
this._segmentAckResponse(msg.header.address, false, server, msg.invokeId, msg.sequencenumber, msg.proposedWindowNumber);
|
|
344
|
+
}
|
|
345
|
+
this._performDefaultSegmentHandling(msg, first, moreFollows, buffer, offset, length);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
*
|
|
350
|
+
* @param serviceMap
|
|
351
|
+
* @param content
|
|
352
|
+
* @param buffer
|
|
353
|
+
* @param offset
|
|
354
|
+
* @param length
|
|
355
|
+
* @returns {*}
|
|
356
|
+
* @private
|
|
357
|
+
*/
|
|
358
|
+
_processServiceRequest(serviceMap, content, buffer, offset, length) {
|
|
359
|
+
let result;
|
|
360
|
+
|
|
361
|
+
const sender = content.header.sender;
|
|
362
|
+
if (sender.address === LOCALHOST_INTERFACES_IPV4) {
|
|
363
|
+
debug('Received and skipped localhost service request:', content.service);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const name = serviceMap[content.service];
|
|
368
|
+
if (!name) {
|
|
369
|
+
debug('Received unsupported service request:', content.service);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const id = content.invokeId ? '@' + content.invokeId : '';
|
|
374
|
+
trace(`Received service request${id}:`, name);
|
|
375
|
+
|
|
376
|
+
// Find a function to decode the packet.
|
|
377
|
+
const serviceHandler = baServices[name];
|
|
378
|
+
if (serviceHandler) {
|
|
379
|
+
try {
|
|
380
|
+
content.payload = serviceHandler.decode(buffer, offset, length);
|
|
381
|
+
trace(`Handled service request${id}:`, name, JSON.stringify(content));
|
|
382
|
+
} catch (e) {
|
|
383
|
+
// Sometimes incomplete or corrupted messages will cause exceptions
|
|
384
|
+
// during decoding, but we don't want these to terminate the program, so
|
|
385
|
+
// we'll just log them and ignore them.
|
|
386
|
+
debug('Exception thrown when processing message:', e);
|
|
387
|
+
debug('Original message was', name + ':', content);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (!content.payload) {
|
|
391
|
+
return debug('Received invalid', name, 'message');
|
|
392
|
+
}
|
|
393
|
+
} else {
|
|
394
|
+
debug('No serviceHandler defined for:', name);
|
|
395
|
+
// Call the callback anyway, just with no payload.
|
|
396
|
+
}
|
|
397
|
+
//trace('Passing payload over to callback:', content);
|
|
398
|
+
|
|
399
|
+
// Call the user code, if they've defined a callback.
|
|
400
|
+
if (this.listenerCount(name)) {
|
|
401
|
+
trace('listener count by name emits ' + name + ' with content');
|
|
402
|
+
this.emit(name, content);
|
|
403
|
+
} else {
|
|
404
|
+
if (this.listenerCount('unhandledEvent')) {
|
|
405
|
+
trace('unhandled event emiting with content');
|
|
406
|
+
this.emit('unhandledEvent', content);
|
|
407
|
+
} else {
|
|
408
|
+
// No 'unhandled event' handler, so respond with an error ourselves.
|
|
409
|
+
// This is better than doing nothing, which can often make the other
|
|
410
|
+
// device think we have gone offline.
|
|
411
|
+
trace('no unhandled event handler with header: ' + JSON.stringify(content.header));
|
|
412
|
+
if (content.header.expectingReply) {
|
|
413
|
+
debug('Replying with error for unhandled service:', name);
|
|
414
|
+
// Make sure we don't reply pretending to be the caller, if we got a
|
|
415
|
+
// forwarded message! Really this should be overridden to be your
|
|
416
|
+
// own IP, but only if it's not null/undefined to begin with.
|
|
417
|
+
content.header.sender.forwardedFrom = null;
|
|
418
|
+
this.errorResponse(
|
|
419
|
+
content.header.sender,
|
|
420
|
+
content.service,
|
|
421
|
+
content.invokeId,
|
|
422
|
+
baEnum.ErrorClass.SERVICES,
|
|
423
|
+
baEnum.ErrorCode.REJECT_UNRECOGNIZED_SERVICE
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
*
|
|
432
|
+
* @param buffer
|
|
433
|
+
* @param offset
|
|
434
|
+
* @param length
|
|
435
|
+
* @param header
|
|
436
|
+
* @private
|
|
437
|
+
*/
|
|
438
|
+
_handlePdu(buffer, offset, length, header) {
|
|
439
|
+
let msg;
|
|
440
|
+
trace('handlePdu Header: ', header);
|
|
441
|
+
// Handle different PDU types
|
|
442
|
+
switch (header.apduType & baEnum.PDU_TYPE_MASK) {
|
|
443
|
+
case baEnum.PduType.UNCONFIRMED_REQUEST:
|
|
444
|
+
msg = baApdu.decodeUnconfirmedServiceRequest(buffer, offset);
|
|
445
|
+
msg.header = header;
|
|
446
|
+
msg.header.confirmedService = false;
|
|
447
|
+
this._processServiceRequest(unconfirmedServiceMap, msg, buffer, offset + msg.len, length - msg.len);
|
|
448
|
+
break;
|
|
449
|
+
case baEnum.PduType.SIMPLE_ACK:
|
|
450
|
+
msg = baApdu.decodeSimpleAck(buffer, offset);
|
|
451
|
+
offset += msg.len;
|
|
452
|
+
length -= msg.len;
|
|
453
|
+
this._invokeCallback(msg.invokeId, null, {msg: msg, buffer: buffer, offset: offset + msg.len, length: length - msg.len});
|
|
454
|
+
break;
|
|
455
|
+
case baEnum.PduType.COMPLEX_ACK:
|
|
456
|
+
msg = baApdu.decodeComplexAck(buffer, offset);
|
|
457
|
+
msg.header = header;
|
|
458
|
+
if ((header.apduType & baEnum.PduConReqBit.SEGMENTED_MESSAGE) === 0) {
|
|
459
|
+
this._invokeCallback(msg.invokeId, null, {msg: msg, buffer: buffer, offset: offset + msg.len, length: length - msg.len});
|
|
460
|
+
} else {
|
|
461
|
+
this._processSegment(msg, true, buffer, offset + msg.len, length - msg.len);
|
|
462
|
+
}
|
|
463
|
+
break;
|
|
464
|
+
case baEnum.PduType.SEGMENT_ACK:
|
|
465
|
+
msg = baApdu.decodeSegmentAck(buffer, offset);
|
|
466
|
+
msg.header = header;
|
|
467
|
+
this._processSegment(msg, true, buffer, offset + msg.len, length - msg.len);
|
|
468
|
+
break;
|
|
469
|
+
case baEnum.PduType.ERROR:
|
|
470
|
+
msg = baApdu.decodeError(buffer, offset);
|
|
471
|
+
this._processError(msg.invokeId, buffer, offset + msg.len, length - msg.len);
|
|
472
|
+
break;
|
|
473
|
+
case baEnum.PduType.REJECT:
|
|
474
|
+
case baEnum.PduType.ABORT:
|
|
475
|
+
msg = baApdu.decodeAbort(buffer, offset);
|
|
476
|
+
this._processAbort(msg.invokeId, msg.reason);
|
|
477
|
+
break;
|
|
478
|
+
case baEnum.PduType.CONFIRMED_REQUEST:
|
|
479
|
+
msg = baApdu.decodeConfirmedServiceRequest(buffer, offset);
|
|
480
|
+
msg.header = header;
|
|
481
|
+
msg.header.confirmedService = true;
|
|
482
|
+
if ((header.apduType & baEnum.PduConReqBit.SEGMENTED_MESSAGE) === 0) {
|
|
483
|
+
this._processServiceRequest(confirmedServiceMap, msg, buffer, offset + msg.len, length - msg.len);
|
|
484
|
+
} else {
|
|
485
|
+
this._processSegment(msg, true, buffer, offset + msg.len, length - msg.len);
|
|
486
|
+
}
|
|
487
|
+
break;
|
|
488
|
+
default:
|
|
489
|
+
debug(`Received unknown PDU type ${header.apduType} -> Drop packet`);
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
*
|
|
496
|
+
* @param buffer
|
|
497
|
+
* @param offset
|
|
498
|
+
* @param msgLength
|
|
499
|
+
* @param header
|
|
500
|
+
* @returns {*}
|
|
501
|
+
* @private
|
|
502
|
+
*/
|
|
503
|
+
_handleNpdu(buffer, offset, msgLength, header) {
|
|
504
|
+
// Check data length
|
|
505
|
+
if (msgLength <= 0) {
|
|
506
|
+
return trace('No NPDU data -> Drop package');
|
|
507
|
+
}
|
|
508
|
+
// Parse baNpdu header
|
|
509
|
+
const result = baNpdu.decode(buffer, offset);
|
|
510
|
+
if (!result) {
|
|
511
|
+
return trace('Received invalid NPDU header -> Drop package');
|
|
512
|
+
}
|
|
513
|
+
if (result.funct & baEnum.NpduControlBit.NETWORK_LAYER_MESSAGE) {
|
|
514
|
+
return trace('Received network layer message -> Drop package');
|
|
515
|
+
}
|
|
516
|
+
offset += result.len;
|
|
517
|
+
msgLength -= result.len;
|
|
518
|
+
if (msgLength <= 0) {
|
|
519
|
+
return trace('No APDU data -> Drop package');
|
|
520
|
+
}
|
|
521
|
+
header.apduType = baApdu.getDecodedType(buffer, offset);
|
|
522
|
+
header.expectingReply = !!(result.funct & baEnum.NpduControlBit.EXPECTING_REPLY);
|
|
523
|
+
this._handlePdu(buffer, offset, msgLength, header);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
*
|
|
528
|
+
* @param buffer
|
|
529
|
+
* @param remoteAddress
|
|
530
|
+
* @returns {*}
|
|
531
|
+
* @private
|
|
532
|
+
*/
|
|
533
|
+
_receiveData(buffer, remoteAddress) {
|
|
534
|
+
// Check data length
|
|
535
|
+
if (buffer.length < baEnum.BVLC_HEADER_LENGTH) {
|
|
536
|
+
return trace('Received invalid data -> Drop package');
|
|
537
|
+
}
|
|
538
|
+
// Parse BVLC header
|
|
539
|
+
const result = baBvlc.decode(buffer, 0);
|
|
540
|
+
if (!result) {
|
|
541
|
+
return trace('Received invalid BVLC header -> Drop package');
|
|
542
|
+
}
|
|
543
|
+
let header = {
|
|
544
|
+
// Which function the packet came in on, so later code can distinguish
|
|
545
|
+
// between ORIGINAL_BROADCAST_NPDU and DISTRIBUTE_BROADCAST_TO_NETWORK.
|
|
546
|
+
func: result.func,
|
|
547
|
+
sender: {
|
|
548
|
+
// Address of the host we are directly connected to. String, IP:port.
|
|
549
|
+
address: remoteAddress,
|
|
550
|
+
// If the host is a BBMD passing messages along to another node, this
|
|
551
|
+
// is the address of the distant BACnet node. String, IP:port.
|
|
552
|
+
// Typically we won't have network connectivity to this address, but
|
|
553
|
+
// we have to include it in replies so the host we are connect to knows
|
|
554
|
+
// where to forward the messages.
|
|
555
|
+
forwardedFrom: null,
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
// Check BVLC function
|
|
559
|
+
switch (result.func) {
|
|
560
|
+
case baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU:
|
|
561
|
+
case baEnum.BvlcResultPurpose.ORIGINAL_BROADCAST_NPDU:
|
|
562
|
+
this._handleNpdu(buffer, result.len, buffer.length - result.len, header);
|
|
563
|
+
break;
|
|
564
|
+
case baEnum.BvlcResultPurpose.FORWARDED_NPDU:
|
|
565
|
+
// Preserve the IP of the node behind the BBMD so we know where to send
|
|
566
|
+
// replies back to.
|
|
567
|
+
header.sender.forwardedFrom = result.originatingIP;
|
|
568
|
+
this._handleNpdu(buffer, result.len, buffer.length - result.len, header);
|
|
569
|
+
break;
|
|
570
|
+
case baEnum.BvlcResultPurpose.REGISTER_FOREIGN_DEVICE:
|
|
571
|
+
let decodeResult = baServices.registerForeignDevice.decode(buffer, result.len, buffer.length - result.len);
|
|
572
|
+
if (!decodeResult) {
|
|
573
|
+
return trace('Received invalid registerForeignDevice message');
|
|
574
|
+
}
|
|
575
|
+
this.emit('registerForeignDevice', {
|
|
576
|
+
header: header,
|
|
577
|
+
payload: decodeResult,
|
|
578
|
+
});
|
|
579
|
+
break;
|
|
580
|
+
case baEnum.BvlcResultPurpose.DISTRIBUTE_BROADCAST_TO_NETWORK:
|
|
581
|
+
this._handleNpdu(buffer, result.len, buffer.length - result.len, header);
|
|
582
|
+
break;
|
|
583
|
+
default:
|
|
584
|
+
debug('Received unknown BVLC function ' + result.func + ' -> Drop package');
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* @event bacnet.error
|
|
591
|
+
* @param {error} err - The error object thrown by the underlying transport layer.
|
|
592
|
+
* @example
|
|
593
|
+
* const bacnet = require('node-bacnet');
|
|
594
|
+
* const client = new bacnet();
|
|
595
|
+
*
|
|
596
|
+
* client.on('error', (err) => {
|
|
597
|
+
* console.log('Error occurred: ', err);
|
|
598
|
+
* client.close();
|
|
599
|
+
* });
|
|
600
|
+
*/
|
|
601
|
+
_receiveError(err) {
|
|
602
|
+
this.emit('error', err);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* The whoIs command discovers all BACNET devices in a network.
|
|
607
|
+
* @function bacnet.whoIs
|
|
608
|
+
* @param {string} receiver - IP address of the target device.
|
|
609
|
+
* @param {object=} options
|
|
610
|
+
* @param {number=} options.lowLimit - Minimal device instance number to search for.
|
|
611
|
+
* @param {number=} options.highLimit - Maximal device instance number to search for.
|
|
612
|
+
* @fires bacnet.iAm
|
|
613
|
+
* @example
|
|
614
|
+
* const bacnet = require('node-bacnet');
|
|
615
|
+
* const client = new bacnet();
|
|
616
|
+
*
|
|
617
|
+
* client.whoIs();
|
|
618
|
+
*/
|
|
619
|
+
whoIs(receiver, options) {
|
|
620
|
+
if (!options) {
|
|
621
|
+
if (
|
|
622
|
+
receiver &&
|
|
623
|
+
typeof receiver === 'object' &&
|
|
624
|
+
receiver.address === undefined &&
|
|
625
|
+
receiver.forwardedFrom === undefined &&
|
|
626
|
+
(receiver.lowLimit !== undefined || receiver.highLimit !== undefined)
|
|
627
|
+
) {
|
|
628
|
+
// receiver seems to be an options object
|
|
629
|
+
options = receiver;
|
|
630
|
+
receiver = undefined;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
options = options || {};
|
|
634
|
+
|
|
635
|
+
const settings = {
|
|
636
|
+
lowLimit: options.lowLimit,
|
|
637
|
+
highLimit: options.highLimit,
|
|
638
|
+
};
|
|
639
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
640
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
|
|
641
|
+
baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduType.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.WHO_IS);
|
|
642
|
+
baServices.whoIs.encode(buffer, settings.lowLimit, settings.highLimit);
|
|
643
|
+
this.sendBvlc(receiver, buffer);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* The timeSync command sets the time of a target device.
|
|
648
|
+
* @function bacnet.timeSync
|
|
649
|
+
* @param {string} receiver - IP address of the target device.
|
|
650
|
+
* @param {date} dateTime - The date and time to set on the target device.
|
|
651
|
+
* @example
|
|
652
|
+
* const bacnet = require('node-bacnet');
|
|
653
|
+
* const client = new bacnet();
|
|
654
|
+
*
|
|
655
|
+
* client.timeSync('192.168.1.43', new Date());
|
|
656
|
+
*/
|
|
657
|
+
timeSync(receiver, dateTime) {
|
|
658
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
659
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
660
|
+
baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduType.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.TIME_SYNCHRONIZATION);
|
|
661
|
+
baServices.timeSync.encode(buffer, dateTime);
|
|
662
|
+
this.sendBvlc(receiver, buffer);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* The timeSyncUTC command sets the UTC time of a target device.
|
|
667
|
+
* @function bacnet.timeSyncUTC
|
|
668
|
+
* @param {string} receiver - IP address of the target device.
|
|
669
|
+
* @param {date} dateTime - The date and time to set on the target device.
|
|
670
|
+
* @example
|
|
671
|
+
* const bacnet = require('node-bacnet');
|
|
672
|
+
* const client = new bacnet();
|
|
673
|
+
*
|
|
674
|
+
* client.timeSyncUTC('192.168.1.43', new Date());
|
|
675
|
+
*/
|
|
676
|
+
timeSyncUTC(receiver, dateTime) {
|
|
677
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
678
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
679
|
+
baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduType.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.UTC_TIME_SYNCHRONIZATION);
|
|
680
|
+
baServices.timeSync.encode(buffer, dateTime);
|
|
681
|
+
this.sendBvlc(receiver, buffer);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* The readProperty command reads a single property of an object from a device.
|
|
686
|
+
* @function bacnet.readProperty
|
|
687
|
+
* @param {string} receiver - IP address of the target device.
|
|
688
|
+
* @param {object} objectId - The BACNET object ID to read.
|
|
689
|
+
* @param {number} objectId.type - The BACNET object type to read.
|
|
690
|
+
* @param {number} objectId.instance - The BACNET object instance to read.
|
|
691
|
+
* @param {number} propertyId - The BACNET property id in the specified object to read.
|
|
692
|
+
* @param {object=} options
|
|
693
|
+
* @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
|
|
694
|
+
* @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
|
|
695
|
+
* @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
|
|
696
|
+
* @param {number=} options.arrayIndex - The array index of the property to be read.
|
|
697
|
+
* @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
|
|
698
|
+
* @example
|
|
699
|
+
* const bacnet = require('node-bacnet');
|
|
700
|
+
* const client = new bacnet();
|
|
701
|
+
*
|
|
702
|
+
* client.readProperty('192.168.1.43', {type: 8, instance: 44301}, 28, (err, value) => {
|
|
703
|
+
* console.log('value: ', value);
|
|
704
|
+
* });
|
|
705
|
+
*/
|
|
706
|
+
readProperty(receiver, objectId, propertyId, options, next) {
|
|
707
|
+
next = next || options;
|
|
708
|
+
const settings = {
|
|
709
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
710
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
711
|
+
invokeId: options.invokeId || this._getInvokeId(),
|
|
712
|
+
arrayIndex: (options.arrayIndex || options.arrayIndex === 0) ? options.arrayIndex : baEnum.ASN1_ARRAY_ALL
|
|
713
|
+
};
|
|
714
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
715
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
|
|
716
|
+
const type = baEnum.PduType.CONFIRMED_REQUEST | (settings.maxSegments !== baEnum.MaxSegmentsAccepted.SEGMENTS_0 ? baEnum.PduConReqBit.SEGMENTED_RESPONSE_ACCEPTED : 0);
|
|
717
|
+
baApdu.encodeConfirmedServiceRequest(buffer, type, baEnum.ConfirmedServiceChoice.READ_PROPERTY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
718
|
+
baServices.readProperty.encode(buffer, objectId.type, objectId.instance, propertyId, settings.arrayIndex);
|
|
719
|
+
this.sendBvlc(receiver, buffer);
|
|
720
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
721
|
+
if (err) {
|
|
722
|
+
return void next(err);
|
|
723
|
+
}
|
|
724
|
+
const result = baServices.readProperty.decodeAcknowledge(data.buffer, data.offset, data.length);
|
|
725
|
+
if (!result) {
|
|
726
|
+
return void next(new Error('INVALID_DECODING'));
|
|
727
|
+
}
|
|
728
|
+
next(null, result);
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* The writeProperty command writes a single property of an object to a device.
|
|
734
|
+
* @function bacnet.writeProperty
|
|
735
|
+
* @param {string} receiver - IP address of the target device.
|
|
736
|
+
* @param {object} objectId - The BACNET object ID to write.
|
|
737
|
+
* @param {number} objectId.type - The BACNET object type to write.
|
|
738
|
+
* @param {number} objectId.instance - The BACNET object instance to write.
|
|
739
|
+
* @param {number} propertyId - The BACNET property id in the specified object to write.
|
|
740
|
+
* @param {object[]} values - A list of values to be written to the specified property.
|
|
741
|
+
* @param {ApplicationTag} values.type - The data-type of the value to be written.
|
|
742
|
+
* @param {number} values.value - The actual value to be written.
|
|
743
|
+
* @param {object=} options
|
|
744
|
+
* @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
|
|
745
|
+
* @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
|
|
746
|
+
* @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
|
|
747
|
+
* @param {number=} options.arrayIndex - The array index of the property to be read.
|
|
748
|
+
* @param {number=} options.priority - The priority of the value to be written.
|
|
749
|
+
* @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
|
|
750
|
+
* @example
|
|
751
|
+
* const bacnet = require('node-bacnet');
|
|
752
|
+
* const client = new bacnet();
|
|
753
|
+
*
|
|
754
|
+
* client.writeProperty('192.168.1.43', {type: 8, instance: 44301}, 28, [
|
|
755
|
+
* {type: bacnet.enum.ApplicationTag.REAL, value: 100}
|
|
756
|
+
* ], (err, value) => {
|
|
757
|
+
* console.log('value: ', value);
|
|
758
|
+
* });
|
|
759
|
+
*/
|
|
760
|
+
writeProperty(receiver, objectId, propertyId, values, options, next) {
|
|
761
|
+
next = next || options;
|
|
762
|
+
const settings = {
|
|
763
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
764
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
765
|
+
invokeId: options.invokeId || this._getInvokeId(),
|
|
766
|
+
arrayIndex: options.arrayIndex || baEnum.ASN1_ARRAY_ALL,
|
|
767
|
+
priority: options.priority || baEnum.ASN1_NO_PRIORITY
|
|
768
|
+
};
|
|
769
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
770
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
|
|
771
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.WRITE_PROPERTY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
772
|
+
baServices.writeProperty.encode(buffer, objectId.type, objectId.instance, propertyId, settings.arrayIndex, settings.priority, values);
|
|
773
|
+
this.sendBvlc(receiver, buffer);
|
|
774
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
775
|
+
next(err);
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* The readPropertyMultiple command reads multiple properties in multiple objects from a device.
|
|
781
|
+
* @function bacnet.readPropertyMultiple
|
|
782
|
+
* @param {string} receiver - IP address of the target device.
|
|
783
|
+
* @param {object[]} propertiesArray - List of object and property specifications to be read.
|
|
784
|
+
* @param {object} propertiesArray.objectId - Specifies which object to read.
|
|
785
|
+
* @param {number} propertiesArray.objectId.type - The BACNET object type to read.
|
|
786
|
+
* @param {number} propertiesArray.objectId.instance - The BACNET object instance to read.
|
|
787
|
+
* @param {object[]} propertiesArray.properties - List of properties to be read.
|
|
788
|
+
* @param {number} propertiesArray.properties.id - The BACNET property id in the specified object to read. Also supports 8 for all properties.
|
|
789
|
+
* @param {object=} options
|
|
790
|
+
* @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
|
|
791
|
+
* @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
|
|
792
|
+
* @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
|
|
793
|
+
* @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
|
|
794
|
+
* @example
|
|
795
|
+
* const bacnet = require('node-bacnet');
|
|
796
|
+
* const client = new bacnet();
|
|
797
|
+
*
|
|
798
|
+
* const requestArray = [
|
|
799
|
+
* {objectId: {type: 8, instance: 4194303}, properties: [{id: 8}]}
|
|
800
|
+
* ];
|
|
801
|
+
* client.readPropertyMultiple('192.168.1.43', requestArray, (err, value) => {
|
|
802
|
+
* console.log('value: ', value);
|
|
803
|
+
* });
|
|
804
|
+
*/
|
|
805
|
+
readPropertyMultiple(receiver, propertiesArray, options, next) {
|
|
806
|
+
next = next || options;
|
|
807
|
+
const settings = {
|
|
808
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
809
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
810
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
811
|
+
};
|
|
812
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
813
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
|
|
814
|
+
const type = baEnum.PduType.CONFIRMED_REQUEST | (settings.maxSegments !== baEnum.MaxSegmentsAccepted.SEGMENTS_0 ? baEnum.PduConReqBit.SEGMENTED_RESPONSE_ACCEPTED : 0);
|
|
815
|
+
baApdu.encodeConfirmedServiceRequest(buffer, type, baEnum.ConfirmedServiceChoice.READ_PROPERTY_MULTIPLE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
816
|
+
baServices.readPropertyMultiple.encode(buffer, propertiesArray);
|
|
817
|
+
this.sendBvlc(receiver, buffer);
|
|
818
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
819
|
+
if (err) {
|
|
820
|
+
return void next(err);
|
|
821
|
+
}
|
|
822
|
+
const result = baServices.readPropertyMultiple.decodeAcknowledge(data.buffer, data.offset, data.length);
|
|
823
|
+
if (!result) {
|
|
824
|
+
return void next(new Error('INVALID_DECODING'));
|
|
825
|
+
}
|
|
826
|
+
next(null, result);
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* The writePropertyMultiple command writes multiple properties in multiple objects to a device.
|
|
832
|
+
* @function bacnet.writePropertyMultiple
|
|
833
|
+
* @param {string} receiver - IP address of the target device.
|
|
834
|
+
* @param {object[]} values - List of object and property specifications to be written.
|
|
835
|
+
* @param {object} values.objectId - Specifies which object to read.
|
|
836
|
+
* @param {number} values.objectId.type - The BACNET object type to read.
|
|
837
|
+
* @param {number} values.objectId.instance - The BACNET object instance to read.
|
|
838
|
+
* @param {object[]} values.values - List of properties to be written.
|
|
839
|
+
* @param {object} values.values.property - Property specifications to be written.
|
|
840
|
+
* @param {number} values.values.property.id - The BACNET property id in the specified object to write.
|
|
841
|
+
* @param {number} values.values.property.index - The array index of the property to be written.
|
|
842
|
+
* @param {object[]} values.values.value - A list of values to be written to the specified property.
|
|
843
|
+
* @param {ApplicationTag} values.values.value.type - The data-type of the value to be written.
|
|
844
|
+
* @param {object} values.values.value.value - The actual value to be written.
|
|
845
|
+
* @param {number} values.values.priority - The priority to be used for writing to the property.
|
|
846
|
+
* @param {object=} options
|
|
847
|
+
* @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
|
|
848
|
+
* @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
|
|
849
|
+
* @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
|
|
850
|
+
* @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
|
|
851
|
+
* @example
|
|
852
|
+
* const bacnet = require('node-bacnet');
|
|
853
|
+
* const client = new bacnet();
|
|
854
|
+
*
|
|
855
|
+
* const values = [
|
|
856
|
+
* {objectId: {type: 8, instance: 44301}, values: [
|
|
857
|
+
* {property: {id: 28, index: 12}, value: [{type: bacnet.enum.ApplicationTag.BOOLEAN, value: true}], priority: 8}
|
|
858
|
+
* ]}
|
|
859
|
+
* ];
|
|
860
|
+
* client.writePropertyMultiple('192.168.1.43', values, (err, value) => {
|
|
861
|
+
* console.log('value: ', value);
|
|
862
|
+
* });
|
|
863
|
+
*/
|
|
864
|
+
writePropertyMultiple(receiver, values, options, next) {
|
|
865
|
+
next = next || options;
|
|
866
|
+
const settings = {
|
|
867
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
868
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
869
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
870
|
+
};
|
|
871
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
872
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
873
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.WRITE_PROPERTY_MULTIPLE, settings.maxSegments, settings.maxApdu, settings.invokeId);
|
|
874
|
+
baServices.writePropertyMultiple.encodeObject(buffer, values);
|
|
875
|
+
this.sendBvlc(receiver, buffer);
|
|
876
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
877
|
+
next(err);
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* The confirmedCOVNotification command is used to push notifications to other
|
|
883
|
+
* systems that have registered with us via a subscribeCOV message.
|
|
884
|
+
* @function bacnet.confirmedCOVNotification
|
|
885
|
+
* @param {string} receiver - IP address of the target device.
|
|
886
|
+
* @param {object} monitoredObject - The object being monitored, from subscribeCOV.
|
|
887
|
+
* @param {number} monitoredObject.type - Object type.
|
|
888
|
+
* @param {number} monitoredObject.instance - Object instance.
|
|
889
|
+
* @param {number} subscribeId - Subscriber ID from subscribeCOV,
|
|
890
|
+
* @param {number} initiatingDeviceId - Our BACnet device ID.
|
|
891
|
+
* @param {number} lifetime - Number of seconds left until the subscription expires.
|
|
892
|
+
* @param {array} values - values for the monitored object. See example.
|
|
893
|
+
* @param {object=} options
|
|
894
|
+
* @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
|
|
895
|
+
* @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
|
|
896
|
+
* @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
|
|
897
|
+
* @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
|
|
898
|
+
* @example
|
|
899
|
+
* const bacnet = require('node-bacnet');
|
|
900
|
+
* const client = new bacnet();
|
|
901
|
+
*
|
|
902
|
+
* const settings = {deviceId: 123}; // our BACnet device
|
|
903
|
+
*
|
|
904
|
+
* // Items saved from subscribeCOV message
|
|
905
|
+
* const monitoredObject = {type: 1, instance: 1};
|
|
906
|
+
* const subscriberProcessId = 123;
|
|
907
|
+
*
|
|
908
|
+
* client.confirmedCOVNotification(
|
|
909
|
+
* '192.168.1.43',
|
|
910
|
+
* monitoredObject,
|
|
911
|
+
* subscriberProcessId,
|
|
912
|
+
* settings.deviceId,
|
|
913
|
+
* 30, // should be lifetime of subscription really
|
|
914
|
+
* [
|
|
915
|
+
* {
|
|
916
|
+
* property: { id: bacnet.enum.PropertyIdentifier.PRESENT_VALUE },
|
|
917
|
+
* value: [
|
|
918
|
+
* {value: 123, type: bacnet.enum.ApplicationTag.REAL},
|
|
919
|
+
* ],
|
|
920
|
+
* },
|
|
921
|
+
* ],
|
|
922
|
+
* (err) => {
|
|
923
|
+
* console.log('error: ', err);
|
|
924
|
+
* }
|
|
925
|
+
* );
|
|
926
|
+
*/
|
|
927
|
+
confirmedCOVNotification(receiver, monitoredObject, subscribeId, initiatingDeviceId, lifetime, values, options, next) {
|
|
928
|
+
next = next || options;
|
|
929
|
+
const settings = {
|
|
930
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
931
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
932
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
933
|
+
};
|
|
934
|
+
const buffer = this._getBuffer();
|
|
935
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
936
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CONFIRMED_COV_NOTIFICATION, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
937
|
+
baServices.covNotify.encode(buffer, subscribeId, initiatingDeviceId, monitoredObject, lifetime, values);
|
|
938
|
+
baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
|
|
939
|
+
this.sendBvlc(receiver, buffer);
|
|
940
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
941
|
+
if (err) {
|
|
942
|
+
return void next(err);
|
|
943
|
+
}
|
|
944
|
+
next();
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* The deviceCommunicationControl command enables or disables network communication of the target device.
|
|
950
|
+
* @function bacnet.deviceCommunicationControl
|
|
951
|
+
* @param {string} receiver - IP address of the target device.
|
|
952
|
+
* @param {number} timeDuration - The time to hold the network communication state in seconds. 0 for infinite.
|
|
953
|
+
* @param {EnableDisable} enableDisable - The network communication state to set.
|
|
954
|
+
* @param {object=} options
|
|
955
|
+
* @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
|
|
956
|
+
* @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
|
|
957
|
+
* @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
|
|
958
|
+
* @param {string=} options.password - The optional password used to set the network communication state.
|
|
959
|
+
* @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
|
|
960
|
+
* @example
|
|
961
|
+
* const bacnet = require('node-bacnet');
|
|
962
|
+
* const client = new bacnet();
|
|
963
|
+
*
|
|
964
|
+
* client.deviceCommunicationControl('192.168.1.43', 0, bacnet.enum.EnableDisable.DISABLE, (err) => {
|
|
965
|
+
* console.log('error: ', err);
|
|
966
|
+
* });
|
|
967
|
+
*/
|
|
968
|
+
deviceCommunicationControl(receiver, timeDuration, enableDisable, options, next) {
|
|
969
|
+
next = next || options;
|
|
970
|
+
const settings = {
|
|
971
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
972
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
973
|
+
invokeId: options.invokeId || this._getInvokeId(),
|
|
974
|
+
password: options.password
|
|
975
|
+
};
|
|
976
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
977
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
978
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.DEVICE_COMMUNICATION_CONTROL, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
979
|
+
baServices.deviceCommunicationControl.encode(buffer, timeDuration, enableDisable, settings.password);
|
|
980
|
+
this.sendBvlc(receiver, buffer);
|
|
981
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
982
|
+
next(err);
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* The reinitializeDevice command initiates a restart of the target device.
|
|
988
|
+
* @function bacnet.reinitializeDevice
|
|
989
|
+
* @param {string} receiver - IP address of the target device.
|
|
990
|
+
* @param {ReinitializedState} state - The type of restart to be initiated.
|
|
991
|
+
* @param {object=} options
|
|
992
|
+
* @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
|
|
993
|
+
* @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
|
|
994
|
+
* @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
|
|
995
|
+
* @param {string=} options.password - The optional password used to restart the device.
|
|
996
|
+
* @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
|
|
997
|
+
* @example
|
|
998
|
+
* const bacnet = require('node-bacnet');
|
|
999
|
+
* const client = new bacnet();
|
|
1000
|
+
*
|
|
1001
|
+
* client.reinitializeDevice('192.168.1.43', bacnet.enum.ReinitializedState.COLDSTART, (err, value) => {
|
|
1002
|
+
* console.log('value: ', value);
|
|
1003
|
+
* });
|
|
1004
|
+
*/
|
|
1005
|
+
reinitializeDevice(receiver, state, options, next) {
|
|
1006
|
+
next = next || options;
|
|
1007
|
+
const settings = {
|
|
1008
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1009
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1010
|
+
invokeId: options.invokeId || this._getInvokeId(),
|
|
1011
|
+
password: options.password
|
|
1012
|
+
};
|
|
1013
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1014
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1015
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.REINITIALIZE_DEVICE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1016
|
+
baServices.reinitializeDevice.encode(buffer, state, settings.password);
|
|
1017
|
+
this.sendBvlc(receiver, buffer);
|
|
1018
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1019
|
+
next(err);
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
*
|
|
1025
|
+
* @param receiver
|
|
1026
|
+
* @param objectId
|
|
1027
|
+
* @param position
|
|
1028
|
+
* @param fileBuffer
|
|
1029
|
+
* @param options
|
|
1030
|
+
* @param next
|
|
1031
|
+
*/
|
|
1032
|
+
writeFile(receiver, objectId, position, fileBuffer, options, next) {
|
|
1033
|
+
next = next || options;
|
|
1034
|
+
const settings = {
|
|
1035
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1036
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1037
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1038
|
+
};
|
|
1039
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1040
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1041
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ATOMIC_WRITE_FILE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1042
|
+
baServices.atomicWriteFile.encode(buffer, false, objectId, position, fileBuffer);
|
|
1043
|
+
this.sendBvlc(receiver, buffer);
|
|
1044
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1045
|
+
if (err) {
|
|
1046
|
+
return void next(err);
|
|
1047
|
+
}
|
|
1048
|
+
const result = baServices.atomicWriteFile.decodeAcknowledge(data.buffer, data.offset, data.length);
|
|
1049
|
+
if (!result) {
|
|
1050
|
+
return void next(new Error('INVALID_DECODING'));
|
|
1051
|
+
}
|
|
1052
|
+
next(null, result);
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
*
|
|
1058
|
+
* @param receiver
|
|
1059
|
+
* @param objectId
|
|
1060
|
+
* @param position
|
|
1061
|
+
* @param count
|
|
1062
|
+
* @param options
|
|
1063
|
+
* @param next
|
|
1064
|
+
*/
|
|
1065
|
+
readFile(receiver, objectId, position, count, options, next) {
|
|
1066
|
+
next = next || options;
|
|
1067
|
+
const settings = {
|
|
1068
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1069
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1070
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1071
|
+
};
|
|
1072
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1073
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1074
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ATOMIC_READ_FILE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1075
|
+
baServices.atomicReadFile.encode(buffer, true, objectId, position, count);
|
|
1076
|
+
this.sendBvlc(receiver, buffer);
|
|
1077
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1078
|
+
if (err) {
|
|
1079
|
+
return void next(err);
|
|
1080
|
+
}
|
|
1081
|
+
const result = baServices.atomicReadFile.decodeAcknowledge(data.buffer, data.offset, data.length);
|
|
1082
|
+
if (!result) {
|
|
1083
|
+
return void next(new Error('INVALID_DECODING'));
|
|
1084
|
+
}
|
|
1085
|
+
next(null, result);
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
*
|
|
1091
|
+
* @param receiver
|
|
1092
|
+
* @param objectId
|
|
1093
|
+
* @param idxBegin
|
|
1094
|
+
* @param quantity
|
|
1095
|
+
* @param options
|
|
1096
|
+
* @param next
|
|
1097
|
+
*/
|
|
1098
|
+
readRange(receiver, objectId, idxBegin, quantity, options, next) {
|
|
1099
|
+
next = next || options;
|
|
1100
|
+
const settings = {
|
|
1101
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1102
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1103
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1104
|
+
};
|
|
1105
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1106
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1107
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.READ_RANGE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1108
|
+
baServices.readRange.encode(buffer, objectId, baEnum.PropertyIdentifier.LOG_BUFFER, baEnum.ASN1_ARRAY_ALL, baEnum.ReadRangeType.BY_POSITION, idxBegin, new Date(), quantity);
|
|
1109
|
+
this.sendBvlc(receiver, buffer);
|
|
1110
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1111
|
+
if (err) {
|
|
1112
|
+
return void next(err);
|
|
1113
|
+
}
|
|
1114
|
+
const result = baServices.readRange.decodeAcknowledge(data.buffer, data.offset, data.length);
|
|
1115
|
+
if (!result) {
|
|
1116
|
+
return void next(new Error('INVALID_DECODING'));
|
|
1117
|
+
}
|
|
1118
|
+
next(null, result);
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
*
|
|
1124
|
+
* @param receiver
|
|
1125
|
+
* @param objectId
|
|
1126
|
+
* @param subscribeId
|
|
1127
|
+
* @param cancel
|
|
1128
|
+
* @param issueConfirmedNotifications
|
|
1129
|
+
* @param lifetime
|
|
1130
|
+
* @param options
|
|
1131
|
+
* @param next
|
|
1132
|
+
*/
|
|
1133
|
+
subscribeCov(receiver, objectId, subscribeId, cancel, issueConfirmedNotifications, lifetime, options, next) {
|
|
1134
|
+
next = next || options;
|
|
1135
|
+
const settings = {
|
|
1136
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1137
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1138
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1139
|
+
};
|
|
1140
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1141
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1142
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1143
|
+
baServices.subscribeCov.encode(buffer, subscribeId, objectId, cancel, issueConfirmedNotifications, lifetime);
|
|
1144
|
+
this.sendBvlc(receiver, buffer);
|
|
1145
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1146
|
+
if (err) {
|
|
1147
|
+
return void next(err);
|
|
1148
|
+
}
|
|
1149
|
+
next();
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
/**
|
|
1154
|
+
*
|
|
1155
|
+
* @param receiver
|
|
1156
|
+
* @param objectId
|
|
1157
|
+
* @param monitoredProperty
|
|
1158
|
+
* @param subscribeId
|
|
1159
|
+
* @param cancel
|
|
1160
|
+
* @param issueConfirmedNotifications
|
|
1161
|
+
* @param options
|
|
1162
|
+
* @param next
|
|
1163
|
+
*/
|
|
1164
|
+
subscribeProperty(receiver, objectId, monitoredProperty, subscribeId, cancel, issueConfirmedNotifications, options, next) {
|
|
1165
|
+
next = next || options;
|
|
1166
|
+
const settings = {
|
|
1167
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1168
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1169
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1170
|
+
};
|
|
1171
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1172
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1173
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV_PROPERTY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1174
|
+
baServices.subscribeProperty.encode(buffer, subscribeId, objectId, cancel, issueConfirmedNotifications, 0, monitoredProperty, false, 0x0f);
|
|
1175
|
+
this.sendBvlc(receiver, buffer);
|
|
1176
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1177
|
+
if (err) {
|
|
1178
|
+
return void next(err);
|
|
1179
|
+
}
|
|
1180
|
+
next();
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* The unconfirmedCOVNotification command sends an unconfirmed COV notification to a device
|
|
1186
|
+
* @function bacnet.unconfirmedCOVNotification
|
|
1187
|
+
* @param {string} receiver - IP address of the target device.
|
|
1188
|
+
* @param {number} subscriberProcessId - The process id which was used by a target device in the subscription.
|
|
1189
|
+
* @param {number} initiatingDeviceId - The id of this device.
|
|
1190
|
+
* @param {object} monitoredObjectId - Specifies about which object the notification is.
|
|
1191
|
+
* @param {number} monitoredObjectId.type - The BACNET object type of the notification.
|
|
1192
|
+
* @param {number} monitoredObjectId.instance - The BACNET object instance of the notification.
|
|
1193
|
+
* @param {number} timeRemaining - How long the subscription is still active in seconds.
|
|
1194
|
+
* @param {object[]} values - List of properties with updated values.
|
|
1195
|
+
* @param {object} values.property - Property specifications.
|
|
1196
|
+
* @param {number} values.property.id - The updated BACNET property id.
|
|
1197
|
+
* @param {number} values.property.index - The array index of the updated property.
|
|
1198
|
+
* @param {object[]} values.value - A list of updated values.
|
|
1199
|
+
* @param {ApplicationTag} values.value.type - The data-type of the updated value.
|
|
1200
|
+
* @param {object} values.value.value - The actual updated value.
|
|
1201
|
+
* @param {number} values.priority - The priority of the updated property.
|
|
1202
|
+
* @example
|
|
1203
|
+
* const bacnet = require('node-bacnet');
|
|
1204
|
+
* const client = new bacnet();
|
|
1205
|
+
*
|
|
1206
|
+
* client.unconfirmedCOVNotification(
|
|
1207
|
+
* '127.0.0.1',
|
|
1208
|
+
* 3,
|
|
1209
|
+
* 433,
|
|
1210
|
+
* {type: 2, instance: 122},
|
|
1211
|
+
* 120,
|
|
1212
|
+
* [
|
|
1213
|
+
* {
|
|
1214
|
+
* property: {id: 85},
|
|
1215
|
+
* value: [{type: baEnum.ApplicationTag.REAL, value: 12.3}]
|
|
1216
|
+
* },
|
|
1217
|
+
* {
|
|
1218
|
+
* property: {id: 111},
|
|
1219
|
+
* value: [{type: baEnum.ApplicationTag.BIT_STRING, value: 0xFFFF}]
|
|
1220
|
+
* }
|
|
1221
|
+
* ]);
|
|
1222
|
+
*/
|
|
1223
|
+
unconfirmedCOVNotification(receiver, subscriberProcessId, initiatingDeviceId, monitoredObjectId, timeRemaining, values) {
|
|
1224
|
+
const buffer = this._getBuffer();
|
|
1225
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
1226
|
+
baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduType.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.UNCONFIRMED_COV_NOTIFICATION);
|
|
1227
|
+
baServices.covNotify.encode(buffer, subscriberProcessId, initiatingDeviceId, monitoredObjectId, timeRemaining, values);
|
|
1228
|
+
baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
|
|
1229
|
+
this._transport.send(buffer.buffer, buffer.offset, receiver);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
*
|
|
1234
|
+
* @param receiver
|
|
1235
|
+
* @param objectId
|
|
1236
|
+
* @param values
|
|
1237
|
+
* @param options
|
|
1238
|
+
* @param next
|
|
1239
|
+
*/
|
|
1240
|
+
createObject(receiver, objectId, values, options, next) {
|
|
1241
|
+
next = next || options;
|
|
1242
|
+
const settings = {
|
|
1243
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1244
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1245
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1246
|
+
};
|
|
1247
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1248
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1249
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CREATE_OBJECT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1250
|
+
baServices.createObject.encode(buffer, objectId, values);
|
|
1251
|
+
this.sendBvlc(receiver, buffer);
|
|
1252
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1253
|
+
if (err) {
|
|
1254
|
+
return void next(err);
|
|
1255
|
+
}
|
|
1256
|
+
next();
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
*
|
|
1262
|
+
* @param receiver
|
|
1263
|
+
* @param objectId
|
|
1264
|
+
* @param options
|
|
1265
|
+
* @param next
|
|
1266
|
+
*/
|
|
1267
|
+
deleteObject(receiver, objectId, options, next) {
|
|
1268
|
+
next = next || options;
|
|
1269
|
+
const settings = {
|
|
1270
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1271
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1272
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1273
|
+
};
|
|
1274
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1275
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1276
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.DELETE_OBJECT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1277
|
+
baServices.deleteObject.encode(buffer, objectId);
|
|
1278
|
+
this.sendBvlc(receiver, buffer);
|
|
1279
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1280
|
+
if (err) {
|
|
1281
|
+
return void next(err);
|
|
1282
|
+
}
|
|
1283
|
+
next();
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
/**
|
|
1288
|
+
*
|
|
1289
|
+
* @param receiver
|
|
1290
|
+
* @param objectId
|
|
1291
|
+
* @param reference
|
|
1292
|
+
* @param values
|
|
1293
|
+
* @param options
|
|
1294
|
+
* @param next
|
|
1295
|
+
*/
|
|
1296
|
+
removeListElement(receiver, objectId, reference, values, options, next) {
|
|
1297
|
+
next = next || options;
|
|
1298
|
+
const settings = {
|
|
1299
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1300
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1301
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1302
|
+
};
|
|
1303
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1304
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1305
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.REMOVE_LIST_ELEMENT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1306
|
+
baServices.addListElement.encode(buffer, objectId, reference.id, reference.index, values);
|
|
1307
|
+
this.sendBvlc(receiver, buffer);
|
|
1308
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1309
|
+
if (err) {
|
|
1310
|
+
return void next(err);
|
|
1311
|
+
}
|
|
1312
|
+
next();
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
/**
|
|
1317
|
+
*
|
|
1318
|
+
* @param receiver
|
|
1319
|
+
* @param objectId
|
|
1320
|
+
* @param reference
|
|
1321
|
+
* @param values
|
|
1322
|
+
* @param options
|
|
1323
|
+
* @param next
|
|
1324
|
+
*/
|
|
1325
|
+
addListElement(receiver, objectId, reference, values, options, next) {
|
|
1326
|
+
next = next || options;
|
|
1327
|
+
const settings = {
|
|
1328
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1329
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1330
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1331
|
+
};
|
|
1332
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1333
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1334
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ADD_LIST_ELEMENT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1335
|
+
baServices.addListElement.encode(buffer, objectId, reference.id, reference.index, values);
|
|
1336
|
+
this.sendBvlc(receiver, buffer);
|
|
1337
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1338
|
+
if (err) {
|
|
1339
|
+
return void next(err);
|
|
1340
|
+
}
|
|
1341
|
+
next();
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/**
|
|
1346
|
+
*
|
|
1347
|
+
* @param receiver
|
|
1348
|
+
* @param options
|
|
1349
|
+
* @param next
|
|
1350
|
+
*/
|
|
1351
|
+
getAlarmSummary(receiver, options, next) {
|
|
1352
|
+
next = next || options;
|
|
1353
|
+
const settings = {
|
|
1354
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1355
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1356
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1357
|
+
};
|
|
1358
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1359
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1360
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.GET_ALARM_SUMMARY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1361
|
+
this.sendBvlc(receiver, buffer);
|
|
1362
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1363
|
+
if (err) {
|
|
1364
|
+
return void next(err);
|
|
1365
|
+
}
|
|
1366
|
+
const result = baServices.alarmSummary.decode(data.buffer, data.offset, data.length);
|
|
1367
|
+
if (!result) {
|
|
1368
|
+
return void next(new Error('INVALID_DECODING'));
|
|
1369
|
+
}
|
|
1370
|
+
next(null, result);
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
*
|
|
1376
|
+
* @param receiver
|
|
1377
|
+
* @param objectId
|
|
1378
|
+
* @param options
|
|
1379
|
+
* @param next
|
|
1380
|
+
*/
|
|
1381
|
+
getEventInformation(receiver, objectId, options, next) {
|
|
1382
|
+
next = next || options;
|
|
1383
|
+
const settings = {
|
|
1384
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1385
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1386
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1387
|
+
};
|
|
1388
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1389
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1390
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.GET_EVENT_INFORMATION, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1391
|
+
baAsn1.encodeContextObjectId(buffer, 0, objectId.type, objectId.instance);
|
|
1392
|
+
this.sendBvlc(receiver, buffer);
|
|
1393
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1394
|
+
if (err) {
|
|
1395
|
+
return void next(err);
|
|
1396
|
+
}
|
|
1397
|
+
const result = baServices.eventInformation.decode(data.buffer, data.offset, data.length);
|
|
1398
|
+
if (!result) {
|
|
1399
|
+
return void next(new Error('INVALID_DECODING'));
|
|
1400
|
+
}
|
|
1401
|
+
next(null, result);
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
*
|
|
1407
|
+
* @param receiver
|
|
1408
|
+
* @param objectId
|
|
1409
|
+
* @param eventState
|
|
1410
|
+
* @param ackText
|
|
1411
|
+
* @param evTimeStamp
|
|
1412
|
+
* @param ackTimeStamp
|
|
1413
|
+
* @param options
|
|
1414
|
+
* @param next
|
|
1415
|
+
*/
|
|
1416
|
+
acknowledgeAlarm(receiver, objectId, eventState, ackText, evTimeStamp, ackTimeStamp, options, next) {
|
|
1417
|
+
next = next || options;
|
|
1418
|
+
const settings = {
|
|
1419
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1420
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1421
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1422
|
+
};
|
|
1423
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1424
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1425
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ACKNOWLEDGE_ALARM, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1426
|
+
baServices.alarmAcknowledge.encode(buffer, 57, objectId, eventState, ackText, evTimeStamp, ackTimeStamp);
|
|
1427
|
+
this.sendBvlc(receiver, buffer);
|
|
1428
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1429
|
+
if (err) {
|
|
1430
|
+
return void next(err);
|
|
1431
|
+
}
|
|
1432
|
+
next();
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
/**
|
|
1437
|
+
*
|
|
1438
|
+
* @param receiver
|
|
1439
|
+
* @param vendorId
|
|
1440
|
+
* @param serviceNumber
|
|
1441
|
+
* @param data
|
|
1442
|
+
* @param options
|
|
1443
|
+
* @param next
|
|
1444
|
+
*/
|
|
1445
|
+
confirmedPrivateTransfer(receiver, vendorId, serviceNumber, data, options, next) {
|
|
1446
|
+
next = next || options;
|
|
1447
|
+
const settings = {
|
|
1448
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1449
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1450
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1451
|
+
};
|
|
1452
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1453
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1454
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CONFIRMED_PRIVATE_TRANSFER, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1455
|
+
baServices.privateTransfer.encode(buffer, vendorId, serviceNumber, data);
|
|
1456
|
+
this.sendBvlc(receiver, buffer);
|
|
1457
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1458
|
+
if (err) {
|
|
1459
|
+
return void next(err);
|
|
1460
|
+
}
|
|
1461
|
+
next();
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/**
|
|
1466
|
+
*
|
|
1467
|
+
* @param receiver
|
|
1468
|
+
* @param vendorId
|
|
1469
|
+
* @param serviceNumber
|
|
1470
|
+
* @param data
|
|
1471
|
+
*/
|
|
1472
|
+
unconfirmedPrivateTransfer(receiver, vendorId, serviceNumber, data) {
|
|
1473
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1474
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
1475
|
+
baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduType.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.UNCONFIRMED_PRIVATE_TRANSFER);
|
|
1476
|
+
baServices.privateTransfer.encode(buffer, vendorId, serviceNumber, data);
|
|
1477
|
+
this.sendBvlc(receiver, buffer);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
/**
|
|
1481
|
+
*
|
|
1482
|
+
* @param receiver
|
|
1483
|
+
* @param acknowledgmentFilter
|
|
1484
|
+
* @param options
|
|
1485
|
+
* @param next
|
|
1486
|
+
*/
|
|
1487
|
+
getEnrollmentSummary(receiver, acknowledgmentFilter, options, next) {
|
|
1488
|
+
next = next || options;
|
|
1489
|
+
const settings = {
|
|
1490
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1491
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1492
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1493
|
+
};
|
|
1494
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1495
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1496
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.GET_ENROLLMENT_SUMMARY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1497
|
+
baServices.getEnrollmentSummary.encode(buffer, acknowledgmentFilter, options.enrollmentFilter, options.eventStateFilter, options.eventTypeFilter, options.priorityFilter, options.notificationClassFilter);
|
|
1498
|
+
this.sendBvlc(receiver, buffer);
|
|
1499
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1500
|
+
if (err) {
|
|
1501
|
+
return void next(err);
|
|
1502
|
+
}
|
|
1503
|
+
const result = baServices.getEnrollmentSummary.decodeAcknowledge(data.buffer, data.offset, data.length);
|
|
1504
|
+
if (!result) {
|
|
1505
|
+
return void next(new Error('INVALID_DECODING'));
|
|
1506
|
+
}
|
|
1507
|
+
next(null, result);
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
*
|
|
1513
|
+
* @param receiver
|
|
1514
|
+
* @param eventNotification
|
|
1515
|
+
*/
|
|
1516
|
+
unconfirmedEventNotification(receiver, eventNotification) {
|
|
1517
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1518
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
1519
|
+
baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduType.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.UNCONFIRMED_EVENT_NOTIFICATION);
|
|
1520
|
+
baServices.eventNotifyData.encode(buffer, eventNotification);
|
|
1521
|
+
this.sendBvlc(receiver, buffer);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
/**
|
|
1525
|
+
*
|
|
1526
|
+
* @param receiver
|
|
1527
|
+
* @param eventNotification
|
|
1528
|
+
* @param options
|
|
1529
|
+
* @param next
|
|
1530
|
+
*/
|
|
1531
|
+
confirmedEventNotification(receiver, eventNotification, options, next) {
|
|
1532
|
+
next = next || options;
|
|
1533
|
+
const settings = {
|
|
1534
|
+
maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
|
|
1535
|
+
maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
|
|
1536
|
+
invokeId: options.invokeId || this._getInvokeId()
|
|
1537
|
+
};
|
|
1538
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1539
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBit.EXPECTING_REPLY, receiver);
|
|
1540
|
+
baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduType.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CONFIRMED_EVENT_NOTIFICATION, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
|
|
1541
|
+
baServices.eventNotifyData.encode(buffer, eventNotification);
|
|
1542
|
+
this.sendBvlc(receiver, buffer);
|
|
1543
|
+
this._addCallback(settings.invokeId, (err, data) => {
|
|
1544
|
+
if (err) {
|
|
1545
|
+
return void next(err);
|
|
1546
|
+
}
|
|
1547
|
+
next();
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
/**
|
|
1552
|
+
* The readPropertyResponse call sends a response with information about one of our properties.
|
|
1553
|
+
* @function bacnet.readPropertyResponse
|
|
1554
|
+
* @param {string} receiver - IP address of the target device.
|
|
1555
|
+
* @param {number} invokeId - ID of the original readProperty request.
|
|
1556
|
+
* @param {object} objectId - objectId from the original request,
|
|
1557
|
+
* @param {object} property - property being read, taken from the original request.
|
|
1558
|
+
* @param {object=} options varying behaviour for special circumstances
|
|
1559
|
+
* @param {string=} options.forwardedFrom - If functioning as a BBMD, the IP address this message originally came from.
|
|
1560
|
+
*/
|
|
1561
|
+
readPropertyResponse(receiver, invokeId, objectId, property, value, options = {}) {
|
|
1562
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1563
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
1564
|
+
baApdu.encodeComplexAck(buffer, baEnum.PduType.COMPLEX_ACK, baEnum.ConfirmedServiceChoice.READ_PROPERTY, invokeId);
|
|
1565
|
+
baServices.readProperty.encodeAcknowledge(buffer, objectId, property.id, property.index, value);
|
|
1566
|
+
this.sendBvlc(receiver, buffer);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
readPropertyMultipleResponse(receiver, invokeId, values) {
|
|
1570
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1571
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
1572
|
+
baApdu.encodeComplexAck(buffer, baEnum.PduType.COMPLEX_ACK, baEnum.ConfirmedServiceChoice.READ_PROPERTY_MULTIPLE, invokeId);
|
|
1573
|
+
baServices.readPropertyMultiple.encodeAcknowledge(buffer, values);
|
|
1574
|
+
this.sendBvlc(receiver, buffer);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
/**
|
|
1578
|
+
* The iAmResponse command is sent as a reply to a whoIs request.
|
|
1579
|
+
* @function bacnet.iAmResponse
|
|
1580
|
+
* @param {object} receiver - address to send packet to, null for local broadcast.
|
|
1581
|
+
* @param {number} deviceId - Our device ID.
|
|
1582
|
+
* @param {number} segmentation - an enum.Segmentation value.
|
|
1583
|
+
* @param {number} vendorId - The numeric ID assigned to the organisation providing this application.
|
|
1584
|
+
*/
|
|
1585
|
+
iAmResponse(receiver, deviceId, segmentation, vendorId) {
|
|
1586
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1587
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
1588
|
+
baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduType.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.I_AM);
|
|
1589
|
+
baServices.iAm.encode(buffer, deviceId, this._transport.getMaxPayload(), segmentation, vendorId);
|
|
1590
|
+
this.sendBvlc(receiver, buffer);
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
/**
|
|
1594
|
+
*
|
|
1595
|
+
* @param receiver
|
|
1596
|
+
* @param deviceId
|
|
1597
|
+
* @param objectId
|
|
1598
|
+
* @param objectName
|
|
1599
|
+
*/
|
|
1600
|
+
iHaveResponse(receiver, deviceId, objectId, objectName) {
|
|
1601
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1602
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
1603
|
+
baApdu.EecodeUnconfirmedServiceRequest(buffer, baEnum.PduType.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.I_HAVE);
|
|
1604
|
+
baServices.iHave(buffer, deviceId, objectId, objectName);
|
|
1605
|
+
this.sendBvlc(receiver, buffer);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/**
|
|
1609
|
+
*
|
|
1610
|
+
* @param receiver
|
|
1611
|
+
* @param service
|
|
1612
|
+
* @param invokeId
|
|
1613
|
+
*/
|
|
1614
|
+
simpleAckResponse(receiver, service, invokeId) {
|
|
1615
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1616
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
1617
|
+
baApdu.encodeSimpleAck(buffer, baEnum.PduType.SIMPLE_ACK, service, invokeId);
|
|
1618
|
+
this.sendBvlc(receiver, buffer);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
*
|
|
1623
|
+
* @param receiver
|
|
1624
|
+
* @param service
|
|
1625
|
+
* @param invokeId
|
|
1626
|
+
* @param errorClass
|
|
1627
|
+
* @param errorCode
|
|
1628
|
+
*/
|
|
1629
|
+
errorResponse(receiver, service, invokeId, errorClass, errorCode) {
|
|
1630
|
+
trace(`error response on ${JSON.stringify(receiver)} service: ${JSON.stringify(service)} invokeId: ${invokeId} errorClass: ${errorClass} errorCode: ${errorCode}`);
|
|
1631
|
+
trace(`error message ${baServices.error.buildMessage({ 'class': errorClass, 'code': errorCode })}`);
|
|
1632
|
+
const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
|
|
1633
|
+
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
1634
|
+
baApdu.encodeError(buffer, baEnum.PduType.ERROR, service, invokeId);
|
|
1635
|
+
baServices.error.encode(buffer, errorClass, errorCode);
|
|
1636
|
+
this.sendBvlc(receiver, buffer);
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
*
|
|
1641
|
+
* @param receiver
|
|
1642
|
+
* @param buffer
|
|
1643
|
+
*/
|
|
1644
|
+
sendBvlc(receiver, buffer) {
|
|
1645
|
+
if (typeof receiver === 'string') {
|
|
1646
|
+
receiver = {
|
|
1647
|
+
address: receiver
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
if (receiver && receiver.forwardedFrom) {
|
|
1651
|
+
// Remote node address given, forward to BBMD
|
|
1652
|
+
baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.FORWARDED_NPDU, buffer.offset, receiver.forwardedFrom);
|
|
1653
|
+
} else if (receiver && receiver.address) {
|
|
1654
|
+
// Specific address, unicast
|
|
1655
|
+
baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
|
|
1656
|
+
} else {
|
|
1657
|
+
// No address, broadcast
|
|
1658
|
+
baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_BROADCAST_NPDU, buffer.offset);
|
|
1659
|
+
}
|
|
1660
|
+
this._transport.send(
|
|
1661
|
+
buffer.buffer,
|
|
1662
|
+
buffer.offset,
|
|
1663
|
+
(receiver && receiver.address) || null
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
/**
|
|
1668
|
+
* The resultResponse is a BVLC-Result message used to respond to certain events, such as BBMD registration.
|
|
1669
|
+
* This message cannot be wrapped for passing through a BBMD, as it is used as a BBMD control message.
|
|
1670
|
+
* @function bacnet.resultResponse
|
|
1671
|
+
* @param {string} receiver - IP address of the target device.
|
|
1672
|
+
* @param {number} resultCode - Single value from BvlcResultFormat enum.
|
|
1673
|
+
*/
|
|
1674
|
+
resultResponse(receiver, resultCode) {
|
|
1675
|
+
const buffer = this._getBuffer();
|
|
1676
|
+
baApdu.encodeResult(buffer, resultCode);
|
|
1677
|
+
baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.BVLC_RESULT, buffer.offset);
|
|
1678
|
+
this._transport.send(buffer.buffer, buffer.offset, receiver.address);
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
/**
|
|
1682
|
+
* Unloads the current bacnet instance and closes the underlying UDP socket.
|
|
1683
|
+
* @function bacnet.close
|
|
1684
|
+
* @example
|
|
1685
|
+
* const bacnet = require('node-bacnet');
|
|
1686
|
+
* const client = new bacnet();
|
|
1687
|
+
*
|
|
1688
|
+
* client.close();
|
|
1689
|
+
*/
|
|
1690
|
+
close() {
|
|
1691
|
+
this._transport.close();
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
/**
|
|
1695
|
+
* Helper function to take an array of enums and produce a bitstring suitable
|
|
1696
|
+
* for inclusion as a property.
|
|
1697
|
+
*
|
|
1698
|
+
* @example
|
|
1699
|
+
* [bacnet.enum.PropertyIdentifier.PROTOCOL_OBJECT_TYPES_SUPPORTED]: [
|
|
1700
|
+
* {value: bacnet.createBitstring([
|
|
1701
|
+
* bacnet.enum.ObjectTypesSupported.ANALOG_INPUT,
|
|
1702
|
+
* bacnet.enum.ObjectTypesSupported.ANALOG_OUTPUT,
|
|
1703
|
+
* ]),
|
|
1704
|
+
* type: bacnet.enum.ApplicationTag.BIT_STRING},
|
|
1705
|
+
* ],
|
|
1706
|
+
*/
|
|
1707
|
+
static createBitstring(items) {
|
|
1708
|
+
let offset = 0;
|
|
1709
|
+
let bytes = [];
|
|
1710
|
+
let bitsUsed = 0;
|
|
1711
|
+
while (items.length) {
|
|
1712
|
+
// Find any values between offset and offset+8, for the next byte
|
|
1713
|
+
let value = 0;
|
|
1714
|
+
items = items.filter(i => {
|
|
1715
|
+
if (i >= offset + 8) {
|
|
1716
|
+
return true;
|
|
1717
|
+
} // leave for future iteration
|
|
1718
|
+
value |= 1 << (i - offset);
|
|
1719
|
+
bitsUsed = Math.max(bitsUsed, i);
|
|
1720
|
+
return false; // remove from list
|
|
1721
|
+
});
|
|
1722
|
+
bytes.push(value);
|
|
1723
|
+
offset += 8;
|
|
1724
|
+
}
|
|
1725
|
+
bitsUsed++;
|
|
1726
|
+
|
|
1727
|
+
return {
|
|
1728
|
+
value: bytes,
|
|
1729
|
+
bitsUsed,
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
}
|
|
1734
|
+
module.exports = Client;
|
|
1735
|
+
</code></pre>
|
|
1736
|
+
</article>
|
|
1737
|
+
</section>
|
|
1738
|
+
|
|
1739
|
+
|
|
1740
|
+
|
|
1741
|
+
|
|
1742
|
+
|
|
1743
|
+
|
|
1744
|
+
</div>
|
|
1745
|
+
|
|
1746
|
+
<br class="clear">
|
|
1747
|
+
|
|
1748
|
+
<footer>
|
|
1749
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.4</a> on Tue Jun 02 2020 20:24:55 GMT+0200 (Central European Summer Time) using the <a href="https://github.com/mochajs/mocha-docdash">@mocha/docdash</a> theme.
|
|
1750
|
+
</footer>
|
|
1751
|
+
|
|
1752
|
+
<script src="scripts/prettify/prettify.js"></script>
|
|
1753
|
+
<script src="scripts/prettify/lang-css.js"></script>
|
|
1754
|
+
<script>prettyPrint();</script>
|
|
1755
|
+
<script src="scripts/linenumber.js"></script>
|
|
1756
|
+
|
|
1757
|
+
|
|
1758
|
+
</body>
|
|
1759
|
+
</html>
|