edge-impulse-linux 1.11.0 → 1.12.0
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/build/cli/linux/runner.js +31 -4
- package/build/cli/linux/runner.js.map +1 -1
- package/build/cli-common/aws-iotcore-connector.d.ts +97 -6
- package/build/cli-common/aws-iotcore-connector.js +952 -61
- package/build/cli-common/aws-iotcore-connector.js.map +1 -1
- package/build/cli-common/init-cli-app.js +5 -3
- package/build/cli-common/init-cli-app.js.map +1 -1
- package/build/sdk/studio/sdk/api/adminApi.d.ts +162 -8
- package/build/sdk/studio/sdk/api/adminApi.js +874 -7
- package/build/sdk/studio/sdk/api/adminApi.js.map +1 -1
- package/build/sdk/studio/sdk/api/learnApi.d.ts +5 -1
- package/build/sdk/studio/sdk/api/learnApi.js +5 -1
- package/build/sdk/studio/sdk/api/learnApi.js.map +1 -1
- package/build/sdk/studio/sdk/api/organizationBlocksApi.d.ts +6 -10
- package/build/sdk/studio/sdk/api/organizationBlocksApi.js +5 -5
- package/build/sdk/studio/sdk/api/organizationBlocksApi.js.map +1 -1
- package/build/sdk/studio/sdk/api/organizationDataApi.d.ts +3 -2
- package/build/sdk/studio/sdk/api/organizationDataApi.js +2 -2
- package/build/sdk/studio/sdk/api/organizationDataApi.js.map +1 -1
- package/build/sdk/studio/sdk/api/organizationPipelinesApi.d.ts +2 -2
- package/build/sdk/studio/sdk/api/organizationPipelinesApi.js +1 -1
- package/build/sdk/studio/sdk/api/organizationPipelinesApi.js.map +1 -1
- package/build/sdk/studio/sdk/api/organizationsApi.d.ts +11 -10
- package/build/sdk/studio/sdk/api/organizationsApi.js +8 -8
- package/build/sdk/studio/sdk/api/organizationsApi.js.map +1 -1
- package/build/sdk/studio/sdk/api/projectsApi.d.ts +6 -5
- package/build/sdk/studio/sdk/api/projectsApi.js +4 -4
- package/build/sdk/studio/sdk/api/projectsApi.js.map +1 -1
- package/build/sdk/studio/sdk/api/rawDataApi.d.ts +42 -2
- package/build/sdk/studio/sdk/api/rawDataApi.js +82 -2
- package/build/sdk/studio/sdk/api/rawDataApi.js.map +1 -1
- package/build/sdk/studio/sdk/model/addApiKeyRequest.d.ts +2 -2
- package/build/sdk/studio/sdk/model/{addOrganizationSecretResponse.d.ts → addApiKeyResponse.d.ts} +6 -2
- package/build/sdk/studio/sdk/model/{createPipelineResponse.js → addApiKeyResponse.js} +12 -7
- package/build/sdk/studio/sdk/model/addApiKeyResponse.js.map +1 -0
- package/build/sdk/studio/sdk/model/{addOrganizationSecretResponseAllOf.d.ts → addApiKeyResponseAllOf.d.ts} +6 -2
- package/build/sdk/studio/sdk/model/{addOrganizationSecretResponseAllOf.js → addApiKeyResponseAllOf.js} +12 -7
- package/build/sdk/studio/sdk/model/addApiKeyResponseAllOf.js.map +1 -0
- package/build/sdk/studio/sdk/model/addOrganizationApiKeyRequest.d.ts +2 -2
- package/build/sdk/studio/sdk/model/addProjectApiKeyRequest.d.ts +2 -2
- package/build/sdk/studio/sdk/model/adminAddOrganizationApiKeyRequest.d.ts +2 -2
- package/build/sdk/studio/sdk/model/adminAddProjectApiKeyRequest.d.ts +2 -2
- package/build/sdk/studio/sdk/model/adminApiOrganization.d.ts +4 -0
- package/build/sdk/studio/sdk/model/adminApiOrganization.js +5 -0
- package/build/sdk/studio/sdk/model/adminApiOrganization.js.map +1 -1
- package/build/sdk/studio/sdk/model/adminApiProject.d.ts +1 -0
- package/build/sdk/studio/sdk/model/adminApiProject.js +5 -0
- package/build/sdk/studio/sdk/model/adminApiProject.js.map +1 -1
- package/build/sdk/studio/sdk/model/{addOrganizationTransferLearningBlockResponse.d.ts → adminGetTrashBinResponse.d.ts} +7 -2
- package/build/sdk/studio/sdk/model/{addOrganizationSecretResponse.js → adminGetTrashBinResponse.js} +14 -9
- package/build/sdk/studio/sdk/model/adminGetTrashBinResponse.js.map +1 -0
- package/build/sdk/studio/sdk/model/adminGetTrashBinResponseAllOf.d.ts +33 -0
- package/build/sdk/studio/sdk/model/adminGetTrashBinResponseAllOf.js +37 -0
- package/build/sdk/studio/sdk/model/adminGetTrashBinResponseAllOf.js.map +1 -0
- package/build/sdk/studio/sdk/model/adminGetUsersResponseAllOfUsers.d.ts +1 -0
- package/build/sdk/studio/sdk/model/adminGetUsersResponseAllOfUsers.js +5 -0
- package/build/sdk/studio/sdk/model/adminGetUsersResponseAllOfUsers.js.map +1 -1
- package/build/sdk/studio/sdk/model/{addOrganizationDeployBlockResponse.d.ts → adminProjectInfoResponse.d.ts} +3 -2
- package/build/sdk/studio/sdk/model/{createAIActionResponse.js → adminProjectInfoResponse.js} +10 -10
- package/build/sdk/studio/sdk/model/adminProjectInfoResponse.js.map +1 -0
- package/build/sdk/studio/sdk/model/{dspRunResponseAllOfPerformance.d.ts → adminProjectInfoResponseAllOf.d.ts} +3 -3
- package/build/sdk/studio/sdk/model/adminProjectInfoResponseAllOf.js +29 -0
- package/build/sdk/studio/sdk/model/adminProjectInfoResponseAllOf.js.map +1 -0
- package/build/sdk/studio/sdk/model/createEnterpriseTrialResponse.d.ts +2 -2
- package/build/sdk/studio/sdk/model/dSPConfig.d.ts +1 -1
- package/build/sdk/studio/sdk/model/dSPConfigResponse.d.ts +1 -1
- package/build/sdk/studio/sdk/model/dSPInfo.d.ts +2 -2
- package/build/sdk/studio/sdk/model/dSPInfo.js +1 -1
- package/build/sdk/studio/sdk/model/dSPInfo.js.map +1 -1
- package/build/sdk/studio/sdk/model/deploymentTarget.d.ts +4 -0
- package/build/sdk/studio/sdk/model/deploymentTarget.js +5 -0
- package/build/sdk/studio/sdk/model/deploymentTarget.js.map +1 -1
- package/build/sdk/studio/sdk/model/deploymentTargetEngine.d.ts +1 -1
- package/build/sdk/studio/sdk/model/deploymentTargetEngine.js +1 -1
- package/build/sdk/studio/sdk/model/deploymentTargetEngine.js.map +1 -1
- package/build/sdk/studio/sdk/model/{addOrganizationDspBlockResponse.d.ts → dspPerformance.d.ts} +5 -8
- package/build/sdk/studio/sdk/model/{dspRunResponseAllOfPerformance.js → dspPerformance.js} +12 -7
- package/build/sdk/studio/sdk/model/dspPerformance.js.map +1 -0
- package/build/sdk/studio/sdk/model/dspRunResponse.d.ts +2 -2
- package/build/sdk/studio/sdk/model/dspRunResponse.js +1 -1
- package/build/sdk/studio/sdk/model/dspRunResponse.js.map +1 -1
- package/build/sdk/studio/sdk/model/dspRunResponseAllOf.d.ts +2 -2
- package/build/sdk/studio/sdk/model/dspRunResponseAllOf.js +1 -1
- package/build/sdk/studio/sdk/model/dspRunResponseAllOf.js.map +1 -1
- package/build/sdk/studio/sdk/model/dspRunResponseWithSample.d.ts +2 -2
- package/build/sdk/studio/sdk/model/dspRunResponseWithSample.js +1 -1
- package/build/sdk/studio/sdk/model/dspRunResponseWithSample.js.map +1 -1
- package/build/sdk/studio/sdk/model/dspRunResponseWithSampleAllOf.d.ts +2 -2
- package/build/sdk/studio/sdk/model/dspRunResponseWithSampleAllOf.js +1 -1
- package/build/sdk/studio/sdk/model/dspRunResponseWithSampleAllOf.js.map +1 -1
- package/build/sdk/studio/sdk/model/entityCreatedResponse.d.ts +2 -2
- package/build/sdk/studio/sdk/model/entityCreatedResponseAllOf.d.ts +2 -2
- package/build/sdk/studio/sdk/model/feature.d.ts +1 -1
- package/build/sdk/studio/sdk/model/feature.js +1 -1
- package/build/sdk/studio/sdk/model/feature.js.map +1 -1
- package/build/sdk/studio/sdk/model/findSyntiantPosteriorRequest.d.ts +1 -1
- package/build/sdk/studio/sdk/model/findSyntiantPosteriorRequest.js +1 -1
- package/build/sdk/studio/sdk/model/findSyntiantPosteriorRequest.js.map +1 -1
- package/build/sdk/studio/sdk/model/models.d.ts +11 -11
- package/build/sdk/studio/sdk/model/models.js +36 -33
- package/build/sdk/studio/sdk/model/models.js.map +1 -1
- package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponse.d.ts +2 -2
- package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponse.js +1 -1
- package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponse.js.map +1 -1
- package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponseAllOf.d.ts +2 -2
- package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponseAllOf.js +1 -1
- package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponseAllOf.js.map +1 -1
- package/build/sdk/studio/sdk/model/{addOrganizationTransformationBlockResponseAllOf.d.ts → objectDetectionLabelQueueResponseAllOfSamples.d.ts} +1 -1
- package/build/sdk/studio/sdk/model/{addOrganizationTransformationBlockResponseAllOf.js → objectDetectionLabelQueueResponseAllOfSamples.js} +7 -7
- package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponseAllOfSamples.js.map +1 -0
- package/build/sdk/studio/sdk/model/optimizeConfig.d.ts +13 -2
- package/build/sdk/studio/sdk/model/optimizeConfig.js +16 -1
- package/build/sdk/studio/sdk/model/optimizeConfig.js.map +1 -1
- package/build/sdk/studio/sdk/model/{createAIActionResponse.d.ts → optimizeConfigOptimizationObjectives.d.ts} +9 -6
- package/build/sdk/studio/sdk/model/optimizeConfigOptimizationObjectives.js +39 -0
- package/build/sdk/studio/sdk/model/optimizeConfigOptimizationObjectives.js.map +1 -0
- package/build/sdk/studio/sdk/model/optimizeConfigResponse.d.ts +13 -2
- package/build/sdk/studio/sdk/model/optimizeConfigResponse.js +16 -1
- package/build/sdk/studio/sdk/model/optimizeConfigResponse.js.map +1 -1
- package/build/sdk/studio/sdk/model/optimizeConfigSearchSpaceSource.d.ts +45 -0
- package/build/sdk/studio/sdk/model/optimizeConfigSearchSpaceSource.js +48 -0
- package/build/sdk/studio/sdk/model/optimizeConfigSearchSpaceSource.js.map +1 -0
- package/build/sdk/studio/sdk/model/optimizeStateResponse.d.ts +6 -0
- package/build/sdk/studio/sdk/model/optimizeStateResponse.js +7 -1
- package/build/sdk/studio/sdk/model/optimizeStateResponse.js.map +1 -1
- package/build/sdk/studio/sdk/model/optimizeStateResponseAllOf.d.ts +6 -0
- package/build/sdk/studio/sdk/model/optimizeStateResponseAllOf.js +7 -1
- package/build/sdk/studio/sdk/model/optimizeStateResponseAllOf.js.map +1 -1
- package/build/sdk/studio/sdk/model/organization.d.ts +4 -0
- package/build/sdk/studio/sdk/model/organization.js +5 -0
- package/build/sdk/studio/sdk/model/organization.js.map +1 -1
- package/build/sdk/studio/sdk/model/permission.d.ts +1 -1
- package/build/sdk/studio/sdk/model/permission.js +1 -1
- package/build/sdk/studio/sdk/model/permission.js.map +1 -1
- package/build/sdk/studio/sdk/model/project.d.ts +7 -0
- package/build/sdk/studio/sdk/model/project.js +20 -0
- package/build/sdk/studio/sdk/model/project.js.map +1 -1
- package/build/sdk/studio/sdk/model/projectDeploymentTarget.d.ts +4 -0
- package/build/sdk/studio/sdk/model/projectDeploymentTarget.js +5 -0
- package/build/sdk/studio/sdk/model/projectDeploymentTarget.js.map +1 -1
- package/build/sdk/studio/sdk/model/socketTokenResponse.d.ts +1 -1
- package/build/sdk/studio/sdk/model/socketTokenResponseAllOf.d.ts +1 -1
- package/build/sdk/studio/sdk/model/trashBinEntity.d.ts +55 -0
- package/build/sdk/studio/sdk/model/trashBinEntity.js +62 -0
- package/build/sdk/studio/sdk/model/trashBinEntity.js.map +1 -0
- package/build/sdk/studio/sdk/model/tunerRun.d.ts +1 -0
- package/build/sdk/studio/sdk/model/tunerRun.js +5 -0
- package/build/sdk/studio/sdk/model/tunerRun.js.map +1 -1
- package/package.json +5 -1
- package/build/sdk/studio/sdk/model/addOrganizationDeployBlockResponse.js +0 -39
- package/build/sdk/studio/sdk/model/addOrganizationDeployBlockResponse.js.map +0 -1
- package/build/sdk/studio/sdk/model/addOrganizationDspBlockResponse.js +0 -39
- package/build/sdk/studio/sdk/model/addOrganizationDspBlockResponse.js.map +0 -1
- package/build/sdk/studio/sdk/model/addOrganizationSecretResponse.js.map +0 -1
- package/build/sdk/studio/sdk/model/addOrganizationSecretResponseAllOf.js.map +0 -1
- package/build/sdk/studio/sdk/model/addOrganizationTransferLearningBlockResponse.js +0 -39
- package/build/sdk/studio/sdk/model/addOrganizationTransferLearningBlockResponse.js.map +0 -1
- package/build/sdk/studio/sdk/model/addOrganizationTransformationBlockResponse.d.ts +0 -33
- package/build/sdk/studio/sdk/model/addOrganizationTransformationBlockResponse.js +0 -39
- package/build/sdk/studio/sdk/model/addOrganizationTransformationBlockResponse.js.map +0 -1
- package/build/sdk/studio/sdk/model/addOrganizationTransformationBlockResponseAllOf.js.map +0 -1
- package/build/sdk/studio/sdk/model/createAIActionResponse.js.map +0 -1
- package/build/sdk/studio/sdk/model/createPipelineResponse.d.ts +0 -33
- package/build/sdk/studio/sdk/model/createPipelineResponse.js.map +0 -1
- package/build/sdk/studio/sdk/model/developmentBoardCreatedResponse.d.ts +0 -33
- package/build/sdk/studio/sdk/model/developmentBoardCreatedResponse.js +0 -39
- package/build/sdk/studio/sdk/model/developmentBoardCreatedResponse.js.map +0 -1
- package/build/sdk/studio/sdk/model/dspRunResponseAllOfPerformance.js.map +0 -1
|
@@ -1,12 +1,252 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
4
24
|
};
|
|
5
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.AWSIoTCoreConnector = void 0;
|
|
26
|
+
exports.AWSIoTCoreConnector = exports.MetricsCollector = exports.ModelInfo = exports.ThresholdFilter = void 0;
|
|
7
27
|
const client_iot_data_plane_1 = require("@aws-sdk/client-iot-data-plane");
|
|
8
|
-
const
|
|
9
|
-
const
|
|
28
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
29
|
+
const fs = __importStar(require("fs"));
|
|
30
|
+
const path = __importStar(require("path"));
|
|
31
|
+
const uuid_1 = require("uuid");
|
|
32
|
+
const PREFIX = '\x1b[34m[AWS_IOTCORE_CONNECTOR]\x1b[0m';
|
|
33
|
+
// maximum size of IoTCore Message
|
|
34
|
+
const IOTCORE_MAX_PAYLOAD = 131072;
|
|
35
|
+
// number of digits to the right of the decimal...
|
|
36
|
+
const NUM_PLACES = 6;
|
|
37
|
+
// Default metrics collector interval in MS
|
|
38
|
+
const DEFAULT_METRICS_COLLECTION_TIME_MS = 60000; // 60 seconds
|
|
39
|
+
// Default polling interval in MS
|
|
40
|
+
const DEFAULT_POLLING_TIME_MS = 2500; // 2.5 seconds
|
|
41
|
+
// Default Threshold value
|
|
42
|
+
const DEFAULT_THRESHOLD_VALUE = 0.70; // 70% confidence minimum
|
|
43
|
+
// Default Threshold Criteria
|
|
44
|
+
const DEFAULT_THRESHOLD_CRITERIA = "ge"; // ">="
|
|
45
|
+
// threshold filter class
|
|
46
|
+
class ThresholdFilter {
|
|
47
|
+
constructor(opts) {
|
|
48
|
+
this._confidenceThreshold = opts.default_threshold;
|
|
49
|
+
this._confidenceThresholdCriteria = opts.threshold_criteria;
|
|
50
|
+
this._enabled = this.sanizeYesNo(process.env.EI_ENABLE_THRESHOLD_LIMIT);
|
|
51
|
+
}
|
|
52
|
+
// sanitize yes/no options
|
|
53
|
+
sanizeYesNo(value) {
|
|
54
|
+
if (value !== undefined && value !== "") {
|
|
55
|
+
if (value.toLowerCase() === "yes") {
|
|
56
|
+
return "yes";
|
|
57
|
+
}
|
|
58
|
+
if (value.toLowerCase() === "no") {
|
|
59
|
+
return "no";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return "no";
|
|
63
|
+
}
|
|
64
|
+
// get the confidence threshold
|
|
65
|
+
getConfidenceThreshold() {
|
|
66
|
+
return this._confidenceThreshold;
|
|
67
|
+
}
|
|
68
|
+
// confirm confidence threshold in bounds
|
|
69
|
+
confidenceThresholdValueInBounds(threshold) {
|
|
70
|
+
return (threshold > 0 && threshold <= 1.0);
|
|
71
|
+
}
|
|
72
|
+
// set the confidence threshold
|
|
73
|
+
setConfidenceThreshold(threshold) {
|
|
74
|
+
this._confidenceThreshold = threshold;
|
|
75
|
+
return this.getConfidenceThreshold();
|
|
76
|
+
}
|
|
77
|
+
// get the confidence criteria
|
|
78
|
+
getConfidenceCriteria() {
|
|
79
|
+
return this._confidenceThresholdCriteria;
|
|
80
|
+
}
|
|
81
|
+
// set the confidence criteria
|
|
82
|
+
setConfidenceCriteria(criteria) {
|
|
83
|
+
this._confidenceThresholdCriteria = criteria.toLowerCase();
|
|
84
|
+
return this.getConfidenceCriteria();
|
|
85
|
+
}
|
|
86
|
+
// enable the filter
|
|
87
|
+
enable() {
|
|
88
|
+
this._enabled = "yes";
|
|
89
|
+
}
|
|
90
|
+
// disable the filter
|
|
91
|
+
disable() {
|
|
92
|
+
this._enabled = "no";
|
|
93
|
+
}
|
|
94
|
+
// meets criteria?
|
|
95
|
+
meetsThresholdCriteria(threshold) {
|
|
96
|
+
let result = false;
|
|
97
|
+
if (this._confidenceThresholdCriteria === "gt") {
|
|
98
|
+
result = (threshold > this._confidenceThreshold);
|
|
99
|
+
}
|
|
100
|
+
if (this._confidenceThresholdCriteria === "ge") {
|
|
101
|
+
result = (threshold >= this._confidenceThreshold);
|
|
102
|
+
}
|
|
103
|
+
if (this._confidenceThresholdCriteria === "eq") {
|
|
104
|
+
result = (threshold === this._confidenceThreshold);
|
|
105
|
+
}
|
|
106
|
+
if (this._confidenceThresholdCriteria === "le") {
|
|
107
|
+
result = (threshold <= this._confidenceThreshold);
|
|
108
|
+
}
|
|
109
|
+
if (this._confidenceThresholdCriteria === "lt") {
|
|
110
|
+
result = (threshold < this._confidenceThreshold);
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
// valid criteria
|
|
115
|
+
validCriteria(criteria) {
|
|
116
|
+
return (criteria !== undefined && criteria !== "" &&
|
|
117
|
+
((criteria.toLowerCase() === "gt") ||
|
|
118
|
+
(criteria.toLowerCase() === "ge") ||
|
|
119
|
+
(criteria.toLowerCase() === "eq") ||
|
|
120
|
+
(criteria.toLowerCase() === "le") ||
|
|
121
|
+
(criteria.toLowerCase() === "lt")));
|
|
122
|
+
}
|
|
123
|
+
// filter enabled
|
|
124
|
+
enabled() {
|
|
125
|
+
return (this._enabled === "yes");
|
|
126
|
+
}
|
|
127
|
+
// get threshold filter config info as JSON
|
|
128
|
+
json() {
|
|
129
|
+
return {
|
|
130
|
+
"enabled": this._enabled,
|
|
131
|
+
"confidence_threshold": this._confidenceThreshold,
|
|
132
|
+
"threshold_criteria": this._confidenceThresholdCriteria,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
exports.ThresholdFilter = ThresholdFilter;
|
|
137
|
+
;
|
|
138
|
+
// model info class
|
|
139
|
+
class ModelInfo {
|
|
140
|
+
constructor(opts) {
|
|
141
|
+
this._modelName = opts.model_name;
|
|
142
|
+
this._modelVersion = opts.model_version;
|
|
143
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
144
|
+
this._modelParams = opts.model_params;
|
|
145
|
+
}
|
|
146
|
+
// get model name
|
|
147
|
+
getModelName() {
|
|
148
|
+
return this._modelName;
|
|
149
|
+
}
|
|
150
|
+
// get model version
|
|
151
|
+
getModelVersion() {
|
|
152
|
+
return this._modelVersion;
|
|
153
|
+
}
|
|
154
|
+
// get model params
|
|
155
|
+
getModelParams() {
|
|
156
|
+
return this._modelParams;
|
|
157
|
+
}
|
|
158
|
+
// get model info as JSON
|
|
159
|
+
json() {
|
|
160
|
+
return {
|
|
161
|
+
"model_name": this._modelName,
|
|
162
|
+
"model_version": this._modelVersion,
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
164
|
+
"model_params": this._modelParams,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
exports.ModelInfo = ModelInfo;
|
|
169
|
+
// metrics collector class
|
|
170
|
+
class MetricsCollector {
|
|
171
|
+
constructor() {
|
|
172
|
+
this._count = 0;
|
|
173
|
+
this._sumConfidences = 0;
|
|
174
|
+
this._sumSquaredConfidences = 0;
|
|
175
|
+
this._meanConfidenceTrend = "flat";
|
|
176
|
+
}
|
|
177
|
+
// get the average confidence
|
|
178
|
+
getMeanConfidence() {
|
|
179
|
+
if (this._sumConfidences > 0 && this._count > 0) {
|
|
180
|
+
return (this._sumConfidences / this._count);
|
|
181
|
+
}
|
|
182
|
+
return 0.0;
|
|
183
|
+
}
|
|
184
|
+
// reset
|
|
185
|
+
reset() {
|
|
186
|
+
this._count = 0;
|
|
187
|
+
this._sumConfidences = 0;
|
|
188
|
+
this._sumSquaredConfidences = 0;
|
|
189
|
+
this._meanConfidenceTrend = "flat";
|
|
190
|
+
return { "metrics_reset": "OK" };
|
|
191
|
+
}
|
|
192
|
+
// update the conference average stat
|
|
193
|
+
updateConfidenceMean(confidence) {
|
|
194
|
+
// get the previous confidence
|
|
195
|
+
const prevMeanConfidence = this.getMeanConfidence();
|
|
196
|
+
// add the confidence
|
|
197
|
+
this._sumConfidences += confidence;
|
|
198
|
+
this._sumSquaredConfidences += (confidence * confidence);
|
|
199
|
+
++this._count;
|
|
200
|
+
const newAdverageConfidence = this.getMeanConfidence();
|
|
201
|
+
// update the average confidence trend
|
|
202
|
+
const trend = newAdverageConfidence - prevMeanConfidence;
|
|
203
|
+
if (trend > 0) {
|
|
204
|
+
this._meanConfidenceTrend = "incr";
|
|
205
|
+
}
|
|
206
|
+
if (trend === 0) {
|
|
207
|
+
this._meanConfidenceTrend = "flat";
|
|
208
|
+
}
|
|
209
|
+
if (trend < 0) {
|
|
210
|
+
this._meanConfidenceTrend = "decr";
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// update metrics
|
|
214
|
+
updateConfidenceMetrics(confidences) {
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
216
|
+
for (let i = 0; i < confidences.length; ++i) {
|
|
217
|
+
// update confidence average
|
|
218
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
219
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument
|
|
220
|
+
this.updateConfidenceMean(confidences[i]);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// get metrics as a JSON
|
|
224
|
+
json() {
|
|
225
|
+
return {
|
|
226
|
+
"mean_confidence": Number(this.getMeanConfidence().toFixed(NUM_PLACES)),
|
|
227
|
+
"standard_deviation": Number(this.getStandardDeviation().toFixed(NUM_PLACES)),
|
|
228
|
+
"confidence_trend": this._meanConfidenceTrend,
|
|
229
|
+
"details": {
|
|
230
|
+
"n": this._count,
|
|
231
|
+
"sum_confidences": Number(this._sumConfidences.toFixed(NUM_PLACES)),
|
|
232
|
+
"sum_confidences_squared": Number(this._sumSquaredConfidences.toFixed(NUM_PLACES)),
|
|
233
|
+
},
|
|
234
|
+
"ts": Date.now(),
|
|
235
|
+
"id": (0, uuid_1.v4)(),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
// get the standard deviation
|
|
239
|
+
getStandardDeviation() {
|
|
240
|
+
if (this._sumSquaredConfidences > 0 && this._count > 0) {
|
|
241
|
+
const mean = this.getMeanConfidence();
|
|
242
|
+
return Math.sqrt((this._sumSquaredConfidences / this._count) - (mean * mean));
|
|
243
|
+
}
|
|
244
|
+
return 0.0;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
exports.MetricsCollector = MetricsCollector;
|
|
248
|
+
;
|
|
249
|
+
// Primary AWS IoTCore Connector/Processor class
|
|
10
250
|
class AWSIoTCoreConnector {
|
|
11
251
|
constructor(opts) {
|
|
12
252
|
this._notSilent = (!opts.silentArgv);
|
|
@@ -18,17 +258,155 @@ class AWSIoTCoreConnector {
|
|
|
18
258
|
this._iot = undefined;
|
|
19
259
|
this._connected = false;
|
|
20
260
|
this._delayCountdown = 0;
|
|
21
|
-
this._iotcoreQoS = Number(process.env.EI_IOTCORE_QOS);
|
|
22
|
-
this._poolSleepTimeMS = Number(process.env.EI_IOTCORE_POLL_SLEEP_TIME_MS);
|
|
261
|
+
this._iotcoreQoS = this.sanitizeQoS(Number(process.env.EI_IOTCORE_QOS));
|
|
23
262
|
this._opts = opts;
|
|
263
|
+
// sanitize the poll sleep time number
|
|
264
|
+
this._poolSleepTimeMS = Number(process.env.EI_IOTCORE_POLL_SLEEP_TIME_MS);
|
|
265
|
+
if (this._poolSleepTimeMS === undefined
|
|
266
|
+
|| Number.isNaN(this._poolSleepTimeMS)
|
|
267
|
+
|| this._poolSleepTimeMS <= 100) {
|
|
268
|
+
console.log(PREFIX + ": no polling sleep time specified. Defaulting to "
|
|
269
|
+
+ DEFAULT_POLLING_TIME_MS + "ms...");
|
|
270
|
+
this._poolSleepTimeMS = DEFAULT_POLLING_TIME_MS;
|
|
271
|
+
}
|
|
24
272
|
// We can slow down the publication to IoTCore to save IoTCore message cost$.
|
|
25
273
|
// set "iotcore_backoff" in Greengrass component config to "n" > 0 to enable
|
|
26
|
-
// countdown backoff... "-1" to disable... (default: "
|
|
27
|
-
this._delayInferences = Number(process.env.EI_OUTPUT_BACKOFF_COUNT);
|
|
274
|
+
// countdown backoff... "-1" to disable... (default: "-1")
|
|
275
|
+
this._delayInferences = this.sanitizeDelayInferences(Number(process.env.EI_OUTPUT_BACKOFF_COUNT));
|
|
28
276
|
// optional write to file configuration
|
|
29
|
-
this._enableWriteToFile = process.env.EI_ENABLE_WRITE_TO_FILE;
|
|
30
|
-
this._writeToFileDir = process.env.EI_FILE_WRITE_DIRECTORY;
|
|
277
|
+
this._enableWriteToFile = this.sanizeYesNo(process.env.EI_ENABLE_WRITE_TO_FILE);
|
|
278
|
+
this._writeToFileDir = this.sanitizeString(process.env.EI_FILE_WRITE_DIRECTORY);
|
|
279
|
+
// optional write to S3 configuration
|
|
280
|
+
this._enableWriteToS3 = this.sanizeYesNo(process.env.EI_ENABLE_WRITE_TO_S3);
|
|
281
|
+
this._s3Bucket = this.sanitizeString(process.env.EI_S3_BUCKET);
|
|
282
|
+
// initalize the threshold filter
|
|
283
|
+
this.initThresholdFilter();
|
|
284
|
+
// sanitize and inititalize the metrics collector
|
|
285
|
+
this._metricsDispatchSleepTimeMS = Number(process.env.EI_IOTCORE_METRICS_DISPATCH_TIME_MS);
|
|
286
|
+
if (this._metricsDispatchSleepTimeMS === undefined
|
|
287
|
+
|| Number.isNaN(this._metricsDispatchSleepTimeMS)
|
|
288
|
+
|| this._metricsDispatchSleepTimeMS <= 100) {
|
|
289
|
+
console.log(PREFIX + ": no metrics sleep time specified. Defaulting to "
|
|
290
|
+
+ DEFAULT_METRICS_COLLECTION_TIME_MS + "ms...");
|
|
291
|
+
this._metricsDispatchSleepTimeMS = DEFAULT_METRICS_COLLECTION_TIME_MS;
|
|
292
|
+
}
|
|
293
|
+
this.initMetricsCollector();
|
|
294
|
+
}
|
|
295
|
+
// sanitize string
|
|
296
|
+
sanitizeString(value) {
|
|
297
|
+
if (value !== undefined && value !== "") {
|
|
298
|
+
return value;
|
|
299
|
+
}
|
|
300
|
+
return "__none__";
|
|
301
|
+
}
|
|
302
|
+
// sanitize delay inferences
|
|
303
|
+
sanitizeDelayInferences(value) {
|
|
304
|
+
if (value !== undefined && !Number.isNaN(value)) {
|
|
305
|
+
if (value <= 0) {
|
|
306
|
+
return -1;
|
|
307
|
+
}
|
|
308
|
+
return value;
|
|
309
|
+
}
|
|
310
|
+
return -1;
|
|
311
|
+
}
|
|
312
|
+
// sanitize QoS
|
|
313
|
+
sanitizeQoS(value) {
|
|
314
|
+
if (value !== undefined && !Number.isNaN(value) && value > 0 && value < 4) {
|
|
315
|
+
return value;
|
|
316
|
+
}
|
|
317
|
+
return 0;
|
|
318
|
+
}
|
|
319
|
+
// sanitize yes/no options
|
|
320
|
+
sanizeYesNo(value) {
|
|
321
|
+
if (value !== undefined && value !== "") {
|
|
322
|
+
if (value.toLowerCase() === "yes") {
|
|
323
|
+
return "yes";
|
|
324
|
+
}
|
|
325
|
+
if (value.toLowerCase() === "no") {
|
|
326
|
+
return "no";
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return "no";
|
|
330
|
+
}
|
|
331
|
+
// sanitize threshold value
|
|
332
|
+
sanitizeThresholdValue(value) {
|
|
333
|
+
if (!Number.isNaN(value) && value > 0 && value <= 1.0) {
|
|
334
|
+
return value;
|
|
335
|
+
}
|
|
336
|
+
return DEFAULT_THRESHOLD_VALUE;
|
|
337
|
+
}
|
|
338
|
+
// sanitize threshold criteria
|
|
339
|
+
sanitizeThresholdCriteria(value) {
|
|
340
|
+
if (value !== undefined && value !== "" &&
|
|
341
|
+
(value === "gt" ||
|
|
342
|
+
value === "ge" ||
|
|
343
|
+
value === "eq" ||
|
|
344
|
+
value === "le" ||
|
|
345
|
+
value === "lt")) {
|
|
346
|
+
return value;
|
|
347
|
+
}
|
|
348
|
+
return DEFAULT_THRESHOLD_CRITERIA;
|
|
31
349
|
}
|
|
350
|
+
// initialize the optional threshold confidence filter
|
|
351
|
+
initThresholdFilter() {
|
|
352
|
+
const opts = {
|
|
353
|
+
default_threshold: this.sanitizeThresholdValue(Number(process.env.EI_DEFAULT_THRESHOLD)),
|
|
354
|
+
threshold_criteria: this.sanitizeThresholdCriteria(process.env.EI_THRESHOLD_CRITERIA),
|
|
355
|
+
};
|
|
356
|
+
this._thresholdFilter = new ThresholdFilter(opts);
|
|
357
|
+
}
|
|
358
|
+
// initialize the model info object instance
|
|
359
|
+
initModelInfo(modelName, modelVersion, modelParams) {
|
|
360
|
+
const opts = {
|
|
361
|
+
model_name: modelName,
|
|
362
|
+
model_version: modelVersion,
|
|
363
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
364
|
+
model_params: modelParams,
|
|
365
|
+
};
|
|
366
|
+
this._modelInfo = new ModelInfo(opts);
|
|
367
|
+
}
|
|
368
|
+
// initialize the metrics collector
|
|
369
|
+
initMetricsCollector() {
|
|
370
|
+
this._metricsCollector = new MetricsCollector();
|
|
371
|
+
}
|
|
372
|
+
// command identifiers
|
|
373
|
+
isRestartCommand(command) {
|
|
374
|
+
return (command === "restart");
|
|
375
|
+
}
|
|
376
|
+
isEnableThresholdFilterCommand(command) {
|
|
377
|
+
return (command === "enable_threshold_filter");
|
|
378
|
+
}
|
|
379
|
+
isDisableThresholdFilterCommand(command) {
|
|
380
|
+
return (command === "disable_threshold_filter");
|
|
381
|
+
}
|
|
382
|
+
isSetThresholdFilterCriteriaCommand(command) {
|
|
383
|
+
return (command === "set_threshold_filter_criteria");
|
|
384
|
+
}
|
|
385
|
+
isGetThresholdFilterCriteriaCommand(command) {
|
|
386
|
+
return (command === "get_threshold_filter_criteria");
|
|
387
|
+
}
|
|
388
|
+
isGetThresholdFilterConfidenceCommand(command) {
|
|
389
|
+
return (command === "get_threshold_filter_confidence");
|
|
390
|
+
}
|
|
391
|
+
isSetThresholdFilterConfidenceCommand(command) {
|
|
392
|
+
return (command === "set_threshold_filter_confidence");
|
|
393
|
+
}
|
|
394
|
+
isGetThresholdFilterConfigCommand(command) {
|
|
395
|
+
return (command === "get_threshold_filter_config");
|
|
396
|
+
}
|
|
397
|
+
isGetModelInfoCommand(command) {
|
|
398
|
+
return (command === "get_model_info");
|
|
399
|
+
}
|
|
400
|
+
isClearCatchCommand(command) {
|
|
401
|
+
return (command === "clear_cache");
|
|
402
|
+
}
|
|
403
|
+
isClearFileInCacheCommand(command) {
|
|
404
|
+
return (command === "clear_cache_file");
|
|
405
|
+
}
|
|
406
|
+
isResetMetricsCommand(command) {
|
|
407
|
+
return (command === "reset_metrics");
|
|
408
|
+
}
|
|
409
|
+
// IoTCore topic creation
|
|
32
410
|
createTopics() {
|
|
33
411
|
// Inference result topic
|
|
34
412
|
this._inferenceOutputTopic = process.env.EI_INFERENCE_OUTPUT_TOPIC;
|
|
@@ -38,6 +416,22 @@ class AWSIoTCoreConnector {
|
|
|
38
416
|
this._commandInputTopic = process.env.EI_COMMAND_INPUT_TOPIC;
|
|
39
417
|
this._commandOutputTopic = process.env.EI_COMMAND_OUTPUT_TOPIC;
|
|
40
418
|
}
|
|
419
|
+
// initialize the S3 processor
|
|
420
|
+
async s3Init() {
|
|
421
|
+
if (this._enableWriteToS3 === "yes") {
|
|
422
|
+
try {
|
|
423
|
+
this._s3Client = await new client_s3_1.S3Client(this._clientConfig);
|
|
424
|
+
}
|
|
425
|
+
catch (err) {
|
|
426
|
+
// unable to allocate
|
|
427
|
+
if (this._notSilent) {
|
|
428
|
+
// not connected.. so connect to IoTCore
|
|
429
|
+
console.log(PREFIX + " EI: ERROR - Unable to allocate S3Client with exception: " + err);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// connect to AWS IoTCore
|
|
41
435
|
async connect() {
|
|
42
436
|
if (this._iot === undefined) {
|
|
43
437
|
if (this._notSilent) {
|
|
@@ -51,6 +445,10 @@ class AWSIoTCoreConnector {
|
|
|
51
445
|
this._iot = await new client_iot_data_plane_1.IoTDataPlaneClient(this._clientConfig);
|
|
52
446
|
// we are connected!
|
|
53
447
|
this._connected = true;
|
|
448
|
+
// manage S3 connection as well
|
|
449
|
+
await this.s3Init();
|
|
450
|
+
// send startup status/timestamp
|
|
451
|
+
await this.sendCommandResult({ "status": "started", "ts": Date.now(), "id": (0, uuid_1.v4)() });
|
|
54
452
|
}
|
|
55
453
|
catch (err) {
|
|
56
454
|
// unable to allocate
|
|
@@ -71,6 +469,7 @@ class AWSIoTCoreConnector {
|
|
|
71
469
|
isConnected() {
|
|
72
470
|
return this._connected;
|
|
73
471
|
}
|
|
472
|
+
// is the inference result empty?
|
|
74
473
|
isEmptyInference(payload, key) {
|
|
75
474
|
if (payload && key in payload) {
|
|
76
475
|
const value = payload[key];
|
|
@@ -84,9 +483,19 @@ class AWSIoTCoreConnector {
|
|
|
84
483
|
}
|
|
85
484
|
return true;
|
|
86
485
|
}
|
|
87
|
-
async
|
|
88
|
-
|
|
486
|
+
// launch all async tasks
|
|
487
|
+
async launchAsyncTasks() {
|
|
488
|
+
// launch the metrics dispatcher
|
|
489
|
+
console.log(PREFIX + " Launching metrics collector...");
|
|
490
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
491
|
+
this.dispatchMetricsCollector(this._metricsOutputTopic, this._metricsDispatchSleepTimeMS);
|
|
492
|
+
// launch the command receiver
|
|
493
|
+
console.log(PREFIX + " Launching command receiver...");
|
|
494
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
495
|
+
this.dispatchCommandListener(this._commandInputTopic, this._poolSleepTimeMS);
|
|
496
|
+
// XXX add more async tasks here...
|
|
89
497
|
}
|
|
498
|
+
// clear retained queue in IoTCore
|
|
90
499
|
async clearRetainedQueue(cmd_topic) {
|
|
91
500
|
try {
|
|
92
501
|
if (this._iot !== undefined) {
|
|
@@ -101,17 +510,43 @@ class AWSIoTCoreConnector {
|
|
|
101
510
|
// ignore
|
|
102
511
|
}
|
|
103
512
|
}
|
|
104
|
-
|
|
105
|
-
return (command === "restart");
|
|
106
|
-
}
|
|
513
|
+
// restart the runner handler
|
|
107
514
|
processRestartCommand() {
|
|
108
515
|
// announce
|
|
109
516
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
110
|
-
console.log("
|
|
517
|
+
console.log(PREFIX + " processRestartCommand(): Restarting Runner: " + this._opts.appName + "...");
|
|
111
518
|
// send ourselves a single to gracefully shutdown...
|
|
112
519
|
process.kill(process.pid, "SIGINT");
|
|
113
520
|
}
|
|
114
|
-
|
|
521
|
+
// metrics collector process
|
|
522
|
+
async dispatchMetricsCollector(metricsOutputTopic, sleepMS) {
|
|
523
|
+
while (this.isConnected() === true) {
|
|
524
|
+
try {
|
|
525
|
+
if (this._iot !== undefined && this._metricsCollector !== undefined) {
|
|
526
|
+
// publish the current metrics
|
|
527
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
528
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
529
|
+
this.sendModelMetrics(this._metricsCollector.json());
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
catch (err) {
|
|
533
|
+
// ignore
|
|
534
|
+
}
|
|
535
|
+
// Sleep
|
|
536
|
+
await new Promise(resolve => setTimeout(resolve, sleepMS));
|
|
537
|
+
}
|
|
538
|
+
// WARN
|
|
539
|
+
console.log(PREFIX + " dispatchMetricsCollector() WARNING: process loop has halted...");
|
|
540
|
+
}
|
|
541
|
+
// reset metrics collection
|
|
542
|
+
resetMetrics() {
|
|
543
|
+
if (this._metricsCollector !== undefined) {
|
|
544
|
+
return this._metricsCollector.reset();
|
|
545
|
+
}
|
|
546
|
+
return { "metrics_reset": "OK" };
|
|
547
|
+
}
|
|
548
|
+
// command listener/processor process
|
|
549
|
+
async dispatchCommandListener(commandInputTopic, longPollSleepMS) {
|
|
115
550
|
while (this.isConnected() === true) {
|
|
116
551
|
// Long Poll via GetRetainedMessageCommand()...
|
|
117
552
|
try {
|
|
@@ -125,25 +560,172 @@ class AWSIoTCoreConnector {
|
|
|
125
560
|
await this.clearRetainedQueue(commandInputTopic);
|
|
126
561
|
// ensure we have a received command...
|
|
127
562
|
if (rcvdCmd !== undefined) {
|
|
128
|
-
//
|
|
563
|
+
// COMMAND: restart the runner
|
|
129
564
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
130
565
|
if (this.isRestartCommand((rcvdCmd.cmd)) === true) {
|
|
566
|
+
// send confirmation
|
|
567
|
+
console.log(PREFIX + " dispatchCommandListener(): restarting runner process...");
|
|
568
|
+
await this.sendCommandResult({ "restart": "OK" });
|
|
131
569
|
// process the restart command...
|
|
132
570
|
this.processRestartCommand();
|
|
133
571
|
}
|
|
134
|
-
//
|
|
135
|
-
//
|
|
572
|
+
// COMMAND: get the threshold filter confidence threshold value
|
|
573
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
574
|
+
else if (this.isGetThresholdFilterConfidenceCommand((rcvdCmd.cmd)) === true &&
|
|
575
|
+
this._thresholdFilter !== undefined) {
|
|
576
|
+
// send the confidence threshold
|
|
577
|
+
console.log(PREFIX + " dispatchCommandListener(): getting threshold filter confidence setting...");
|
|
578
|
+
await this.sendCommandResult({ "confidence_threshold": this._thresholdFilter.getConfidenceThreshold() });
|
|
579
|
+
}
|
|
580
|
+
// COMMAND: set the threshold filter criteria value
|
|
581
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
582
|
+
else if (this.isSetThresholdFilterConfidenceCommand((rcvdCmd.cmd)) === true &&
|
|
583
|
+
this._thresholdFilter !== undefined) {
|
|
584
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
585
|
+
if (this._thresholdFilter.confidenceThresholdValueInBounds(rcvdCmd.value)) {
|
|
586
|
+
// set the confidence threshold
|
|
587
|
+
console.log(PREFIX +
|
|
588
|
+
" dispatchCommandListener(): Setting threshold filter confidence threshold to: " +
|
|
589
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
590
|
+
rcvdCmd.value);
|
|
591
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
592
|
+
this._thresholdFilter.setConfidenceThreshold(rcvdCmd.value);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
// confidence threshold must be 0 < x <= 1.0
|
|
596
|
+
console.log(PREFIX +
|
|
597
|
+
" dispatchCommandListener(): Unable to set threshold filter confidence threshold to: " +
|
|
598
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
599
|
+
rcvdCmd.value + "... Threshold value must be 0 < x < 1.0. No changes made.");
|
|
600
|
+
}
|
|
601
|
+
// send the command result
|
|
602
|
+
await this.sendCommandResult({ "confidence_threshold": this._thresholdFilter.getConfidenceThreshold() });
|
|
603
|
+
}
|
|
604
|
+
// COMMAND: get the threshold filter criteria value
|
|
605
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
606
|
+
else if (this.isGetThresholdFilterCriteriaCommand((rcvdCmd.cmd)) === true &&
|
|
607
|
+
this._thresholdFilter !== undefined) {
|
|
608
|
+
// ssend the command result
|
|
609
|
+
console.log(PREFIX + " dispatchCommandListener(): getting threshold criteria setting...");
|
|
610
|
+
await this.sendCommandResult({ "criteria": this._thresholdFilter.getConfidenceCriteria() });
|
|
611
|
+
}
|
|
612
|
+
// COMMAND: set the threshold filter criteria value
|
|
613
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
614
|
+
else if (this.isSetThresholdFilterCriteriaCommand((rcvdCmd.cmd)) === true &&
|
|
615
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
616
|
+
this._thresholdFilter !== undefined && rcvdCmd.value !== undefined) {
|
|
617
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
618
|
+
if (this._thresholdFilter.validCriteria(rcvdCmd.value) === true) {
|
|
619
|
+
// set the confidence criteria
|
|
620
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
621
|
+
console.log(PREFIX + " dispatchCommandListener(): Setting threshold criteria to: " + rcvdCmd.value);
|
|
622
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
623
|
+
this._thresholdFilter.setConfidenceCriteria(rcvdCmd.value);
|
|
624
|
+
// send the confidence criteria
|
|
625
|
+
await this.sendCommandResult({ "criteria": this._thresholdFilter.getConfidenceCriteria() });
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
// log the error
|
|
629
|
+
console.log(PREFIX + " dispatchCommandListener(): Unable to set threshold criteria to: " +
|
|
630
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
631
|
+
rcvdCmd.value + " (not recognized). Sending current critiera");
|
|
632
|
+
// send the confidence criteria
|
|
633
|
+
await this.sendCommandResult({ "criteria": this._thresholdFilter.getConfidenceCriteria() });
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
// COMMAND: enable threshold filter
|
|
637
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
638
|
+
else if (this.isEnableThresholdFilterCommand((rcvdCmd.cmd)) === true &&
|
|
639
|
+
this._thresholdFilter !== undefined) {
|
|
640
|
+
// display the new config
|
|
641
|
+
console.log(PREFIX + " dispatchCommandListener(): enabling threshold filter...");
|
|
642
|
+
this._thresholdFilter.enable();
|
|
643
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
644
|
+
await this.sendCommandResult({ "threshold_filter_config": this._thresholdFilter.json() });
|
|
645
|
+
}
|
|
646
|
+
// COMMAND: disable threshold filter
|
|
647
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
648
|
+
else if (this.isDisableThresholdFilterCommand((rcvdCmd.cmd)) === true &&
|
|
649
|
+
this._thresholdFilter !== undefined) {
|
|
650
|
+
// display the new config
|
|
651
|
+
console.log(PREFIX + " dispatchCommandListener(): disabling threshold filter...");
|
|
652
|
+
this._thresholdFilter.disable();
|
|
653
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
654
|
+
await this.sendCommandResult({ "threshold_filter_config": this._thresholdFilter.json() });
|
|
655
|
+
}
|
|
656
|
+
// COMMAND: get the threshold filter config info
|
|
657
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
658
|
+
else if (this.isGetThresholdFilterConfigCommand((rcvdCmd.cmd)) === true &&
|
|
659
|
+
this._thresholdFilter !== undefined) {
|
|
660
|
+
// get model info
|
|
661
|
+
console.log(PREFIX + " dispatchCommandListener(): getting current threshold filter config...");
|
|
662
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
663
|
+
await this.sendCommandResult({ "threshold_filter_config": this._thresholdFilter.json() });
|
|
664
|
+
}
|
|
665
|
+
// COMMAND: get the model info
|
|
666
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
667
|
+
else if (this.isGetModelInfoCommand((rcvdCmd.cmd)) === true &&
|
|
668
|
+
this._modelInfo !== undefined) {
|
|
669
|
+
// get model info
|
|
670
|
+
console.log(PREFIX + " dispatchCommandListener(): getting current model information...");
|
|
671
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
672
|
+
await this.sendCommandResult({ "model_info": this._modelInfo.json() });
|
|
673
|
+
}
|
|
674
|
+
// COMMAND: clear the cache
|
|
675
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
676
|
+
else if (this.isClearCatchCommand((rcvdCmd.cmd)) === true &&
|
|
677
|
+
this._modelInfo !== undefined) {
|
|
678
|
+
// set the confidence threshold
|
|
679
|
+
console.log(PREFIX + " dispatchCommandListener(): Clearing inference file/json cache...");
|
|
680
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
681
|
+
const result = await this.cacheClear();
|
|
682
|
+
// send the command result
|
|
683
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
684
|
+
await this.sendCommandResult({ "clear_cache": result });
|
|
685
|
+
}
|
|
686
|
+
// COMMAND: clear specified file within the cache
|
|
687
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
688
|
+
else if (this.isClearFileInCacheCommand((rcvdCmd.cmd)) === true &&
|
|
689
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
690
|
+
this._modelInfo !== undefined && rcvdCmd.value !== undefined) {
|
|
691
|
+
// set the confidence threshold
|
|
692
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
693
|
+
console.log(PREFIX +
|
|
694
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
695
|
+
" dispatchCommandListener(): Clearing inference file/json from cache. UUID: " + (rcvdCmd.value));
|
|
696
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
697
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
|
|
698
|
+
const result = await this.cacheClearInferenceAndImageByUUID((rcvdCmd.value));
|
|
699
|
+
// send the command result
|
|
700
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
701
|
+
await this.sendCommandResult({ "clear_cache_file": result });
|
|
702
|
+
}
|
|
703
|
+
// COMMAND: reset the metrics counters
|
|
704
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
705
|
+
else if (this.isResetMetricsCommand((rcvdCmd.cmd)) === true &&
|
|
706
|
+
this._metricsCollector !== undefined) {
|
|
707
|
+
// set the confidence threshold
|
|
708
|
+
console.log(PREFIX + " dispatchCommandListener(): Resetting metrics collection...");
|
|
709
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
710
|
+
const result = await this.resetMetrics();
|
|
711
|
+
// send the command result
|
|
712
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
713
|
+
await this.sendCommandResult(result);
|
|
714
|
+
}
|
|
715
|
+
// XXX COMMAND: more commands supported here over time...
|
|
716
|
+
// COMMAND: default case - unrecognized command...ignore.
|
|
136
717
|
else {
|
|
137
718
|
// command not supported/understood... so ignore
|
|
138
719
|
if (this._notSilent === true) {
|
|
139
|
-
console.log("
|
|
720
|
+
console.log(PREFIX + " dispatchCommandListener() Command: " + JSON.stringify(rcvdCmd) +
|
|
721
|
+
" not supported/understood. Ignoring...");
|
|
140
722
|
}
|
|
141
723
|
}
|
|
142
724
|
}
|
|
143
725
|
else {
|
|
144
726
|
// empty/null command... just ignore
|
|
145
727
|
if (this._notSilent === true) {
|
|
146
|
-
console.log("
|
|
728
|
+
console.log(PREFIX + " dispatchCommandListener() Command is empty/null. Ignoring...");
|
|
147
729
|
}
|
|
148
730
|
}
|
|
149
731
|
}
|
|
@@ -151,7 +733,7 @@ class AWSIoTCoreConnector {
|
|
|
151
733
|
else {
|
|
152
734
|
if (this._notSilent === true) {
|
|
153
735
|
// Connected but null command object
|
|
154
|
-
console.log("
|
|
736
|
+
console.log(PREFIX + " dispatchCommandListener(): WARNING Status states connected() but iotcore handle is NULL!");
|
|
155
737
|
}
|
|
156
738
|
}
|
|
157
739
|
}
|
|
@@ -162,7 +744,50 @@ class AWSIoTCoreConnector {
|
|
|
162
744
|
await new Promise(resolve => setTimeout(resolve, longPollSleepMS));
|
|
163
745
|
}
|
|
164
746
|
}
|
|
165
|
-
//
|
|
747
|
+
// send a command's results
|
|
748
|
+
async sendCommandResult(payload) {
|
|
749
|
+
if (this._iot !== undefined && this.isConnected() === true &&
|
|
750
|
+
this._commandOutputTopic !== undefined && this._commandOutputTopic.length > 0) {
|
|
751
|
+
// build the command result
|
|
752
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
753
|
+
const result = { "result": payload };
|
|
754
|
+
// publish!
|
|
755
|
+
try {
|
|
756
|
+
// send the command result
|
|
757
|
+
await this._iot.send(new client_iot_data_plane_1.PublishCommand({
|
|
758
|
+
topic: this._commandOutputTopic,
|
|
759
|
+
qos: this._iotcoreQoS,
|
|
760
|
+
retain: false,
|
|
761
|
+
payload: Buffer.from(JSON.stringify(result))
|
|
762
|
+
}));
|
|
763
|
+
}
|
|
764
|
+
catch (err) {
|
|
765
|
+
if (this._notSilent === true) {
|
|
766
|
+
// exception during send()...
|
|
767
|
+
console.log(PREFIX + " sendCommandResult() ERROR: IoTDataPlaneClient.send() errored with exception: " + err);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
else if (this._iot !== undefined && this.isConnected() === true) {
|
|
772
|
+
// no command result topic
|
|
773
|
+
if (this._notSilent) {
|
|
774
|
+
console.log(PREFIX + " sendCommandResult() ERROR: No command output topic specified in configuration... not sending result.");
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
// not connected
|
|
779
|
+
if (this._notSilent) {
|
|
780
|
+
console.log(PREFIX + " sendCommandResult() ERROR: Not connected to IoTCore... not sending command result.");
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
// update confidence metrics
|
|
785
|
+
updateInferenceMetrics(confidences) {
|
|
786
|
+
if (this._metricsCollector !== undefined) {
|
|
787
|
+
this._metricsCollector.updateConfidenceMetrics(confidences);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
// publish our model metrics
|
|
166
791
|
async sendModelMetrics(payload) {
|
|
167
792
|
if (this._iot !== undefined && this.isConnected() === true &&
|
|
168
793
|
this._metricsOutputTopic !== undefined && this._metricsOutputTopic.length > 0) {
|
|
@@ -179,107 +804,373 @@ class AWSIoTCoreConnector {
|
|
|
179
804
|
catch (err) {
|
|
180
805
|
if (this._notSilent === true) {
|
|
181
806
|
// exception during send()...
|
|
182
|
-
console.log(PREFIX + "
|
|
807
|
+
console.log(PREFIX + " sendModelMetrics() ERROR: IoTDataPlaneClient.send() errored with exception: " + err);
|
|
183
808
|
}
|
|
184
809
|
}
|
|
185
810
|
}
|
|
186
811
|
else if (this._iot !== undefined && this.isConnected() === true) {
|
|
187
812
|
// no model metrics topic
|
|
188
813
|
if (this._notSilent) {
|
|
189
|
-
console.log(PREFIX + "
|
|
814
|
+
console.log(PREFIX + " sendModelMetrics() ERROR: No model metrics topic specified in configuration... not sending model metrics.");
|
|
190
815
|
}
|
|
191
816
|
}
|
|
192
817
|
else {
|
|
193
818
|
// not connected
|
|
194
819
|
if (this._notSilent) {
|
|
195
|
-
console.log(PREFIX + "
|
|
820
|
+
console.log(PREFIX + " sendModelMetrics() ERROR: Not connected to IoTCore... not sending model metrics.");
|
|
196
821
|
}
|
|
197
822
|
}
|
|
198
823
|
}
|
|
824
|
+
// OPTION: cache inference image and inference json to local directory on edge device...
|
|
199
825
|
async saveToFile(payload, imgAsJpg) {
|
|
200
826
|
// construct the filename...
|
|
201
827
|
const filenameBase = this._writeToFileDir + "/" + payload.id;
|
|
202
828
|
const imageFilename = filenameBase + ".img";
|
|
203
829
|
const infFilename = filenameBase + ".json";
|
|
830
|
+
let result = "OK";
|
|
204
831
|
// INFERENCE: write out to disk...
|
|
205
|
-
|
|
832
|
+
fs.writeFile(infFilename, JSON.stringify(payload), function (writeErr) {
|
|
206
833
|
if (writeErr) {
|
|
207
|
-
console.log(writeErr);
|
|
834
|
+
console.log(PREFIX + " saveToFile(): Error in writing out file: " + infFilename + " Error: " + writeErr);
|
|
835
|
+
result = "FAILED";
|
|
208
836
|
}
|
|
209
837
|
});
|
|
210
838
|
// IMAGE: write out to disk...
|
|
211
|
-
|
|
839
|
+
fs.writeFile(imageFilename, imgAsJpg, function (writeErr) {
|
|
212
840
|
if (writeErr) {
|
|
213
|
-
console.log(writeErr);
|
|
841
|
+
console.log(PREFIX + " saveToFile(): Error in writing out file: " + imageFilename + " Error: " + writeErr);
|
|
842
|
+
result = "FAILED";
|
|
214
843
|
}
|
|
215
844
|
});
|
|
845
|
+
return result;
|
|
846
|
+
}
|
|
847
|
+
// create the prefix used to store the inference/image into the S3 bucket
|
|
848
|
+
createS3Prefix() {
|
|
849
|
+
var _a, _b;
|
|
850
|
+
return ((_a = this._modelInfo) === null || _a === void 0 ? void 0 : _a.getModelName()) + "_" + ((_b = this._modelInfo) === null || _b === void 0 ? void 0 : _b.getModelVersion()) + "/";
|
|
851
|
+
}
|
|
852
|
+
// OPTION: cache inference image and inference json to S3 bucket
|
|
853
|
+
async saveToS3(payload, imgAsJpg) {
|
|
854
|
+
// construct the filenames...
|
|
855
|
+
const basePath = this.createS3Prefix();
|
|
856
|
+
const imageFilename = basePath + payload.id + ".img";
|
|
857
|
+
const infFilename = basePath + payload.id + ".json";
|
|
858
|
+
let result = "OK";
|
|
859
|
+
// Create the S3 bucket if it doesn't exist.
|
|
860
|
+
if (this._s3Client !== undefined) {
|
|
861
|
+
try {
|
|
862
|
+
await this._s3Client.send(new client_s3_1.CreateBucketCommand({ Bucket: this._s3Bucket }));
|
|
863
|
+
}
|
|
864
|
+
catch (err) {
|
|
865
|
+
if (this._notSilent) {
|
|
866
|
+
// exception during send()...
|
|
867
|
+
console.log(PREFIX + " saveToS3() ERROR: s3_client.send(CreateBucket) errored with exception: " + err);
|
|
868
|
+
result = "FAILED";
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
// Upload the image file to S3.
|
|
872
|
+
try {
|
|
873
|
+
await this._s3Client.send(new client_s3_1.PutObjectCommand({
|
|
874
|
+
Bucket: this._s3Bucket,
|
|
875
|
+
Key: imageFilename,
|
|
876
|
+
Body: imgAsJpg,
|
|
877
|
+
}));
|
|
878
|
+
}
|
|
879
|
+
catch (err) {
|
|
880
|
+
if (this._notSilent) {
|
|
881
|
+
// exception during send()...
|
|
882
|
+
console.log(PREFIX + " saveToS3() ERROR: s3_client.send(PutObjectImageFile) errored with exception: " + err);
|
|
883
|
+
result = "FAILED";
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
// Upload the inference file to S3.
|
|
887
|
+
try {
|
|
888
|
+
await this._s3Client.send(new client_s3_1.PutObjectCommand({
|
|
889
|
+
Bucket: this._s3Bucket,
|
|
890
|
+
Key: infFilename,
|
|
891
|
+
Body: JSON.stringify(payload),
|
|
892
|
+
}));
|
|
893
|
+
}
|
|
894
|
+
catch (err) {
|
|
895
|
+
if (this._notSilent) {
|
|
896
|
+
// exception during send()...
|
|
897
|
+
console.log(PREFIX + " saveToS3() ERROR: s3_client.send(PutObjectInferenceFile) errored with exception: " + err);
|
|
898
|
+
result = "FAILED";
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return result;
|
|
903
|
+
}
|
|
904
|
+
// process option to delete cache file (local) identified by its uuid
|
|
905
|
+
async localFileCacheClearFileByUUID(uuid) {
|
|
906
|
+
// construct the filenames...
|
|
907
|
+
const filenameBase = this._writeToFileDir + "/" + uuid;
|
|
908
|
+
const imageFilename = filenameBase + ".img";
|
|
909
|
+
const infFilename = filenameBase + ".json";
|
|
910
|
+
let result = "OK";
|
|
911
|
+
// delete the files
|
|
912
|
+
if (fs.existsSync(imageFilename)) {
|
|
913
|
+
try {
|
|
914
|
+
await fs.unlinkSync(imageFilename);
|
|
915
|
+
}
|
|
916
|
+
catch (err) {
|
|
917
|
+
console.log(PREFIX + " localFileCacheClearFileByUUID(): unable to remove file: " + imageFilename + " Exception: " + err);
|
|
918
|
+
result = "FAILED";
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
else {
|
|
922
|
+
// file does not exist
|
|
923
|
+
console.log(PREFIX + "localFileCacheClearFileByUUID(): unable to remove file: " + imageFilename + " does not exist");
|
|
924
|
+
result = "FAILED";
|
|
925
|
+
}
|
|
926
|
+
if (fs.existsSync(infFilename)) {
|
|
927
|
+
try {
|
|
928
|
+
await fs.unlinkSync(infFilename);
|
|
929
|
+
}
|
|
930
|
+
catch (err) {
|
|
931
|
+
console.log(PREFIX + " localFileCacheClearFileByUUID(): unable to remove file: " + infFilename + " Exception: " + err);
|
|
932
|
+
result = "FAILED";
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
// file does not exist
|
|
937
|
+
console.log(PREFIX + "localFileCacheClearFileByUUID(): unable to remove file: " + infFilename + " does not exist");
|
|
938
|
+
result = "FAILED";
|
|
939
|
+
}
|
|
940
|
+
return result;
|
|
941
|
+
}
|
|
942
|
+
// process option to delete cache file (s3) identified by its uuid
|
|
943
|
+
async s3CacheClearByUUID(uuid) {
|
|
944
|
+
var _a, _b;
|
|
945
|
+
let result = "OK";
|
|
946
|
+
let found = false;
|
|
947
|
+
const basePath = ((_a = this._modelInfo) === null || _a === void 0 ? void 0 : _a.getModelName()) + "_" + ((_b = this._modelInfo) === null || _b === void 0 ? void 0 : _b.getModelVersion()) + "/";
|
|
948
|
+
if (this._s3Client !== undefined) {
|
|
949
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
950
|
+
const bucketParams = { Bucket: this._s3Bucket, Prefix: basePath };
|
|
951
|
+
try {
|
|
952
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
953
|
+
const { Contents } = (await this._s3Client.send(new client_s3_1.ListObjectsV2Command(bucketParams)));
|
|
954
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
955
|
+
for (let index = 0; index < Contents.length; index++) {
|
|
956
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
957
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
958
|
+
if (Contents[index].Key.includes(uuid) === true) {
|
|
959
|
+
// found the file... delete it.
|
|
960
|
+
found = true;
|
|
961
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
962
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
963
|
+
const delCommand = { Bucket: this._s3Bucket, Key: Contents[index].Key };
|
|
964
|
+
console.log(PREFIX + " s3CacheClearByUUID(): Deleting: " + JSON.stringify(delCommand));
|
|
965
|
+
await this._s3Client.send(new client_s3_1.DeleteObjectCommand(delCommand));
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
catch (err) {
|
|
970
|
+
console.log(PREFIX + " s3CacheClearByUUID(): caught exception while clearing file in S3 cache: " +
|
|
971
|
+
err + " Bucket: " + this._s3Bucket + " Base Path: " + basePath + " UUID: " + uuid);
|
|
972
|
+
result = "FAILED";
|
|
973
|
+
}
|
|
974
|
+
// make sure we found something
|
|
975
|
+
if (!found) {
|
|
976
|
+
console.log(PREFIX + " s3CacheClearByUUID(): Unable to delete file with UUID: " + uuid + " No matching files found in: " + this._s3Bucket + "/" + basePath);
|
|
977
|
+
result = "FAILED";
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return result;
|
|
981
|
+
}
|
|
982
|
+
// clear the local directory/file cache
|
|
983
|
+
async localFileCacheClear() {
|
|
984
|
+
let result = "OK";
|
|
985
|
+
try {
|
|
986
|
+
const files = await fs.readdirSync(this._writeToFileDir);
|
|
987
|
+
for (const file of files) {
|
|
988
|
+
const filePath = path.join(this._writeToFileDir, file);
|
|
989
|
+
if (fs.existsSync(filePath)) {
|
|
990
|
+
try {
|
|
991
|
+
await fs.unlinkSync(filePath);
|
|
992
|
+
}
|
|
993
|
+
catch (err) {
|
|
994
|
+
console.log(PREFIX + " localFileCacheClear(): unable to remove file: " + filePath + " Exception: " + err);
|
|
995
|
+
result = "FAILED";
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
catch (err) {
|
|
1001
|
+
console.log(PREFIX + " localFileCacheClear(): exception caught while clearing local file cache: " + err + " Directory: " + this._writeToFileDir);
|
|
1002
|
+
result = "FAILED";
|
|
1003
|
+
}
|
|
1004
|
+
return result;
|
|
1005
|
+
}
|
|
1006
|
+
// clear the S3 cache
|
|
1007
|
+
async s3CacheClear() {
|
|
1008
|
+
var _a, _b;
|
|
1009
|
+
// construct the filenames...
|
|
1010
|
+
const basePath = ((_a = this._modelInfo) === null || _a === void 0 ? void 0 : _a.getModelName()) + "_" + ((_b = this._modelInfo) === null || _b === void 0 ? void 0 : _b.getModelVersion()) + "/";
|
|
1011
|
+
// Create the S3 bucket if it doesn't exist.
|
|
1012
|
+
if (this._s3Client !== undefined) {
|
|
1013
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1014
|
+
const bucketParams = { Bucket: this._s3Bucket, Prefix: basePath };
|
|
1015
|
+
try {
|
|
1016
|
+
const listCommand = new client_s3_1.ListObjectsV2Command(bucketParams);
|
|
1017
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1018
|
+
const { Contents } = (await this._s3Client.send(listCommand));
|
|
1019
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1020
|
+
for (let index = 0; index < Contents.length; index++) {
|
|
1021
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
1022
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
1023
|
+
const delCommand = { Bucket: this._s3Bucket, Key: Contents[index].Key };
|
|
1024
|
+
await this._s3Client.send(new client_s3_1.DeleteObjectCommand(delCommand));
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
catch (err) {
|
|
1028
|
+
console.log(PREFIX + " s3CacheClear(): caught exception while clearing S3 cache: " +
|
|
1029
|
+
err + " Bucket: " + this._s3Bucket + " Base Path: " + basePath);
|
|
1030
|
+
return "FAILED";
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
return "OK";
|
|
1034
|
+
}
|
|
1035
|
+
// process option to cache inference image and inference json...
|
|
1036
|
+
async cacheInferenceImageAndJSON(payload, imgAsJpg) {
|
|
1037
|
+
let result = { "local": "OK", "s3": "OK", "uuid": payload.id };
|
|
1038
|
+
// cache to local directory/file (optional)
|
|
1039
|
+
if (this._enableWriteToFile !== undefined && this._enableWriteToFile === "yes" && imgAsJpg !== undefined) {
|
|
1040
|
+
result.local = await this.saveToFile(payload, imgAsJpg);
|
|
1041
|
+
}
|
|
1042
|
+
// cache to S3 (optional)
|
|
1043
|
+
if (this._enableWriteToS3 !== undefined && this._enableWriteToS3 === "yes" && imgAsJpg !== undefined) {
|
|
1044
|
+
result.s3 = await this.saveToS3(payload, imgAsJpg);
|
|
1045
|
+
}
|
|
1046
|
+
return result;
|
|
1047
|
+
}
|
|
1048
|
+
// process option to delete inference image + json from cache via its uuid...
|
|
1049
|
+
async cacheClearInferenceAndImageByUUID(uuid) {
|
|
1050
|
+
let result = { "local": "OK", "s3": "OK", "uuid": uuid };
|
|
1051
|
+
// clear local directory/file cache
|
|
1052
|
+
if (this._enableWriteToFile !== undefined && this._enableWriteToFile === "yes") {
|
|
1053
|
+
console.log(PREFIX + " cacheClearInferenceAndImageByUUID(): Clearing file(s) locally cached prefixed by UUID: " + uuid);
|
|
1054
|
+
result.local = await this.localFileCacheClearFileByUUID(uuid);
|
|
1055
|
+
}
|
|
1056
|
+
// clear the S3 cache
|
|
1057
|
+
if (this._enableWriteToS3 !== undefined && this._enableWriteToS3 === "yes") {
|
|
1058
|
+
console.log(PREFIX + " cacheClearInferenceAndImageByUUID(): Clearing file(s) in S3 prefixed by UUID: " + uuid);
|
|
1059
|
+
result.s3 = await this.s3CacheClearByUUID(uuid);
|
|
1060
|
+
}
|
|
1061
|
+
return result;
|
|
1062
|
+
}
|
|
1063
|
+
// process command to clear the inference image and inference json cache...
|
|
1064
|
+
async cacheClear() {
|
|
1065
|
+
let result = { "local": "OK", "s3": "OK" };
|
|
1066
|
+
// clear local directory/file cache
|
|
1067
|
+
if (this._enableWriteToFile !== undefined && this._enableWriteToFile === "yes") {
|
|
1068
|
+
console.log(PREFIX + " cacheClear(): Clearing local file cache...");
|
|
1069
|
+
result.local = await this.localFileCacheClear();
|
|
1070
|
+
}
|
|
1071
|
+
// clear the S3 cache
|
|
1072
|
+
if (this._enableWriteToS3 !== undefined && this._enableWriteToS3 === "yes") {
|
|
1073
|
+
console.log(PREFIX + " cacheClear(): Clearing S3 cache...");
|
|
1074
|
+
result.s3 = await this.s3CacheClear();
|
|
1075
|
+
}
|
|
1076
|
+
return result;
|
|
216
1077
|
}
|
|
217
|
-
|
|
1078
|
+
// send inference to IoTCore (optionally add Base64 encoded inference image if not too big...)
|
|
1079
|
+
async sendInference(meanConfidence, payload, key, imgAsJpg) {
|
|
218
1080
|
if (this._iot !== undefined && this.isConnected() === true &&
|
|
219
1081
|
this._inferenceOutputTopic !== undefined && this._inferenceOutputTopic.length > 0) {
|
|
220
1082
|
if (this.isEmptyInference(payload, key)) {
|
|
221
1083
|
// empty inference ... so save money and don't publish
|
|
222
1084
|
}
|
|
223
1085
|
else {
|
|
1086
|
+
// add the s3 bucket info if enabled
|
|
1087
|
+
payload.s3_bucket = "unset";
|
|
1088
|
+
payload.s3_prefix = "unset";
|
|
1089
|
+
if (this._enableWriteToS3 !== undefined &&
|
|
1090
|
+
this._enableWriteToS3 === "yes" && this._s3Bucket !== undefined) {
|
|
1091
|
+
payload.s3_bucket = this._s3Bucket;
|
|
1092
|
+
payload.s3_prefix = this.createS3Prefix();
|
|
1093
|
+
}
|
|
1094
|
+
// set the payload
|
|
1095
|
+
const data = Buffer.from(JSON.stringify(payload));
|
|
1096
|
+
// default publish check
|
|
1097
|
+
let doPublish = true;
|
|
1098
|
+
// check if we are using confidence threshold to control publication
|
|
1099
|
+
if (this._thresholdFilter !== undefined && this._thresholdFilter.enabled() === true) {
|
|
1100
|
+
doPublish = (this._thresholdFilter.meetsThresholdCriteria(meanConfidence));
|
|
1101
|
+
}
|
|
1102
|
+
// Check if the data length exceeds the max payload allowed by IoTCore...
|
|
1103
|
+
if (data.length >= IOTCORE_MAX_PAYLOAD) {
|
|
1104
|
+
console.log(PREFIX + " sendInference() WARNING: IoTCore message length "
|
|
1105
|
+
+ data.length + " bytes is larger than the maximum allowed payload size of "
|
|
1106
|
+
+ IOTCORE_MAX_PAYLOAD + " bytes! Message may not be published.");
|
|
1107
|
+
}
|
|
1108
|
+
// process the inference send...
|
|
224
1109
|
if (this._delayInferences > 0) {
|
|
225
1110
|
++this._delayCountdown;
|
|
226
1111
|
if (this._delayCountdown >= this._delayInferences) {
|
|
227
1112
|
// reset:
|
|
228
1113
|
this._delayCountdown = 0;
|
|
1114
|
+
// publish if directed to...
|
|
1115
|
+
if (doPublish === true) {
|
|
1116
|
+
// publish!
|
|
1117
|
+
try {
|
|
1118
|
+
// send the publication
|
|
1119
|
+
await this._iot.send(new client_iot_data_plane_1.PublishCommand({
|
|
1120
|
+
topic: this._inferenceOutputTopic,
|
|
1121
|
+
qos: this._iotcoreQoS,
|
|
1122
|
+
payload: data
|
|
1123
|
+
}));
|
|
1124
|
+
// cache the inference image as configured
|
|
1125
|
+
if (imgAsJpg !== undefined) {
|
|
1126
|
+
await this.cacheInferenceImageAndJSON(payload, imgAsJpg);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
catch (err) {
|
|
1130
|
+
if (this._notSilent) {
|
|
1131
|
+
// exception during send()...
|
|
1132
|
+
console.log(PREFIX + " sendInference() ERROR: IoTDataPlaneClient.send() exception: " + err);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
// publish if directed to...
|
|
1140
|
+
if (doPublish === true) {
|
|
229
1141
|
// publish!
|
|
230
1142
|
try {
|
|
231
1143
|
// send the publication
|
|
232
1144
|
await this._iot.send(new client_iot_data_plane_1.PublishCommand({
|
|
233
1145
|
topic: this._inferenceOutputTopic,
|
|
234
1146
|
qos: this._iotcoreQoS,
|
|
235
|
-
payload:
|
|
1147
|
+
payload: data
|
|
236
1148
|
}));
|
|
237
|
-
//
|
|
238
|
-
if (
|
|
239
|
-
await this.
|
|
1149
|
+
// publish the inference image as configured
|
|
1150
|
+
if (imgAsJpg !== undefined) {
|
|
1151
|
+
await this.cacheInferenceImageAndJSON(payload, imgAsJpg);
|
|
240
1152
|
}
|
|
241
1153
|
}
|
|
242
1154
|
catch (err) {
|
|
243
1155
|
if (this._notSilent) {
|
|
244
1156
|
// exception during send()...
|
|
245
|
-
console.log(PREFIX + "
|
|
1157
|
+
console.log(PREFIX + " ERROR: IoTDataPlaneClient.send() exception: " + err);
|
|
246
1158
|
}
|
|
247
1159
|
}
|
|
248
1160
|
}
|
|
249
1161
|
}
|
|
250
|
-
else {
|
|
251
|
-
// publish!
|
|
252
|
-
try {
|
|
253
|
-
// send the publication
|
|
254
|
-
await this._iot.send(new client_iot_data_plane_1.PublishCommand({
|
|
255
|
-
topic: this._inferenceOutputTopic,
|
|
256
|
-
qos: this._iotcoreQoS,
|
|
257
|
-
payload: Buffer.from(JSON.stringify(payload))
|
|
258
|
-
}));
|
|
259
|
-
// write to file (optional)
|
|
260
|
-
if (this._enableWriteToFile !== undefined && this._enableWriteToFile === "yes" && imgAsJpg !== undefined) {
|
|
261
|
-
await this.saveToFile(payload, imgAsJpg);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
catch (err) {
|
|
265
|
-
if (this._notSilent) {
|
|
266
|
-
// exception during send()...
|
|
267
|
-
console.log(PREFIX + " EI: ERROR - IoTDataPlaneClient.send() errored with exception: " + err);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
1162
|
}
|
|
272
1163
|
}
|
|
273
1164
|
else if (this._iot !== undefined && this.isConnected() === true) {
|
|
274
1165
|
// no publication topic
|
|
275
1166
|
if (this._notSilent) {
|
|
276
|
-
console.log(PREFIX + "
|
|
1167
|
+
console.log(PREFIX + " ERROR: No model inference publication topic specified in configuration... not sending inference results.");
|
|
277
1168
|
}
|
|
278
1169
|
}
|
|
279
1170
|
else {
|
|
280
1171
|
// not connected
|
|
281
1172
|
if (this._notSilent) {
|
|
282
|
-
console.log(PREFIX + "
|
|
1173
|
+
console.log(PREFIX + " ERROR: Not connected to IoTCore... not sending inference.");
|
|
283
1174
|
}
|
|
284
1175
|
}
|
|
285
1176
|
}
|