vrpc 2.5.1 → 3.0.0-alpha.3
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.md +66 -0
- package/README.md +31 -56
- package/binding.gyp.no-auto +4 -26
- package/browser/vrpc.js +1 -1
- package/index.js +2 -2
- package/package.json +12 -16
- package/vrpc/VrpcAdapter.js +202 -193
- package/vrpc/VrpcAgent.js +86 -80
- package/vrpc/{VrpcRemote.js → VrpcClient.js} +302 -425
- package/vrpc/VrpcNative.js +307 -0
- package/vrpc/{vrpc.hpp → adapter.hpp} +1001 -1115
- package/vrpc/addon.cpp +5 -5
- package/vrpc/json.hpp +1769 -870
- package/vrpc/vrpc-agent-js +4 -4
- package/README.rst +0 -18
- package/third_party/include/catch.hpp +0 -12749
- package/third_party/install.sh +0 -22
- package/vrpc/VrpcLocal.js +0 -264
- package/vrpc/VrpcLocal.py +0 -171
- package/vrpc/__init__.py +0 -1
- package/vrpc/module.cpp +0 -183
|
@@ -10,9 +10,9 @@ __\/\\\_______\/\\\__/\\\///////\\\___\/\\\/////////\\\____/\\\////////__
|
|
|
10
10
|
________\///________\///________\///__\///___________________\/////////__
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
Non-intrusively
|
|
13
|
+
Non-intrusively adapts code and provides access in form of asynchronous remote
|
|
14
14
|
procedure calls (RPC).
|
|
15
|
-
Author: Dr. Burkhard C. Heisen (https://github.com/
|
|
15
|
+
Author: Dr. Burkhard C. Heisen (https://github.com/heisenware/vrpc)
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
|
@@ -39,16 +39,19 @@ SOFTWARE.
|
|
|
39
39
|
|
|
40
40
|
const os = require('os')
|
|
41
41
|
const crypto = require('crypto')
|
|
42
|
+
const { nanoid } = require('nanoid')
|
|
42
43
|
const mqtt = require('mqtt')
|
|
43
44
|
const EventEmitter = require('events')
|
|
44
45
|
|
|
46
|
+
const VRPC_PROTOCOL_VERSION = 3
|
|
47
|
+
|
|
45
48
|
/**
|
|
46
49
|
* Client capable of creating proxy objects and remotely calling
|
|
47
50
|
* functions as provided through one or more (distributed) agents.
|
|
48
51
|
*
|
|
49
52
|
* @extends EventEmitter
|
|
50
53
|
*/
|
|
51
|
-
class
|
|
54
|
+
class VrpcClient extends EventEmitter {
|
|
52
55
|
/**
|
|
53
56
|
* Constructs a remote client, able to communicate with any distributed agents
|
|
54
57
|
*
|
|
@@ -66,7 +69,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
66
69
|
* @param {Object} [options.log=console] Log object (must support debug, info, warn, and error level)
|
|
67
70
|
* @param {Boolean} [options.bestEffort=false] If true, message will be sent with best effort, i.e. no caching if offline
|
|
68
71
|
* @example
|
|
69
|
-
* const client = new
|
|
72
|
+
* const client = new VrpcClient({
|
|
70
73
|
* domain: 'public.vrpc',
|
|
71
74
|
* broker: 'mqtt://vrpc.io'
|
|
72
75
|
* })
|
|
@@ -86,11 +89,15 @@ class VrpcRemote extends EventEmitter {
|
|
|
86
89
|
// domain sanity check
|
|
87
90
|
if (!domain) throw new Error('The domain must be specified')
|
|
88
91
|
if (domain.match(/[+/#*]/)) {
|
|
89
|
-
throw new Error(
|
|
92
|
+
throw new Error(
|
|
93
|
+
'The domain must NOT contain any of those characters: "+", "/", "#", "*"'
|
|
94
|
+
)
|
|
90
95
|
}
|
|
91
96
|
// agent sanity check
|
|
92
97
|
if (agent.match(/[+/#]/)) {
|
|
93
|
-
throw new Error(
|
|
98
|
+
throw new Error(
|
|
99
|
+
'The agent must NOT contain any of those characters: "+", "/", "#"'
|
|
100
|
+
)
|
|
94
101
|
}
|
|
95
102
|
this._token = token
|
|
96
103
|
this._username = username
|
|
@@ -101,12 +108,13 @@ class VrpcRemote extends EventEmitter {
|
|
|
101
108
|
this._timeout = timeout
|
|
102
109
|
this._qos = this._qos = bestEffort ? 0 : 1
|
|
103
110
|
|
|
104
|
-
this._instance =
|
|
111
|
+
this._instance = nanoid(8)
|
|
105
112
|
this._mqttClientId = this._createMqttClientId()
|
|
106
113
|
this._vrpcClientId = this._createVrpcClientId()
|
|
107
114
|
this._agents = {}
|
|
108
115
|
this._eventEmitter = new EventEmitter()
|
|
109
116
|
this._invokeId = 0
|
|
117
|
+
this._proxyId = 0
|
|
110
118
|
this._client = null
|
|
111
119
|
this._cachedSubscriptions = new Map()
|
|
112
120
|
this._proxies = {}
|
|
@@ -118,6 +126,15 @@ class VrpcRemote extends EventEmitter {
|
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Provides a unique id for this client instance
|
|
131
|
+
*
|
|
132
|
+
* @returns {String} clientId
|
|
133
|
+
*/
|
|
134
|
+
getClientId () {
|
|
135
|
+
return this._vrpcClientId
|
|
136
|
+
}
|
|
137
|
+
|
|
121
138
|
/**
|
|
122
139
|
* Actually connects to the MQTT broker.
|
|
123
140
|
*
|
|
@@ -154,15 +171,15 @@ class VrpcRemote extends EventEmitter {
|
|
|
154
171
|
if (password === undefined) delete options.password
|
|
155
172
|
this._client = mqtt.connect(this._broker, options)
|
|
156
173
|
|
|
157
|
-
this._client.on('error',
|
|
174
|
+
this._client.on('error', err => {
|
|
158
175
|
this.emit('error', err)
|
|
159
176
|
})
|
|
160
177
|
|
|
161
|
-
this._client.stream.on('error',
|
|
178
|
+
this._client.stream.on('error', err => {
|
|
162
179
|
this.emit('error', err)
|
|
163
180
|
})
|
|
164
181
|
|
|
165
|
-
this.on('error',
|
|
182
|
+
this.on('error', err => {
|
|
166
183
|
this._log.debug(`Encountered MQTT error: ${err.message}`)
|
|
167
184
|
})
|
|
168
185
|
|
|
@@ -194,7 +211,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
194
211
|
this._agents[agent].hostname = hostname
|
|
195
212
|
this._agents[agent].version = version
|
|
196
213
|
this.emit('agent', { domain, agent, status, hostname, version })
|
|
197
|
-
|
|
214
|
+
// ClassInfo message
|
|
198
215
|
} else if (instance === '__classInfo__') {
|
|
199
216
|
// Json properties: { klass, instances, memberFunctions, staticFunctions }
|
|
200
217
|
const json = JSON.parse(message.toString())
|
|
@@ -212,24 +229,23 @@ class VrpcRemote extends EventEmitter {
|
|
|
212
229
|
staticFunctions,
|
|
213
230
|
meta
|
|
214
231
|
} = json
|
|
215
|
-
if (removed.length !== 0)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
'
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
// RPC message
|
|
232
|
+
if (removed.length !== 0)
|
|
233
|
+
this.emit('instanceGone', removed, { domain, agent, className })
|
|
234
|
+
if (added.length !== 0)
|
|
235
|
+
this.emit('instanceNew', added, { domain, agent, className })
|
|
236
|
+
this.emit('class', {
|
|
237
|
+
domain,
|
|
238
|
+
agent,
|
|
239
|
+
className,
|
|
240
|
+
instances,
|
|
241
|
+
memberFunctions,
|
|
242
|
+
staticFunctions,
|
|
243
|
+
meta
|
|
244
|
+
})
|
|
245
|
+
// RPC message
|
|
230
246
|
} else {
|
|
231
|
-
const {
|
|
232
|
-
this._eventEmitter.emit(
|
|
247
|
+
const { i, a, e, r } = JSON.parse(message.toString())
|
|
248
|
+
this._eventEmitter.emit(i, { a, e, r })
|
|
233
249
|
}
|
|
234
250
|
})
|
|
235
251
|
await new Promise((resolve, reject) => {
|
|
@@ -243,11 +259,6 @@ class VrpcRemote extends EventEmitter {
|
|
|
243
259
|
})
|
|
244
260
|
}
|
|
245
261
|
|
|
246
|
-
async connected () {
|
|
247
|
-
this._indicateDeprecation('VrpcRemote.connected(): This API usage is deprecated, use `connect()` instead.')
|
|
248
|
-
return this.connect()
|
|
249
|
-
}
|
|
250
|
-
|
|
251
262
|
/**
|
|
252
263
|
* Creates a new remote instance and provides a proxy to it.
|
|
253
264
|
*
|
|
@@ -296,23 +307,20 @@ class VrpcRemote extends EventEmitter {
|
|
|
296
307
|
*/
|
|
297
308
|
async create ({
|
|
298
309
|
className,
|
|
299
|
-
instance,
|
|
310
|
+
instance = nanoid(8),
|
|
300
311
|
args = [],
|
|
301
312
|
agent = this._agent,
|
|
302
|
-
cacheProxy = false
|
|
313
|
+
cacheProxy = false,
|
|
314
|
+
isIsolated = false
|
|
303
315
|
} = {}) {
|
|
304
316
|
if (agent === '*') throw new Error('Agent must be specified')
|
|
305
|
-
const data = instance ? { _1: instance } : {}
|
|
306
|
-
const offset = instance ? 2 : 1
|
|
307
|
-
args.forEach((value, index) => {
|
|
308
|
-
data[`_${index + offset}`] = value
|
|
309
|
-
})
|
|
310
317
|
const json = {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
318
|
+
c: className,
|
|
319
|
+
f: isIsolated ? '__createIsolated__' : '__createShared__',
|
|
320
|
+
i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
|
|
321
|
+
s: `${this._domain}/${os.hostname()}/${this._instance}`,
|
|
322
|
+
a: [instance, ...args],
|
|
323
|
+
v: VRPC_PROTOCOL_VERSION
|
|
316
324
|
}
|
|
317
325
|
const proxy = await this._getProxy(agent, className, json)
|
|
318
326
|
if (instance && cacheProxy) this._proxies[instance] = proxy
|
|
@@ -328,36 +336,15 @@ class VrpcRemote extends EventEmitter {
|
|
|
328
336
|
*
|
|
329
337
|
* @param {String} instance The instance to be retrieved
|
|
330
338
|
* @param {Object} [options] Explicitly define agent and class
|
|
331
|
-
* @param {String} options.className Name of the instance's class
|
|
332
|
-
* @param {String} options.agent Agent name. If not provided class default is used
|
|
333
|
-
* @param {bool} options.noWait If true immediately fail if instance could not be found in local cache
|
|
339
|
+
* @param {String} [options.className] Name of the instance's class
|
|
340
|
+
* @param {String} [options.agent] Agent name. If not provided class default is used as priority hit
|
|
341
|
+
* @param {bool} [options.noWait=false] If true immediately fail if instance could not be found in local cache
|
|
334
342
|
* @returns {Promise<Proxy>} Proxy object reflecting the remotely existing instance
|
|
335
343
|
*/
|
|
336
|
-
async getInstance (instance, options) {
|
|
344
|
+
async getInstance (instance, options = {}) {
|
|
337
345
|
if (this._proxies[instance]) return this._proxies[instance]
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
agent,
|
|
341
|
-
className,
|
|
342
|
-
instance: instanceString
|
|
343
|
-
} = await this._getInstanceData(instance, options)
|
|
344
|
-
return this._createProxy(agent, className, instanceString)
|
|
345
|
-
} else { // deprecate this
|
|
346
|
-
this._indicateDeprecation(
|
|
347
|
-
'VrpcRemote.getInstance(): ' +
|
|
348
|
-
'Using an object as single argument is deprecated, ' +
|
|
349
|
-
'use "getInstance(instance, options)" instead.'
|
|
350
|
-
)
|
|
351
|
-
const fakedOptions = { ...instance }
|
|
352
|
-
const fakedInstance = fakedOptions.instance
|
|
353
|
-
delete fakedOptions.instance
|
|
354
|
-
const {
|
|
355
|
-
agent,
|
|
356
|
-
className,
|
|
357
|
-
instance: instanceString
|
|
358
|
-
} = await this._getInstanceData(fakedInstance, fakedOptions)
|
|
359
|
-
return this._createProxy(agent, className, instanceString)
|
|
360
|
-
}
|
|
346
|
+
const { agent, className } = await this._getInstanceData(instance, options)
|
|
347
|
+
return this._createProxy(agent, className, instance)
|
|
361
348
|
}
|
|
362
349
|
|
|
363
350
|
/**
|
|
@@ -369,29 +356,18 @@ class VrpcRemote extends EventEmitter {
|
|
|
369
356
|
* @param {String} instance The instance to be deleted
|
|
370
357
|
* @param {Object} [options] Explicitly define agent and class
|
|
371
358
|
* @param {String} options.className Name of the instance's class
|
|
372
|
-
* @param {String} options.agent Agent name. If not provided class default is used
|
|
359
|
+
* @param {String} options.agent Agent name. If not provided class default is used as priority hit
|
|
373
360
|
* @returns {Promise<Boolean>} true if successful, false otherwise
|
|
374
361
|
*/
|
|
375
|
-
async delete (instance, options) {
|
|
376
|
-
|
|
377
|
-
if (typeof instance === 'string') {
|
|
378
|
-
instanceData = await this._getInstanceData(instance)
|
|
379
|
-
if (options) instanceData = { ...options, ...instanceData }
|
|
380
|
-
} else { // deprecate this
|
|
381
|
-
this._indicateDeprecation(
|
|
382
|
-
'VrpcRemote.delete(): ' +
|
|
383
|
-
'Using an object as single argument is deprecated, ' +
|
|
384
|
-
'use "delete(instance, options)" instead.'
|
|
385
|
-
)
|
|
386
|
-
instanceData = { ...instanceData, ...instance }
|
|
387
|
-
}
|
|
388
|
-
const { agent, className, instance: instanceString } = instanceData
|
|
362
|
+
async delete (instance, options = {}) {
|
|
363
|
+
const { agent, className } = await this._getInstanceData(instance, options)
|
|
389
364
|
const json = {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
365
|
+
c: className,
|
|
366
|
+
f: '__delete__',
|
|
367
|
+
i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
|
|
368
|
+
s: `${this._domain}/${os.hostname()}/${this._instance}`,
|
|
369
|
+
a: [instance],
|
|
370
|
+
v: VRPC_PROTOCOL_VERSION
|
|
395
371
|
}
|
|
396
372
|
const topic = `${this._domain}/${agent}/${className}/__static__/__delete__`
|
|
397
373
|
this._mqttPublish(topic, JSON.stringify(json))
|
|
@@ -415,12 +391,15 @@ class VrpcRemote extends EventEmitter {
|
|
|
415
391
|
args = [],
|
|
416
392
|
agent = this._agent
|
|
417
393
|
} = {}) {
|
|
394
|
+
const wrapped = this._wrapArguments(className, functionName, ...args)
|
|
395
|
+
if (!wrapped) return // Skipping remote call -> handled locally
|
|
418
396
|
const json = {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
397
|
+
c: className,
|
|
398
|
+
f: functionName,
|
|
399
|
+
i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
|
|
400
|
+
s: this._vrpcClientId,
|
|
401
|
+
a: wrapped,
|
|
402
|
+
v: VRPC_PROTOCOL_VERSION
|
|
424
403
|
}
|
|
425
404
|
const topic = `${this._domain}/${agent}/${className}/__static__/${functionName}`
|
|
426
405
|
await this._waitUntilClassIsOnline(agent, className)
|
|
@@ -430,19 +409,23 @@ class VrpcRemote extends EventEmitter {
|
|
|
430
409
|
|
|
431
410
|
/**
|
|
432
411
|
* Calls the same function on all instances of a given class and returns an
|
|
433
|
-
* aggregated result.
|
|
412
|
+
* aggregated result. It as well allows for batch event and callback
|
|
413
|
+
* registrations. In this case the instanceId of the emitter is injected as
|
|
414
|
+
* first argument of any event callback.
|
|
434
415
|
*
|
|
435
416
|
* NOTE: When no agent was specified as class default and no agent is
|
|
436
417
|
* specified when calling this function, callAll will act on the requested
|
|
437
|
-
* class across all available agents. The same is true when explicitly using
|
|
438
|
-
*
|
|
418
|
+
* class across all available agents. The same is true when explicitly using a
|
|
419
|
+
* wildcard (*) as agent value.
|
|
439
420
|
*
|
|
440
421
|
* @param {Object} options
|
|
441
422
|
* @param {String} options.className Name of the static function's class
|
|
442
|
-
* @param {Array} [options.args] Positional arguments of the static function
|
|
443
|
-
*
|
|
444
|
-
* @
|
|
445
|
-
*
|
|
423
|
+
* @param {Array} [options.args] Positional arguments of the static function
|
|
424
|
+
* call
|
|
425
|
+
* @param {String} [options.agent] Agent name. If not provided class default
|
|
426
|
+
* is used
|
|
427
|
+
* @returns {Promise<Object[]>} An array of objects `{ id, val, err }`
|
|
428
|
+
* carrying the instance id, the return value and potential errors
|
|
446
429
|
*/
|
|
447
430
|
async callAll ({
|
|
448
431
|
className,
|
|
@@ -450,20 +433,26 @@ class VrpcRemote extends EventEmitter {
|
|
|
450
433
|
args = [],
|
|
451
434
|
agent = this._agent
|
|
452
435
|
} = {}) {
|
|
453
|
-
const data = { _1: functionName }
|
|
454
|
-
args.forEach((value, index) => (data[`_${index + 2}`] = value))
|
|
455
436
|
const json = {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
437
|
+
c: className,
|
|
438
|
+
f: functionName,
|
|
439
|
+
i: `${this._instance}-${this._invokeId++ % Number.MAX_SAFE_INTEGER}`,
|
|
440
|
+
s: this._vrpcClientId,
|
|
441
|
+
a: [],
|
|
442
|
+
v: VRPC_PROTOCOL_VERSION
|
|
461
443
|
}
|
|
462
444
|
if (agent === '*') {
|
|
463
445
|
const result = []
|
|
464
446
|
const agents = this.getAvailableAgents()
|
|
465
447
|
for (const x of agents) {
|
|
466
448
|
const topic = `${this._domain}/${x}/${className}/__static__/__callAll__`
|
|
449
|
+
const wrapped = this._wrapArguments(
|
|
450
|
+
`${x}-${className}`,
|
|
451
|
+
functionName,
|
|
452
|
+
...args
|
|
453
|
+
)
|
|
454
|
+
if (!wrapped) continue // using cache
|
|
455
|
+
json.a = [functionName, ...wrapped]
|
|
467
456
|
await this._waitUntilClassIsOnline(x, className)
|
|
468
457
|
this._mqttPublish(topic, JSON.stringify(json))
|
|
469
458
|
const tmp = await this._handleAgentAnswer(json)
|
|
@@ -472,41 +461,22 @@ class VrpcRemote extends EventEmitter {
|
|
|
472
461
|
return result
|
|
473
462
|
}
|
|
474
463
|
const topic = `${this._domain}/${agent}/${className}/__static__/__callAll__`
|
|
464
|
+
const wrapped = this._wrapArguments(
|
|
465
|
+
`${agent}-${className}`,
|
|
466
|
+
functionName,
|
|
467
|
+
...args
|
|
468
|
+
)
|
|
469
|
+
if (!wrapped) return // using cache
|
|
470
|
+
json.a = [functionName, ...wrapped]
|
|
475
471
|
await this._waitUntilClassIsOnline(agent, className)
|
|
476
472
|
this._mqttPublish(topic, JSON.stringify(json))
|
|
477
473
|
return this._handleAgentAnswer(json)
|
|
478
474
|
}
|
|
479
475
|
|
|
480
|
-
/**
|
|
481
|
-
* Retrieves all agents, instances, classes, member and static
|
|
482
|
-
* functions potentially available for remote control.
|
|
483
|
-
* @deprecated
|
|
484
|
-
* @returns {Object} SystemInformation
|
|
485
|
-
* ```ts
|
|
486
|
-
* type SystemInformation = {
|
|
487
|
-
* [domain].agents[agent].status: string, // 'offline' or 'online'
|
|
488
|
-
* [domain].agents[agent].hostname: string,
|
|
489
|
-
* [domain].agents[agent].version: string,
|
|
490
|
-
* [domain].agents[agent].classes[className].instances: string[],
|
|
491
|
-
* [domain].agents[agent].classes[className].memberFunctions: string[],
|
|
492
|
-
* [domain].agents[agent].classes[className].staticFunctions: string[],
|
|
493
|
-
* [domain].agents[agent].classes[className].meta?: MetaData
|
|
494
|
-
* }
|
|
495
|
-
* ```
|
|
496
|
-
*/
|
|
497
|
-
getAvailabilities () {
|
|
498
|
-
this._indicateDeprecation(
|
|
499
|
-
'VrpcRemote.getAvailabilities(): ' +
|
|
500
|
-
'This function is deprecated and will be removed soon, ' +
|
|
501
|
-
'use `getSystemInformation()` instead.'
|
|
502
|
-
)
|
|
503
|
-
return { [this._domain]: { agents: this._agents } }
|
|
504
|
-
}
|
|
505
|
-
|
|
506
476
|
/**
|
|
507
477
|
* Retrieves all information about the currently available components.
|
|
508
478
|
*
|
|
509
|
-
|
|
479
|
+
* @returns {Object} SystemInformation
|
|
510
480
|
* ```ts
|
|
511
481
|
* type SystemInformation = {
|
|
512
482
|
* [agent].status: string, // 'offline' or 'online'
|
|
@@ -523,21 +493,6 @@ class VrpcRemote extends EventEmitter {
|
|
|
523
493
|
return this._agents
|
|
524
494
|
}
|
|
525
495
|
|
|
526
|
-
/**
|
|
527
|
-
* Retrieves all domains on which agents can be remote controlled
|
|
528
|
-
*
|
|
529
|
-
* @deprecated
|
|
530
|
-
* @returns {Array} Array of domain names
|
|
531
|
-
*/
|
|
532
|
-
getAvailableDomains () {
|
|
533
|
-
this._indicateDeprecation(
|
|
534
|
-
'VrpcRemote.getAvailableDomains(): ' +
|
|
535
|
-
'this function will be removed in the next major release, as the client' +
|
|
536
|
-
'works with a single domain anyways'
|
|
537
|
-
)
|
|
538
|
-
return [this._domain]
|
|
539
|
-
}
|
|
540
|
-
|
|
541
496
|
/**
|
|
542
497
|
* Retrieves all available agents.
|
|
543
498
|
*
|
|
@@ -545,19 +500,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
545
500
|
* @param {Boolean} [options.mustBeOnline=true] Only retrieve currently online agents
|
|
546
501
|
* @returns {Array} Array of agent names.
|
|
547
502
|
*/
|
|
548
|
-
getAvailableAgents (
|
|
549
|
-
/// DEPRECATION INFORMATION AND WORKAROUND - TO BE REMOVED IN 3.x
|
|
550
|
-
// `options = {}` was `domain = this._domain` before
|
|
551
|
-
if (typeof options === 'string') {
|
|
552
|
-
this._indicateDeprecation(
|
|
553
|
-
'VrpcRemote.getAvailableAgents(): ' +
|
|
554
|
-
'Providing a domain here has no effect and will be an error in future.'
|
|
555
|
-
)
|
|
556
|
-
options = { mustBeOnline: true }
|
|
557
|
-
}
|
|
558
|
-
///
|
|
559
|
-
|
|
560
|
-
const { mustBeOnline = true } = options
|
|
503
|
+
getAvailableAgents ({ mustBeOnline = true } = {}) {
|
|
561
504
|
const agents = []
|
|
562
505
|
if (mustBeOnline) {
|
|
563
506
|
// loop agents
|
|
@@ -578,31 +521,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
578
521
|
* @param {Boolean} [options.mustBeOnline=true] Only retrieve currently online classes
|
|
579
522
|
* @returns {Array} Array of class names.
|
|
580
523
|
*/
|
|
581
|
-
getAvailableClasses (
|
|
582
|
-
/// DEPRECATION INFORMATION AND WORKAROUND - TO BE REMOVED IN 3.x
|
|
583
|
-
let agent = this._agent
|
|
584
|
-
let mustBeOnline = true
|
|
585
|
-
// `options = {}` was `agent = this._agent` before
|
|
586
|
-
if (typeof options === 'string') {
|
|
587
|
-
this._indicateDeprecation(
|
|
588
|
-
'VrpcRemote.getAvailableClasses(): ' +
|
|
589
|
-
'setting the agent as string argument is deprecated.`' +
|
|
590
|
-
'Use `{ agent: <myAgent> }` instead.'
|
|
591
|
-
)
|
|
592
|
-
agent = options
|
|
593
|
-
} else {
|
|
594
|
-
agent = options.agent
|
|
595
|
-
mustBeOnline = options.mustBeOnline
|
|
596
|
-
}
|
|
597
|
-
// was `domain = this._domain` before
|
|
598
|
-
if (domain) {
|
|
599
|
-
this._indicateDeprecation(
|
|
600
|
-
'VrpcRemote.getAvailableClasses(): ' +
|
|
601
|
-
'Providing a domain here has no effect and will be an error in future.'
|
|
602
|
-
)
|
|
603
|
-
}
|
|
604
|
-
///
|
|
605
|
-
|
|
524
|
+
getAvailableClasses ({ agent = this._agent, mustBeOnline = true } = {}) {
|
|
606
525
|
if (agent === '*') throw new Error('Agent must be specified')
|
|
607
526
|
if (!this._agents[agent]) return []
|
|
608
527
|
const { classes, status } = this._agents[agent]
|
|
@@ -613,41 +532,17 @@ class VrpcRemote extends EventEmitter {
|
|
|
613
532
|
/**
|
|
614
533
|
* Retrieves all (named) instances on specific class and agent.
|
|
615
534
|
*
|
|
616
|
-
* @param {String} className Class name
|
|
617
535
|
* @param {Object} [options]
|
|
536
|
+
* @param {String} options.className Class name
|
|
618
537
|
* @param {String} [options.agent] Agent name. If not provided class default is used
|
|
619
538
|
* @param {Boolean} [options.mustBeOnline=true] Only retrieve currently online classes
|
|
620
539
|
* @returns {Array} Array of instance names
|
|
621
540
|
*/
|
|
622
|
-
getAvailableInstances (
|
|
541
|
+
getAvailableInstances ({
|
|
623
542
|
className,
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
) {
|
|
627
|
-
/// DEPRECATION INFORMATION AND WORKAROUND - TO BE REMOVED IN 3.x
|
|
628
|
-
let agent = this._agent
|
|
629
|
-
let mustBeOnline = true
|
|
630
|
-
// `options = {}` was `agent = this._agent` before
|
|
631
|
-
if (typeof options === 'string') {
|
|
632
|
-
this._indicateDeprecation(
|
|
633
|
-
'VrpcRemote.getAvailableInstances(): ' +
|
|
634
|
-
'Setting the agent as string argument is deprecated.`' +
|
|
635
|
-
'Use `{ agent: <myAgent> }` instead.'
|
|
636
|
-
)
|
|
637
|
-
agent = options
|
|
638
|
-
} else {
|
|
639
|
-
agent = options.agent
|
|
640
|
-
mustBeOnline = options.mustBeOnline
|
|
641
|
-
}
|
|
642
|
-
// was `domain = this._domain` before
|
|
643
|
-
if (domain) {
|
|
644
|
-
this._indicateDeprecation(
|
|
645
|
-
'VrpcRemote.getAvailableInstances(): ' +
|
|
646
|
-
'Providing a domain here has no effect and will be an error in future.'
|
|
647
|
-
)
|
|
648
|
-
}
|
|
649
|
-
///
|
|
650
|
-
|
|
543
|
+
agent = this._agent,
|
|
544
|
+
mustBeOnline = true
|
|
545
|
+
} = {}) {
|
|
651
546
|
if (agent === '*') throw new Error('Agent must be specified')
|
|
652
547
|
if (!this._agents[agent]) return []
|
|
653
548
|
const { classes, status } = this._agents[agent]
|
|
@@ -658,41 +553,17 @@ class VrpcRemote extends EventEmitter {
|
|
|
658
553
|
/**
|
|
659
554
|
* Retrieves all member functions of specific class and agent.
|
|
660
555
|
*
|
|
661
|
-
* @param {String} className Class name
|
|
662
556
|
* @param {Object} [options]
|
|
557
|
+
* @param {String} options.className Class name
|
|
663
558
|
* @param {String} [options.agent] Agent name. If not provided class default is used
|
|
664
559
|
* @param {Boolean} [options.mustBeOnline=true] Only retrieve currently online classes
|
|
665
560
|
* @returns {Array} Array of member function names
|
|
666
561
|
*/
|
|
667
|
-
getAvailableMemberFunctions (
|
|
562
|
+
getAvailableMemberFunctions ({
|
|
668
563
|
className,
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
) {
|
|
672
|
-
/// DEPRECATION INFORMATION AND WORKAROUND - TO BE REMOVED IN 3.x
|
|
673
|
-
let agent = this._agent
|
|
674
|
-
let mustBeOnline = true
|
|
675
|
-
// `options = {}` was `agent = this._agent` before
|
|
676
|
-
if (typeof options === 'string') {
|
|
677
|
-
this._indicateDeprecation(
|
|
678
|
-
'VrpcRemote.getAvailableMemberFunctions(): ' +
|
|
679
|
-
'Setting the agent as string argument is deprecated.`' +
|
|
680
|
-
'Use `{ agent: <myAgent> }` instead.'
|
|
681
|
-
)
|
|
682
|
-
agent = options
|
|
683
|
-
} else {
|
|
684
|
-
agent = options.agent
|
|
685
|
-
mustBeOnline = options.mustBeOnline
|
|
686
|
-
}
|
|
687
|
-
// was `domain = this._domain` before
|
|
688
|
-
if (domain) {
|
|
689
|
-
this._indicateDeprecation(
|
|
690
|
-
'VrpcRemote.getAvailableMemberFunctions(): ' +
|
|
691
|
-
'Providing a domain here has no effect and will be an error in future.'
|
|
692
|
-
)
|
|
693
|
-
}
|
|
694
|
-
///
|
|
695
|
-
|
|
564
|
+
agent = this._agent,
|
|
565
|
+
mustBeOnline = true
|
|
566
|
+
} = {}) {
|
|
696
567
|
if (agent === '*') throw new Error('Agent must be specified')
|
|
697
568
|
if (!this._agents[agent]) return []
|
|
698
569
|
const { classes, status } = this._agents[agent]
|
|
@@ -705,41 +576,17 @@ class VrpcRemote extends EventEmitter {
|
|
|
705
576
|
/**
|
|
706
577
|
* Retrieves all static functions of specific class and agent.
|
|
707
578
|
*
|
|
708
|
-
* @param {String} className Class name
|
|
709
579
|
* @param {Object} [options]
|
|
580
|
+
* @param {String} options.className Class name
|
|
710
581
|
* @param {String} [options.agent] Agent name. If not provided class default is used
|
|
711
582
|
* @param {Boolean} [options.mustBeOnline=true] Only retrieve currently online classes
|
|
712
583
|
* @returns {Array} Array of static function names
|
|
713
584
|
*/
|
|
714
|
-
getAvailableStaticFunctions (
|
|
585
|
+
getAvailableStaticFunctions ({
|
|
715
586
|
className,
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
) {
|
|
719
|
-
/// DEPRECATION INFORMATION AND WORKAROUND - TO BE REMOVED IN 3.x
|
|
720
|
-
let agent = this._agent
|
|
721
|
-
let mustBeOnline = true
|
|
722
|
-
// `options = {}` was `agent = this._agent` before
|
|
723
|
-
if (typeof options === 'string') {
|
|
724
|
-
this._indicateDeprecation(
|
|
725
|
-
'VrpcRemote.getAvailableStaticFunctions(): ' +
|
|
726
|
-
'Setting the agent as string argument is deprecated.`' +
|
|
727
|
-
'Use `{ agent: <myAgent> }` instead.'
|
|
728
|
-
)
|
|
729
|
-
agent = options
|
|
730
|
-
} else {
|
|
731
|
-
agent = options.agent
|
|
732
|
-
mustBeOnline = options.mustBeOnline
|
|
733
|
-
}
|
|
734
|
-
// was `domain = this._domain` before
|
|
735
|
-
if (domain) {
|
|
736
|
-
this._indicateDeprecation(
|
|
737
|
-
'VrpcRemote.getAvailableStaticFunctions(): ' +
|
|
738
|
-
'Providing a domain here has no effect and will be an error in future.'
|
|
739
|
-
)
|
|
740
|
-
}
|
|
741
|
-
///
|
|
742
|
-
|
|
587
|
+
agent = this._agent,
|
|
588
|
+
mustBeOnline = true
|
|
589
|
+
} = {}) {
|
|
743
590
|
if (agent === '*') throw new Error('Agent must be specified')
|
|
744
591
|
if (!this._agents[agent]) return []
|
|
745
592
|
const { classes, status } = this._agents[agent]
|
|
@@ -757,10 +604,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
757
604
|
* @param {String} options.agent Agent name. If not provided class default is used
|
|
758
605
|
* @returns {Promise} Promise that resolves once re-connected
|
|
759
606
|
*/
|
|
760
|
-
async reconnectWithToken (
|
|
761
|
-
token,
|
|
762
|
-
{ agent = this._agent } = {}
|
|
763
|
-
) {
|
|
607
|
+
async reconnectWithToken (token, { agent = this._agent } = {}) {
|
|
764
608
|
this._token = token
|
|
765
609
|
this._agent = agent
|
|
766
610
|
this._client.end(() => this.connect())
|
|
@@ -799,19 +643,29 @@ class VrpcRemote extends EventEmitter {
|
|
|
799
643
|
async end () {
|
|
800
644
|
this._mqttPublish(
|
|
801
645
|
`${this._vrpcClientId}/__clientInfo__`,
|
|
802
|
-
JSON.stringify({ status: 'offline' })
|
|
646
|
+
JSON.stringify({ status: 'offline', v: VRPC_PROTOCOL_VERSION })
|
|
803
647
|
)
|
|
804
648
|
return new Promise(resolve => this._client.end(false, {}, resolve))
|
|
805
649
|
}
|
|
806
650
|
|
|
807
651
|
_createMqttClientId () {
|
|
808
|
-
const clientInfo =
|
|
809
|
-
|
|
810
|
-
|
|
652
|
+
const clientInfo =
|
|
653
|
+
os.arch() +
|
|
654
|
+
JSON.stringify(os.cpus()) +
|
|
655
|
+
os.homedir() +
|
|
656
|
+
os.hostname() +
|
|
657
|
+
JSON.stringify(os.networkInterfaces()) +
|
|
658
|
+
os.platform() +
|
|
659
|
+
os.release() +
|
|
660
|
+
os.totalmem() +
|
|
661
|
+
os.type()
|
|
811
662
|
// console.log('ClientInfo:', clientInfo)
|
|
812
|
-
const md5 = crypto
|
|
813
|
-
|
|
814
|
-
|
|
663
|
+
const md5 = crypto
|
|
664
|
+
.createHash('md5')
|
|
665
|
+
.update(clientInfo)
|
|
666
|
+
.digest('hex')
|
|
667
|
+
.substr(0, 12)
|
|
668
|
+
return `vc3${this._instance}${md5}` // 3 + 8 + 12 = 23 (max clientId)
|
|
815
669
|
}
|
|
816
670
|
|
|
817
671
|
_createVrpcClientId () {
|
|
@@ -819,29 +673,44 @@ class VrpcRemote extends EventEmitter {
|
|
|
819
673
|
}
|
|
820
674
|
|
|
821
675
|
_mqttPublish (topic, message, options) {
|
|
822
|
-
this._client.publish(
|
|
823
|
-
|
|
824
|
-
|
|
676
|
+
this._client.publish(
|
|
677
|
+
topic,
|
|
678
|
+
message,
|
|
679
|
+
{ qos: this._qos, ...options },
|
|
680
|
+
err => {
|
|
681
|
+
if (err) {
|
|
682
|
+
this._log.warn(
|
|
683
|
+
`Could not publish MQTT message because: ${err.message}`
|
|
684
|
+
)
|
|
685
|
+
}
|
|
825
686
|
}
|
|
826
|
-
|
|
687
|
+
)
|
|
827
688
|
}
|
|
828
689
|
|
|
829
690
|
_mqttSubscribe (topic, options) {
|
|
830
|
-
this._client.subscribe(
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
if (
|
|
835
|
-
this._log.
|
|
691
|
+
this._client.subscribe(
|
|
692
|
+
topic,
|
|
693
|
+
{ qos: this._qos, ...options },
|
|
694
|
+
(err, granted) => {
|
|
695
|
+
if (err) {
|
|
696
|
+
this._log.warn(
|
|
697
|
+
`Could not subscribe to topic: ${topic} because: ${err.message}`
|
|
698
|
+
)
|
|
699
|
+
} else {
|
|
700
|
+
if (granted.length === 0) {
|
|
701
|
+
this._log.debug(`Already subscribed to: ${topic}`)
|
|
702
|
+
}
|
|
836
703
|
}
|
|
837
704
|
}
|
|
838
|
-
|
|
705
|
+
)
|
|
839
706
|
}
|
|
840
707
|
|
|
841
708
|
_mqttUnsubscribe (topic, options) {
|
|
842
|
-
this._client.unsubscribe(topic, options,
|
|
709
|
+
this._client.unsubscribe(topic, options, err => {
|
|
843
710
|
if (err) {
|
|
844
|
-
this._log.warn(
|
|
711
|
+
this._log.warn(
|
|
712
|
+
`Could not unsubscribe from topic: ${topic} because: ${err.message}`
|
|
713
|
+
)
|
|
845
714
|
}
|
|
846
715
|
})
|
|
847
716
|
}
|
|
@@ -853,25 +722,22 @@ class VrpcRemote extends EventEmitter {
|
|
|
853
722
|
}
|
|
854
723
|
|
|
855
724
|
async _getProxy (agent, className, json) {
|
|
856
|
-
const {
|
|
857
|
-
const topic = `${this._domain}/${agent}/${className}/__static__/${
|
|
725
|
+
const { f, i } = json
|
|
726
|
+
const topic = `${this._domain}/${agent}/${className}/__static__/${f}`
|
|
858
727
|
await this._waitUntilClassIsOnline(agent, className)
|
|
859
728
|
this._mqttPublish(topic, JSON.stringify(json))
|
|
860
729
|
return new Promise((resolve, reject) => {
|
|
861
730
|
const msg = `Proxy creation for class "${className}" on agent "${agent}" and domain "${this._domain}" timed out (> ${this._timeout} ms)`
|
|
862
|
-
const timer = setTimeout(
|
|
863
|
-
()
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
this._timeout
|
|
868
|
-
)
|
|
869
|
-
this._eventEmitter.once(json.id, data => {
|
|
731
|
+
const timer = setTimeout(() => {
|
|
732
|
+
this._eventEmitter.removeAllListeners(i)
|
|
733
|
+
reject(new Error(msg))
|
|
734
|
+
}, this._timeout)
|
|
735
|
+
this._eventEmitter.once(i, ({ e, r }) => {
|
|
870
736
|
clearTimeout(timer)
|
|
871
|
-
if (
|
|
872
|
-
reject(new Error(
|
|
737
|
+
if (e) {
|
|
738
|
+
reject(new Error(e))
|
|
873
739
|
} else {
|
|
874
|
-
const proxy = this._createProxy(agent, className,
|
|
740
|
+
const proxy = this._createProxy(agent, className, r)
|
|
875
741
|
resolve(proxy)
|
|
876
742
|
}
|
|
877
743
|
})
|
|
@@ -891,7 +757,11 @@ class VrpcRemote extends EventEmitter {
|
|
|
891
757
|
}
|
|
892
758
|
timer = setTimeout(() => {
|
|
893
759
|
this.removeListener('class', checkClass)
|
|
894
|
-
reject(
|
|
760
|
+
reject(
|
|
761
|
+
new Error(
|
|
762
|
+
`Proxy creation for class "${className}" on agent "${agent}" and domain "${this._domain}" timed out (> ${this._timeout} ms)`
|
|
763
|
+
)
|
|
764
|
+
)
|
|
895
765
|
}, this._timeout)
|
|
896
766
|
this.on('class', checkClass)
|
|
897
767
|
})
|
|
@@ -907,10 +777,11 @@ class VrpcRemote extends EventEmitter {
|
|
|
907
777
|
|
|
908
778
|
_createProxy (agent, className, instance) {
|
|
909
779
|
const targetTopic = `${this._domain}/${agent}/${className}/${instance}`
|
|
910
|
-
const proxyId =
|
|
780
|
+
const proxyId = `${this._instance}-${this._proxyId++}`
|
|
911
781
|
const proxy = {
|
|
912
|
-
|
|
913
|
-
|
|
782
|
+
vrpcClientId: this._vrpcClientId,
|
|
783
|
+
vrpcInstanceId: instance,
|
|
784
|
+
vrpcProxyId: proxyId
|
|
914
785
|
}
|
|
915
786
|
let functions = this._agents[agent].classes[className].memberFunctions
|
|
916
787
|
// Strip off argument signature
|
|
@@ -924,37 +795,38 @@ class VrpcRemote extends EventEmitter {
|
|
|
924
795
|
// Build proxy
|
|
925
796
|
uniqueFuncs.forEach(name => {
|
|
926
797
|
proxy[name] = async (...args) => {
|
|
798
|
+
const wrapped = this._wrapArguments(proxyId, name, ...args)
|
|
799
|
+
if (!wrapped) return // Skipping remote call -> handled locally
|
|
927
800
|
try {
|
|
928
801
|
const json = {
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
802
|
+
c: instance,
|
|
803
|
+
f: name,
|
|
804
|
+
i: `${this._instance}-${this._invokeId++ %
|
|
805
|
+
Number.MAX_SAFE_INTEGER}`,
|
|
806
|
+
s: this._vrpcClientId,
|
|
807
|
+
a: wrapped,
|
|
808
|
+
v: VRPC_PROTOCOL_VERSION
|
|
934
809
|
}
|
|
935
|
-
// Skipping remote call -> handled locally
|
|
936
|
-
if (json.data === null) return
|
|
937
810
|
this._mqttPublish(`${targetTopic}/${name}`, JSON.stringify(json))
|
|
938
811
|
return this._handleAgentAnswer(json)
|
|
939
812
|
} catch (err) {
|
|
940
|
-
throw new Error(
|
|
813
|
+
throw new Error(
|
|
814
|
+
`Could not remotely call "${name}" because: ${err.message}`
|
|
815
|
+
)
|
|
941
816
|
}
|
|
942
817
|
}
|
|
943
818
|
})
|
|
944
819
|
return proxy
|
|
945
820
|
}
|
|
946
821
|
|
|
947
|
-
async _handleAgentAnswer ({
|
|
822
|
+
async _handleAgentAnswer ({ i, c, f }) {
|
|
948
823
|
return new Promise((resolve, reject) => {
|
|
949
|
-
const msg = `Function call "${
|
|
950
|
-
const timer = setTimeout(
|
|
951
|
-
()
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
this._timeout
|
|
956
|
-
)
|
|
957
|
-
this._eventEmitter.once(id, data => {
|
|
824
|
+
const msg = `Function call "${c}::${f}()" timed out (> ${this._timeout} ms)`
|
|
825
|
+
const timer = setTimeout(() => {
|
|
826
|
+
this._eventEmitter.removeAllListeners(i)
|
|
827
|
+
reject(new Error(msg))
|
|
828
|
+
}, this._timeout)
|
|
829
|
+
this._eventEmitter.once(i, data => {
|
|
958
830
|
clearTimeout(timer)
|
|
959
831
|
if (data.e) {
|
|
960
832
|
reject(new Error(data.e))
|
|
@@ -976,7 +848,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
976
848
|
|
|
977
849
|
async _waitForInstance (instance, options = {}) {
|
|
978
850
|
return new Promise((resolve, reject) => {
|
|
979
|
-
const handler =
|
|
851
|
+
const handler = timer => (instances, { agent, className }) => {
|
|
980
852
|
if (instances.includes(instance)) {
|
|
981
853
|
if (options.agent && agent !== options.agent) return
|
|
982
854
|
if (options.className && className !== options.className) return
|
|
@@ -985,75 +857,64 @@ class VrpcRemote extends EventEmitter {
|
|
|
985
857
|
resolve({ agent, className, instance })
|
|
986
858
|
}
|
|
987
859
|
}
|
|
988
|
-
const timer = setTimeout(
|
|
989
|
-
()
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
},
|
|
994
|
-
this._timeout
|
|
995
|
-
)
|
|
860
|
+
const timer = setTimeout(() => {
|
|
861
|
+
this.removeListener('instanceNew', handler(timer))
|
|
862
|
+
const msg = `Could not find instance: ${instance} (> ${this._timeout} ms)`
|
|
863
|
+
reject(new Error(msg))
|
|
864
|
+
}, this._timeout)
|
|
996
865
|
this.on('instanceNew', handler(timer))
|
|
997
866
|
})
|
|
998
867
|
}
|
|
999
868
|
|
|
1000
|
-
|
|
1001
|
-
const
|
|
869
|
+
_wrapArguments (remoteId, functionName, ...args) {
|
|
870
|
+
const wrapped = []
|
|
1002
871
|
let isHandledLocally = false
|
|
1003
|
-
args.forEach((
|
|
872
|
+
args.forEach((x, i) => {
|
|
1004
873
|
// Check whether provided argument is a function
|
|
1005
|
-
if (this._isFunction(
|
|
874
|
+
if (this._isFunction(x)) {
|
|
1006
875
|
// Check special case of an event emitter registration
|
|
1007
876
|
// We test three conditions:
|
|
1008
877
|
// 1) functionName must be "on"
|
|
1009
878
|
// 2) callback is second argument
|
|
1010
879
|
// 3) first argument was string
|
|
1011
|
-
if (functionName === 'on' &&
|
|
1012
|
-
|
|
1013
|
-
typeof args[0] === 'string'
|
|
1014
|
-
) {
|
|
1015
|
-
const id = this._addEventSubscription(proxyId, args[0], value)
|
|
880
|
+
if (functionName === 'on' && i === 1 && typeof args[0] === 'string') {
|
|
881
|
+
const id = this._addEventSubscription(remoteId, args[0], x)
|
|
1016
882
|
if (!id) isHandledLocally = true
|
|
1017
|
-
|
|
883
|
+
wrapped.push(id)
|
|
1018
884
|
} else if (
|
|
1019
885
|
(functionName === 'off' || functionName === 'removeListener') &&
|
|
1020
|
-
|
|
886
|
+
i === 1 &&
|
|
1021
887
|
typeof args[0] === 'string'
|
|
1022
888
|
) {
|
|
1023
|
-
const id = this._removeEventSubscription(
|
|
889
|
+
const id = this._removeEventSubscription(remoteId, args[0], x)
|
|
1024
890
|
if (!id) isHandledLocally = true
|
|
1025
|
-
|
|
1026
|
-
|
|
891
|
+
wrapped.push(id)
|
|
892
|
+
// Regular function callback
|
|
1027
893
|
} else {
|
|
1028
|
-
const id = `__f__${
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
.map(x => data[x])
|
|
1034
|
-
value.apply(null, args) // This is the actual function call
|
|
894
|
+
const id = `__f__${remoteId}-${functionName}-${i}-${this._invokeId++ %
|
|
895
|
+
Number.MAX_SAFE_INTEGER}`
|
|
896
|
+
wrapped.push(id)
|
|
897
|
+
this._eventEmitter.once(id, ({ a }) => {
|
|
898
|
+
x.apply(null, a) // This is the actual function call
|
|
1035
899
|
})
|
|
1036
900
|
}
|
|
1037
|
-
} else if (this._isEmitter(
|
|
1038
|
-
const { emitter, event } =
|
|
1039
|
-
const id = `__f__${
|
|
1040
|
-
|
|
1041
|
-
this._eventEmitter.on(id,
|
|
1042
|
-
|
|
1043
|
-
.filter(x => x[0] === '_')
|
|
1044
|
-
.map(x => data[x])
|
|
1045
|
-
emitter.emit(event, ...args)
|
|
901
|
+
} else if (this._isEmitter(x)) {
|
|
902
|
+
const { emitter, event } = x
|
|
903
|
+
const id = `__f__${remoteId}-${functionName}-${i}-${event}`
|
|
904
|
+
wrapped.push(id)
|
|
905
|
+
this._eventEmitter.on(id, ({ a }) => {
|
|
906
|
+
emitter.emit(event, ...a)
|
|
1046
907
|
})
|
|
1047
908
|
} else {
|
|
1048
|
-
|
|
909
|
+
wrapped.push(x)
|
|
1049
910
|
}
|
|
1050
911
|
})
|
|
1051
912
|
if (isHandledLocally) return null
|
|
1052
|
-
return
|
|
913
|
+
return wrapped
|
|
1053
914
|
}
|
|
1054
915
|
|
|
1055
|
-
_addEventSubscription (
|
|
1056
|
-
const id = `__f__${
|
|
916
|
+
_addEventSubscription (remoteId, event, callback) {
|
|
917
|
+
const id = `__f__${remoteId}-${event}`
|
|
1057
918
|
if (this._cachedSubscriptions.has(id)) {
|
|
1058
919
|
this._cachedSubscriptions.get(id).on(id, callback)
|
|
1059
920
|
return
|
|
@@ -1061,17 +922,14 @@ class VrpcRemote extends EventEmitter {
|
|
|
1061
922
|
const emitter = new EventEmitter()
|
|
1062
923
|
emitter.on(id, callback)
|
|
1063
924
|
this._cachedSubscriptions.set(id, emitter)
|
|
1064
|
-
this._eventEmitter.on(id, (
|
|
1065
|
-
|
|
1066
|
-
.filter(x => x[0] === '_')
|
|
1067
|
-
.map(x => data[x])
|
|
1068
|
-
emitter.emit(id, ...args)
|
|
925
|
+
this._eventEmitter.on(id, ({ a }) => {
|
|
926
|
+
emitter.emit(id, ...a)
|
|
1069
927
|
})
|
|
1070
928
|
return id
|
|
1071
929
|
}
|
|
1072
930
|
|
|
1073
|
-
_removeEventSubscription (
|
|
1074
|
-
const id = `__f__${
|
|
931
|
+
_removeEventSubscription (remoteId, event, callback) {
|
|
932
|
+
const id = `__f__${remoteId}-${event}`
|
|
1075
933
|
if (this._cachedSubscriptions.has(id)) {
|
|
1076
934
|
const emitter = this._cachedSubscriptions.get(id)
|
|
1077
935
|
emitter.removeListener(id, callback)
|
|
@@ -1092,8 +950,10 @@ class VrpcRemote extends EventEmitter {
|
|
|
1092
950
|
_isFunction (variable) {
|
|
1093
951
|
const getType = {}
|
|
1094
952
|
const type = getType.toString.call(variable)
|
|
1095
|
-
return
|
|
953
|
+
return (
|
|
954
|
+
variable &&
|
|
1096
955
|
(type === '[object Function]' || type === '[object AsyncFunction]')
|
|
956
|
+
)
|
|
1097
957
|
}
|
|
1098
958
|
|
|
1099
959
|
_isEmitter (variable) {
|
|
@@ -1106,16 +966,33 @@ class VrpcRemote extends EventEmitter {
|
|
|
1106
966
|
)
|
|
1107
967
|
}
|
|
1108
968
|
|
|
1109
|
-
async _getInstanceData (instance, options
|
|
969
|
+
async _getInstanceData (instance, options) {
|
|
1110
970
|
const instanceData = this._getInstanceFromCache(instance, options)
|
|
1111
971
|
if (!instanceData) {
|
|
1112
|
-
if (options.noWait)
|
|
972
|
+
if (options.noWait) {
|
|
973
|
+
throw new Error(`Could not find instance: ${instance}`)
|
|
974
|
+
}
|
|
1113
975
|
return this._waitForInstance(instance, options)
|
|
1114
976
|
}
|
|
1115
977
|
return instanceData
|
|
1116
978
|
}
|
|
1117
979
|
|
|
1118
980
|
_getInstanceFromCache (instance, options = {}) {
|
|
981
|
+
// when no agent is specified, but an explicit class default exists give
|
|
982
|
+
// such instances priority in being found
|
|
983
|
+
if (!options.agent && this._agent !== '*') {
|
|
984
|
+
const { classes, status } = this._agents[this._agent]
|
|
985
|
+
if (!classes || status === 'offline') {
|
|
986
|
+
for (const className in classes) {
|
|
987
|
+
if (options.className && className !== options.className) continue
|
|
988
|
+
const { instances } = classes[className]
|
|
989
|
+
if (!instances) continue
|
|
990
|
+
if (instances.includes(instance)) {
|
|
991
|
+
return { agent, className, instance }
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
1119
996
|
// loop agents
|
|
1120
997
|
for (const agent in this._agents) {
|
|
1121
998
|
if (options.agent && agent !== options.agent) continue
|
|
@@ -1148,7 +1025,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
1148
1025
|
* This event is fired whenever an agent is added or removed, or whenever
|
|
1149
1026
|
* an agent changes its status (switches between online or offline).
|
|
1150
1027
|
*
|
|
1151
|
-
* @event
|
|
1028
|
+
* @event VrpcClient#agent
|
|
1152
1029
|
* @param {Object} info
|
|
1153
1030
|
* @param {String} info.domain - Domain name
|
|
1154
1031
|
* @param {String} info.agent - Agent name
|
|
@@ -1163,7 +1040,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
1163
1040
|
* Emitted whenever a class is added or removed, or when instances
|
|
1164
1041
|
* or functions of this class have changed.
|
|
1165
1042
|
*
|
|
1166
|
-
* @event
|
|
1043
|
+
* @event VrpcClient#class
|
|
1167
1044
|
* @param {Object} info
|
|
1168
1045
|
* @param {String} info.domain - Domain name
|
|
1169
1046
|
* @param {String} info.agent - Agent name
|
|
@@ -1175,57 +1052,57 @@ class VrpcRemote extends EventEmitter {
|
|
|
1175
1052
|
*/
|
|
1176
1053
|
|
|
1177
1054
|
/**
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1055
|
+
* Event 'instanceNew'
|
|
1056
|
+
*
|
|
1057
|
+
* Emitted whenever a new instance was created.
|
|
1058
|
+
*
|
|
1059
|
+
* @event VrpcClient#instanceNew
|
|
1060
|
+
*
|
|
1061
|
+
* @param {Array.<String>} addedInstances - An array of newly added instances
|
|
1062
|
+
* @param {Object} info
|
|
1063
|
+
* @param {String} info.domain - Domain name
|
|
1064
|
+
* @param {String} info.agent - Agent name
|
|
1065
|
+
* @param {String} info.className - Class name
|
|
1066
|
+
*/
|
|
1190
1067
|
|
|
1191
1068
|
/**
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1069
|
+
* Event 'instanceGone'
|
|
1070
|
+
*
|
|
1071
|
+
* Emitted whenever a new instance was removed.
|
|
1072
|
+
*
|
|
1073
|
+
* @event VrpcClient#instanceGone
|
|
1074
|
+
*
|
|
1075
|
+
* @param {Array.<String>} removedInstances - An array of removed instances
|
|
1076
|
+
* @param {Object} info
|
|
1077
|
+
* @param {String} info.domain - Domain name
|
|
1078
|
+
* @param {String} info.agent - Agent name
|
|
1079
|
+
* @param {String} info.className - Class name
|
|
1080
|
+
*/
|
|
1204
1081
|
|
|
1205
1082
|
/**
|
|
1206
1083
|
* Event 'connect'
|
|
1207
1084
|
*
|
|
1208
1085
|
* Emitted on successful (re)connection (i.e. connack rc=0).
|
|
1209
1086
|
*
|
|
1210
|
-
* @event
|
|
1087
|
+
* @event VrpcClient#connect
|
|
1211
1088
|
* @type {Object}
|
|
1212
1089
|
* @property {Boolean} sessionPresent - A session from a previous connection is already present
|
|
1213
|
-
*/
|
|
1090
|
+
*/
|
|
1214
1091
|
|
|
1215
1092
|
/**
|
|
1216
1093
|
* Event 'reconnect'
|
|
1217
1094
|
*
|
|
1218
1095
|
* Emitted when a reconnect starts.
|
|
1219
1096
|
*
|
|
1220
|
-
* @event
|
|
1221
|
-
*/
|
|
1097
|
+
* @event VrpcClient#reconnect
|
|
1098
|
+
*/
|
|
1222
1099
|
|
|
1223
1100
|
/**
|
|
1224
1101
|
* Event 'close'
|
|
1225
1102
|
*
|
|
1226
1103
|
* Emitted after a disconnection.
|
|
1227
1104
|
*
|
|
1228
|
-
* @event
|
|
1105
|
+
* @event VrpcClient#close
|
|
1229
1106
|
*/
|
|
1230
1107
|
|
|
1231
1108
|
/**
|
|
@@ -1233,7 +1110,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
1233
1110
|
*
|
|
1234
1111
|
* Emitted when the client goes offline.
|
|
1235
1112
|
*
|
|
1236
|
-
* @event
|
|
1113
|
+
* @event VrpcClient#offline
|
|
1237
1114
|
*/
|
|
1238
1115
|
|
|
1239
1116
|
/**
|
|
@@ -1248,7 +1125,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
1248
1125
|
* - EADDRINUSE
|
|
1249
1126
|
* - ENOTFOUND
|
|
1250
1127
|
*
|
|
1251
|
-
* @event
|
|
1128
|
+
* @event VrpcClient#error
|
|
1252
1129
|
* @type {Object} Error
|
|
1253
1130
|
*/
|
|
1254
1131
|
|
|
@@ -1258,7 +1135,7 @@ class VrpcRemote extends EventEmitter {
|
|
|
1258
1135
|
* Emitted when mqtt.Client#end() is called. If a callback was passed to
|
|
1259
1136
|
* mqtt.Client#end(), this event is emitted once the callback returns.
|
|
1260
1137
|
*
|
|
1261
|
-
* @event
|
|
1138
|
+
* @event VrpcClient#end
|
|
1262
1139
|
*/
|
|
1263
1140
|
|
|
1264
|
-
module.exports =
|
|
1141
|
+
module.exports = VrpcClient
|