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.
Files changed (168) hide show
  1. package/build/cli/linux/runner.js +31 -4
  2. package/build/cli/linux/runner.js.map +1 -1
  3. package/build/cli-common/aws-iotcore-connector.d.ts +97 -6
  4. package/build/cli-common/aws-iotcore-connector.js +952 -61
  5. package/build/cli-common/aws-iotcore-connector.js.map +1 -1
  6. package/build/cli-common/init-cli-app.js +5 -3
  7. package/build/cli-common/init-cli-app.js.map +1 -1
  8. package/build/sdk/studio/sdk/api/adminApi.d.ts +162 -8
  9. package/build/sdk/studio/sdk/api/adminApi.js +874 -7
  10. package/build/sdk/studio/sdk/api/adminApi.js.map +1 -1
  11. package/build/sdk/studio/sdk/api/learnApi.d.ts +5 -1
  12. package/build/sdk/studio/sdk/api/learnApi.js +5 -1
  13. package/build/sdk/studio/sdk/api/learnApi.js.map +1 -1
  14. package/build/sdk/studio/sdk/api/organizationBlocksApi.d.ts +6 -10
  15. package/build/sdk/studio/sdk/api/organizationBlocksApi.js +5 -5
  16. package/build/sdk/studio/sdk/api/organizationBlocksApi.js.map +1 -1
  17. package/build/sdk/studio/sdk/api/organizationDataApi.d.ts +3 -2
  18. package/build/sdk/studio/sdk/api/organizationDataApi.js +2 -2
  19. package/build/sdk/studio/sdk/api/organizationDataApi.js.map +1 -1
  20. package/build/sdk/studio/sdk/api/organizationPipelinesApi.d.ts +2 -2
  21. package/build/sdk/studio/sdk/api/organizationPipelinesApi.js +1 -1
  22. package/build/sdk/studio/sdk/api/organizationPipelinesApi.js.map +1 -1
  23. package/build/sdk/studio/sdk/api/organizationsApi.d.ts +11 -10
  24. package/build/sdk/studio/sdk/api/organizationsApi.js +8 -8
  25. package/build/sdk/studio/sdk/api/organizationsApi.js.map +1 -1
  26. package/build/sdk/studio/sdk/api/projectsApi.d.ts +6 -5
  27. package/build/sdk/studio/sdk/api/projectsApi.js +4 -4
  28. package/build/sdk/studio/sdk/api/projectsApi.js.map +1 -1
  29. package/build/sdk/studio/sdk/api/rawDataApi.d.ts +42 -2
  30. package/build/sdk/studio/sdk/api/rawDataApi.js +82 -2
  31. package/build/sdk/studio/sdk/api/rawDataApi.js.map +1 -1
  32. package/build/sdk/studio/sdk/model/addApiKeyRequest.d.ts +2 -2
  33. package/build/sdk/studio/sdk/model/{addOrganizationSecretResponse.d.ts → addApiKeyResponse.d.ts} +6 -2
  34. package/build/sdk/studio/sdk/model/{createPipelineResponse.js → addApiKeyResponse.js} +12 -7
  35. package/build/sdk/studio/sdk/model/addApiKeyResponse.js.map +1 -0
  36. package/build/sdk/studio/sdk/model/{addOrganizationSecretResponseAllOf.d.ts → addApiKeyResponseAllOf.d.ts} +6 -2
  37. package/build/sdk/studio/sdk/model/{addOrganizationSecretResponseAllOf.js → addApiKeyResponseAllOf.js} +12 -7
  38. package/build/sdk/studio/sdk/model/addApiKeyResponseAllOf.js.map +1 -0
  39. package/build/sdk/studio/sdk/model/addOrganizationApiKeyRequest.d.ts +2 -2
  40. package/build/sdk/studio/sdk/model/addProjectApiKeyRequest.d.ts +2 -2
  41. package/build/sdk/studio/sdk/model/adminAddOrganizationApiKeyRequest.d.ts +2 -2
  42. package/build/sdk/studio/sdk/model/adminAddProjectApiKeyRequest.d.ts +2 -2
  43. package/build/sdk/studio/sdk/model/adminApiOrganization.d.ts +4 -0
  44. package/build/sdk/studio/sdk/model/adminApiOrganization.js +5 -0
  45. package/build/sdk/studio/sdk/model/adminApiOrganization.js.map +1 -1
  46. package/build/sdk/studio/sdk/model/adminApiProject.d.ts +1 -0
  47. package/build/sdk/studio/sdk/model/adminApiProject.js +5 -0
  48. package/build/sdk/studio/sdk/model/adminApiProject.js.map +1 -1
  49. package/build/sdk/studio/sdk/model/{addOrganizationTransferLearningBlockResponse.d.ts → adminGetTrashBinResponse.d.ts} +7 -2
  50. package/build/sdk/studio/sdk/model/{addOrganizationSecretResponse.js → adminGetTrashBinResponse.js} +14 -9
  51. package/build/sdk/studio/sdk/model/adminGetTrashBinResponse.js.map +1 -0
  52. package/build/sdk/studio/sdk/model/adminGetTrashBinResponseAllOf.d.ts +33 -0
  53. package/build/sdk/studio/sdk/model/adminGetTrashBinResponseAllOf.js +37 -0
  54. package/build/sdk/studio/sdk/model/adminGetTrashBinResponseAllOf.js.map +1 -0
  55. package/build/sdk/studio/sdk/model/adminGetUsersResponseAllOfUsers.d.ts +1 -0
  56. package/build/sdk/studio/sdk/model/adminGetUsersResponseAllOfUsers.js +5 -0
  57. package/build/sdk/studio/sdk/model/adminGetUsersResponseAllOfUsers.js.map +1 -1
  58. package/build/sdk/studio/sdk/model/{addOrganizationDeployBlockResponse.d.ts → adminProjectInfoResponse.d.ts} +3 -2
  59. package/build/sdk/studio/sdk/model/{createAIActionResponse.js → adminProjectInfoResponse.js} +10 -10
  60. package/build/sdk/studio/sdk/model/adminProjectInfoResponse.js.map +1 -0
  61. package/build/sdk/studio/sdk/model/{dspRunResponseAllOfPerformance.d.ts → adminProjectInfoResponseAllOf.d.ts} +3 -3
  62. package/build/sdk/studio/sdk/model/adminProjectInfoResponseAllOf.js +29 -0
  63. package/build/sdk/studio/sdk/model/adminProjectInfoResponseAllOf.js.map +1 -0
  64. package/build/sdk/studio/sdk/model/createEnterpriseTrialResponse.d.ts +2 -2
  65. package/build/sdk/studio/sdk/model/dSPConfig.d.ts +1 -1
  66. package/build/sdk/studio/sdk/model/dSPConfigResponse.d.ts +1 -1
  67. package/build/sdk/studio/sdk/model/dSPInfo.d.ts +2 -2
  68. package/build/sdk/studio/sdk/model/dSPInfo.js +1 -1
  69. package/build/sdk/studio/sdk/model/dSPInfo.js.map +1 -1
  70. package/build/sdk/studio/sdk/model/deploymentTarget.d.ts +4 -0
  71. package/build/sdk/studio/sdk/model/deploymentTarget.js +5 -0
  72. package/build/sdk/studio/sdk/model/deploymentTarget.js.map +1 -1
  73. package/build/sdk/studio/sdk/model/deploymentTargetEngine.d.ts +1 -1
  74. package/build/sdk/studio/sdk/model/deploymentTargetEngine.js +1 -1
  75. package/build/sdk/studio/sdk/model/deploymentTargetEngine.js.map +1 -1
  76. package/build/sdk/studio/sdk/model/{addOrganizationDspBlockResponse.d.ts → dspPerformance.d.ts} +5 -8
  77. package/build/sdk/studio/sdk/model/{dspRunResponseAllOfPerformance.js → dspPerformance.js} +12 -7
  78. package/build/sdk/studio/sdk/model/dspPerformance.js.map +1 -0
  79. package/build/sdk/studio/sdk/model/dspRunResponse.d.ts +2 -2
  80. package/build/sdk/studio/sdk/model/dspRunResponse.js +1 -1
  81. package/build/sdk/studio/sdk/model/dspRunResponse.js.map +1 -1
  82. package/build/sdk/studio/sdk/model/dspRunResponseAllOf.d.ts +2 -2
  83. package/build/sdk/studio/sdk/model/dspRunResponseAllOf.js +1 -1
  84. package/build/sdk/studio/sdk/model/dspRunResponseAllOf.js.map +1 -1
  85. package/build/sdk/studio/sdk/model/dspRunResponseWithSample.d.ts +2 -2
  86. package/build/sdk/studio/sdk/model/dspRunResponseWithSample.js +1 -1
  87. package/build/sdk/studio/sdk/model/dspRunResponseWithSample.js.map +1 -1
  88. package/build/sdk/studio/sdk/model/dspRunResponseWithSampleAllOf.d.ts +2 -2
  89. package/build/sdk/studio/sdk/model/dspRunResponseWithSampleAllOf.js +1 -1
  90. package/build/sdk/studio/sdk/model/dspRunResponseWithSampleAllOf.js.map +1 -1
  91. package/build/sdk/studio/sdk/model/entityCreatedResponse.d.ts +2 -2
  92. package/build/sdk/studio/sdk/model/entityCreatedResponseAllOf.d.ts +2 -2
  93. package/build/sdk/studio/sdk/model/feature.d.ts +1 -1
  94. package/build/sdk/studio/sdk/model/feature.js +1 -1
  95. package/build/sdk/studio/sdk/model/feature.js.map +1 -1
  96. package/build/sdk/studio/sdk/model/findSyntiantPosteriorRequest.d.ts +1 -1
  97. package/build/sdk/studio/sdk/model/findSyntiantPosteriorRequest.js +1 -1
  98. package/build/sdk/studio/sdk/model/findSyntiantPosteriorRequest.js.map +1 -1
  99. package/build/sdk/studio/sdk/model/models.d.ts +11 -11
  100. package/build/sdk/studio/sdk/model/models.js +36 -33
  101. package/build/sdk/studio/sdk/model/models.js.map +1 -1
  102. package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponse.d.ts +2 -2
  103. package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponse.js +1 -1
  104. package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponse.js.map +1 -1
  105. package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponseAllOf.d.ts +2 -2
  106. package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponseAllOf.js +1 -1
  107. package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponseAllOf.js.map +1 -1
  108. package/build/sdk/studio/sdk/model/{addOrganizationTransformationBlockResponseAllOf.d.ts → objectDetectionLabelQueueResponseAllOfSamples.d.ts} +1 -1
  109. package/build/sdk/studio/sdk/model/{addOrganizationTransformationBlockResponseAllOf.js → objectDetectionLabelQueueResponseAllOfSamples.js} +7 -7
  110. package/build/sdk/studio/sdk/model/objectDetectionLabelQueueResponseAllOfSamples.js.map +1 -0
  111. package/build/sdk/studio/sdk/model/optimizeConfig.d.ts +13 -2
  112. package/build/sdk/studio/sdk/model/optimizeConfig.js +16 -1
  113. package/build/sdk/studio/sdk/model/optimizeConfig.js.map +1 -1
  114. package/build/sdk/studio/sdk/model/{createAIActionResponse.d.ts → optimizeConfigOptimizationObjectives.d.ts} +9 -6
  115. package/build/sdk/studio/sdk/model/optimizeConfigOptimizationObjectives.js +39 -0
  116. package/build/sdk/studio/sdk/model/optimizeConfigOptimizationObjectives.js.map +1 -0
  117. package/build/sdk/studio/sdk/model/optimizeConfigResponse.d.ts +13 -2
  118. package/build/sdk/studio/sdk/model/optimizeConfigResponse.js +16 -1
  119. package/build/sdk/studio/sdk/model/optimizeConfigResponse.js.map +1 -1
  120. package/build/sdk/studio/sdk/model/optimizeConfigSearchSpaceSource.d.ts +45 -0
  121. package/build/sdk/studio/sdk/model/optimizeConfigSearchSpaceSource.js +48 -0
  122. package/build/sdk/studio/sdk/model/optimizeConfigSearchSpaceSource.js.map +1 -0
  123. package/build/sdk/studio/sdk/model/optimizeStateResponse.d.ts +6 -0
  124. package/build/sdk/studio/sdk/model/optimizeStateResponse.js +7 -1
  125. package/build/sdk/studio/sdk/model/optimizeStateResponse.js.map +1 -1
  126. package/build/sdk/studio/sdk/model/optimizeStateResponseAllOf.d.ts +6 -0
  127. package/build/sdk/studio/sdk/model/optimizeStateResponseAllOf.js +7 -1
  128. package/build/sdk/studio/sdk/model/optimizeStateResponseAllOf.js.map +1 -1
  129. package/build/sdk/studio/sdk/model/organization.d.ts +4 -0
  130. package/build/sdk/studio/sdk/model/organization.js +5 -0
  131. package/build/sdk/studio/sdk/model/organization.js.map +1 -1
  132. package/build/sdk/studio/sdk/model/permission.d.ts +1 -1
  133. package/build/sdk/studio/sdk/model/permission.js +1 -1
  134. package/build/sdk/studio/sdk/model/permission.js.map +1 -1
  135. package/build/sdk/studio/sdk/model/project.d.ts +7 -0
  136. package/build/sdk/studio/sdk/model/project.js +20 -0
  137. package/build/sdk/studio/sdk/model/project.js.map +1 -1
  138. package/build/sdk/studio/sdk/model/projectDeploymentTarget.d.ts +4 -0
  139. package/build/sdk/studio/sdk/model/projectDeploymentTarget.js +5 -0
  140. package/build/sdk/studio/sdk/model/projectDeploymentTarget.js.map +1 -1
  141. package/build/sdk/studio/sdk/model/socketTokenResponse.d.ts +1 -1
  142. package/build/sdk/studio/sdk/model/socketTokenResponseAllOf.d.ts +1 -1
  143. package/build/sdk/studio/sdk/model/trashBinEntity.d.ts +55 -0
  144. package/build/sdk/studio/sdk/model/trashBinEntity.js +62 -0
  145. package/build/sdk/studio/sdk/model/trashBinEntity.js.map +1 -0
  146. package/build/sdk/studio/sdk/model/tunerRun.d.ts +1 -0
  147. package/build/sdk/studio/sdk/model/tunerRun.js +5 -0
  148. package/build/sdk/studio/sdk/model/tunerRun.js.map +1 -1
  149. package/package.json +5 -1
  150. package/build/sdk/studio/sdk/model/addOrganizationDeployBlockResponse.js +0 -39
  151. package/build/sdk/studio/sdk/model/addOrganizationDeployBlockResponse.js.map +0 -1
  152. package/build/sdk/studio/sdk/model/addOrganizationDspBlockResponse.js +0 -39
  153. package/build/sdk/studio/sdk/model/addOrganizationDspBlockResponse.js.map +0 -1
  154. package/build/sdk/studio/sdk/model/addOrganizationSecretResponse.js.map +0 -1
  155. package/build/sdk/studio/sdk/model/addOrganizationSecretResponseAllOf.js.map +0 -1
  156. package/build/sdk/studio/sdk/model/addOrganizationTransferLearningBlockResponse.js +0 -39
  157. package/build/sdk/studio/sdk/model/addOrganizationTransferLearningBlockResponse.js.map +0 -1
  158. package/build/sdk/studio/sdk/model/addOrganizationTransformationBlockResponse.d.ts +0 -33
  159. package/build/sdk/studio/sdk/model/addOrganizationTransformationBlockResponse.js +0 -39
  160. package/build/sdk/studio/sdk/model/addOrganizationTransformationBlockResponse.js.map +0 -1
  161. package/build/sdk/studio/sdk/model/addOrganizationTransformationBlockResponseAllOf.js.map +0 -1
  162. package/build/sdk/studio/sdk/model/createAIActionResponse.js.map +0 -1
  163. package/build/sdk/studio/sdk/model/createPipelineResponse.d.ts +0 -33
  164. package/build/sdk/studio/sdk/model/createPipelineResponse.js.map +0 -1
  165. package/build/sdk/studio/sdk/model/developmentBoardCreatedResponse.d.ts +0 -33
  166. package/build/sdk/studio/sdk/model/developmentBoardCreatedResponse.js +0 -39
  167. package/build/sdk/studio/sdk/model/developmentBoardCreatedResponse.js.map +0 -1
  168. package/build/sdk/studio/sdk/model/dspRunResponseAllOfPerformance.js.map +0 -1
