aes70 1.3.6 → 1.3.8
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/Changelog +17 -0
- package/dist/AES70.es5.js +1 -1
- package/package.json +1 -1
- package/src/OCP1/message_generator.js +1 -1
- package/src/controller/ControlClasses/OcaFilterFIR.d.ts +5 -0
- package/src/controller/ControlClasses/OcaFilterFIR.js +9 -1
- package/src/controller/ControlClasses/OcaPowerManager.d.ts +5 -0
- package/src/controller/ControlClasses/OcaPowerManager.js +9 -1
- package/src/controller/ControlClasses/OcaPowerSupply.d.ts +10 -0
- package/src/controller/ControlClasses/OcaPowerSupply.js +20 -2
- package/src/controller/ControlClasses/OcaSensor.d.ts +5 -0
- package/src/controller/ControlClasses/OcaSensor.js +9 -1
- package/src/controller/ControlClasses.d.ts +0 -1
- package/src/controller/abstract_udp_connection.js +211 -0
- package/src/controller/client_connection.d.ts +1 -1
- package/src/controller/client_connection.js +17 -5
- package/src/controller/make_control_class.js +24 -4
- package/src/controller/node_udp.js +141 -0
- package/src/controller/remote_device.js +2 -3
- package/src/controller/udp_connection.js +5 -266
- package/src/index.browser.js +2 -0
- package/src/index.js +2 -0
- package/tests/device/locking.js +2 -1
- package/tests/device/method_callback.js +24 -0
- package/tests/device/test.js +11 -5
- package/tests/device.js +3 -1
- package/bin/connectMany.js +0 -64
|
@@ -1,280 +1,19 @@
|
|
|
1
1
|
/* eslint-env node */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { Buffer } from 'buffer';
|
|
3
|
+
import { NodeUDP } from './node_udp.js';
|
|
5
4
|
import { performance } from 'perf_hooks';
|
|
6
|
-
import {
|
|
7
|
-
import { decodeMessage } from '../OCP1/decode_message.js';
|
|
8
|
-
import { KeepAlive } from '../OCP1/keepalive.js';
|
|
9
|
-
|
|
10
|
-
import { lookup } from 'dns';
|
|
11
|
-
|
|
12
|
-
import { ClientConnection } from './client_connection.js';
|
|
13
|
-
|
|
14
|
-
function delay(n) {
|
|
15
|
-
return new Promise((resolve) => setTimeout(resolve, n));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async function waitForKeepalive(socket, options) {
|
|
19
|
-
const waiter = new Promise((resolve, reject) => {
|
|
20
|
-
let onMessage;
|
|
21
|
-
|
|
22
|
-
onMessage = (data, rinfo) => {
|
|
23
|
-
const pdus = [];
|
|
24
|
-
let pos = 0;
|
|
25
|
-
|
|
26
|
-
pos = decodeMessage(new DataView(data.buffer), 0, pdus);
|
|
27
|
-
|
|
28
|
-
if (pdus.length !== 1) throw new Error('Expected keepalive response.');
|
|
29
|
-
|
|
30
|
-
socket.off('message', onMessage);
|
|
31
|
-
socket.off('error', reject);
|
|
32
|
-
resolve(true);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
socket.on('message', onMessage);
|
|
36
|
-
socket.on('error', reject);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const msg = Buffer.from(encodeMessage(new KeepAlive(1000)));
|
|
40
|
-
const t = 5 * (options.retry_interval || 250);
|
|
41
|
-
|
|
42
|
-
for (let i = 0; i < 3; i++) {
|
|
43
|
-
socket.send(msg);
|
|
44
|
-
|
|
45
|
-
if (await Promise.race([waiter, delay(t)])) return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
throw new Error('Failed to connect.');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function is_ip(host) {
|
|
52
|
-
let tmp = host.split('.');
|
|
53
|
-
|
|
54
|
-
if (tmp.length !== 4) return false;
|
|
55
|
-
|
|
56
|
-
tmp = tmp.map(parseInt);
|
|
57
|
-
|
|
58
|
-
return !tmp.some(function (v) {
|
|
59
|
-
return v < 0 || v > 255;
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// TODO: handle IPv6
|
|
64
|
-
function lookup_address(options) {
|
|
65
|
-
const host = options.host;
|
|
66
|
-
const port = parseInt(options.port);
|
|
67
|
-
|
|
68
|
-
const o = { port: port };
|
|
69
|
-
|
|
70
|
-
if (is_ip(host))
|
|
71
|
-
return Promise.resolve(Object.assign({}, options, o, { address: host }));
|
|
72
|
-
|
|
73
|
-
return new Promise(function (resolve, reject) {
|
|
74
|
-
lookup(host, { family: 4 }, function (err, address) {
|
|
75
|
-
if (err) reject(err);
|
|
76
|
-
else resolve(Object.assign({}, options, o, { address: address }));
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
}
|
|
5
|
+
import { AbstractUDPConnection } from './abstract_udp_connection.js';
|
|
80
6
|
|
|
81
7
|
/**
|
|
82
8
|
* :class:`ClientConnection` subclass which implements OCP.1 with UDP
|
|
83
9
|
* transport.
|
|
84
10
|
*/
|
|
85
|
-
export class UDPConnection extends
|
|
86
|
-
constructor(socket, options) {
|
|
87
|
-
// allow us to batch 128 bytes max
|
|
88
|
-
// Set this to a higher value, e.g. close to MTU
|
|
89
|
-
// if you are sure that the device can handle it.
|
|
90
|
-
if (!(options.batch >= 0)) options.batch = 128;
|
|
91
|
-
super(options);
|
|
92
|
-
this.socket = socket;
|
|
93
|
-
this.delay = options.delay >= 0 ? options.delay : 5;
|
|
94
|
-
this.retry_interval =
|
|
95
|
-
options.retry_interval >= 0 ? options.retry_interval : 250;
|
|
96
|
-
this.retry_count = options.retry_count >= 0 ? options.retry_count : 3;
|
|
97
|
-
this._write_out_id = -1;
|
|
98
|
-
this._write_out_callback = () => {
|
|
99
|
-
this._write_out_id = -1;
|
|
100
|
-
this._write_out();
|
|
101
|
-
};
|
|
102
|
-
this._retry_id =
|
|
103
|
-
this.retry_interval > 0
|
|
104
|
-
? setInterval(() => this._retryCommands(), this.retry_interval)
|
|
105
|
-
: -1;
|
|
106
|
-
this.q = [];
|
|
107
|
-
socket.on('message', (data, rinfo) => {
|
|
108
|
-
try {
|
|
109
|
-
this.read(data.buffer);
|
|
110
|
-
} catch (err) {
|
|
111
|
-
console.warn('Failed to parse incoming AES70 packet: %o', err);
|
|
112
|
-
}
|
|
113
|
-
if (this.inbuf !== null) this.close();
|
|
114
|
-
});
|
|
115
|
-
socket.on('error', (err) => this.error(err));
|
|
116
|
-
this.set_keepalive_interval(1);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
get is_reliable() {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Connect to the given endpoint.
|
|
125
|
-
*
|
|
126
|
-
* @param {String} options.host - hostname or ip
|
|
127
|
-
* @param {number} options.port - port number
|
|
128
|
-
* @param {number} [options.delay=10] - Delay in ms between individual packets.
|
|
129
|
-
* This can be a useful strategy when communicating with devices which
|
|
130
|
-
* cannot handle high packet rates.
|
|
131
|
-
* @param {number} [options.retry_interval=250] - Delay in ms after which a
|
|
132
|
-
* command should be automatically re-sent if no response has been
|
|
133
|
-
* received, yet.
|
|
134
|
-
* @param {number} [options.retry_count=3] - Number of times to retry sending
|
|
135
|
-
* commands. If no response has been received after all retries, the
|
|
136
|
-
* command will fail with an error.
|
|
137
|
-
* @param {number} [options.batch=128] - Maximum number of bytes to send
|
|
138
|
-
* in an individual UDP packet. Note that AES70 messages which are larger
|
|
139
|
-
* than this limit are sent anyway. This only limits how many seperate
|
|
140
|
-
* messages are batched into a single packet.
|
|
141
|
-
* @returns {Promise<UDPConnection>}
|
|
142
|
-
* The connection.
|
|
143
|
-
*/
|
|
144
|
-
static connect(options) {
|
|
145
|
-
return lookup_address(options).then((options) => {
|
|
146
|
-
return new Promise((resolve, reject) => {
|
|
147
|
-
const socket = createSocket(options.type || 'udp4');
|
|
148
|
-
const onerror = function (ev) {
|
|
149
|
-
reject(ev);
|
|
150
|
-
};
|
|
151
|
-
socket.on('error', onerror);
|
|
152
|
-
socket.bind(
|
|
153
|
-
{
|
|
154
|
-
exclusive: true,
|
|
155
|
-
},
|
|
156
|
-
() => {
|
|
157
|
-
socket.on('connect', async () => {
|
|
158
|
-
try {
|
|
159
|
-
await waitForKeepalive(socket, options);
|
|
160
|
-
|
|
161
|
-
socket.removeListener('error', onerror);
|
|
162
|
-
|
|
163
|
-
const connection = new this(socket, options);
|
|
164
|
-
resolve(connection);
|
|
165
|
-
} catch (err) {
|
|
166
|
-
reject(err);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
socket.connect(options.port, options.host);
|
|
170
|
-
}
|
|
171
|
-
);
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
write(buf) {
|
|
177
|
-
this.q.push(buf);
|
|
178
|
-
|
|
179
|
-
if (this.tx_idle_time() >= this.delay) this._write_out();
|
|
180
|
-
else this._schedule_write_out();
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
flush() {
|
|
184
|
-
super.flush();
|
|
185
|
-
if (this.tx_idle_time() > this.delay) this._write_out();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
cleanup() {
|
|
189
|
-
super.cleanup();
|
|
190
|
-
if (this.socket) {
|
|
191
|
-
this.socket.close();
|
|
192
|
-
this.socket = null;
|
|
193
|
-
}
|
|
194
|
-
if (this._write_out_id !== -1) {
|
|
195
|
-
clearTimeout(this._write_out_id);
|
|
196
|
-
this._write_out_id = -1;
|
|
197
|
-
}
|
|
198
|
-
if (this._retry_id !== -1) {
|
|
199
|
-
clearInterval(this._retry_id);
|
|
200
|
-
this._retry_id = -1;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
11
|
+
export class UDPConnection extends AbstractUDPConnection {
|
|
204
12
|
_now() {
|
|
205
13
|
return performance.now();
|
|
206
14
|
}
|
|
207
15
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const q = this.q;
|
|
211
|
-
|
|
212
|
-
if (!q.length) return;
|
|
213
|
-
|
|
214
|
-
const buf = q.shift();
|
|
215
|
-
|
|
216
|
-
this.socket.send(Buffer.from(buf));
|
|
217
|
-
super.write(buf);
|
|
218
|
-
|
|
219
|
-
if (q.length) this._schedule_write_out();
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
_schedule_write_out() {
|
|
223
|
-
const tx_idle_time = this.tx_idle_time();
|
|
224
|
-
const delay = this.delay;
|
|
225
|
-
|
|
226
|
-
if (tx_idle_time >= delay) {
|
|
227
|
-
this._write_out();
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Already scheduled.
|
|
232
|
-
if (this._write_out_id !== -1) return;
|
|
233
|
-
|
|
234
|
-
this._write_out_id = setTimeout(
|
|
235
|
-
this._write_out_callback,
|
|
236
|
-
delay - tx_idle_time
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
_retryCommands() {
|
|
241
|
-
const now = this._now();
|
|
242
|
-
const retryTime = now - this.retry_interval;
|
|
243
|
-
// This is an estimate for how many commands we would manage to send
|
|
244
|
-
// off.
|
|
245
|
-
const max = 5 * (this.retry_interval / this.delay) - this.q.length;
|
|
246
|
-
const pendingCommands = this._pendingCommands;
|
|
247
|
-
|
|
248
|
-
const retries = [];
|
|
249
|
-
const failed = [];
|
|
250
|
-
|
|
251
|
-
for (const entry of pendingCommands) {
|
|
252
|
-
const [handle, pendingCommand] = entry;
|
|
253
|
-
|
|
254
|
-
// All later commands are newer than the cutoff.
|
|
255
|
-
if (pendingCommand.lastSent > retryTime) break;
|
|
256
|
-
if (pendingCommand.retries >= this.retry_count) {
|
|
257
|
-
failed.push(entry);
|
|
258
|
-
} else if (retries.length < max) {
|
|
259
|
-
retries.push(entry);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (failed.length) {
|
|
264
|
-
const timeoutError = new Error('Timeout.');
|
|
265
|
-
|
|
266
|
-
failed.forEach(([handle, pendingCommand]) => {
|
|
267
|
-
pendingCommands.delete(handle);
|
|
268
|
-
pendingCommand.reject(timeoutError);
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
retries.forEach(([handle, pendingCommand]) => {
|
|
273
|
-
pendingCommands.delete(handle);
|
|
274
|
-
pendingCommands.set(handle, pendingCommand);
|
|
275
|
-
this.send(pendingCommand.command);
|
|
276
|
-
pendingCommand.lastSent = now;
|
|
277
|
-
pendingCommand.retries++;
|
|
278
|
-
});
|
|
16
|
+
static connect(options) {
|
|
17
|
+
return AbstractUDPConnection.connect.call(this, NodeUDP, options);
|
|
279
18
|
}
|
|
280
19
|
}
|
package/src/index.browser.js
CHANGED
|
@@ -16,6 +16,7 @@ import { define_custom_class } from './controller/define_custom_class.js';
|
|
|
16
16
|
import * as RemoteControlClasses from './controller/ControlClasses.js';
|
|
17
17
|
import * as Types from './types.js';
|
|
18
18
|
import { WebSocketConnection } from './controller/websocket_connection.js';
|
|
19
|
+
import { AbstractUDPConnection } from './controller/abstract_udp_connection.js';
|
|
19
20
|
|
|
20
21
|
const controller = {
|
|
21
22
|
Connection: ClientConnection,
|
|
@@ -43,4 +44,5 @@ export {
|
|
|
43
44
|
warn,
|
|
44
45
|
WebSocketConnection,
|
|
45
46
|
RemoteControlClasses,
|
|
47
|
+
AbstractUDPConnection,
|
|
46
48
|
};
|
package/src/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import * as Types from './types.js';
|
|
|
18
18
|
import { WebSocketConnection } from './controller/websocket_connection_node.js';
|
|
19
19
|
import { TCPConnection } from './controller/tcp_connection.js';
|
|
20
20
|
import { UDPConnection } from './controller/udp_connection.js';
|
|
21
|
+
import { AbstractUDPConnection } from './controller/abstract_udp_connection.js';
|
|
21
22
|
|
|
22
23
|
export const controller = {
|
|
23
24
|
Connection: ClientConnection,
|
|
@@ -47,4 +48,5 @@ export {
|
|
|
47
48
|
WebSocketConnection,
|
|
48
49
|
TCPConnection,
|
|
49
50
|
UDPConnection,
|
|
51
|
+
AbstractUDPConnection,
|
|
50
52
|
};
|
package/tests/device/locking.js
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Test } from './test.js';
|
|
2
|
+
|
|
3
|
+
class CheckCallbackMethod extends Test {
|
|
4
|
+
async run() {
|
|
5
|
+
const root = this.device.Root;
|
|
6
|
+
|
|
7
|
+
const result = await root.GetMembers();
|
|
8
|
+
|
|
9
|
+
const result2 = await new Promise((resolve, reject) => {
|
|
10
|
+
const tmp = root.GetMembers((ok, result) => {
|
|
11
|
+
(ok ? resolve : reject)(result);
|
|
12
|
+
});
|
|
13
|
+
if (tmp) {
|
|
14
|
+
console.error('got %o', tmp);
|
|
15
|
+
throw new Error('Unexpected return value.');
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (result.length !== result2.length)
|
|
20
|
+
throw new Error('Result mismatch.');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default [ CheckCallbackMethod ];
|
package/tests/device/test.js
CHANGED
|
@@ -44,16 +44,22 @@ export class Test {
|
|
|
44
44
|
{
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
close_device(device)
|
|
48
48
|
{
|
|
49
|
-
if (
|
|
49
|
+
if (device !== null)
|
|
50
50
|
{
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
device.connection.emit('test_done');
|
|
52
|
+
device.close();
|
|
53
|
+
device = null;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
cleanup()
|
|
58
|
+
{
|
|
59
|
+
this.close_device(this.device);
|
|
60
|
+
this.device = null;
|
|
61
|
+
}
|
|
62
|
+
|
|
57
63
|
static focus()
|
|
58
64
|
{
|
|
59
65
|
return false;
|
package/tests/device.js
CHANGED
|
@@ -13,6 +13,7 @@ import CheckTreeTests from './device/check_tree.js';
|
|
|
13
13
|
import KeepaliveTests from './device/keepalive.js';
|
|
14
14
|
import LockingTests from './device/locking.js';
|
|
15
15
|
import PropertyChangesTests from './device/property_changes.js';
|
|
16
|
+
import MethodCallbackTests from './device/method_callback.js';
|
|
16
17
|
|
|
17
18
|
if (argv.length < 3)
|
|
18
19
|
{
|
|
@@ -169,7 +170,8 @@ function get_runner(get_device)
|
|
|
169
170
|
CheckTreeTests,
|
|
170
171
|
//KeepaliveTests,
|
|
171
172
|
LockingTests,
|
|
172
|
-
PropertyChangesTests
|
|
173
|
+
PropertyChangesTests,
|
|
174
|
+
MethodCallbackTests
|
|
173
175
|
].flat();
|
|
174
176
|
|
|
175
177
|
// require('./device/property_changes'),
|
package/bin/connectMany.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { stdout, argv, exit } from 'process';
|
|
4
|
-
import { RemoteDevice } from '../src/controller/remote_device.js';
|
|
5
|
-
import { TCPConnection } from '../src/controller/tcp_connection.js';
|
|
6
|
-
import { format } from 'util';
|
|
7
|
-
|
|
8
|
-
if (argv.length < 4)
|
|
9
|
-
{
|
|
10
|
-
console.log('Usage: node print_tree.js <ip> <port>');
|
|
11
|
-
exit(1);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function delay(n) {
|
|
15
|
-
if (!n) return Promise.resolve();
|
|
16
|
-
return new Promise((resolve) => setTimeout(resolve, n));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const host = argv[2];
|
|
20
|
-
const port = parseInt(argv[3]);
|
|
21
|
-
const N = 1000;
|
|
22
|
-
|
|
23
|
-
const destinations = new Array(N).fill(0).map((_, index) => {
|
|
24
|
-
return { host: host, port: port + index };
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
async function connectAndDiscover(destination) {
|
|
28
|
-
const con = await TCPConnection.connect(destination);
|
|
29
|
-
|
|
30
|
-
const device = new RemoteDevice(con);
|
|
31
|
-
|
|
32
|
-
const objectTree = await device.get_device_tree();
|
|
33
|
-
|
|
34
|
-
device.close();
|
|
35
|
-
|
|
36
|
-
return objectTree;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function run() {
|
|
40
|
-
for (let i = 0; i < destinations.length;) {
|
|
41
|
-
let tasks = [];
|
|
42
|
-
|
|
43
|
-
while (tasks.length < 5) {
|
|
44
|
-
tasks.push(connectAndDiscover(destinations[i++]));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
await Promise.all(tasks);
|
|
48
|
-
|
|
49
|
-
stdout.write(format('\r %d %% done.',
|
|
50
|
-
Math.floor(100 * i / destinations.length)));
|
|
51
|
-
|
|
52
|
-
await delay(50);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
run().then(
|
|
57
|
-
() => {
|
|
58
|
-
console.log('\nDone');
|
|
59
|
-
},
|
|
60
|
-
(err) => {
|
|
61
|
-
console.error(err);
|
|
62
|
-
exit(1);
|
|
63
|
-
}
|
|
64
|
-
);
|