@alwaysai/device-agent 2.0.1 → 2.0.2
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/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +5 -1
- package/lib/application-control/install.js.map +1 -1
- package/lib/cloud-connection/connection-manager.d.ts +4 -6
- package/lib/cloud-connection/connection-manager.d.ts.map +1 -1
- package/lib/cloud-connection/connection-manager.js +23 -35
- package/lib/cloud-connection/connection-manager.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +3 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +51 -40
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/publisher.d.ts +3 -2
- package/lib/cloud-connection/publisher.d.ts.map +1 -1
- package/lib/cloud-connection/publisher.js +8 -4
- package/lib/cloud-connection/publisher.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.d.ts +1 -0
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +3 -0
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.test.js +5 -2
- package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.d.ts +1 -3
- package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
- package/lib/cloud-connection/transaction-manager.js +1 -2
- package/lib/cloud-connection/transaction-manager.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.test.js +12 -24
- package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
- package/lib/environment.d.ts +3 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js +12 -1
- package/lib/environment.js.map +1 -1
- package/lib/jobs/job-handler.d.ts +1 -1
- package/lib/jobs/job-handler.d.ts.map +1 -1
- package/lib/jobs/job-handler.js +4 -4
- package/lib/jobs/job-handler.js.map +1 -1
- package/lib/subcommands/app/analytics.d.ts.map +1 -1
- package/lib/subcommands/app/analytics.js +2 -3
- package/lib/subcommands/app/analytics.js.map +1 -1
- package/lib/subcommands/app/env-vars.d.ts.map +1 -1
- package/lib/subcommands/app/env-vars.js +4 -6
- package/lib/subcommands/app/env-vars.js.map +1 -1
- package/lib/subcommands/app/models.d.ts.map +1 -1
- package/lib/subcommands/app/models.js +2 -3
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/app/shadow.d.ts.map +1 -1
- package/lib/subcommands/app/shadow.js +4 -6
- package/lib/subcommands/app/shadow.js.map +1 -1
- package/lib/subcommands/app/version.d.ts.map +1 -1
- package/lib/subcommands/app/version.js +6 -9
- package/lib/subcommands/app/version.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +16 -4
- package/src/application-control/install.ts +8 -3
- package/src/cloud-connection/connection-manager.ts +24 -37
- package/src/cloud-connection/device-agent-cloud-connection.ts +94 -88
- package/src/cloud-connection/publisher.ts +28 -23
- package/src/cloud-connection/shadow-handler.test.ts +6 -2
- package/src/cloud-connection/shadow-handler.ts +4 -0
- package/src/cloud-connection/transaction-manager.test.ts +13 -44
- package/src/cloud-connection/transaction-manager.ts +1 -4
- package/src/environment.ts +18 -0
- package/src/jobs/job-handler.ts +4 -4
- package/src/subcommands/app/analytics.ts +2 -3
- package/src/subcommands/app/env-vars.ts +4 -6
- package/src/subcommands/app/models.ts +2 -3
- package/src/subcommands/app/shadow.ts +4 -6
- package/src/subcommands/app/version.ts +7 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/subcommands/app/version.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/subcommands/app/version.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,eAAe,yKAQ1B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;4DAiE5B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;4DA4B9B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;4DAc7B,CAAC"}
|
|
@@ -6,7 +6,6 @@ const device_agent_schemas_1 = require("@alwaysai/device-agent-schemas");
|
|
|
6
6
|
const application_control_1 = require("../../application-control");
|
|
7
7
|
const device_agent_cloud_connection_1 = require("../../cloud-connection/device-agent-cloud-connection");
|
|
8
8
|
const agent_config_1 = require("../../infrastructure/agent-config");
|
|
9
|
-
const sleep_1 = require("../../util/sleep");
|
|
10
9
|
const logger_1 = require("../../util/logger");
|
|
11
10
|
exports.listAppsCliLeaf = (0, alwayscli_1.CliLeaf)({
|
|
12
11
|
name: 'list',
|
|
@@ -69,10 +68,9 @@ exports.installAppCliLeaf = (0, alwayscli_1.CliLeaf)({
|
|
|
69
68
|
envVars
|
|
70
69
|
}
|
|
71
70
|
};
|
|
72
|
-
await deviceAgent.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
71
|
+
await deviceAgent.waitForConnection();
|
|
72
|
+
await deviceAgent.sendCliCmd(topic, message);
|
|
73
|
+
await deviceAgent.waitForCmd(project);
|
|
76
74
|
await deviceAgent.stop();
|
|
77
75
|
}
|
|
78
76
|
});
|
|
@@ -99,10 +97,9 @@ exports.uninstallAppCliLeaf = (0, alwayscli_1.CliLeaf)({
|
|
|
99
97
|
projectId: project
|
|
100
98
|
}
|
|
101
99
|
};
|
|
102
|
-
await deviceAgent.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
100
|
+
await deviceAgent.waitForConnection();
|
|
101
|
+
await deviceAgent.sendCliCmd(topic, message);
|
|
102
|
+
await deviceAgent.waitForCmd(project);
|
|
106
103
|
await deviceAgent.stop();
|
|
107
104
|
}
|
|
108
105
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/subcommands/app/version.ts"],"names":[],"mappings":";;;AAAA,mDAA6E;AAC7E,yEAKwC;AACxC,mEAAwD;AACxD,wGAAkG;AAClG,oEAAoE;
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/subcommands/app/version.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -53,14 +53,23 @@ change.
|
|
|
53
53
|
* Passwordless `sudo` for `/sbin/shutdown` for device restart functionality
|
|
54
54
|
|
|
55
55
|
To enable passwordless `sudo` for `npm` and `sbin/shutdown` for the current
|
|
56
|
-
user, run `sudo visudo` and add the following lines
|
|
56
|
+
user, run `sudo visudo` and add the following lines, where `<username>` is replaced with your username on the device:
|
|
57
|
+
|
|
58
|
+
> **Note**: It is critical that these lines be added to the **end of the file**.
|
|
57
59
|
|
|
58
60
|
```bash
|
|
59
|
-
<username>
|
|
60
|
-
<username>
|
|
61
|
+
<username> ALL = NOPASSWD: /usr/bin/pm2
|
|
62
|
+
<username> ALL = NOPASSWD: /usr/lib/node_modules/pm2/bin/pm2
|
|
63
|
+
<username> ALL = NOPASSWD: /usr/bin/npm
|
|
64
|
+
<username> ALL = NOPASSWD: /sbin/shutdown
|
|
61
65
|
```
|
|
62
66
|
|
|
63
|
-
On a linux system
|
|
67
|
+
On a linux system, the username can be obtained by typing `whoami` into the terminal. For example:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
$ whoami
|
|
71
|
+
alwaysAI
|
|
72
|
+
```
|
|
64
73
|
|
|
65
74
|
## Provision Device
|
|
66
75
|
|
|
@@ -429,6 +438,7 @@ $ pm2 delete 0
|
|
|
429
438
|
$ pm2 flush
|
|
430
439
|
$ pm2 unstartup
|
|
431
440
|
```
|
|
441
|
+
|
|
432
442
|
### Remove the device configuration
|
|
433
443
|
|
|
434
444
|
Open a new terminal and run `aai-agent device clean`.
|
|
@@ -443,3 +453,5 @@ running `aai-agent`. You should see a response similar to
|
|
|
443
453
|
|
|
444
454
|
Go to the [Devices](https://console.alwaysai.co/dashboard/devices) tab, find the
|
|
445
455
|
device in question, and remove the device using the trashcan icon.
|
|
456
|
+
|
|
457
|
+
|
|
@@ -16,7 +16,8 @@ import {
|
|
|
16
16
|
appCleanDocker,
|
|
17
17
|
getPythonVenvPaths,
|
|
18
18
|
createPythonVenv,
|
|
19
|
-
installPythonReqs
|
|
19
|
+
installPythonReqs,
|
|
20
|
+
TargetHardware
|
|
20
21
|
} from 'alwaysai/lib/core/app';
|
|
21
22
|
import { DOCKER_IMAGE_ID_INITIAL_VALUE } from 'alwaysai/lib/constants';
|
|
22
23
|
import { runInDir } from '../util/run-in-dir';
|
|
@@ -30,6 +31,7 @@ import {
|
|
|
30
31
|
} from '../local-connection/constants';
|
|
31
32
|
import { DOCKERFILE, PYTHON_REQUIREMENTS_FILE_NAME } from 'alwaysai/lib/paths';
|
|
32
33
|
import { downloadToFile } from '../util/file';
|
|
34
|
+
import { ALWAYSAI_TARGET_HW_OVERRIDE, parseTargetHW } from '../environment';
|
|
33
35
|
import { setEnvInternal } from './environment-variables';
|
|
34
36
|
import { AppConfig } from '@alwaysai/app-configuration-schemas';
|
|
35
37
|
|
|
@@ -234,15 +236,18 @@ async function checkValidProjectFiles({ appDir }) {
|
|
|
234
236
|
if (!AppJsonFile(appDir).readIfExists()) {
|
|
235
237
|
throw new Error('App JSON file does not exist!');
|
|
236
238
|
}
|
|
237
|
-
|
|
238
239
|
// write target json
|
|
239
240
|
if (!fs.existsSync(path.join(appDir, DOCKERFILE))) {
|
|
240
241
|
throw new Error('No Dockerfile found for application!');
|
|
241
242
|
}
|
|
243
|
+
const targetHw: TargetHardware =
|
|
244
|
+
parseTargetHW(ALWAYSAI_TARGET_HW_OVERRIDE) ??
|
|
245
|
+
(await getTargetHardwareType({}));
|
|
246
|
+
logger.debug(`Target Hardware selected to: ${targetHw}`);
|
|
242
247
|
TargetJsonFile(appDir).write({
|
|
243
248
|
targetProtocol: 'docker:',
|
|
244
249
|
dockerImageId: DOCKER_IMAGE_ID_INITIAL_VALUE,
|
|
245
|
-
targetHardware:
|
|
250
|
+
targetHardware: targetHw
|
|
246
251
|
});
|
|
247
252
|
}
|
|
248
253
|
|
|
@@ -16,40 +16,32 @@ export class ConnectionManager extends MessageDispatcher<any> {
|
|
|
16
16
|
private clientId: string;
|
|
17
17
|
private host: string;
|
|
18
18
|
private port: number;
|
|
19
|
-
private device
|
|
19
|
+
private device: awsIot.device;
|
|
20
20
|
private subscribedTopics: Set<string> = new Set();
|
|
21
|
+
private connected = false;
|
|
21
22
|
|
|
22
23
|
constructor(clientId: string, host: string, port: number) {
|
|
23
24
|
super();
|
|
24
25
|
this.clientId = clientId;
|
|
25
26
|
this.host = host;
|
|
26
27
|
this.port = port;
|
|
28
|
+
this.device = awsIot.device({
|
|
29
|
+
keyPath: DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
30
|
+
certPath: DEVICE_CERTIFICATE_FILE_PATH,
|
|
31
|
+
caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
32
|
+
clientId: this.clientId,
|
|
33
|
+
host: this.host,
|
|
34
|
+
port: this.port,
|
|
35
|
+
keepalive: 10 // time before re-connect attempt on dropped connection, default is 400 seconds
|
|
36
|
+
});
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
public getIoTDevice() {
|
|
30
40
|
return this.device;
|
|
31
41
|
}
|
|
32
42
|
|
|
33
|
-
public
|
|
34
|
-
this.
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public connect(): void {
|
|
38
|
-
try {
|
|
39
|
-
this.device = awsIot.device({
|
|
40
|
-
keyPath: DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
41
|
-
certPath: DEVICE_CERTIFICATE_FILE_PATH,
|
|
42
|
-
caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
43
|
-
clientId: this.clientId,
|
|
44
|
-
host: this.host,
|
|
45
|
-
port: this.port,
|
|
46
|
-
keepalive: 10 // time before re-connect attempt on dropped connection, default is 400 seconds
|
|
47
|
-
});
|
|
48
|
-
this.setupHandlers();
|
|
49
|
-
} catch (error) {
|
|
50
|
-
logger.error(`Error connecting to cloud!\n${stringifyError(error)}`);
|
|
51
|
-
this.handleReconnection();
|
|
52
|
-
}
|
|
43
|
+
public isConnected(): boolean {
|
|
44
|
+
return this.connected;
|
|
53
45
|
}
|
|
54
46
|
|
|
55
47
|
public registerHandler(topic: string, handler: MessageHandler) {
|
|
@@ -58,14 +50,12 @@ export class ConnectionManager extends MessageDispatcher<any> {
|
|
|
58
50
|
}
|
|
59
51
|
|
|
60
52
|
public disconnect(): void {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
logger.debug(`Device Agent has been disconnected from the AWS IoT Core.`);
|
|
64
|
-
}
|
|
53
|
+
this.device.end();
|
|
54
|
+
logger.debug(`Device Agent has been disconnected from the AWS IoT Core.`);
|
|
65
55
|
}
|
|
66
56
|
|
|
67
57
|
public subscribe(topic: string): void {
|
|
68
|
-
if (
|
|
58
|
+
if (!this.subscribedTopics.has(topic)) {
|
|
69
59
|
this.device.subscribe(topic);
|
|
70
60
|
this.subscribedTopics.add(topic);
|
|
71
61
|
logger.debug(`Subscribed to topic: ${topic}`);
|
|
@@ -73,29 +63,23 @@ export class ConnectionManager extends MessageDispatcher<any> {
|
|
|
73
63
|
}
|
|
74
64
|
|
|
75
65
|
public unsubscribe(topic: string): void {
|
|
76
|
-
if (this.
|
|
66
|
+
if (this.subscribedTopics.has(topic)) {
|
|
77
67
|
this.device.unsubscribe(topic);
|
|
78
68
|
this.subscribedTopics.delete(topic);
|
|
79
69
|
logger.debug(`Unsubscribed from topic: ${topic}`);
|
|
80
70
|
}
|
|
81
71
|
}
|
|
82
72
|
|
|
83
|
-
public
|
|
84
|
-
return this.device ? this.device.connected : false;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
private handleReconnection() {
|
|
88
|
-
logger.debug(`Attempting to reconnect to AWS IoT Core...`);
|
|
89
|
-
setTimeout(() => this.connect(), 5000); // try in 5 seconds.
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
private setupHandlers(): void {
|
|
73
|
+
public initConnectionHandlers(connectCallback: () => void): void {
|
|
93
74
|
this.device.on('connect', (connack: any) => {
|
|
94
75
|
logger.info('Device Agent has connected to the cloud.');
|
|
76
|
+
this.connected = true;
|
|
77
|
+
connectCallback();
|
|
95
78
|
});
|
|
96
79
|
|
|
97
80
|
this.device.on('disconnect', () => {
|
|
98
81
|
logger.warn('Device Agent has been disconnected from the cloud');
|
|
82
|
+
this.connected = false;
|
|
99
83
|
});
|
|
100
84
|
|
|
101
85
|
this.device.on('reconnect', () => {
|
|
@@ -108,15 +92,18 @@ export class ConnectionManager extends MessageDispatcher<any> {
|
|
|
108
92
|
logger.error(
|
|
109
93
|
`Error connecting to the AWS IoT Core!\n${stringifyError(e)}`
|
|
110
94
|
);
|
|
95
|
+
this.connected = false;
|
|
111
96
|
});
|
|
112
97
|
|
|
113
98
|
this.device.on('close', () => {
|
|
114
99
|
logger.warn('Device Agent AWS IoT Core connection closed.');
|
|
100
|
+
this.connected = false;
|
|
115
101
|
});
|
|
116
102
|
|
|
117
103
|
this.device.on('offline', () => {
|
|
118
104
|
logger.warn(`Device Agent is offline ${new Date().toLocaleString()}`);
|
|
119
105
|
void this.logConnectionInfo();
|
|
106
|
+
this.connected = false;
|
|
120
107
|
});
|
|
121
108
|
|
|
122
109
|
this.device.on('message', async (topic: string, payload: string) => {
|
|
@@ -41,109 +41,100 @@ export class DeviceAgentCloudConnection {
|
|
|
41
41
|
SecureTunnelHandlerSingleton.getInstance();
|
|
42
42
|
|
|
43
43
|
constructor() {
|
|
44
|
-
this.liveUpdatesHandler = new LiveUpdatesHandler();
|
|
45
|
-
|
|
46
44
|
// Initialize & setup the connection
|
|
47
45
|
this.connectionManager = new ConnectionManager(
|
|
48
46
|
this.clientId,
|
|
49
47
|
this.host,
|
|
50
48
|
this.port
|
|
51
49
|
);
|
|
52
|
-
this.connectionManager.setupConnection();
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
this.publisher = new Publisher(
|
|
56
|
-
this.connectionManager.getIoTDevice(),
|
|
57
|
-
this.clientId
|
|
58
|
-
);
|
|
51
|
+
this.publisher = new Publisher(this.connectionManager, this.clientId);
|
|
59
52
|
this.shadowHandler = new ShadowHandler(this.clientId, this.publisher);
|
|
60
|
-
|
|
61
|
-
this.transactionManager = new TransactionManager(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
53
|
+
this.liveUpdatesHandler = new LiveUpdatesHandler();
|
|
54
|
+
this.transactionManager = new TransactionManager(this.liveUpdatesHandler);
|
|
55
|
+
|
|
56
|
+
this.connectionManager.initConnectionHandlers(() => {
|
|
57
|
+
// Construct a HandlerContext used by all the message handlers
|
|
58
|
+
const handlerContext: HandlerContext = {
|
|
59
|
+
clientId: this.clientId,
|
|
60
|
+
txnMgr: this.transactionManager,
|
|
61
|
+
publisher: this.publisher,
|
|
62
|
+
shadowHandler: this.shadowHandler,
|
|
63
|
+
liveUpdatesHandler: this.liveUpdatesHandler,
|
|
64
|
+
secureTunnelHandler: this.secureTunnelHandler
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Instantiate & register message handlers for Project Shadow topics
|
|
68
|
+
const projectShadowMessageHandler = new ProjectShadowMessageHandler(
|
|
69
|
+
handlerContext
|
|
70
|
+
);
|
|
71
|
+
this.shadowHandler.projectShadowTopics.forEach((topic) => {
|
|
72
|
+
this.connectionManager.registerHandler(
|
|
73
|
+
topic,
|
|
74
|
+
projectShadowMessageHandler
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Instantiate & register message handlers for to-device and secureTunnel topics
|
|
81
79
|
this.connectionManager.registerHandler(
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
this.toDeviceTopic,
|
|
81
|
+
new DeviceAgentMessageHandler(
|
|
82
|
+
handlerContext,
|
|
83
|
+
(txId: string, errorMsg: string) => {
|
|
84
|
+
const msg = buildToClientStatusResponseMessage(
|
|
85
|
+
this.publisher.getClientId(),
|
|
86
|
+
{
|
|
87
|
+
status: keyMirrors.statusResponse.failure,
|
|
88
|
+
message: errorMsg
|
|
89
|
+
},
|
|
90
|
+
txId
|
|
91
|
+
);
|
|
92
|
+
this.publisher.publishToClient(msg);
|
|
93
|
+
},
|
|
94
|
+
(txId: string) => {
|
|
95
|
+
const msg = buildToClientStatusResponseMessage(
|
|
96
|
+
this.publisher.getClientId(),
|
|
97
|
+
{ status: keyMirrors.statusResponse.success },
|
|
98
|
+
txId
|
|
99
|
+
);
|
|
100
|
+
this.publisher.publishToClient(msg);
|
|
101
|
+
}
|
|
102
|
+
)
|
|
84
103
|
);
|
|
85
|
-
});
|
|
86
104
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
this.publisher.getClientId(),
|
|
95
|
-
{
|
|
96
|
-
status: keyMirrors.statusResponse.failure,
|
|
97
|
-
message: errorMsg
|
|
98
|
-
},
|
|
99
|
-
txId
|
|
100
|
-
);
|
|
101
|
-
this.publisher.publishToClient(msg);
|
|
102
|
-
},
|
|
103
|
-
(txId: string) => {
|
|
104
|
-
const msg = buildToClientStatusResponseMessage(
|
|
105
|
-
this.publisher.getClientId(),
|
|
106
|
-
{ status: keyMirrors.statusResponse.success },
|
|
107
|
-
txId
|
|
108
|
-
);
|
|
109
|
-
this.publisher.publishToClient(msg);
|
|
110
|
-
}
|
|
111
|
-
)
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
const secureTunnelMessageHandler = new SecureTunnelMessageHandler(
|
|
115
|
-
handlerContext
|
|
116
|
-
);
|
|
117
|
-
this.connectionManager.registerHandler(
|
|
118
|
-
secureTunnelMessageHandler.getNotifyTopic(),
|
|
119
|
-
secureTunnelMessageHandler
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
this.connectionManager.registerHandler(
|
|
123
|
-
this.shadowHandler.shadowTopics.secureTunnel.updateDelta,
|
|
124
|
-
secureTunnelMessageHandler
|
|
125
|
-
);
|
|
126
|
-
this.connectionManager.registerHandler(
|
|
127
|
-
this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted,
|
|
128
|
-
secureTunnelMessageHandler
|
|
129
|
-
);
|
|
105
|
+
const secureTunnelMessageHandler = new SecureTunnelMessageHandler(
|
|
106
|
+
handlerContext
|
|
107
|
+
);
|
|
108
|
+
this.connectionManager.registerHandler(
|
|
109
|
+
secureTunnelMessageHandler.getNotifyTopic(),
|
|
110
|
+
secureTunnelMessageHandler
|
|
111
|
+
);
|
|
130
112
|
|
|
131
|
-
|
|
113
|
+
this.connectionManager.registerHandler(
|
|
114
|
+
this.shadowHandler.shadowTopics.secureTunnel.updateDelta,
|
|
115
|
+
secureTunnelMessageHandler
|
|
116
|
+
);
|
|
117
|
+
this.connectionManager.registerHandler(
|
|
118
|
+
this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted,
|
|
119
|
+
secureTunnelMessageHandler
|
|
120
|
+
);
|
|
132
121
|
|
|
133
|
-
|
|
122
|
+
const jobHandler = new JobHandler(handlerContext);
|
|
123
|
+
const JOB_HANDLER_TOPICS = jobHandler.getJobTopics();
|
|
134
124
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
125
|
+
this.connectionManager.registerHandler(
|
|
126
|
+
JOB_HANDLER_TOPICS.NOTIFY_NEXT,
|
|
127
|
+
jobHandler
|
|
128
|
+
);
|
|
139
129
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
130
|
+
this.connectionManager.registerHandler(
|
|
131
|
+
JOB_HANDLER_TOPICS.START_NEXT_ACCEPTED,
|
|
132
|
+
jobHandler
|
|
133
|
+
);
|
|
134
|
+
this.publisher.publish(JOB_HANDLER_TOPICS.START_NEXT, JSON.stringify({}));
|
|
144
135
|
|
|
145
|
-
|
|
146
|
-
|
|
136
|
+
void this.shadowHandler.initShadows();
|
|
137
|
+
});
|
|
147
138
|
}
|
|
148
139
|
|
|
149
140
|
/*=================================================================
|
|
@@ -164,7 +155,22 @@ export class DeviceAgentCloudConnection {
|
|
|
164
155
|
await sleep(1000);
|
|
165
156
|
this.connectionManager.disconnect();
|
|
166
157
|
}
|
|
167
|
-
|
|
158
|
+
|
|
159
|
+
// CLI methods
|
|
160
|
+
|
|
161
|
+
public async waitForConnection() {
|
|
162
|
+
while (!this.connectionManager.isConnected()) {
|
|
163
|
+
await sleep(1000);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public async waitForCmd(projectId: string) {
|
|
168
|
+
while (this.isCmdInProgress(projectId)) {
|
|
169
|
+
await sleep(1000);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
public async sendCliCmd(topic: string, message: any) {
|
|
168
174
|
this.connectionManager.dispatch(topic, message);
|
|
169
175
|
}
|
|
170
176
|
}
|
|
@@ -6,15 +6,16 @@ import {
|
|
|
6
6
|
getToCloudTopic
|
|
7
7
|
} from '@alwaysai/device-agent-schemas';
|
|
8
8
|
import * as winston from 'winston';
|
|
9
|
+
import { ConnectionManager } from './connection-manager';
|
|
9
10
|
|
|
10
11
|
export class Publisher {
|
|
11
|
-
private
|
|
12
|
+
private connectionManager: ConnectionManager;
|
|
12
13
|
private clientId: string;
|
|
13
14
|
private readonly toClientTopic: string;
|
|
14
15
|
private readonly toCloudTopic: string;
|
|
15
16
|
|
|
16
|
-
constructor(
|
|
17
|
-
this.
|
|
17
|
+
constructor(connectionManager: ConnectionManager, clientId: string) {
|
|
18
|
+
this.connectionManager = connectionManager;
|
|
18
19
|
this.clientId = clientId;
|
|
19
20
|
this.toClientTopic = getToClientTopic(this.clientId);
|
|
20
21
|
this.toCloudTopic = getToCloudTopic(this.clientId);
|
|
@@ -34,13 +35,15 @@ export class Publisher {
|
|
|
34
35
|
2
|
|
35
36
|
)}`
|
|
36
37
|
);
|
|
37
|
-
this.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
this.connectionManager
|
|
39
|
+
.getIoTDevice()
|
|
40
|
+
.publish(topic, payload, (err: any) => {
|
|
41
|
+
if (err) {
|
|
42
|
+
logger.error(
|
|
43
|
+
`Error publishing message: \nTopic: ${topic}\nMessage: ${payload}\nError: ${err}`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
public publishToCloudWithAck(
|
|
@@ -48,19 +51,21 @@ export class Publisher {
|
|
|
48
51
|
ackNackCallback: CallableFunction
|
|
49
52
|
) {
|
|
50
53
|
const topic = this.toCloudTopic;
|
|
51
|
-
this.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
this.connectionManager
|
|
55
|
+
.getIoTDevice()
|
|
56
|
+
.publish(topic, payload, { qos: 1 }, (err: any, resp: any) => {
|
|
57
|
+
if (err) {
|
|
58
|
+
logger.error(
|
|
59
|
+
`Error publishing message: \nTopic: ${topic}\nMessage: ${payload}\nError: ${err}`
|
|
60
|
+
);
|
|
61
|
+
ackNackCallback(false);
|
|
62
|
+
} else if (resp) {
|
|
63
|
+
logger.debug(
|
|
64
|
+
`Successfully published message: \nTopic: ${topic}\nMessage: ${payload}`
|
|
65
|
+
);
|
|
66
|
+
ackNackCallback(true);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
public publishDeviceAgentMessage(
|
|
@@ -5,10 +5,14 @@ import { ShadowHandler } from './shadow-handler';
|
|
|
5
5
|
import { Logger } from 'winston';
|
|
6
6
|
import { logger } from '../util/logger';
|
|
7
7
|
import { getShadowTopic } from '@alwaysai/device-agent-schemas';
|
|
8
|
+
import { ConnectionManager } from './connection-manager';
|
|
8
9
|
|
|
9
10
|
jest.mock('../application-control');
|
|
10
11
|
jest.mock('./publisher');
|
|
11
|
-
|
|
12
|
+
jest.mock('./connection-manager');
|
|
13
|
+
const mockConnectionManager = {
|
|
14
|
+
getIotDevice: jest.fn()
|
|
15
|
+
} as any as ConnectionManager;
|
|
12
16
|
const clientId = 'test-client';
|
|
13
17
|
const projectId1 = 'test-project';
|
|
14
18
|
const projectId2 = 'test-project-2';
|
|
@@ -18,7 +22,7 @@ describe('Test Shadow Handler', () => {
|
|
|
18
22
|
let shadowHandler: ShadowHandler;
|
|
19
23
|
|
|
20
24
|
beforeEach(() => {
|
|
21
|
-
publisher = new Publisher(
|
|
25
|
+
publisher = new Publisher(mockConnectionManager, clientId);
|
|
22
26
|
shadowHandler = new ShadowHandler(clientId, publisher);
|
|
23
27
|
});
|
|
24
28
|
|
|
@@ -346,6 +346,10 @@ export class ShadowHandler {
|
|
|
346
346
|
return [];
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
public async initShadows() {
|
|
350
|
+
await this.updateSystemInfoShadow();
|
|
351
|
+
}
|
|
352
|
+
|
|
349
353
|
public async updateSystemInfoShadow() {
|
|
350
354
|
const systemInfo = await getSystemInformation();
|
|
351
355
|
this.publisher.publish(
|