@alwaysai/device-agent 0.0.7 → 0.0.9
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 +2 -0
- package/lib/application-control/backup.js.map +1 -1
- package/lib/application-control/config.d.ts +17 -0
- package/lib/application-control/config.d.ts.map +1 -0
- package/lib/application-control/config.js +62 -0
- package/lib/application-control/config.js.map +1 -0
- package/lib/application-control/environment-variables.d.ts.map +1 -1
- package/lib/application-control/environment-variables.js +6 -14
- package/lib/application-control/environment-variables.js.map +1 -1
- package/lib/application-control/index.d.ts +2 -1
- package/lib/application-control/index.d.ts.map +1 -1
- package/lib/application-control/index.js +6 -1
- package/lib/application-control/index.js.map +1 -1
- package/lib/application-control/install.d.ts +16 -10
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +95 -57
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/models.d.ts +3 -0
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +96 -20
- package/lib/application-control/models.js.map +1 -1
- package/lib/application-control/status.d.ts +3 -2
- package/lib/application-control/status.d.ts.map +1 -1
- package/lib/application-control/status.js +8 -6
- package/lib/application-control/status.js.map +1 -1
- package/lib/application-control/utils.d.ts +5 -0
- package/lib/application-control/utils.d.ts.map +1 -1
- package/lib/application-control/utils.js +47 -13
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +42 -15
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +357 -195
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/infrastructure/agent-config.d.ts.map +1 -1
- package/lib/infrastructure/agent-config.js +7 -18
- package/lib/infrastructure/agent-config.js.map +1 -1
- package/lib/infrastructure/agent-config.test.js +47 -0
- package/lib/infrastructure/agent-config.test.js.map +1 -1
- package/lib/subcommands/app/app.js +1 -1
- package/lib/subcommands/app/app.js.map +1 -1
- package/lib/subcommands/login.d.ts.map +1 -1
- package/lib/subcommands/login.js +4 -3
- package/lib/subcommands/login.js.map +1 -1
- package/lib/util/copy-dir.d.ts.map +1 -1
- package/lib/util/copy-dir.js +3 -1
- package/lib/util/copy-dir.js.map +1 -1
- package/lib/util/run-in-dir.d.ts +2 -0
- package/lib/util/run-in-dir.d.ts.map +1 -0
- package/lib/util/run-in-dir.js +17 -0
- package/lib/util/run-in-dir.js.map +1 -0
- package/lib/util/sleep.d.ts +2 -0
- package/lib/util/sleep.d.ts.map +1 -0
- package/lib/util/sleep.js +9 -0
- package/lib/util/sleep.js.map +1 -0
- package/package.json +4 -3
- package/src/application-control/backup.ts +3 -0
- package/src/application-control/config.ts +61 -0
- package/src/application-control/environment-variables.ts +6 -12
- package/src/application-control/index.ts +5 -0
- package/src/application-control/install.ts +147 -68
- package/src/application-control/models.ts +136 -23
- package/src/application-control/status.ts +19 -9
- package/src/application-control/utils.ts +58 -13
- package/src/cloud-connection/device-agent-cloud-connection.ts +459 -216
- package/src/infrastructure/agent-config.test.ts +56 -0
- package/src/infrastructure/agent-config.ts +10 -19
- package/src/subcommands/app/app.ts +1 -1
- package/src/subcommands/login.ts +6 -4
- package/src/util/copy-dir.ts +3 -1
- package/src/util/run-in-dir.ts +15 -0
- package/src/util/sleep.ts +5 -0
- package/lib/util/run-cli-cmd.d.ts +0 -5
- package/lib/util/run-cli-cmd.d.ts.map +0 -1
- package/lib/util/run-cli-cmd.js +0 -24
- package/lib/util/run-cli-cmd.js.map +0 -1
- package/src/util/run-cli-cmd.ts +0 -18
|
@@ -1,32 +1,61 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
3
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
4
|
-
var m = o[Symbol.asyncIterator], i;
|
|
5
|
-
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
6
|
-
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
7
|
-
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
8
|
-
};
|
|
9
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
3
|
exports.runDeviceAgentCloudInterface = exports.DeviceAgentCloudConnection = void 0;
|
|
11
4
|
const awsIot = require('aws-iot-device-sdk');
|
|
12
5
|
const urls_1 = require("../infrastructure/urls");
|
|
13
6
|
const directories_1 = require("../util/directories");
|
|
7
|
+
const sleep_1 = require("../util/sleep");
|
|
14
8
|
const status_1 = require("../application-control/status");
|
|
15
9
|
const install_1 = require("../application-control/install");
|
|
16
|
-
const device_control_1 = require("../device-control/device-control");
|
|
17
10
|
const device_agent_schemas_1 = require("@alwaysai/device-agent-schemas");
|
|
18
11
|
const get_device_id_1 = require("../util/get-device-id");
|
|
12
|
+
const util_1 = require("alwaysai/lib/util");
|
|
13
|
+
const device_control_1 = require("../device-control/device-control");
|
|
19
14
|
const agent_config_1 = require("../infrastructure/agent-config");
|
|
15
|
+
const utils_1 = require("../application-control/utils");
|
|
16
|
+
const models_1 = require("../application-control/models");
|
|
17
|
+
const config_1 = require("../application-control/config");
|
|
20
18
|
class DeviceAgentCloudConnection {
|
|
21
19
|
constructor() {
|
|
22
20
|
this.clientId = (0, get_device_id_1.getDeviceId)();
|
|
23
21
|
this.host = (0, urls_1.getIoTCoreEndpointUrl)();
|
|
24
|
-
this.
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
this.liveUpdatesAlive = {
|
|
23
|
+
[device_agent_schemas_1.keyMirrors.agentMessageType.device_stats]: false,
|
|
24
|
+
[device_agent_schemas_1.keyMirrors.agentMessageType.app_state]: false,
|
|
25
|
+
[device_agent_schemas_1.keyMirrors.agentMessageType.app_logs]: false,
|
|
26
|
+
};
|
|
27
|
+
this.liveUpdatesSleepIntervals = {
|
|
28
|
+
[device_agent_schemas_1.keyMirrors.agentMessageType.device_stats]: 5000,
|
|
29
|
+
[device_agent_schemas_1.keyMirrors.agentMessageType.app_state]: 5000,
|
|
30
|
+
[device_agent_schemas_1.keyMirrors.agentMessageType.app_logs]: 5000,
|
|
31
|
+
[device_agent_schemas_1.keyMirrors.agentMessageType.app_install_status]: 5000,
|
|
32
|
+
};
|
|
33
|
+
this.appLogStreams = new Set();
|
|
34
|
+
this.deviceType = 'aai-device';
|
|
35
|
+
this.shadowPrefix = `$aws/things/${this.clientId}/shadow/name/`;
|
|
36
|
+
this.shadowTopics = {
|
|
37
|
+
projects: {
|
|
38
|
+
updateDelta: `${this.shadowPrefix}projects/update/delta`,
|
|
39
|
+
getAccepted: `${this.shadowPrefix}projects/get/accepted`,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
this.toCloudTopic = `topic/to_cloud/${this.deviceType}/${this.clientId}`;
|
|
43
|
+
this.toClientTopic = `topic/to_client/${this.deviceType}/${this.clientId}`;
|
|
44
|
+
this.toDeviceTopic = `topic/to_device/${this.deviceType}/${this.clientId}`;
|
|
45
|
+
// must be arrow function due to this context when function is passed as param
|
|
46
|
+
this.getAppInstallStatusMessage = async () => {
|
|
47
|
+
const appInstallStatus = this.getAppInstallStatus();
|
|
48
|
+
const appInstallStatusMessage = {
|
|
49
|
+
messageType: device_agent_schemas_1.keyMirrors.agentMessageType.app_install_status,
|
|
50
|
+
appInstallStatus,
|
|
51
|
+
};
|
|
52
|
+
return appInstallStatusMessage;
|
|
53
|
+
};
|
|
54
|
+
this.handleDeviceCommand = async (packet) => {
|
|
55
|
+
// TODO
|
|
56
|
+
};
|
|
57
|
+
// Public Methods
|
|
27
58
|
this.device = awsIot.device;
|
|
28
|
-
this.agentTopicPrefix = `destination/agent/device/${this.clientId}/topic/`;
|
|
29
|
-
this.cloudTopicPrefix = `destination/cloud/device/${this.clientId}/topic/`;
|
|
30
59
|
this.device = awsIot.device({
|
|
31
60
|
keyPath: (0, directories_1.getPrivateKeyFilePath)(),
|
|
32
61
|
certPath: (0, directories_1.getCertificateFilePath)(),
|
|
@@ -34,118 +63,117 @@ class DeviceAgentCloudConnection {
|
|
|
34
63
|
clientId: this.clientId,
|
|
35
64
|
host: this.host,
|
|
36
65
|
});
|
|
37
|
-
this.device.subscribe(
|
|
38
|
-
this.device.subscribe(
|
|
66
|
+
this.device.subscribe(this.toDeviceTopic);
|
|
67
|
+
this.device.subscribe(this.shadowTopics.projects.getAccepted);
|
|
68
|
+
this.device.subscribe(this.shadowTopics.projects.updateDelta);
|
|
39
69
|
}
|
|
40
|
-
|
|
41
|
-
|
|
70
|
+
// device shadow utils
|
|
71
|
+
getShadowPrefix() {
|
|
72
|
+
return this.shadowPrefix;
|
|
42
73
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
async handleNamedShadowUpdate({ payload }) {
|
|
75
|
+
const delta = JSON.parse(payload);
|
|
76
|
+
const deltaKeys = Object.keys(delta);
|
|
77
|
+
for (const projectId of deltaKeys) {
|
|
78
|
+
const projectShadow = delta[projectId];
|
|
79
|
+
if (projectShadow.appConfig) {
|
|
80
|
+
const appConfig = projectShadow.appConfig;
|
|
81
|
+
const appDir = (0, utils_1.getAppDir)(projectId);
|
|
82
|
+
await (0, config_1.updateAppConfig)(projectId, appConfig);
|
|
83
|
+
if (appConfig.models) {
|
|
84
|
+
this.publishCloudRequest({
|
|
85
|
+
messageType: device_agent_schemas_1.keyMirrors.agentMessageType.signed_urls_request,
|
|
86
|
+
modelsOnlyUrlsRequest: {
|
|
87
|
+
projectId,
|
|
88
|
+
models: appConfig.models,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (appConfig.scripts && !appConfig.models) {
|
|
93
|
+
const appState = await (0, status_1.getAppStatus)({ projectId });
|
|
94
|
+
await (0, utils_1.buildApp)({ appDir });
|
|
95
|
+
if (appState.services.length &&
|
|
96
|
+
appState.services[0].state !== device_agent_schemas_1.keyMirrors.appState.stopped) {
|
|
97
|
+
(0, status_1.restartApp)({ projectId });
|
|
98
|
+
}
|
|
99
|
+
await this.publishReportedState(projectId);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
57
103
|
}
|
|
58
|
-
|
|
59
|
-
|
|
104
|
+
async startAppLogStream(projectId) {
|
|
105
|
+
this.appLogStreams.add(projectId);
|
|
106
|
+
const readable = await (0, status_1.getAppLogs)({
|
|
107
|
+
projectId,
|
|
108
|
+
args: ['--tail', '100', '--no-log-prefix'],
|
|
109
|
+
});
|
|
110
|
+
readable.on('data', (chunk) => {
|
|
111
|
+
if (!this.appLogStreams.has(projectId)) {
|
|
112
|
+
// why doesn't typescript know about this function?
|
|
113
|
+
// @ts-ignore
|
|
114
|
+
readable.destroy();
|
|
115
|
+
util_1.logger.info(`App log stream terminated for project ${projectId}`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const logStr = chunk.toString();
|
|
119
|
+
const message = {
|
|
120
|
+
messageType: device_agent_schemas_1.keyMirrors.agentMessageType.app_logs,
|
|
121
|
+
appLogs: {
|
|
122
|
+
projectId,
|
|
123
|
+
logChunk: logStr,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
const packet = this.buildMessagePacket(this.getClientId(), this.toClientTopic, message);
|
|
127
|
+
this.publishMessage(this.toClientTopic, JSON.stringify(packet));
|
|
128
|
+
});
|
|
129
|
+
readable.on('error', (error) => {
|
|
130
|
+
util_1.logger.error(`App log stream terminated for project ${projectId}: ${error}`);
|
|
131
|
+
});
|
|
132
|
+
readable.on('finished', () => {
|
|
133
|
+
util_1.logger.info(`App logs finished piping for project ${projectId}`);
|
|
134
|
+
});
|
|
60
135
|
}
|
|
61
|
-
|
|
62
|
-
|
|
136
|
+
// must contain app release hash
|
|
137
|
+
initAppInstallStatus(installationStatus) {
|
|
138
|
+
this.appInstallStatus = installationStatus;
|
|
63
139
|
}
|
|
64
|
-
|
|
65
|
-
|
|
140
|
+
updateAppInstallStatus(installationStatus) {
|
|
141
|
+
this.appInstallStatus.status = installationStatus.status;
|
|
142
|
+
this.appInstallStatus.message = installationStatus.message;
|
|
66
143
|
}
|
|
67
|
-
|
|
68
|
-
this.
|
|
144
|
+
getAppInstallStatus() {
|
|
145
|
+
return this.appInstallStatus;
|
|
69
146
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
function runDeviceAgentCloudInterface() {
|
|
73
|
-
const deviceAgent = new DeviceAgentCloudConnection();
|
|
74
|
-
async function buildMessagePacket(topic, payload) {
|
|
147
|
+
// Message Builders
|
|
148
|
+
buildMessagePacket(deviceId, topic, payload) {
|
|
75
149
|
const packet = {
|
|
76
150
|
timestamp: new Date().toUTCString(),
|
|
77
|
-
deviceId
|
|
151
|
+
deviceId,
|
|
78
152
|
topic,
|
|
79
153
|
payload,
|
|
80
154
|
};
|
|
81
155
|
return packet;
|
|
82
156
|
}
|
|
83
|
-
async
|
|
84
|
-
var e_1, _a;
|
|
85
|
-
const appLogsList = [];
|
|
86
|
-
const apps = await (0, agent_config_1.AgentConfigFile)().getReadyApps();
|
|
87
|
-
for (const app of apps) {
|
|
88
|
-
const projectId = app.projectId;
|
|
89
|
-
const readable = await (0, status_1.getAppLogs)({ projectId });
|
|
90
|
-
const logs = [];
|
|
91
|
-
readable.setEncoding('utf8');
|
|
92
|
-
try {
|
|
93
|
-
for (var readable_1 = (e_1 = void 0, __asyncValues(readable)), readable_1_1; readable_1_1 = await readable_1.next(), !readable_1_1.done;) {
|
|
94
|
-
const chunk = readable_1_1.value;
|
|
95
|
-
logs.push(chunk);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
99
|
-
finally {
|
|
100
|
-
try {
|
|
101
|
-
if (readable_1_1 && !readable_1_1.done && (_a = readable_1.return)) await _a.call(readable_1);
|
|
102
|
-
}
|
|
103
|
-
finally { if (e_1) throw e_1.error; }
|
|
104
|
-
}
|
|
105
|
-
const appLogs = {
|
|
106
|
-
projectId,
|
|
107
|
-
logs,
|
|
108
|
-
};
|
|
109
|
-
appLogsList.push(appLogs);
|
|
110
|
-
}
|
|
111
|
-
const applicationStatePackage = {
|
|
112
|
-
applicationLogs: appLogsList,
|
|
113
|
-
};
|
|
114
|
-
return applicationStatePackage;
|
|
115
|
-
}
|
|
116
|
-
// * This was causing massive CPU overhead
|
|
117
|
-
// const publishAppLogs = setInterval(async function () {
|
|
118
|
-
// const appLogsMessage = await getAppLogsMessage();
|
|
119
|
-
// const topic = `device/${deviceAgent.getClientId()}/topic/application-management`;
|
|
120
|
-
// const appLogsPacket = await buildMessagePacket(topic, appLogsMessage);
|
|
121
|
-
// deviceAgent.publishMessage(topic, JSON.stringify({ appLogsPacket }));
|
|
122
|
-
// }, deviceAgent.getPublishInterval());
|
|
123
|
-
async function getAppStateMessage() {
|
|
157
|
+
async getAppStateMessage() {
|
|
124
158
|
const appStateMessage = [];
|
|
125
|
-
const apps = await (0, agent_config_1.AgentConfigFile)().
|
|
159
|
+
const apps = await (0, agent_config_1.AgentConfigFile)().getReadyApps();
|
|
126
160
|
for (const app of apps) {
|
|
127
161
|
const projectId = app.projectId;
|
|
128
162
|
const status = await (0, status_1.getAppStatus)({ projectId });
|
|
129
163
|
appStateMessage.push(status);
|
|
130
164
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
165
|
+
const appStatePackage = {
|
|
166
|
+
messageType: device_agent_schemas_1.keyMirrors.agentMessageType.app_state,
|
|
167
|
+
appState: appStateMessage,
|
|
133
168
|
};
|
|
134
|
-
return
|
|
169
|
+
return appStatePackage;
|
|
135
170
|
}
|
|
136
|
-
|
|
137
|
-
if (deviceAgent.getPublishable()) {
|
|
138
|
-
const topic = `${deviceAgent.cloudTopicPrefix}application-management`;
|
|
139
|
-
const appStateMessage = await getAppStateMessage();
|
|
140
|
-
const appStatePacket = await buildMessagePacket(topic, appStateMessage);
|
|
141
|
-
deviceAgent.publishMessage(topic, JSON.stringify({ appStatePacket }));
|
|
142
|
-
}
|
|
143
|
-
}, deviceAgent.getPublishInterval());
|
|
144
|
-
async function getDeviceStatsMessage() {
|
|
171
|
+
async getDeviceStatsMessage() {
|
|
145
172
|
const cpuUsage = await (0, device_control_1.getCpuUtil)();
|
|
146
173
|
const diskUtil = await (0, device_control_1.getDiskUtil)();
|
|
147
174
|
const memUtil = await (0, device_control_1.getMemUtil)();
|
|
148
175
|
const deviceStatsMessage = {
|
|
176
|
+
messageType: device_agent_schemas_1.keyMirrors.agentMessageType.device_stats,
|
|
149
177
|
deviceStats: {
|
|
150
178
|
cpuUsage,
|
|
151
179
|
diskUtil,
|
|
@@ -154,122 +182,256 @@ function runDeviceAgentCloudInterface() {
|
|
|
154
182
|
};
|
|
155
183
|
return deviceStatsMessage;
|
|
156
184
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
185
|
+
async startPublishingLiveUpdates(topic, messageType, getMessageData) {
|
|
186
|
+
while (true) {
|
|
187
|
+
try {
|
|
188
|
+
const message = await getMessageData();
|
|
189
|
+
const packet = this.buildMessagePacket(this.getClientId(), topic, message);
|
|
190
|
+
this.publishMessage(topic, JSON.stringify(packet));
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
util_1.logger.error(`Error publishing live updates for ${messageType}: ${e.message}`);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
if (!this.continuePublishing(messageType)) {
|
|
197
|
+
util_1.logger.info(`Turned off live updates for ${messageType}`);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
await (0, sleep_1.default)(this.getLiveUpdatesInterval(messageType));
|
|
163
201
|
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
202
|
+
}
|
|
203
|
+
continuePublishing(flag) {
|
|
204
|
+
switch (flag) {
|
|
205
|
+
case device_agent_schemas_1.keyMirrors.agentMessageType.device_stats:
|
|
206
|
+
case device_agent_schemas_1.keyMirrors.agentMessageType.app_state:
|
|
207
|
+
return this.liveUpdatesAlive[flag];
|
|
208
|
+
case device_agent_schemas_1.keyMirrors.agentMessageType.app_install_status:
|
|
209
|
+
return this.appInstallStatus.status === device_agent_schemas_1.keyMirrors.appInstallStatus.in_progress;
|
|
210
|
+
default:
|
|
211
|
+
util_1.logger.error(`Unrecognized publishable flag ${flag}`);
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
getLiveUpdatesInterval(flag) {
|
|
216
|
+
const exists = this.liveUpdatesSleepIntervals[flag];
|
|
217
|
+
if (exists) {
|
|
218
|
+
return exists;
|
|
219
|
+
}
|
|
220
|
+
util_1.logger.error(`Unrecognized live updates flag ${flag}`);
|
|
221
|
+
return -1;
|
|
222
|
+
}
|
|
223
|
+
setLiveUpdates(toggles) {
|
|
224
|
+
if (toggles.deviceStats) {
|
|
225
|
+
this.liveUpdatesAlive.device_stats = toggles.deviceStats;
|
|
226
|
+
}
|
|
227
|
+
if (toggles.appState) {
|
|
228
|
+
this.liveUpdatesAlive.app_state = toggles.appState;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
handleAppStateControl(payload) {
|
|
232
|
+
const { baseCommand, projectId } = payload;
|
|
233
|
+
switch (baseCommand) {
|
|
234
|
+
case device_agent_schemas_1.keyMirrors.appStateControl.start:
|
|
235
|
+
(0, status_1.startApp)({ projectId });
|
|
236
|
+
break;
|
|
237
|
+
case device_agent_schemas_1.keyMirrors.appStateControl.stop:
|
|
238
|
+
(0, status_1.stopApp)({ projectId });
|
|
239
|
+
break;
|
|
240
|
+
case device_agent_schemas_1.keyMirrors.appStateControl.restart:
|
|
241
|
+
(0, status_1.restartApp)({ projectId });
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
handleAppVersionControl(payload) {
|
|
246
|
+
const { projectId, appReleaseHash } = payload;
|
|
247
|
+
const signedUrlsRequest = { projectId, appReleaseHash };
|
|
248
|
+
this.publishCloudRequest({
|
|
249
|
+
messageType: device_agent_schemas_1.keyMirrors.agentMessageType.signed_urls_request,
|
|
250
|
+
signedUrlsRequest,
|
|
181
251
|
});
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
252
|
+
}
|
|
253
|
+
handleAgentCommand(message) {
|
|
254
|
+
switch (message.messageType) {
|
|
255
|
+
case device_agent_schemas_1.keyMirrors.clientMessageType.live_state_updates:
|
|
256
|
+
this.liveUpdatesBroker(message.liveUpdatesToggles);
|
|
257
|
+
break;
|
|
258
|
+
default:
|
|
259
|
+
util_1.logger.error(`Invalid agent action message type from message '${message}'`);
|
|
185
260
|
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
261
|
+
}
|
|
262
|
+
restartLiveUpdatesTimeout() {
|
|
263
|
+
clearTimeout(this.liveUpdatesTimeout);
|
|
264
|
+
this.liveUpdatesTimeout = setTimeout(() => {
|
|
265
|
+
this.setLiveUpdates({
|
|
266
|
+
deviceStats: false,
|
|
267
|
+
appState: false,
|
|
268
|
+
});
|
|
269
|
+
this.appLogStreams.clear();
|
|
270
|
+
// TODO: Make constant, not hard coded
|
|
271
|
+
}, 600000); // 10 min
|
|
272
|
+
}
|
|
273
|
+
async liveUpdatesBroker({ deviceStats, appState, appLogs, }) {
|
|
274
|
+
this.restartLiveUpdatesTimeout();
|
|
275
|
+
if (deviceStats !== undefined) {
|
|
276
|
+
this.liveUpdatesAlive.device_stats = deviceStats;
|
|
277
|
+
if (deviceStats) {
|
|
278
|
+
this.startPublishingLiveUpdates(this.toClientTopic, device_agent_schemas_1.keyMirrors.agentMessageType.device_stats, this.getDeviceStatsMessage);
|
|
279
|
+
}
|
|
193
280
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
281
|
+
if (appState !== undefined) {
|
|
282
|
+
this.liveUpdatesAlive.app_state = appState;
|
|
283
|
+
if (appState) {
|
|
284
|
+
this.startPublishingLiveUpdates(this.toClientTopic, device_agent_schemas_1.keyMirrors.agentMessageType.app_state, this.getAppStateMessage);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (appLogs !== undefined) {
|
|
288
|
+
if (appLogs.toggle) {
|
|
289
|
+
this.startAppLogStream(appLogs.projectId);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
this.appLogStreams.delete(appLogs.projectId);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async publishReportedState(projectId) {
|
|
297
|
+
const newAppCfg = await (0, utils_1.getAppConfig)(projectId);
|
|
298
|
+
const packet = {
|
|
299
|
+
state: {
|
|
300
|
+
reported: {
|
|
301
|
+
[projectId]: { appConfig: newAppCfg },
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
this.publishMessage(`${this.shadowPrefix}projects/update`, JSON.stringify(packet));
|
|
306
|
+
}
|
|
307
|
+
async publishCloudRequest(payload) {
|
|
308
|
+
const topic = this.toCloudTopic;
|
|
309
|
+
const deviceRequestPacket = this.buildMessagePacket(this.getClientId(), topic, payload);
|
|
310
|
+
this.publishMessage(topic, JSON.stringify({ deviceRequestPacket }));
|
|
311
|
+
}
|
|
312
|
+
getClientId() {
|
|
313
|
+
return this.clientId;
|
|
314
|
+
}
|
|
315
|
+
publishMessage(topic, message) {
|
|
316
|
+
// TODO: topic validation
|
|
317
|
+
this.device.publish(topic, message);
|
|
318
|
+
}
|
|
319
|
+
async handleClientMessage({ topic, message, }) {
|
|
320
|
+
const payload = message.payload;
|
|
321
|
+
switch (payload.messageType) {
|
|
322
|
+
case device_agent_schemas_1.keyMirrors.clientMessageType.app_state_control: {
|
|
323
|
+
this.handleAppStateControl(payload.appStateControl);
|
|
235
324
|
break;
|
|
236
|
-
|
|
237
|
-
|
|
325
|
+
}
|
|
326
|
+
case device_agent_schemas_1.keyMirrors.clientMessageType.app_version_control: {
|
|
327
|
+
this.handleAppVersionControl(payload.appVersionControl);
|
|
238
328
|
break;
|
|
239
|
-
|
|
240
|
-
|
|
329
|
+
}
|
|
330
|
+
case device_agent_schemas_1.keyMirrors.clientMessageType.live_state_updates: {
|
|
331
|
+
this.handleAgentCommand(payload);
|
|
241
332
|
break;
|
|
242
|
-
|
|
243
|
-
|
|
333
|
+
}
|
|
334
|
+
case device_agent_schemas_1.keyMirrors.clientMessageType.app_install_cloud_response: {
|
|
335
|
+
const { projectId, appReleaseHash, appInstallPayload, modelsInstallPayload } = payload.appInstallCloudResponse;
|
|
336
|
+
this.initAppInstallStatus({
|
|
337
|
+
status: device_agent_schemas_1.keyMirrors.appInstallStatus.in_progress,
|
|
338
|
+
appReleaseHash,
|
|
339
|
+
});
|
|
340
|
+
this.startPublishingLiveUpdates(this.toClientTopic, device_agent_schemas_1.keyMirrors.agentMessageType.app_install_status, this.getAppInstallStatusMessage);
|
|
341
|
+
// Install the app and models
|
|
342
|
+
try {
|
|
343
|
+
const signedUrlsPayload = {
|
|
344
|
+
appInstallPayload,
|
|
345
|
+
modelsInstallPayload,
|
|
346
|
+
};
|
|
347
|
+
await (0, install_1.installApp)({
|
|
348
|
+
projectId,
|
|
349
|
+
appReleaseHash,
|
|
350
|
+
signedUrlsPayload,
|
|
351
|
+
});
|
|
352
|
+
this.updateAppInstallStatus({
|
|
353
|
+
status: device_agent_schemas_1.keyMirrors.appInstallStatus.success,
|
|
354
|
+
});
|
|
355
|
+
// update app config shadow for project
|
|
356
|
+
await this.publishReportedState(projectId);
|
|
357
|
+
}
|
|
358
|
+
catch (e) {
|
|
359
|
+
console.error(e);
|
|
360
|
+
const message = e.message;
|
|
361
|
+
// uninstall the failed app to put system back in good state
|
|
362
|
+
await (0, install_1.uninstallApp)({ projectId });
|
|
363
|
+
this.updateAppInstallStatus({
|
|
364
|
+
status: device_agent_schemas_1.keyMirrors.appInstallStatus.failure,
|
|
365
|
+
message,
|
|
366
|
+
});
|
|
367
|
+
// delete shadow for project
|
|
368
|
+
this.publishMessage(`${this.shadowPrefix}${projectId}/delete`, '');
|
|
369
|
+
}
|
|
244
370
|
break;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
deviceAgent.setPublishable(actionPayload);
|
|
256
|
-
deviceAgent.restartPublishableTimeout();
|
|
371
|
+
}
|
|
372
|
+
case device_agent_schemas_1.keyMirrors.clientMessageType.models_install_cloud_response: {
|
|
373
|
+
const { projectId, newModels } = payload.modelsInstallCloudResponse;
|
|
374
|
+
try {
|
|
375
|
+
await (0, models_1.updateModelsWithPresignedUrls)(projectId, newModels);
|
|
376
|
+
await this.publishReportedState(projectId);
|
|
377
|
+
}
|
|
378
|
+
catch (e) {
|
|
379
|
+
console.error(e);
|
|
380
|
+
}
|
|
257
381
|
break;
|
|
382
|
+
}
|
|
258
383
|
default:
|
|
259
|
-
|
|
384
|
+
util_1.logger.error(`Invalid Client Message '${JSON.stringify(payload)}'`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async handleShadowTopic({ topic, payload }) {
|
|
388
|
+
const shadowName = topic.split('/')[5];
|
|
389
|
+
const message = JSON.parse(payload);
|
|
390
|
+
if (topic === this.shadowTopics.projects.updateDelta) {
|
|
391
|
+
this.handleNamedShadowUpdate({ payload });
|
|
260
392
|
}
|
|
261
|
-
|
|
393
|
+
else if (topic === this.shadowTopics.projects.getAccepted) {
|
|
394
|
+
if (message.delta) {
|
|
395
|
+
this.handleNamedShadowUpdate({
|
|
396
|
+
payload: JSON.stringify(message.delta),
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
console.log(`No delta updates in shadow ${shadowName}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
exports.DeviceAgentCloudConnection = DeviceAgentCloudConnection;
|
|
406
|
+
function runDeviceAgentCloudInterface() {
|
|
407
|
+
const deviceAgent = new DeviceAgentCloudConnection();
|
|
262
408
|
deviceAgent.device.on('connect', function () {
|
|
263
409
|
deviceAgent.publishMessage('connection', deviceAgent.getClientId());
|
|
264
410
|
console.log('Device Agent has connected to the cloud');
|
|
411
|
+
// Get shadow updates
|
|
412
|
+
deviceAgent.publishMessage(`${deviceAgent.getShadowPrefix()}projects/get`, '');
|
|
265
413
|
});
|
|
266
414
|
deviceAgent.device.on('disconnect', function () {
|
|
267
415
|
console.log('Device Agent has been disconnected from the cloud');
|
|
268
416
|
});
|
|
269
417
|
deviceAgent.device.on('message', function (topic, payload) {
|
|
270
|
-
// ToDo: insert valdiation here for incoming messages. Maybe we will use a JSON schema for the message structure.
|
|
271
418
|
try {
|
|
272
|
-
|
|
419
|
+
const jsonPacket = JSON.parse(payload);
|
|
420
|
+
if (jsonPacket.hasOwnProperty('state')) {
|
|
421
|
+
deviceAgent.handleShadowTopic({
|
|
422
|
+
topic,
|
|
423
|
+
payload: JSON.stringify(jsonPacket.state),
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
const valid = (0, device_agent_schemas_1.validateClientMessage)(jsonPacket);
|
|
428
|
+
if (!valid) {
|
|
429
|
+
console.error(JSON.stringify(device_agent_schemas_1.validateClientMessage.errors));
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
deviceAgent.handleClientMessage({ topic, message: jsonPacket });
|
|
433
|
+
}
|
|
434
|
+
}
|
|
273
435
|
}
|
|
274
436
|
catch (error) {
|
|
275
437
|
console.error(error);
|