@alwaysai/device-agent 0.0.12 → 0.0.14
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/backup.d.ts.map +1 -1
- package/lib/application-control/backup.js +11 -5
- package/lib/application-control/backup.js.map +1 -1
- package/lib/application-control/config.d.ts +12 -4
- package/lib/application-control/config.d.ts.map +1 -1
- package/lib/application-control/config.js +59 -16
- package/lib/application-control/config.js.map +1 -1
- package/lib/application-control/environment-variables.d.ts.map +1 -1
- package/lib/application-control/environment-variables.js.map +1 -1
- package/lib/application-control/index.d.ts +5 -5
- package/lib/application-control/index.d.ts.map +1 -1
- package/lib/application-control/index.js +4 -6
- package/lib/application-control/index.js.map +1 -1
- package/lib/application-control/install.d.ts +1 -1
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +58 -57
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/models.d.ts +7 -5
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +78 -57
- package/lib/application-control/models.js.map +1 -1
- package/lib/application-control/status.d.ts +0 -6
- package/lib/application-control/status.d.ts.map +1 -1
- package/lib/application-control/status.js +21 -33
- package/lib/application-control/status.js.map +1 -1
- package/lib/application-control/utils.d.ts +3 -2
- package/lib/application-control/utils.d.ts.map +1 -1
- package/lib/application-control/utils.js +54 -34
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/app-install-status.d.ts +16 -0
- package/lib/cloud-connection/app-install-status.d.ts.map +1 -0
- package/lib/cloud-connection/app-install-status.js +53 -0
- package/lib/cloud-connection/app-install-status.js.map +1 -0
- package/lib/cloud-connection/bootstrap-provision.d.ts +2 -0
- package/lib/cloud-connection/bootstrap-provision.d.ts.map +1 -0
- package/lib/cloud-connection/bootstrap-provision.js +34 -0
- package/lib/cloud-connection/bootstrap-provision.js.map +1 -0
- package/lib/cloud-connection/cmd-status.d.ts +16 -0
- package/lib/cloud-connection/cmd-status.d.ts.map +1 -0
- package/lib/cloud-connection/cmd-status.js +49 -0
- package/lib/cloud-connection/cmd-status.js.map +1 -0
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +21 -34
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +211 -387
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/device-agent.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent.js +22 -26
- package/lib/cloud-connection/device-agent.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts +34 -0
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -0
- package/lib/cloud-connection/live-updates-handler.js +167 -0
- package/lib/cloud-connection/live-updates-handler.js.map +1 -0
- package/lib/cloud-connection/messages.d.ts +14 -0
- package/lib/cloud-connection/messages.d.ts.map +1 -0
- package/lib/cloud-connection/messages.js +38 -0
- package/lib/cloud-connection/messages.js.map +1 -0
- package/lib/cloud-connection/passthrough-handler.d.ts +11 -0
- package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -0
- package/lib/cloud-connection/passthrough-handler.js +59 -0
- package/lib/cloud-connection/passthrough-handler.js.map +1 -0
- package/lib/cloud-connection/publisher.d.ts +15 -0
- package/lib/cloud-connection/publisher.d.ts.map +1 -0
- package/lib/cloud-connection/publisher.js +58 -0
- package/lib/cloud-connection/publisher.js.map +1 -0
- package/lib/cloud-connection/shadow-handler.d.ts +33 -0
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -0
- package/lib/cloud-connection/shadow-handler.js +108 -0
- package/lib/cloud-connection/shadow-handler.js.map +1 -0
- package/lib/cloud-connection/shadow-handler.test.d.ts +2 -0
- package/lib/cloud-connection/shadow-handler.test.d.ts.map +1 -0
- package/lib/cloud-connection/shadow-handler.test.js +321 -0
- package/lib/cloud-connection/shadow-handler.test.js.map +1 -0
- package/lib/cloud-connection/shadow.d.ts +16 -0
- package/lib/cloud-connection/shadow.d.ts.map +1 -0
- package/lib/cloud-connection/shadow.js +36 -0
- package/lib/cloud-connection/shadow.js.map +1 -0
- package/lib/device-control/device-control.d.ts.map +1 -1
- package/lib/device-control/device-control.js +1 -0
- package/lib/device-control/device-control.js.map +1 -1
- package/lib/docker/docker-cmd.js +1 -1
- package/lib/docker/docker-compose-cmd.d.ts.map +1 -1
- package/lib/docker/docker-compose-cmd.js +1 -1
- package/lib/docker/docker-compose-cmd.js.map +1 -1
- package/lib/endpoints.js +10 -10
- package/lib/endpoints.js.map +1 -1
- package/lib/environment.d.ts +1 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js +2 -1
- package/lib/environment.js.map +1 -1
- package/lib/infrastructure/agent-config.d.ts +15 -58
- package/lib/infrastructure/agent-config.d.ts.map +1 -1
- package/lib/infrastructure/agent-config.js +22 -15
- package/lib/infrastructure/agent-config.js.map +1 -1
- package/lib/infrastructure/agent-config.test.js +25 -23
- package/lib/infrastructure/agent-config.test.js.map +1 -1
- package/lib/infrastructure/system-id.d.ts +2 -0
- package/lib/infrastructure/system-id.d.ts.map +1 -0
- package/lib/infrastructure/system-id.js +21 -0
- package/lib/infrastructure/system-id.js.map +1 -0
- package/lib/infrastructure/tokens-and-device-cfg.d.ts +4 -0
- package/lib/infrastructure/tokens-and-device-cfg.d.ts.map +1 -0
- package/lib/infrastructure/tokens-and-device-cfg.js +27 -0
- package/lib/infrastructure/tokens-and-device-cfg.js.map +1 -0
- package/lib/infrastructure/urls.d.ts.map +1 -1
- package/lib/infrastructure/urls.js +3 -3
- package/lib/infrastructure/urls.js.map +1 -1
- package/lib/local-connection/rabbitmq-connection.d.ts +4 -0
- package/lib/local-connection/rabbitmq-connection.d.ts.map +1 -0
- package/lib/local-connection/rabbitmq-connection.js +58 -0
- package/lib/local-connection/rabbitmq-connection.js.map +1 -0
- package/lib/root.d.ts.map +1 -1
- package/lib/root.js +2 -7
- package/lib/root.js.map +1 -1
- package/lib/subcommands/app/app.d.ts +2 -1
- package/lib/subcommands/app/app.d.ts.map +1 -1
- package/lib/subcommands/app/app.js +112 -77
- package/lib/subcommands/app/app.js.map +1 -1
- package/lib/subcommands/app/index.js +2 -2
- package/lib/subcommands/device/clean.d.ts +2 -0
- package/lib/subcommands/device/clean.d.ts.map +1 -0
- package/lib/subcommands/device/clean.js +29 -0
- package/lib/subcommands/device/clean.js.map +1 -0
- package/lib/subcommands/device/device.d.ts +1 -1
- package/lib/subcommands/device/device.d.ts.map +1 -1
- package/lib/subcommands/device/device.js +44 -33
- package/lib/subcommands/device/device.js.map +1 -1
- package/lib/subcommands/device/index.d.ts.map +1 -1
- package/lib/subcommands/device/index.js +2 -1
- package/lib/subcommands/device/index.js.map +1 -1
- package/lib/subcommands/get-model-package.js +5 -5
- package/lib/subcommands/index.d.ts +0 -1
- package/lib/subcommands/index.d.ts.map +1 -1
- package/lib/subcommands/index.js +1 -1
- package/lib/subcommands/login.d.ts +0 -1
- package/lib/subcommands/login.d.ts.map +1 -1
- package/lib/subcommands/login.js +6 -14
- package/lib/subcommands/login.js.map +1 -1
- package/lib/util/clean-certs.d.ts +2 -0
- package/lib/util/clean-certs.d.ts.map +1 -0
- package/lib/util/clean-certs.js +16 -0
- package/lib/util/clean-certs.js.map +1 -0
- package/lib/util/directories.d.ts +16 -15
- package/lib/util/directories.d.ts.map +1 -1
- package/lib/util/directories.js +45 -26
- package/lib/util/directories.js.map +1 -1
- package/lib/util/fetch-with-timeout.d.ts +4 -0
- package/lib/util/fetch-with-timeout.d.ts.map +1 -0
- package/lib/util/fetch-with-timeout.js +15 -0
- package/lib/util/fetch-with-timeout.js.map +1 -0
- package/lib/util/get-device-id.d.ts +1 -1
- package/lib/util/get-device-id.d.ts.map +1 -1
- package/lib/util/get-device-id.js +14 -19
- package/lib/util/get-device-id.js.map +1 -1
- package/lib/util/http-client.d.ts +1 -1
- package/lib/util/http-client.d.ts.map +1 -1
- package/lib/util/http-client.js +10 -8
- package/lib/util/http-client.js.map +1 -1
- package/lib/util/logger.d.ts.map +1 -1
- package/lib/util/logger.js +4 -5
- package/lib/util/logger.js.map +1 -1
- package/lib/util/require-logged-in-and-paid-plan.d.ts +2 -0
- package/lib/util/require-logged-in-and-paid-plan.d.ts.map +1 -0
- package/lib/util/require-logged-in-and-paid-plan.js +18 -0
- package/lib/util/require-logged-in-and-paid-plan.js.map +1 -0
- package/lib/util/run-in-dir.d.ts.map +1 -1
- package/lib/util/run-in-dir.js +1 -0
- package/lib/util/run-in-dir.js.map +1 -1
- package/lib/util/timer.d.ts +2 -0
- package/lib/util/timer.d.ts.map +1 -0
- package/lib/util/timer.js +6 -0
- package/lib/util/timer.js.map +1 -0
- package/package.json +32 -35
- package/readme.md +100 -89
- package/src/application-control/backup.ts +11 -6
- package/src/application-control/config.ts +75 -13
- package/src/application-control/environment-variables.ts +3 -3
- package/src/application-control/index.ts +18 -11
- package/src/application-control/install.ts +82 -78
- package/src/application-control/models.ts +104 -72
- package/src/application-control/status.ts +29 -40
- package/src/application-control/utils.ts +66 -38
- package/src/cloud-connection/app-install-status.ts +62 -0
- package/src/cloud-connection/bootstrap-provision.ts +40 -0
- package/src/cloud-connection/cmd-status.ts +52 -0
- package/src/cloud-connection/device-agent-cloud-connection.ts +302 -526
- package/src/cloud-connection/device-agent.ts +31 -38
- package/src/cloud-connection/live-updates-handler.ts +226 -0
- package/src/cloud-connection/messages.ts +39 -0
- package/src/cloud-connection/passthrough-handler.ts +67 -0
- package/src/cloud-connection/publisher.ts +86 -0
- package/src/cloud-connection/shadow-handler.test.ts +361 -0
- package/src/cloud-connection/shadow-handler.ts +175 -0
- package/src/cloud-connection/shadow.ts +50 -0
- package/src/device-control/device-control.ts +1 -0
- package/src/docker/docker-cmd.ts +1 -1
- package/src/docker/docker-compose-cmd.ts +5 -2
- package/src/endpoints.ts +9 -9
- package/src/environment.ts +11 -3
- package/src/infrastructure/agent-config.test.ts +33 -29
- package/src/infrastructure/agent-config.ts +57 -22
- package/src/infrastructure/system-id.ts +18 -0
- package/src/infrastructure/tokens-and-device-cfg.ts +34 -0
- package/src/infrastructure/urls.ts +4 -2
- package/src/local-connection/rabbitmq-connection.ts +53 -0
- package/src/root.ts +2 -8
- package/src/subcommands/app/app.ts +119 -83
- package/src/subcommands/app/index.ts +3 -3
- package/src/subcommands/device/clean.ts +26 -0
- package/src/subcommands/device/device.ts +67 -54
- package/src/subcommands/device/index.ts +2 -1
- package/src/subcommands/get-model-package.ts +5 -5
- package/src/subcommands/index.ts +1 -1
- package/src/subcommands/login.ts +6 -14
- package/src/util/clean-certs.ts +12 -0
- package/src/util/directories.ts +68 -52
- package/src/util/fetch-with-timeout.ts +18 -0
- package/src/util/get-device-id.ts +16 -18
- package/src/util/http-client.ts +18 -13
- package/src/util/logger.ts +6 -6
- package/src/util/require-logged-in-and-paid-plan.ts +16 -0
- package/src/util/run-in-dir.ts +2 -1
- package/src/util/timer.ts +1 -0
- package/lib/infrastructure/certificates-and-tokens.d.ts +0 -6
- package/lib/infrastructure/certificates-and-tokens.d.ts.map +0 -1
- package/lib/infrastructure/certificates-and-tokens.js +0 -43
- package/lib/infrastructure/certificates-and-tokens.js.map +0 -1
- package/src/infrastructure/certificates-and-tokens.ts +0 -53
|
@@ -1,318 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
1
|
+
// eslint-disable-next-line
|
|
2
|
+
const awsIot = require('aws-iot-device-sdk');
|
|
3
|
+
import { getIoTCoreEndpointUrl } from '../infrastructure/urls';
|
|
4
|
+
import { existsSync } from 'fs';
|
|
4
5
|
import {
|
|
5
6
|
getPrivateKeyFilePath,
|
|
6
7
|
getCertificateFilePath,
|
|
7
|
-
getRootCertificateFilePath,
|
|
8
|
-
BOOTSTRAP_CLAIM_ID_FILE_PATH,
|
|
9
|
-
DEVICE_CLAIM_ID_FILE_PATH,
|
|
10
|
-
BOOTSTRAP_DEVICE_CERTIFICATE_FILE_PATH,
|
|
11
8
|
BOOTSTRAP_DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
12
9
|
AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
import { BootstrapAgent } from "./device-agent";
|
|
16
|
-
|
|
17
|
-
import sleep from "../util/sleep";
|
|
18
|
-
import {
|
|
19
|
-
startApp,
|
|
20
|
-
stopApp,
|
|
21
|
-
restartApp,
|
|
22
|
-
getAppLogs,
|
|
23
|
-
getAppStatus,
|
|
24
|
-
} from "../application-control/status";
|
|
25
|
-
import {
|
|
26
|
-
getInstalledApps,
|
|
27
|
-
installApp,
|
|
28
|
-
uninstallApp,
|
|
29
|
-
} from "../application-control/install";
|
|
10
|
+
BOOTSTRAP_CERTIFICATES_DIR_PATH
|
|
11
|
+
} from '../util/directories';
|
|
30
12
|
import {
|
|
31
13
|
keyMirrors,
|
|
32
14
|
validateClientMessage,
|
|
33
|
-
DeviceAgentMessagePayload,
|
|
34
|
-
AppInstallStatusPacket,
|
|
35
15
|
AppStateControlPacket,
|
|
36
16
|
AppVersionControlPacket,
|
|
37
|
-
LiveUpdatesToggles,
|
|
38
|
-
AppLogsPacket,
|
|
39
|
-
AppStatePacket,
|
|
40
17
|
LiveUpdatesToggleMessage,
|
|
41
|
-
DeviceStatsMessage,
|
|
42
|
-
AppStateMessage,
|
|
43
|
-
AppLogsMessage,
|
|
44
|
-
AppInstallStatusMessage,
|
|
45
18
|
SignedUrlsRequestMessage,
|
|
46
|
-
DeviceAgentMessage,
|
|
47
19
|
ClientMessage,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
} from
|
|
53
|
-
import { getDeviceId } from "../util/get-device-id";
|
|
54
|
-
import { JsSpawner, logger } from "alwaysai/lib/util";
|
|
55
|
-
import {
|
|
56
|
-
getCpuUtil,
|
|
57
|
-
getDiskUtil,
|
|
58
|
-
getMemUtil,
|
|
59
|
-
} from "../device-control/device-control";
|
|
60
|
-
import { AgentConfigFile } from "../infrastructure/agent-config";
|
|
20
|
+
getDeviceTopic
|
|
21
|
+
} from '@alwaysai/device-agent-schemas';
|
|
22
|
+
import { getDeviceUuid } from '../util/get-device-id';
|
|
23
|
+
import { logger } from '../util/logger';
|
|
24
|
+
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
61
25
|
import {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
26
|
+
startApp,
|
|
27
|
+
stopApp,
|
|
28
|
+
restartApp,
|
|
29
|
+
updateModelsWithPresignedUrls,
|
|
30
|
+
installApp,
|
|
31
|
+
uninstallApp,
|
|
32
|
+
updateAppCfg
|
|
33
|
+
} from '../application-control';
|
|
34
|
+
import { AppConfigUpdate, ShadowHandler, ShadowTopics } from './shadow-handler';
|
|
35
|
+
import { Publisher } from './publisher';
|
|
36
|
+
import { LiveUpdatesHandler } from './live-updates-handler';
|
|
37
|
+
import { bootstrapProvision } from './bootstrap-provision';
|
|
38
|
+
import { AppInstallStatusManager } from './app-install-status';
|
|
39
|
+
import { AppConfig } from '@alwaysai/app-configuration-schemas';
|
|
40
|
+
import { CmdStatusManager, CmdStatusType } from './cmd-status';
|
|
41
|
+
import { PassthroughHandler, runChannel } from './passthrough-handler';
|
|
42
|
+
import { ALWAYSAI_ANALYTICS_PASSTHROUGH } from '../environment';
|
|
68
43
|
|
|
69
44
|
export class DeviceAgentCloudConnection {
|
|
70
|
-
private
|
|
45
|
+
private shadowHandler: ShadowHandler;
|
|
46
|
+
public publisher: Publisher;
|
|
47
|
+
private cmdStatusMgr: CmdStatusManager;
|
|
48
|
+
private liveUpdatesHandler: LiveUpdatesHandler;
|
|
49
|
+
private appInstallStatusMgr: AppInstallStatusManager;
|
|
50
|
+
private device = awsIot.device;
|
|
51
|
+
|
|
52
|
+
private clientId = getDeviceUuid();
|
|
71
53
|
private host = getIoTCoreEndpointUrl();
|
|
72
|
-
private appInstallStatus: AppInstallStatusPacket;
|
|
73
|
-
private liveUpdatesTimeout: ReturnType<typeof setTimeout>;
|
|
74
|
-
private liveUpdatesAlive = {
|
|
75
|
-
[keyMirrors.agentMessageType.device_stats]: false,
|
|
76
|
-
[keyMirrors.agentMessageType.app_state]: false,
|
|
77
|
-
[keyMirrors.agentMessageType.app_logs]: false,
|
|
78
|
-
};
|
|
79
|
-
private liveUpdatesSleepIntervals = {
|
|
80
|
-
[keyMirrors.agentMessageType.device_stats]: 5000,
|
|
81
|
-
[keyMirrors.agentMessageType.app_state]: 5000,
|
|
82
|
-
[keyMirrors.agentMessageType.app_logs]: 5000,
|
|
83
|
-
[keyMirrors.agentMessageType.app_install_status]: 5000,
|
|
84
|
-
};
|
|
85
|
-
private appLogStreams = new Set<string>();
|
|
86
|
-
private readonly shadowPrefix = `$aws/things/${this.clientId}/shadow/name/`;
|
|
87
|
-
private readonly shadowTopics = {
|
|
88
|
-
projects: {
|
|
89
|
-
updateDelta: `${this.shadowPrefix}projects/update/delta`,
|
|
90
|
-
getAccepted: `${this.shadowPrefix}projects/get/accepted`,
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
private readonly toCloudTopic = getCloudTopic(this.clientId);
|
|
94
|
-
private readonly toClientTopic = getClientTopic(this.clientId);
|
|
95
54
|
private readonly toDeviceTopic = getDeviceTopic(this.clientId);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
public getShadowPrefix() {
|
|
100
|
-
return this.shadowPrefix;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
private async handleNamedShadowUpdate({ payload }: { payload: string }) {
|
|
104
|
-
const delta = JSON.parse(payload);
|
|
105
|
-
const deltaKeys = Object.keys(delta);
|
|
106
|
-
|
|
107
|
-
for (const projectId of deltaKeys) {
|
|
108
|
-
const projectShadow = delta[projectId];
|
|
109
|
-
if (projectShadow.appConfig) {
|
|
110
|
-
const appConfig = projectShadow.appConfig;
|
|
111
|
-
const appDir = getAppDir(projectId);
|
|
112
|
-
await updateAppConfig(projectId, appConfig);
|
|
113
|
-
|
|
114
|
-
if (appConfig.models) {
|
|
115
|
-
this.publishCloudRequest({
|
|
116
|
-
messageType: keyMirrors.agentMessageType.signed_urls_request,
|
|
117
|
-
modelsOnlyUrlsRequest: {
|
|
118
|
-
projectId,
|
|
119
|
-
models: appConfig.models,
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (appConfig.scripts && !appConfig.models) {
|
|
125
|
-
const appState = await getAppStatus({ projectId });
|
|
126
|
-
|
|
127
|
-
await buildApp({ appDir });
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
appState.services.length &&
|
|
131
|
-
appState.services[0].state !== keyMirrors.appState.stopped
|
|
132
|
-
) {
|
|
133
|
-
restartApp({ projectId });
|
|
134
|
-
}
|
|
135
|
-
await this.publishReportedState(projectId);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
private async startAppLogStream(projectId: string) {
|
|
142
|
-
this.appLogStreams.add(projectId);
|
|
143
|
-
const readable = await getAppLogs({
|
|
144
|
-
projectId,
|
|
145
|
-
args: ["--tail", "100", "--no-log-prefix"],
|
|
146
|
-
});
|
|
147
|
-
readable.on("data", (chunk: Buffer) => {
|
|
148
|
-
if (!this.appLogStreams.has(projectId)) {
|
|
149
|
-
// why doesn't typescript know about this function?
|
|
150
|
-
// @ts-ignore
|
|
151
|
-
readable.destroy();
|
|
152
|
-
logger.info(`App log stream terminated for project ${projectId}`);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const logStr = chunk.toString();
|
|
156
|
-
const message = {
|
|
157
|
-
messageType: keyMirrors.agentMessageType.app_logs,
|
|
158
|
-
appLogs: {
|
|
159
|
-
projectId,
|
|
160
|
-
logChunk: logStr,
|
|
161
|
-
},
|
|
162
|
-
};
|
|
163
|
-
const packet = this.buildMessagePacket(
|
|
164
|
-
this.getClientId(),
|
|
165
|
-
this.toClientTopic,
|
|
166
|
-
message
|
|
167
|
-
);
|
|
168
|
-
this.publishMessage(this.toClientTopic, JSON.stringify(packet));
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
readable.on("error", (error) => {
|
|
172
|
-
logger.error(
|
|
173
|
-
`App log stream terminated for project ${projectId}: ${error}`
|
|
174
|
-
);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
readable.on("finished", () => {
|
|
178
|
-
logger.info(`App logs finished piping for project ${projectId}`);
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// must contain app release hash
|
|
183
|
-
private initAppInstallStatus(installationStatus: AppInstallStatusPacket) {
|
|
184
|
-
this.appInstallStatus = installationStatus;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
private updateAppInstallStatus(
|
|
188
|
-
installationStatus: Omit<AppInstallStatusPacket, "appReleaseHash">
|
|
189
|
-
) {
|
|
190
|
-
this.appInstallStatus.status = installationStatus.status;
|
|
191
|
-
this.appInstallStatus.message = installationStatus.message;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
private getAppInstallStatus(): AppInstallStatusPacket {
|
|
195
|
-
return this.appInstallStatus;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Message Builders
|
|
199
|
-
private buildMessagePacket(
|
|
200
|
-
deviceId: string,
|
|
201
|
-
topic: string,
|
|
202
|
-
payload: DeviceAgentMessagePayload
|
|
203
|
-
): DeviceAgentMessage {
|
|
204
|
-
const packet = {
|
|
205
|
-
timestamp: new Date().toUTCString(),
|
|
206
|
-
deviceId,
|
|
207
|
-
topic,
|
|
208
|
-
payload,
|
|
209
|
-
};
|
|
210
|
-
return packet;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
private async getAppStateMessage(): Promise<AppStateMessage> {
|
|
214
|
-
const appStateMessage: AppStatePacket[] = [];
|
|
215
|
-
const apps = await AgentConfigFile().getReadyApps();
|
|
216
|
-
for (const app of apps) {
|
|
217
|
-
const projectId = app.projectId;
|
|
218
|
-
const status = await getAppStatus({ projectId });
|
|
219
|
-
appStateMessage.push(status);
|
|
220
|
-
}
|
|
221
|
-
const appStatePackage = {
|
|
222
|
-
messageType: keyMirrors.agentMessageType.app_state,
|
|
223
|
-
appState: appStateMessage,
|
|
224
|
-
};
|
|
225
|
-
return appStatePackage;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
private async getDeviceStatsMessage(): Promise<DeviceStatsMessage> {
|
|
229
|
-
const cpuUsage = await getCpuUtil();
|
|
230
|
-
const diskUtil = await getDiskUtil();
|
|
231
|
-
const memUtil = await getMemUtil();
|
|
232
|
-
|
|
233
|
-
const deviceStatsMessage = {
|
|
234
|
-
messageType: keyMirrors.agentMessageType.device_stats,
|
|
235
|
-
deviceStats: {
|
|
236
|
-
cpuUsage,
|
|
237
|
-
diskUtil,
|
|
238
|
-
usedMemoryPercentage: memUtil,
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
return deviceStatsMessage;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// must be arrow function due to this context when function is passed as param
|
|
245
|
-
private getAppInstallStatusMessage =
|
|
246
|
-
async (): Promise<AppInstallStatusMessage> => {
|
|
247
|
-
const appInstallStatus = this.getAppInstallStatus();
|
|
248
|
-
const appInstallStatusMessage = {
|
|
249
|
-
messageType: keyMirrors.agentMessageType.app_install_status,
|
|
250
|
-
appInstallStatus,
|
|
251
|
-
};
|
|
252
|
-
return appInstallStatusMessage;
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
private async startPublishingLiveUpdates(
|
|
256
|
-
topic: string,
|
|
257
|
-
messageType: string,
|
|
258
|
-
getMessageData: () => Promise<DeviceAgentMessagePayload>
|
|
259
|
-
) {
|
|
260
|
-
while (true) {
|
|
261
|
-
try {
|
|
262
|
-
const message = await getMessageData();
|
|
263
|
-
const packet = this.buildMessagePacket(
|
|
264
|
-
this.getClientId(),
|
|
265
|
-
topic,
|
|
266
|
-
message
|
|
267
|
-
);
|
|
268
|
-
this.publishMessage(topic, JSON.stringify(packet));
|
|
269
|
-
} catch (e) {
|
|
270
|
-
logger.error(
|
|
271
|
-
`Error publishing live updates for ${messageType}: ${e.message}`
|
|
272
|
-
);
|
|
273
|
-
break;
|
|
274
|
-
}
|
|
275
|
-
if (!this.continuePublishing(messageType)) {
|
|
276
|
-
logger.info(`Turned off live updates for ${messageType}`);
|
|
277
|
-
break;
|
|
278
|
-
}
|
|
279
|
-
await sleep(this.getLiveUpdatesInterval(messageType));
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
private continuePublishing(flag: string): boolean {
|
|
284
|
-
switch (flag) {
|
|
285
|
-
case keyMirrors.agentMessageType.device_stats:
|
|
286
|
-
case keyMirrors.agentMessageType.app_state:
|
|
287
|
-
return this.liveUpdatesAlive[flag];
|
|
288
|
-
case keyMirrors.agentMessageType.app_install_status:
|
|
289
|
-
return (
|
|
290
|
-
this.appInstallStatus.status ===
|
|
291
|
-
keyMirrors.appInstallStatus.in_progress
|
|
292
|
-
);
|
|
293
|
-
default:
|
|
294
|
-
logger.error(`Unrecognized publishable flag ${flag}`);
|
|
295
|
-
return false;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
private getLiveUpdatesInterval(flag: string): number {
|
|
300
|
-
const exists = this.liveUpdatesSleepIntervals[flag];
|
|
301
|
-
if (exists) {
|
|
302
|
-
return exists;
|
|
303
|
-
}
|
|
304
|
-
logger.error(`Unrecognized live updates flag ${flag}`);
|
|
305
|
-
return -1;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
private setLiveUpdates(toggles: LiveUpdatesToggles) {
|
|
309
|
-
if (toggles.deviceStats) {
|
|
310
|
-
this.liveUpdatesAlive.device_stats = toggles.deviceStats;
|
|
311
|
-
}
|
|
312
|
-
if (toggles.appState) {
|
|
313
|
-
this.liveUpdatesAlive.app_state = toggles.appState;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
55
|
+
// FIXME: Add support for multiple simultaneous project updates
|
|
56
|
+
private newAppCfgQueue: AppConfig[] = [];
|
|
316
57
|
|
|
317
58
|
private handleAppStateControl(payload: AppStateControlPacket) {
|
|
318
59
|
const { baseCommand, projectId } = payload;
|
|
@@ -330,12 +71,26 @@ export class DeviceAgentCloudConnection {
|
|
|
330
71
|
}
|
|
331
72
|
|
|
332
73
|
private handleAppVersionControl(payload: AppVersionControlPacket) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
74
|
+
switch (payload.baseCommand) {
|
|
75
|
+
case keyMirrors.appVersionControl.install: {
|
|
76
|
+
const { projectId, appReleaseHash } = payload;
|
|
77
|
+
this.cmdStatusMgr.update(projectId, 'in_progress');
|
|
78
|
+
const signedUrlsRequest = { projectId, appReleaseHash };
|
|
79
|
+
this.publishCloudRequest({
|
|
80
|
+
messageType: keyMirrors.agentMessageType.signed_urls_request,
|
|
81
|
+
signedUrlsRequest
|
|
82
|
+
});
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
default:
|
|
86
|
+
logger.warn(
|
|
87
|
+
`Ignore App Version Control packet: ${JSON.stringify(
|
|
88
|
+
payload,
|
|
89
|
+
null,
|
|
90
|
+
2
|
|
91
|
+
)}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
339
94
|
}
|
|
340
95
|
|
|
341
96
|
private handleDeviceCommand = async (packet: any) => {
|
|
@@ -345,7 +100,7 @@ export class DeviceAgentCloudConnection {
|
|
|
345
100
|
private handleAgentCommand(message: LiveUpdatesToggleMessage) {
|
|
346
101
|
switch (message.messageType) {
|
|
347
102
|
case keyMirrors.clientMessageType.live_state_updates:
|
|
348
|
-
this.
|
|
103
|
+
this.liveUpdatesHandler.update(message.liveUpdatesToggles);
|
|
349
104
|
break;
|
|
350
105
|
default:
|
|
351
106
|
logger.error(
|
|
@@ -354,91 +109,112 @@ export class DeviceAgentCloudConnection {
|
|
|
354
109
|
}
|
|
355
110
|
}
|
|
356
111
|
|
|
357
|
-
private
|
|
358
|
-
|
|
359
|
-
this.liveUpdatesTimeout = setTimeout(() => {
|
|
360
|
-
this.setLiveUpdates({
|
|
361
|
-
deviceStats: false,
|
|
362
|
-
appState: false,
|
|
363
|
-
});
|
|
364
|
-
this.appLogStreams.clear();
|
|
365
|
-
// TODO: Make constant, not hard coded
|
|
366
|
-
}, 600000); // 10 min
|
|
112
|
+
private async publishCloudRequest(payload: SignedUrlsRequestMessage) {
|
|
113
|
+
this.publisher.publishToCloud(payload);
|
|
367
114
|
}
|
|
368
115
|
|
|
369
|
-
private
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}: {
|
|
374
|
-
deviceStats?: boolean;
|
|
375
|
-
appState?: boolean;
|
|
376
|
-
appLogs?: {
|
|
377
|
-
projectId: string;
|
|
378
|
-
toggle: boolean;
|
|
379
|
-
};
|
|
380
|
-
}) {
|
|
381
|
-
this.restartLiveUpdatesTimeout();
|
|
382
|
-
if (deviceStats !== undefined) {
|
|
383
|
-
this.liveUpdatesAlive.device_stats = deviceStats;
|
|
384
|
-
if (deviceStats) {
|
|
385
|
-
this.startPublishingLiveUpdates(
|
|
386
|
-
this.toClientTopic,
|
|
387
|
-
keyMirrors.agentMessageType.device_stats,
|
|
388
|
-
this.getDeviceStatsMessage
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
116
|
+
private subscribe(topic: string) {
|
|
117
|
+
logger.info(`Subscribing to ${topic}`);
|
|
118
|
+
this.device.subscribe(topic);
|
|
119
|
+
}
|
|
392
120
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
121
|
+
// eslint-disable-next-line
|
|
122
|
+
private async atomicApplicationUpdate<T extends any[], R extends any>(
|
|
123
|
+
func: (...args: T) => R,
|
|
124
|
+
args: T,
|
|
125
|
+
projectId: string,
|
|
126
|
+
appReleaseHash: string
|
|
127
|
+
) {
|
|
128
|
+
this.appInstallStatusMgr.update(
|
|
129
|
+
appReleaseHash,
|
|
130
|
+
keyMirrors.appInstallStatus.in_progress
|
|
131
|
+
);
|
|
403
132
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
133
|
+
this.liveUpdatesHandler.update({
|
|
134
|
+
appInstallStatus: { toggle: true, appReleaseHash }
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Install the app and models
|
|
138
|
+
try {
|
|
139
|
+
const out: R = await func(...args);
|
|
140
|
+
this.appInstallStatusMgr.update(
|
|
141
|
+
appReleaseHash,
|
|
142
|
+
keyMirrors.appInstallStatus.success
|
|
143
|
+
);
|
|
144
|
+
this.liveUpdatesHandler.update({
|
|
145
|
+
appInstallStatus: { toggle: false, appReleaseHash }
|
|
146
|
+
});
|
|
147
|
+
this.cmdStatusMgr.update(projectId, 'idle');
|
|
148
|
+
|
|
149
|
+
// update app config shadow for project
|
|
150
|
+
await this.shadowHandler.publishAppState(projectId);
|
|
151
|
+
return out;
|
|
152
|
+
} catch (e) {
|
|
153
|
+
logger.error(e);
|
|
154
|
+
const message: string = e.message;
|
|
155
|
+
|
|
156
|
+
// uninstall the failed app to put system back in good state
|
|
157
|
+
await uninstallApp({ projectId });
|
|
158
|
+
this.appInstallStatusMgr.update(
|
|
159
|
+
appReleaseHash,
|
|
160
|
+
keyMirrors.appInstallStatus.failure,
|
|
161
|
+
message
|
|
162
|
+
);
|
|
163
|
+
this.liveUpdatesHandler.update({
|
|
164
|
+
appInstallStatus: { toggle: false, appReleaseHash }
|
|
165
|
+
});
|
|
166
|
+
this.cmdStatusMgr.update(projectId, 'idle');
|
|
167
|
+
|
|
168
|
+
// delete shadow for project
|
|
169
|
+
this.shadowHandler.deleteProjectShadow(projectId);
|
|
410
170
|
}
|
|
411
171
|
}
|
|
412
172
|
|
|
413
|
-
private async
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
173
|
+
private async handleAppConfigUpdates(appConfgUpdates: AppConfigUpdate[]) {
|
|
174
|
+
for (const appConfigUpdate of appConfgUpdates) {
|
|
175
|
+
const { projectId, newAppCfg, updatedModels } = appConfigUpdate;
|
|
176
|
+
this.cmdStatusMgr.update(projectId, 'in_progress');
|
|
177
|
+
if (updatedModels && Object.keys(updatedModels).length) {
|
|
178
|
+
// Publish request for model urls
|
|
179
|
+
this.newAppCfgQueue.push(newAppCfg);
|
|
180
|
+
|
|
181
|
+
logger.debug(
|
|
182
|
+
`Requesting presigned urls from cloud for model versions: ${JSON.stringify(
|
|
183
|
+
updatedModels
|
|
184
|
+
)}`
|
|
185
|
+
);
|
|
186
|
+
this.publisher.publishToCloud({
|
|
187
|
+
messageType: keyMirrors.agentMessageType.signed_urls_request,
|
|
188
|
+
modelsOnlyUrlsRequest: {
|
|
189
|
+
projectId,
|
|
190
|
+
models: updatedModels
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
// FIXME: do we need to send this up to the cloud?
|
|
195
|
+
// should it be something other than appReleaseHash?
|
|
196
|
+
const appReleaseHash = await AgentConfigFile().getAppVersion({
|
|
197
|
+
projectId
|
|
198
|
+
});
|
|
427
199
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
200
|
+
await this.atomicApplicationUpdate(
|
|
201
|
+
updateAppCfg,
|
|
202
|
+
[
|
|
203
|
+
{
|
|
204
|
+
projectId,
|
|
205
|
+
appReleaseHash,
|
|
206
|
+
newAppCfg
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
projectId,
|
|
210
|
+
appReleaseHash
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
436
214
|
}
|
|
437
215
|
|
|
438
216
|
// Public Methods
|
|
439
217
|
|
|
440
|
-
public device = awsIot.device;
|
|
441
|
-
|
|
442
218
|
constructor() {
|
|
443
219
|
this.device = awsIot.device({
|
|
444
220
|
keyPath: getPrivateKeyFilePath(),
|
|
@@ -447,29 +223,56 @@ export class DeviceAgentCloudConnection {
|
|
|
447
223
|
clientId: this.clientId,
|
|
448
224
|
host: this.host,
|
|
449
225
|
port: 8883,
|
|
226
|
+
keepalive: 1 // time before re-connect attempt on dropped connection, default is 400 seconds
|
|
450
227
|
});
|
|
228
|
+
this.publisher = new Publisher(this.device, this.clientId);
|
|
229
|
+
this.shadowHandler = new ShadowHandler(this.clientId, this.publisher);
|
|
230
|
+
this.cmdStatusMgr = new CmdStatusManager();
|
|
231
|
+
this.appInstallStatusMgr = new AppInstallStatusManager();
|
|
232
|
+
this.liveUpdatesHandler = new LiveUpdatesHandler(
|
|
233
|
+
this.publisher,
|
|
234
|
+
this.appInstallStatusMgr
|
|
235
|
+
);
|
|
451
236
|
|
|
452
|
-
this.
|
|
453
|
-
this.
|
|
454
|
-
this.
|
|
237
|
+
this.subscribe(this.toDeviceTopic);
|
|
238
|
+
this.subscribe(this.shadowHandler.shadowTopics.projects.getAccepted);
|
|
239
|
+
this.subscribe(this.shadowHandler.shadowTopics.projects.updateDelta);
|
|
455
240
|
}
|
|
456
241
|
|
|
457
242
|
public getClientId(): string {
|
|
458
243
|
return this.clientId;
|
|
459
244
|
}
|
|
460
245
|
|
|
461
|
-
public
|
|
462
|
-
|
|
463
|
-
|
|
246
|
+
public getToDeviceTopic() {
|
|
247
|
+
return this.toDeviceTopic;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public getShadowTopics(): ShadowTopics {
|
|
251
|
+
return this.shadowHandler.shadowTopics;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
public getCmdStatus(projectId: string): CmdStatusType {
|
|
255
|
+
return this.cmdStatusMgr.getAppCmdStatus(projectId);
|
|
464
256
|
}
|
|
465
257
|
|
|
466
258
|
public async handleClientMessage({
|
|
467
259
|
topic,
|
|
468
|
-
message
|
|
260
|
+
message
|
|
469
261
|
}: {
|
|
470
262
|
topic: string;
|
|
471
263
|
message: ClientMessage;
|
|
472
264
|
}) {
|
|
265
|
+
const valid = validateClientMessage(message);
|
|
266
|
+
if (!valid) {
|
|
267
|
+
logger.error(
|
|
268
|
+
`Error validating message: ${JSON.stringify(
|
|
269
|
+
{ topic, message, errors: validateClientMessage.errors },
|
|
270
|
+
null,
|
|
271
|
+
2
|
|
272
|
+
)}`
|
|
273
|
+
);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
473
276
|
const payload = message.payload;
|
|
474
277
|
switch (payload.messageType) {
|
|
475
278
|
case keyMirrors.clientMessageType.app_state_control: {
|
|
@@ -489,175 +292,148 @@ export class DeviceAgentCloudConnection {
|
|
|
489
292
|
projectId,
|
|
490
293
|
appReleaseHash,
|
|
491
294
|
appInstallPayload,
|
|
492
|
-
modelsInstallPayload
|
|
295
|
+
modelsInstallPayload
|
|
493
296
|
} = payload.appInstallCloudResponse;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
this.getAppInstallStatusMessage
|
|
297
|
+
const signedUrlsPayload = {
|
|
298
|
+
appInstallPayload,
|
|
299
|
+
modelsInstallPayload
|
|
300
|
+
};
|
|
301
|
+
await this.atomicApplicationUpdate(
|
|
302
|
+
installApp,
|
|
303
|
+
[{ projectId, appReleaseHash, signedUrlsPayload }],
|
|
304
|
+
projectId,
|
|
305
|
+
appReleaseHash
|
|
504
306
|
);
|
|
505
307
|
|
|
506
|
-
// Install the app and models
|
|
507
|
-
try {
|
|
508
|
-
const signedUrlsPayload = {
|
|
509
|
-
appInstallPayload,
|
|
510
|
-
modelsInstallPayload,
|
|
511
|
-
};
|
|
512
|
-
await installApp({
|
|
513
|
-
projectId,
|
|
514
|
-
appReleaseHash,
|
|
515
|
-
signedUrlsPayload,
|
|
516
|
-
});
|
|
517
|
-
this.updateAppInstallStatus({
|
|
518
|
-
status: keyMirrors.appInstallStatus.success,
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
// update app config shadow for project
|
|
522
|
-
await this.publishReportedState(projectId);
|
|
523
|
-
} catch (e) {
|
|
524
|
-
logger.error(e);
|
|
525
|
-
const message: string = e.message;
|
|
526
|
-
|
|
527
|
-
// uninstall the failed app to put system back in good state
|
|
528
|
-
await uninstallApp({ projectId });
|
|
529
|
-
this.updateAppInstallStatus({
|
|
530
|
-
status: keyMirrors.appInstallStatus.failure,
|
|
531
|
-
message,
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
// delete shadow for project
|
|
535
|
-
this.publishMessage(`${this.shadowPrefix}${projectId}/delete`, "");
|
|
536
|
-
}
|
|
537
308
|
break;
|
|
538
309
|
}
|
|
539
310
|
case keyMirrors.clientMessageType.models_install_cloud_response: {
|
|
540
311
|
const { projectId, newModels } = payload.modelsInstallCloudResponse;
|
|
312
|
+
const appReleaseHash = await AgentConfigFile().getAppVersion({
|
|
313
|
+
projectId
|
|
314
|
+
});
|
|
541
315
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
316
|
+
const newAppCfg = this.newAppCfgQueue.shift();
|
|
317
|
+
if (newAppCfg === undefined) {
|
|
318
|
+
logger.error(
|
|
319
|
+
'Unknown error while updating models via application config! No config present for model update.'
|
|
320
|
+
);
|
|
321
|
+
return;
|
|
548
322
|
}
|
|
323
|
+
|
|
324
|
+
await this.atomicApplicationUpdate(
|
|
325
|
+
updateModelsWithPresignedUrls,
|
|
326
|
+
[
|
|
327
|
+
{
|
|
328
|
+
projectId,
|
|
329
|
+
modelInstallPayloads: newModels,
|
|
330
|
+
newAppCfg,
|
|
331
|
+
appReleaseHash
|
|
332
|
+
}
|
|
333
|
+
],
|
|
334
|
+
projectId,
|
|
335
|
+
appReleaseHash
|
|
336
|
+
);
|
|
337
|
+
|
|
549
338
|
break;
|
|
550
339
|
}
|
|
551
340
|
default:
|
|
552
|
-
logger.error(
|
|
341
|
+
logger.error(
|
|
342
|
+
`Invalid client message: '${JSON.stringify(
|
|
343
|
+
{ topic, message },
|
|
344
|
+
null,
|
|
345
|
+
2
|
|
346
|
+
)}'`
|
|
347
|
+
);
|
|
553
348
|
}
|
|
554
349
|
}
|
|
555
350
|
|
|
556
|
-
public async
|
|
557
|
-
topic
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const shadowName = topic.split("/")[5];
|
|
564
|
-
const message = JSON.parse(payload);
|
|
565
|
-
if (topic === this.shadowTopics.projects.updateDelta) {
|
|
566
|
-
this.handleNamedShadowUpdate({ payload });
|
|
567
|
-
} else if (topic === this.shadowTopics.projects.getAccepted) {
|
|
568
|
-
if (message.delta) {
|
|
569
|
-
this.handleNamedShadowUpdate({
|
|
570
|
-
payload: JSON.stringify(message.delta),
|
|
351
|
+
public async handleMessage(topic: string, message: ClientMessage | any) {
|
|
352
|
+
switch (topic) {
|
|
353
|
+
case this.shadowHandler.shadowTopics.projects.getAccepted:
|
|
354
|
+
case this.shadowHandler.shadowTopics.projects.updateDelta: {
|
|
355
|
+
const appConfigUpdates = await this.shadowHandler.handleShadowTopic({
|
|
356
|
+
topic,
|
|
357
|
+
payload: message.state
|
|
571
358
|
});
|
|
572
|
-
|
|
573
|
-
|
|
359
|
+
await this.handleAppConfigUpdates(appConfigUpdates);
|
|
360
|
+
break;
|
|
574
361
|
}
|
|
362
|
+
case this.toDeviceTopic:
|
|
363
|
+
this.handleClientMessage({
|
|
364
|
+
topic,
|
|
365
|
+
message
|
|
366
|
+
});
|
|
367
|
+
break;
|
|
368
|
+
default:
|
|
369
|
+
logger.error(`Unexpected topic, ignoring! ${topic}`);
|
|
575
370
|
}
|
|
576
371
|
}
|
|
577
|
-
}
|
|
578
372
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
{
|
|
583
|
-
const deviceAgent = new DeviceAgentCloudConnection();
|
|
584
|
-
|
|
585
|
-
deviceAgent.device.on("connect", function (connack: any) {
|
|
586
|
-
logger.info("Device Agent has connected to the cloud");
|
|
587
|
-
|
|
588
|
-
// Get shadow updates
|
|
589
|
-
deviceAgent.publishMessage(
|
|
590
|
-
`${deviceAgent.getShadowPrefix()}projects/get`,
|
|
591
|
-
""
|
|
592
|
-
);
|
|
593
|
-
});
|
|
373
|
+
public async setupHandlers() {
|
|
374
|
+
this.device.on('connect', (connack: any) => {
|
|
375
|
+
logger.info('Device Agent has connected to the cloud');
|
|
594
376
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
});
|
|
377
|
+
this.shadowHandler.getShadowUpdates();
|
|
378
|
+
});
|
|
598
379
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
console.log(topic);
|
|
603
|
-
console.log(JSON.parse(payload));
|
|
604
|
-
try {
|
|
605
|
-
const jsonPacket = JSON.parse(payload);
|
|
606
|
-
if (jsonPacket.hasOwnProperty("state")) {
|
|
607
|
-
deviceAgent.handleShadowTopic({
|
|
608
|
-
topic,
|
|
609
|
-
payload: JSON.stringify(jsonPacket.state),
|
|
610
|
-
});
|
|
611
|
-
} else {
|
|
612
|
-
const valid = validateClientMessage(jsonPacket);
|
|
613
|
-
if (!valid) {
|
|
614
|
-
console.error(JSON.stringify(validateClientMessage.errors));
|
|
615
|
-
} else {
|
|
616
|
-
deviceAgent.handleClientMessage({
|
|
617
|
-
topic,
|
|
618
|
-
message: jsonPacket,
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
} catch (error) {
|
|
623
|
-
logger.error(error);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
);
|
|
380
|
+
this.device.on('disconnect', () => {
|
|
381
|
+
logger.warn('Device Agent has been disconnected from the cloud');
|
|
382
|
+
});
|
|
627
383
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
}
|
|
384
|
+
this.device.on('reconnect', () => {
|
|
385
|
+
logger.info(
|
|
386
|
+
`Device Agent attempting to re-connect ${new Date().toLocaleString()}`
|
|
387
|
+
);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
this.device.on('error', function (error) {
|
|
391
|
+
const errorString = error.message.toString();
|
|
392
|
+
logger.error(`${errorString}`);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
this.device.on('message', async (topic: string, payload: string) => {
|
|
396
|
+
try {
|
|
397
|
+
const jsonPacket = JSON.parse(payload);
|
|
398
|
+
logger.debug(
|
|
399
|
+
`Received message: ${JSON.stringify({ topic, jsonPacket }, null, 2)}`
|
|
400
|
+
);
|
|
401
|
+
await this.handleMessage(topic, jsonPacket);
|
|
402
|
+
} catch (error) {
|
|
403
|
+
logger.error(`Error parsing message: ${error}`);
|
|
631
404
|
}
|
|
405
|
+
});
|
|
632
406
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
const bootstrapConfig = {
|
|
638
|
-
keyPath: BOOTSTRAP_DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
639
|
-
certPath: BOOTSTRAP_DEVICE_CERTIFICATE_FILE_PATH,
|
|
640
|
-
caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
641
|
-
clientId,
|
|
642
|
-
host: getIoTCoreEndpointUrl(),
|
|
643
|
-
};
|
|
644
|
-
|
|
645
|
-
const bootstrapAgent = new BootstrapAgent(bootstrapConfig);
|
|
646
|
-
bootstrapAgent.subscribeToAllTopics();
|
|
647
|
-
|
|
648
|
-
bootstrapAgent.publishMessage("$aws/certificates/create/json", "");
|
|
649
|
-
|
|
650
|
-
bootstrapAgent.device.on("connect", () => {
|
|
651
|
-
console.log("Your device is being provisioned");
|
|
652
|
-
});
|
|
407
|
+
this.device.on('offline', () => {
|
|
408
|
+
logger.warn(`Device Agent is offline ${new Date().toLocaleString()}`);
|
|
409
|
+
});
|
|
410
|
+
}
|
|
653
411
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
412
|
+
public stop() {
|
|
413
|
+
this.device.end();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
657
416
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
417
|
+
export async function runDeviceAgentCloudInterface() {
|
|
418
|
+
// FIXME: Check for KeyPath as well
|
|
419
|
+
if (existsSync(getCertificateFilePath())) {
|
|
420
|
+
const deviceAgent = new DeviceAgentCloudConnection();
|
|
421
|
+
await deviceAgent.setupHandlers();
|
|
422
|
+
if (ALWAYSAI_ANALYTICS_PASSTHROUGH === true) {
|
|
423
|
+
const publisher = deviceAgent.publisher;
|
|
424
|
+
const passthroughHandler = new PassthroughHandler(publisher);
|
|
425
|
+
await passthroughHandler.setup();
|
|
426
|
+
runChannel(passthroughHandler);
|
|
661
427
|
}
|
|
428
|
+
} else if (existsSync(BOOTSTRAP_DEVICE_PRIVATE_KEY_FILE_PATH())) {
|
|
429
|
+
bootstrapProvision();
|
|
430
|
+
} else if (existsSync(BOOTSTRAP_CERTIFICATES_DIR_PATH())) {
|
|
431
|
+
throw new Error(
|
|
432
|
+
"Device has not been created using 'aai-agent device init' or there has been an issue with device initialization"
|
|
433
|
+
);
|
|
434
|
+
} else {
|
|
435
|
+
throw new Error(
|
|
436
|
+
"Set device agent to local mode and retry the 'aai-agent device init' command"
|
|
437
|
+
);
|
|
662
438
|
}
|
|
663
439
|
}
|