@@ -1,12 +1,252 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
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 fs_1 = __importDefault(require("fs"));
9
- const PREFIX = '\x1b[34m[AWS_IOTCORE]\x1b[0m';
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: "10")
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 launchCommandReceiver() {
88
- await this.listenForCommands(this._commandInputTopic, this._poolSleepTimeMS);
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
- isRestartCommand(command) {
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("EI: Restarting Service: " + this._opts.appName + "...");
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
- async listenForCommands(commandInputTopic, longPollSleepMS) {
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
- // Process the received command
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
- // more commands supported here over time...
135
- // default case
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("EI: listenForCommands() Command: " + JSON.stringify(rcvdCmd) + " not supported/understood. Ignoring...");
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("EI: listenForCommands() Command is empty/null. Ignoring...");
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("listenForCommands(): WARNING Status states connected() but iotcore handle is NULL!");
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
- // FUTURE: XXX
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 + " EI: sendModelMetrics() ERROR - IoTDataPlaneClient.send() errored with exception: " + err);
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 + " EI: ERROR - No model metrics topic specified in configuration... not sending inference.");
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 + " EI: ERROR - Not connected to IoTCore... not sending mdoel metrics.");
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
- fs_1.default.writeFile(infFilename, JSON.stringify(payload), function (writeErr) {
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
- fs_1.default.writeFile(imageFilename, imgAsJpg, function (writeErr) {
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
- async sendInference(payload, key, imgAsJpg) {
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: Buffer.from(JSON.stringify(payload))
1147
+ payload: data
236
1148
  }));
237
- // write to file (optional)
238
- if (this._enableWriteToFile !== undefined && this._enableWriteToFile === "yes" && imgAsJpg !== undefined) {
239
- await this.saveToFile(payload, imgAsJpg);
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 + " EI: sendInference() ERROR - IoTDataPlaneClient.send() errored with exception: " + err);
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 + " EI: ERROR - No model inference publication topic specified in configuration... not sending inference results.");
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 + " EI: ERROR - Not connected to IoTCore... not sending inference.");
1173
+ console.log(PREFIX + " ERROR: Not connected to IoTCore... not sending inference.");
283
1174
  }
284
1175
  }
285
1176
  }