@phystack/device-phyos 4.3.40-dev

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 (61) hide show
  1. package/README.md +6 -0
  2. package/bin/index.js +2 -0
  3. package/dist/edge-hub.js +1089 -0
  4. package/dist/edge-hub.js.map +1 -0
  5. package/dist/http-proxy.js +54 -0
  6. package/dist/http-proxy.js.map +1 -0
  7. package/dist/index.js +489 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/methods/devdevice.js +123 -0
  10. package/dist/methods/devdevice.js.map +1 -0
  11. package/dist/methods/environment.js +356 -0
  12. package/dist/methods/environment.js.map +1 -0
  13. package/dist/methods/index.js +24 -0
  14. package/dist/methods/index.js.map +1 -0
  15. package/dist/methods/network/ca.js +48 -0
  16. package/dist/methods/network/ca.js.map +1 -0
  17. package/dist/methods/network/hostname.js +56 -0
  18. package/dist/methods/network/hostname.js.map +1 -0
  19. package/dist/methods/network/lan.js +193 -0
  20. package/dist/methods/network/lan.js.map +1 -0
  21. package/dist/methods/network/wifi.js +299 -0
  22. package/dist/methods/network/wifi.js.map +1 -0
  23. package/dist/methods/reboot.js +22 -0
  24. package/dist/methods/reboot.js.map +1 -0
  25. package/dist/methods/ssh.js +148 -0
  26. package/dist/methods/ssh.js.map +1 -0
  27. package/dist/services/scheduler.service.js +335 -0
  28. package/dist/services/scheduler.service.js.map +1 -0
  29. package/dist/store/types/network-interface.type.js +3 -0
  30. package/dist/store/types/network-interface.type.js.map +1 -0
  31. package/dist/time-sync.js +119 -0
  32. package/dist/time-sync.js.map +1 -0
  33. package/dist/types/command.types.js +8 -0
  34. package/dist/types/command.types.js.map +1 -0
  35. package/dist/types/container.types.js +3 -0
  36. package/dist/types/container.types.js.map +1 -0
  37. package/dist/types/event.types.js +9 -0
  38. package/dist/types/event.types.js.map +1 -0
  39. package/dist/types/job.types.js +15 -0
  40. package/dist/types/job.types.js.map +1 -0
  41. package/dist/types/twin.types.js +21 -0
  42. package/dist/types/twin.types.js.map +1 -0
  43. package/dist/utilities/docker-progress-tracker.js +28 -0
  44. package/dist/utilities/docker-progress-tracker.js.map +1 -0
  45. package/dist/utilities/docker.js +819 -0
  46. package/dist/utilities/docker.js.map +1 -0
  47. package/dist/utilities/instances.js +169 -0
  48. package/dist/utilities/instances.js.map +1 -0
  49. package/dist/utilities/jobs.js +103 -0
  50. package/dist/utilities/jobs.js.map +1 -0
  51. package/dist/utilities/local-twins.js +204 -0
  52. package/dist/utilities/local-twins.js.map +1 -0
  53. package/dist/utilities/network-settings.js +147 -0
  54. package/dist/utilities/network-settings.js.map +1 -0
  55. package/dist/utilities/symlink.js +47 -0
  56. package/dist/utilities/symlink.js.map +1 -0
  57. package/dist/utilities/sysinfo.js +128 -0
  58. package/dist/utilities/sysinfo.js.map +1 -0
  59. package/dist/utilities/twin-manager.js +108 -0
  60. package/dist/utilities/twin-manager.js.map +1 -0
  61. package/package.json +68 -0
@@ -0,0 +1,1089 @@
1
+ "use strict";
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 () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.EdgeHubServer = exports.TwinStatusEnum = void 0;
40
+ exports.startEdgeHub = startEdgeHub;
41
+ const fs_1 = __importDefault(require("fs"));
42
+ const path_1 = __importDefault(require("path"));
43
+ const express_1 = __importDefault(require("express"));
44
+ const http_1 = require("http");
45
+ const socket_io_1 = require("socket.io");
46
+ const child_process_1 = require("child_process");
47
+ const util_1 = require("util");
48
+ const hub_device_1 = __importStar(require("@phystack/hub-device"));
49
+ const http_proxy_1 = require("./http-proxy");
50
+ const wifi_1 = require("./methods/network/wifi");
51
+ const lan_1 = require("./methods/network/lan");
52
+ const environment_1 = require("./methods/environment");
53
+ const hostname_1 = require("./methods/network/hostname");
54
+ const phy_logger_1 = require("@phystack/phy-logger");
55
+ const twin_types_1 = require("./types/twin.types");
56
+ const sysinfo_1 = require("./utilities/sysinfo");
57
+ const devdevice_1 = __importDefault(require("./methods/devdevice"));
58
+ const symlink_1 = require("./utilities/symlink");
59
+ const twin_manager_1 = require("./utilities/twin-manager");
60
+ const instances_1 = require("./utilities/instances");
61
+ const ca_1 = require("./methods/network/ca");
62
+ const crypto_1 = require("crypto");
63
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
64
+ const logger = new phy_logger_1.PhyLogger({
65
+ logToFile: false,
66
+ logToConsole: true,
67
+ includeTrace: true,
68
+ namespace: 'edge-hub',
69
+ });
70
+ var TwinStatusEnum;
71
+ (function (TwinStatusEnum) {
72
+ TwinStatusEnum["Offline"] = "Offline";
73
+ TwinStatusEnum["Online"] = "Online";
74
+ TwinStatusEnum["Starting"] = "Starting";
75
+ TwinStatusEnum["Exited"] = "Exited";
76
+ TwinStatusEnum["ImageNotFound"] = "ImageNotFound";
77
+ })(TwinStatusEnum || (exports.TwinStatusEnum = TwinStatusEnum = {}));
78
+ class EdgeHubServer {
79
+ static instance = null;
80
+ io;
81
+ connectedModuleSockets = {};
82
+ gridEnv;
83
+ osVersion;
84
+ provisioningCode = 'N/A2';
85
+ deviceSerial = 'N/A2';
86
+ config;
87
+ certPaths;
88
+ twinManager;
89
+ twinSubscriptions = new Map();
90
+ messageDeliveryTracker = new Map();
91
+ MAX_TRACKED_MESSAGES = 1000;
92
+ constructor() {
93
+ this.io = new socket_io_1.Server();
94
+ this.gridEnv = this.loadGridEnv();
95
+ this.osVersion = this.loadOsVersion();
96
+ this.loadDeviceSerial();
97
+ this.initializeTwinManager();
98
+ this.config = {
99
+ certDir: '/usr/local/share/ca-certificates/',
100
+ certName: 'phyos',
101
+ moduleMessagingPort: 55000,
102
+ moduleMessagingPortSSL: 55500,
103
+ phyDeviceIP: '172.26.128.1',
104
+ localhostIP: '127.0.0.1',
105
+ };
106
+ this.certPaths = this.initializeCertPaths();
107
+ }
108
+ static getInstance() {
109
+ if (!EdgeHubServer.instance) {
110
+ EdgeHubServer.instance = new EdgeHubServer();
111
+ }
112
+ return EdgeHubServer.instance;
113
+ }
114
+ loadGridEnv() {
115
+ try {
116
+ (0, symlink_1.ensureEnvSymlink)();
117
+ return fs_1.default.readFileSync('/data/settings/phyos.env', 'utf8').trim() || 'PROD';
118
+ }
119
+ catch (err) {
120
+ logger.error('loadGridEnv(): Error reading GRID_ENV:', err);
121
+ return 'PROD';
122
+ }
123
+ }
124
+ loadOsVersion() {
125
+ try {
126
+ return fs_1.default.readFileSync('/etc/phyos.version', 'utf8').trim() || 'PROD';
127
+ }
128
+ catch (err) {
129
+ logger.error('loadGridEnv(): Error reading GRID_ENV:', err);
130
+ return 'PROD';
131
+ }
132
+ }
133
+ loadDeviceSerial() {
134
+ }
135
+ initializeCertPaths() {
136
+ const { certDir, certName } = this.config;
137
+ const caCertName = `${certName}-ca`;
138
+ return {
139
+ ca: path_1.default.join(certDir, `${caCertName}.crt`),
140
+ caKey: path_1.default.join(certDir, `${caCertName}.key`),
141
+ cert: path_1.default.join(certDir, `${certName}.crt`),
142
+ key: path_1.default.join(certDir, `${certName}.key`),
143
+ };
144
+ }
145
+ async generateCertificates() {
146
+ logger.info('generateCertificates(): Generating CA and self-signed certificate...');
147
+ const { ca, caKey, cert, key } = this.certPaths;
148
+ const csr = path_1.default.join(this.config.certDir, `${this.config.certName}.csr`);
149
+ const hasValidContent = (filePath) => {
150
+ try {
151
+ const content = fs_1.default.readFileSync(filePath, 'utf8');
152
+ return content.length > 0;
153
+ }
154
+ catch {
155
+ return false;
156
+ }
157
+ };
158
+ if (fs_1.default.existsSync(ca) &&
159
+ fs_1.default.existsSync(caKey) &&
160
+ fs_1.default.existsSync(cert) &&
161
+ fs_1.default.existsSync(key) &&
162
+ fs_1.default.existsSync(csr) &&
163
+ hasValidContent(ca) &&
164
+ hasValidContent(caKey) &&
165
+ hasValidContent(cert) &&
166
+ hasValidContent(key) &&
167
+ hasValidContent(csr)) {
168
+ logger.info('generateCertificates(): CA and certificates already exist. Skipping generation.');
169
+ return true;
170
+ }
171
+ try {
172
+ const caCertName = `${this.config.certName}-ca`;
173
+ const certName = this.config.certName;
174
+ const certDir = this.config.certDir;
175
+ await execAsync(`openssl req -x509 -nodes -newkey rsa:4096 -keyout ${caKey} ` +
176
+ `-out ${ca} -days 365 -subj "/CN=${caCertName}/O=Phygrid"`);
177
+ logger.info('generateCertificates(): CA generated successfully.');
178
+ await execAsync(`openssl req -newkey rsa:4096 -nodes -keyout ${key} ` +
179
+ `-out ${certDir}/${certName}.csr -subj "/CN=${certName}/O=Phygrid" && ` +
180
+ `openssl x509 -req -in ${certDir}/${certName}.csr -CA ${ca} ` +
181
+ `-CAkey ${caKey} -CAcreateserial -out ${cert} -days 365`);
182
+ logger.info('generateCertificates(): Certificate generated successfully.');
183
+ await execAsync('update-ca-certificates');
184
+ logger.info('generateCertificates(): CA added to system trust.');
185
+ return true;
186
+ }
187
+ catch (error) {
188
+ logger.error('generateCertificates(): Failed to generate certificates:', error);
189
+ return false;
190
+ }
191
+ }
192
+ async waitForIP(ip) {
193
+ logger.info('waitForIP(): Waiting for IP:', ip);
194
+ while (true) {
195
+ try {
196
+ const { stdout } = await execAsync(`ip a | grep ${ip}`);
197
+ if (stdout.includes(ip))
198
+ return;
199
+ }
200
+ catch (error) {
201
+ await new Promise(resolve => setTimeout(resolve, 100));
202
+ }
203
+ }
204
+ }
205
+ async getDeviceStatus() {
206
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
207
+ if (!phyHub) {
208
+ logger.warn('getDeviceStatus(): No phyHub instance, returning offline status.');
209
+ const provisioningInfo = await hub_device_1.default.getProvisioningInfo();
210
+ this.deviceSerial = provisioningInfo.deviceSerial;
211
+ this.provisioningCode = provisioningInfo.provisioningCode;
212
+ const response = {
213
+ status: 'success',
214
+ gridEnv: this.gridEnv,
215
+ osVersion: this.osVersion,
216
+ isConnected: 'false',
217
+ socketAuthenticated: false,
218
+ socketConnected: false,
219
+ ...provisioningInfo,
220
+ };
221
+ return response;
222
+ }
223
+ const system = await phyHub.getDeviceStatus();
224
+ if (system.displayName) {
225
+ try {
226
+ (0, hostname_1.updateHostname)(system.displayName);
227
+ }
228
+ catch (error) {
229
+ logger.error(`getDeviceStatus(): Failed to update hostname to: ${system.displayName}:`, error);
230
+ }
231
+ }
232
+ this.deviceSerial = system.deviceSerial;
233
+ this.provisioningCode = system.provisioningCode;
234
+ const response = {
235
+ status: 'success',
236
+ isConnected: 'true',
237
+ ...system,
238
+ };
239
+ return response;
240
+ }
241
+ handleNetworkConfig(payload, configType) {
242
+ const { data } = payload;
243
+ if (!data) {
244
+ return { status: 'error', message: 'Invalid configuration data' };
245
+ }
246
+ switch (configType) {
247
+ case 'wireless':
248
+ return (0, wifi_1.configureWirelessNetwork)(data);
249
+ case 'wired':
250
+ return (0, lan_1.configureWiredNetwork)(data);
251
+ case 'proxy':
252
+ return (0, environment_1.setEnvironmentConfiguration)(data);
253
+ }
254
+ }
255
+ async getScreenInstance() {
256
+ logger.info(`getScreenInstance(): Getting screen instance`);
257
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
258
+ if (!phyHub) {
259
+ try {
260
+ const provisioningCode = await fs_1.default.promises.readFile('/tmp/provisioning_code');
261
+ return { status: 'success', isConnected: 'false', provisioningCode };
262
+ }
263
+ catch (err) {
264
+ logger.error('getStatus(): Failed to read provisioning code:', err);
265
+ return { status: 'success', isConnected: 'false' };
266
+ }
267
+ }
268
+ const screenInstance = await phyHub.getScreenInstance();
269
+ if (!screenInstance.twin || !screenInstance.twin.properties?.desired) {
270
+ logger.warn('getScreenInstance(): No screen twin found or missing desired properties');
271
+ return {
272
+ status: 'error',
273
+ message: 'No screen twin configuration available',
274
+ isConnected: 'true'
275
+ };
276
+ }
277
+ return { status: 'success', ...screenInstance };
278
+ }
279
+ async getDeviceInstance() {
280
+ logger.info(`getDeviceInstance(): Getting device instance`);
281
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
282
+ if (!phyHub) {
283
+ const { gridEnv, osVersion } = this;
284
+ const provisioningInfo = await hub_device_1.default.getProvisioningInfo();
285
+ return {
286
+ status: 'success',
287
+ gridEnv,
288
+ osVersion,
289
+ isConnected: 'false',
290
+ provisioningCode: provisioningInfo.provisioningCode
291
+ };
292
+ }
293
+ const deviceTwin = phyHub ? await phyHub.getDeviceInstance() : null;
294
+ return { status: 'success', ...deviceTwin };
295
+ }
296
+ async getDeviceNetworks() {
297
+ logger.info(`getDeviceNetworks(): Getting device networks`);
298
+ try {
299
+ const deviceNetworks = await (0, sysinfo_1.getDeviceNetworks)();
300
+ return { status: 'success', data: deviceNetworks };
301
+ }
302
+ catch (error) {
303
+ logger.error('getDeviceNetworks(): Failed to get device networks:', error);
304
+ return { status: 'error', data: {
305
+ wifiNetworks: [],
306
+ wifiConnections: [],
307
+ networkInterfaces: [],
308
+ wifiInterfaces: [],
309
+ } };
310
+ }
311
+ }
312
+ async setScreenInstanceReportedProperties(payload) {
313
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
314
+ if (!phyHub) {
315
+ try {
316
+ const provisioningCode = await fs_1.default.promises.readFile('/tmp/provisioning_code');
317
+ return { status: 'success', isConnected: 'false', provisioningCode };
318
+ }
319
+ catch (err) {
320
+ logger.error('setScreenInstanceReportedProperties(): Failed to read provisioning code:', err);
321
+ return { status: 'success', isConnected: 'false' };
322
+ }
323
+ }
324
+ return new Promise((resolve, reject) => {
325
+ try {
326
+ phyHub.reportScreenInstanceProperties(payload, (response) => {
327
+ resolve(response);
328
+ });
329
+ }
330
+ catch (error) {
331
+ logger.error('Error in reportScreenInstanceProperties', error);
332
+ reject(error);
333
+ }
334
+ });
335
+ }
336
+ async getEdgeInstance(payload) {
337
+ logger.info(`getEdgeInstance(): Get edge instance ${JSON.stringify(payload)}`);
338
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
339
+ if (!phyHub) {
340
+ return { status: 'error', isConnected: 'false' };
341
+ }
342
+ const edgeInstance = await phyHub.getEdgeInstance(payload);
343
+ return { status: 'success', ...edgeInstance };
344
+ }
345
+ async sendEventSignal(payload) {
346
+ logger.info(`sendEventSignal(): Send Event Signal`);
347
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
348
+ if (!phyHub) {
349
+ return { status: 'error', isConnected: 'false' };
350
+ }
351
+ await phyHub.sendEventSignal(payload);
352
+ return { status: 'success' };
353
+ }
354
+ async sendSessionSignal(payload) {
355
+ logger.info(`sendSessionSignal(): Send Session Signal`);
356
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
357
+ if (!phyHub) {
358
+ return { status: 'error', isConnected: 'false' };
359
+ }
360
+ await phyHub.sendSessionSignal(payload);
361
+ return { status: 'success' };
362
+ }
363
+ async sendClientSignal(payload) {
364
+ logger.info(`sendClientSignal(): Send Client Signal`);
365
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
366
+ if (!phyHub) {
367
+ return { status: 'error', isConnected: 'false' };
368
+ }
369
+ await phyHub.sendClientSignal(payload);
370
+ return { status: 'success' };
371
+ }
372
+ async initializeTwinManager() {
373
+ this.twinManager = await (0, twin_manager_1.getTwinManagerInstance)();
374
+ }
375
+ async handleInstanceMessage(hubClientId, payload, callback) {
376
+ if (payload.method !== 'getDeviceStatus') {
377
+ logger.info(`handleInstanceMessage(): Received message: ${JSON.stringify({ hubClientId, payload })}`);
378
+ }
379
+ const { method } = payload;
380
+ let result;
381
+ switch (method) {
382
+ case 'setWirelessNetwork':
383
+ result = this.handleNetworkConfig(payload, 'wireless');
384
+ break;
385
+ case 'setLanNetwork':
386
+ result = this.handleNetworkConfig(payload, 'wired');
387
+ break;
388
+ case 'setProxy':
389
+ result = this.handleNetworkConfig(payload, 'proxy');
390
+ break;
391
+ case 'ping':
392
+ result = await this.handlePing(payload);
393
+ break;
394
+ case 'reboot':
395
+ result = await this.handleReboot();
396
+ break;
397
+ case 'setEnv':
398
+ result = await this.handleSetEnv(payload);
399
+ if (callback)
400
+ callback(result);
401
+ if (result.status === 'success') {
402
+ setTimeout(() => {
403
+ logger.info('handleInstanceMessage(): Exiting after env change...');
404
+ process.exit(0);
405
+ }, 1000);
406
+ }
407
+ break;
408
+ case 'getDeviceStatus':
409
+ result = await this.getDeviceStatus();
410
+ break;
411
+ case 'getScreenInstance':
412
+ result = await this.getScreenInstance();
413
+ break;
414
+ case 'getDeviceInstance':
415
+ result = await this.getDeviceInstance();
416
+ break;
417
+ case 'getDeviceNetworks':
418
+ result = await this.getDeviceNetworks();
419
+ break;
420
+ case 'setScreenInstanceReportedProperties':
421
+ result = await this.setScreenInstanceReportedProperties(payload);
422
+ break;
423
+ case 'getEdgeInstance':
424
+ result = await this.getEdgeInstance(payload);
425
+ break;
426
+ case 'sendEventSignal':
427
+ result = await this.sendEventSignal(payload);
428
+ break;
429
+ case 'sendSessionSignal':
430
+ result = await this.sendSessionSignal(payload);
431
+ break;
432
+ case 'sendClientSignal':
433
+ result = await this.sendClientSignal(payload);
434
+ break;
435
+ case 'callModule':
436
+ result = await this.handleCallModule(payload, callback);
437
+ break;
438
+ case 'devdevice':
439
+ try {
440
+ await (0, devdevice_1.default)(payload);
441
+ result = { status: 'success', message: 'Developer mode enabled successfully' };
442
+ }
443
+ catch (error) {
444
+ logger.error('handleInstanceMessage(): Failed to enable developer mode:', error);
445
+ result = { status: 'error', message: 'Failed to enable developer mode' };
446
+ }
447
+ break;
448
+ case 'twinMessage':
449
+ result = await this.handleTwinMessage(payload, callback);
450
+ break;
451
+ case 'twinUpdated':
452
+ result = await this.handleTwinUpdated(payload, callback);
453
+ break;
454
+ case 'createLocalTwin': {
455
+ try {
456
+ const twinData = payload.data;
457
+ if (!twinData?.twin) {
458
+ throw new Error('Twin data is required');
459
+ }
460
+ const config = {
461
+ type: twin_types_1.TwinTypeEnum.Edge,
462
+ properties: {
463
+ desired: {
464
+ ...twinData.twin.properties?.desired,
465
+ image: twinData.twin.properties?.desired?.image
466
+ }
467
+ }
468
+ };
469
+ logger.info('Creating local twin with config:', config);
470
+ const twinId = await this.twinManager.getLocalTwinManager().createLocalTwin(config);
471
+ logger.info('Twin created with ID:', twinId);
472
+ const newTwin = await this.twinManager.getCachedTwins(twinId);
473
+ logger.info('Retrieved twin from cache:', newTwin);
474
+ if (newTwin) {
475
+ await (0, instances_1.updateInstances)(this.twinManager, [newTwin]);
476
+ result = { status: 'success', twin: newTwin };
477
+ }
478
+ else {
479
+ throw new Error('Failed to create twin');
480
+ }
481
+ }
482
+ catch (error) {
483
+ logger.error('Failed to create local twin:', error);
484
+ result = { status: 'error', message: error?.message || 'Unknown error' };
485
+ }
486
+ break;
487
+ }
488
+ case 'removeLocalTwin': {
489
+ try {
490
+ const twinData = payload.data;
491
+ if (!twinData?.twinId) {
492
+ throw new Error('Twin ID is required');
493
+ }
494
+ logger.info(`handleInstanceMessage(): Removing local twin ${twinData.twinId}`, twinData);
495
+ const twin = await this.twinManager.getCachedTwins(twinData.twinId);
496
+ if (!twin) {
497
+ throw new Error('Twin not found');
498
+ }
499
+ logger.info(`handleInstanceMessage(): Stopping container for twin ${twinData.twinId}`);
500
+ try {
501
+ await (0, instances_1.removeInstances)(this.twinManager, [twin]);
502
+ }
503
+ catch (error) {
504
+ logger.error(`handleInstanceMessage(): Error removing container:`, error);
505
+ throw new Error(`Failed to remove container: ${error.message || 'Unknown error'}`);
506
+ }
507
+ await new Promise(resolve => setTimeout(resolve, 1000));
508
+ logger.info(`handleInstanceMessage(): Removing twin ${twinData.twinId} from storage`);
509
+ await this.twinManager.getLocalTwinManager().removeCachedTwin(twinData.twinId);
510
+ await (0, instances_1.updateInstances)(this.twinManager, [await this.twinManager.getCachedTwins(twinData.twinId)]);
511
+ logger.info(`handleInstanceMessage(): Successfully removed twin ${twinData.twinId}`);
512
+ result = { status: 'success' };
513
+ }
514
+ catch (error) {
515
+ logger.error(`handleInstanceMessage(): Error removing local twin:`, error);
516
+ result = { status: 'error', message: error?.message || 'Unknown error' };
517
+ }
518
+ break;
519
+ }
520
+ case 'listLocalTwins':
521
+ try {
522
+ const twins = await this.twinManager.getAllLocalTwins();
523
+ result = { status: 'success', twins };
524
+ }
525
+ catch (error) {
526
+ result = { status: 'error', message: error?.message || 'Unknown error' };
527
+ }
528
+ break;
529
+ case 'setCACertificate':
530
+ try {
531
+ const config = payload.data;
532
+ if (!config)
533
+ throw new Error('Missing CA certificate configuration');
534
+ result = await (0, ca_1.configureCACertificate)(config);
535
+ }
536
+ catch (error) {
537
+ console.error('Error configuring CA certificate:', error);
538
+ result = { status: 'error', error };
539
+ }
540
+ break;
541
+ case 'getTwinById':
542
+ try {
543
+ const { twinId } = payload.data;
544
+ if (!twinId) {
545
+ result = { status: 'error', message: 'Twin ID is required' };
546
+ break;
547
+ }
548
+ const localTwin = await this.twinManager.getCachedTwins(twinId);
549
+ if (localTwin) {
550
+ result = { status: 'success', twin: localTwin };
551
+ break;
552
+ }
553
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
554
+ if (!phyHub) {
555
+ result = { status: 'error', message: 'PhyHub instance not found' };
556
+ break;
557
+ }
558
+ result = await new Promise((resolve) => {
559
+ phyHub.emitAuth('getTwinById', payload, (response) => {
560
+ resolve(response);
561
+ });
562
+ });
563
+ }
564
+ catch (error) {
565
+ result = { status: 'error', message: error?.message || 'Unknown error' };
566
+ }
567
+ break;
568
+ default:
569
+ switch (method) {
570
+ case 'twinSubscribe':
571
+ if (payload.twinId) {
572
+ await this.handleTwinSubscribe(hubClientId, payload.twinId);
573
+ }
574
+ break;
575
+ case 'twinUnsubscribe':
576
+ if (payload.twinId) {
577
+ await this.handleTwinUnsubscribe(hubClientId, payload.twinId);
578
+ }
579
+ break;
580
+ }
581
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
582
+ if (!phyHub) {
583
+ result = { status: 'error', message: 'PhyHub instance not found' };
584
+ break;
585
+ }
586
+ result = await new Promise((resolve) => {
587
+ phyHub.emitAuth(method, payload, (response) => {
588
+ resolve(response);
589
+ });
590
+ });
591
+ }
592
+ if (callback && result)
593
+ callback(result);
594
+ return result;
595
+ }
596
+ async handlePing(payload) {
597
+ const { data, targetInstanceId } = payload;
598
+ if (!data) {
599
+ return { status: 'error', message: 'Empty payload' };
600
+ }
601
+ if (!targetInstanceId) {
602
+ return { status: 'error', message: 'Target instance id is undefined' };
603
+ }
604
+ logger.info(`handlePing(): Ping called: ${{ targetInstanceId }}`);
605
+ if (!targetInstanceId || !this.connectedModuleSockets[targetInstanceId] ||
606
+ this.connectedModuleSockets[targetInstanceId].length === 0) {
607
+ return { status: 'error', message: 'Target module not found' };
608
+ }
609
+ this.connectedModuleSockets[targetInstanceId].forEach(socket => {
610
+ socket.emit('pong');
611
+ });
612
+ return { status: 'success', message: 'Ping initiated' };
613
+ }
614
+ async handleReboot() {
615
+ logger.info('handleReboot(): Initiating device reboot');
616
+ try {
617
+ const cmd = (0, child_process_1.spawn)('/usr/bin/systemctl', ['reboot']);
618
+ return { status: 'success', message: 'Device reboot initiated' };
619
+ }
620
+ catch (error) {
621
+ logger.error('handleReboot(): Failed to reboot device:', error);
622
+ return { status: 'error', message: 'Failed to reboot device' };
623
+ }
624
+ }
625
+ async handleSetEnv(payload) {
626
+ const { data } = payload;
627
+ if (!data?.env) {
628
+ return { status: 'error', message: 'No environment specified' };
629
+ }
630
+ if (data.env === this.gridEnv) {
631
+ return { status: 'error', message: `Environment is already ${data.env}` };
632
+ }
633
+ try {
634
+ try {
635
+ await execAsync('systemctl disable physcreen');
636
+ await execAsync('systemctl stop physcreen');
637
+ }
638
+ catch (error) {
639
+ logger.error('handleSetEnv(): Failed to disable physcreen service:', error);
640
+ }
641
+ try {
642
+ logger.info('handleSetEnv(): Removing existing settings directory');
643
+ await execAsync('sudo rm -rf /data/settings/phyhub');
644
+ logger.info('handleSetEnv(): Settings directory removed successfully');
645
+ }
646
+ catch (error) {
647
+ logger.error('handleSetEnv(): Failed to remove settings directory:', error);
648
+ return {
649
+ status: 'error',
650
+ message: `Failed to remove settings: ${error.message}`
651
+ };
652
+ }
653
+ try {
654
+ logger.info('handleSetEnv(): Clearing environment file contents');
655
+ await execAsync('echo -n > /data/settings/environment');
656
+ logger.info('handleSetEnv(): Environment file cleared successfully');
657
+ }
658
+ catch (error) {
659
+ logger.error('handleSetEnv(): Failed to clear environment file:', error);
660
+ return {
661
+ status: 'error',
662
+ message: `Failed to clear environment file: ${error.message}`
663
+ };
664
+ }
665
+ const symlinkFixed = (0, symlink_1.ensureEnvSymlink)();
666
+ fs_1.default.writeFileSync('/data/settings/phyos.env', data.env);
667
+ logger.info(`handleSetEnv(): Environment updated to ${data.env}${symlinkFixed ? ' and symlink fixed' : ''}`);
668
+ try {
669
+ setTimeout(async () => {
670
+ await execAsync('systemctl restart phydevice');
671
+ logger.info('handleSetEnv(): Restarted phydevice service');
672
+ }, 2000);
673
+ }
674
+ catch (error) {
675
+ logger.error('handleSetEnv(): Failed to restart phydevice service:', error);
676
+ }
677
+ return { status: 'success', message: `Environment set to ${data.env}` };
678
+ }
679
+ catch (error) {
680
+ logger.error('handleSetEnv(): Failed to update environment:', error);
681
+ return {
682
+ status: 'error',
683
+ message: `Failed to set environment: ${error.message}`
684
+ };
685
+ }
686
+ }
687
+ async handleCallModule(payload, callback) {
688
+ const { data, targetMethod, targetInstanceId } = payload;
689
+ if (!targetInstanceId || !this.connectedModuleSockets[targetInstanceId] ||
690
+ this.connectedModuleSockets[targetInstanceId].length === 0) {
691
+ logger.error(`handleCallModule(): Target ${targetInstanceId} not found`);
692
+ return { status: 'error', message: 'Target module not found' };
693
+ }
694
+ const payloadToEmit = {
695
+ data: payload.data,
696
+ };
697
+ this.connectedModuleSockets[targetInstanceId].forEach(socket => {
698
+ socket.emit(targetMethod, payloadToEmit, callback);
699
+ });
700
+ return { status: 'success', message: 'Call module sent' };
701
+ }
702
+ setupSocketHandlers(socket) {
703
+ const { instanceId: instanceIdFromAuth, moduleName: moduleNameFromAuth } = socket.auth;
704
+ const hubClientId = instanceIdFromAuth || moduleNameFromAuth;
705
+ if (!hubClientId) {
706
+ return;
707
+ }
708
+ logger.info(`setupSocketHandlers(): Client ${hubClientId} connected`);
709
+ if (!this.connectedModuleSockets[hubClientId]) {
710
+ this.connectedModuleSockets[hubClientId] = [];
711
+ }
712
+ this.connectedModuleSockets[hubClientId].push(socket);
713
+ socket.join(hubClientId);
714
+ socket.on('disconnect', () => {
715
+ logger.info(`setupSocketHandlers(): Client ${hubClientId} disconnected`);
716
+ if (this.connectedModuleSockets[hubClientId]) {
717
+ const index = this.connectedModuleSockets[hubClientId].indexOf(socket);
718
+ if (index !== -1) {
719
+ this.connectedModuleSockets[hubClientId].splice(index, 1);
720
+ }
721
+ if (this.connectedModuleSockets[hubClientId].length === 0) {
722
+ delete this.connectedModuleSockets[hubClientId];
723
+ }
724
+ }
725
+ });
726
+ socket.on(hubClientId, async (payload, callback) => {
727
+ await this.handleInstanceMessage(hubClientId, payload, callback);
728
+ });
729
+ socket.on('reconnect', () => {
730
+ logger.info(`setupSocketHandlers(): Client ${hubClientId} reconnected`);
731
+ socket.join(hubClientId);
732
+ });
733
+ socket.on('getDeviceTwin', (callback) => {
734
+ callback({ example: 'deviceTwin' });
735
+ });
736
+ socket.on('ping', (data) => {
737
+ setTimeout(() => {
738
+ socket.emit('pong', { count: data.count + 1 });
739
+ }, 3000);
740
+ });
741
+ }
742
+ setupExpressApp() {
743
+ const app = (0, express_1.default)();
744
+ app.get('/', (_req, res) => {
745
+ res.json({ hello: 'world' });
746
+ });
747
+ app.get('/status', (_req, res) => {
748
+ res.json({ status: 'ok' });
749
+ });
750
+ return app;
751
+ }
752
+ async setupProxyServers(sslEnabled) {
753
+ const { localhostIP, phyDeviceIP, moduleMessagingPort, moduleMessagingPortSSL } = this.config;
754
+ const { key: keyPath, cert: certPath } = this.certPaths;
755
+ if (sslEnabled) {
756
+ (0, http_proxy_1.startProxyServer)({
757
+ sourceIp: localhostIP,
758
+ sourcePort: moduleMessagingPort,
759
+ destIp: localhostIP,
760
+ destPort: moduleMessagingPortSSL,
761
+ useHttps: true,
762
+ keyPath,
763
+ certPath,
764
+ });
765
+ }
766
+ await this.waitForIP(phyDeviceIP);
767
+ (0, http_proxy_1.startProxyServer)({
768
+ sourceIp: localhostIP,
769
+ sourcePort: moduleMessagingPort,
770
+ destIp: phyDeviceIP,
771
+ destPort: moduleMessagingPort,
772
+ useHttps: false,
773
+ });
774
+ if (sslEnabled) {
775
+ (0, http_proxy_1.startProxyServer)({
776
+ sourceIp: localhostIP,
777
+ sourcePort: moduleMessagingPort,
778
+ destIp: phyDeviceIP,
779
+ destPort: moduleMessagingPortSSL,
780
+ useHttps: true,
781
+ keyPath,
782
+ certPath,
783
+ });
784
+ }
785
+ }
786
+ async start() {
787
+ logger.info('start(): Starting edge hub messaging...');
788
+ let sslEnabled = await this.generateCertificates();
789
+ this.io.use((socket, next) => {
790
+ const extSocket = socket;
791
+ const { auth } = socket.handshake;
792
+ extSocket.auth = auth;
793
+ next();
794
+ });
795
+ this.io.on('connection', (socket) => {
796
+ this.setupSocketHandlers(socket);
797
+ });
798
+ const app = this.setupExpressApp();
799
+ const { localhostIP, moduleMessagingPort } = this.config;
800
+ const httpServerLocal = (0, http_1.createServer)(app);
801
+ this.io.attach(httpServerLocal);
802
+ httpServerLocal.listen(moduleMessagingPort, localhostIP, () => {
803
+ logger.info(`start(): edgeHub Messaging HTTP Server listening on http://${localhostIP}:${moduleMessagingPort}`);
804
+ });
805
+ setTimeout(() => this.setupProxyServers(sslEnabled), 0);
806
+ }
807
+ generateMessageId(payload) {
808
+ const { twinId, sourceTwinId, method, data } = payload;
809
+ const timestamp = Math.floor(Date.now() / 3000) * 3;
810
+ const dataString = data ? JSON.stringify(data, (_, v) => Buffer.isBuffer(v) ? v.toString('base64') : v) : '';
811
+ const hash = (0, crypto_1.createHash)('sha256')
812
+ .update(`${twinId}-${sourceTwinId}-${method}-${dataString}-${timestamp}`)
813
+ .digest('hex');
814
+ return hash;
815
+ }
816
+ trackMessageDelivery(messageId, recipientId) {
817
+ if (!this.messageDeliveryTracker.has(messageId)) {
818
+ this.messageDeliveryTracker.set(messageId, new Set());
819
+ if (this.messageDeliveryTracker.size > this.MAX_TRACKED_MESSAGES) {
820
+ const oldestKey = this.messageDeliveryTracker.keys().next().value;
821
+ if (oldestKey) {
822
+ this.messageDeliveryTracker.delete(oldestKey);
823
+ }
824
+ }
825
+ }
826
+ this.messageDeliveryTracker.get(messageId).add(recipientId);
827
+ }
828
+ hasMessageBeenDelivered(messageId, recipientId) {
829
+ return this.messageDeliveryTracker.get(messageId)?.has(recipientId) || false;
830
+ }
831
+ async handleTwinMessage(payload, callback, fromPhyHub = false) {
832
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
833
+ if (!phyHub) {
834
+ logger.error('handleTwinMessage(): PhyHub instance not found');
835
+ if (callback)
836
+ callback({ status: 'error', message: 'PhyHub instance not found' });
837
+ return { status: 'error', message: 'PhyHub instance not found' };
838
+ }
839
+ const { twinId, sourceTwinId } = payload;
840
+ if (!twinId && !sourceTwinId) {
841
+ logger.error('handleTwinMessage(): No twin ID provided');
842
+ return { status: 'error', message: 'No twin ID provided' };
843
+ }
844
+ const deviceTwins = await phyHub.getCachedTwins();
845
+ const messageId = this.generateMessageId(payload);
846
+ let messageForwardedToPhyHub = false;
847
+ if (twinId === sourceTwinId) {
848
+ if (!fromPhyHub) {
849
+ messageForwardedToPhyHub = true;
850
+ this.forwardToPhyHub(payload, callback);
851
+ }
852
+ }
853
+ this.twinSubscriptions.forEach((subscribedTwins, subscriberTwinId) => {
854
+ if (subscribedTwins.has(twinId) && !this.hasMessageBeenDelivered(messageId, subscriberTwinId)) {
855
+ this.io.to(subscriberTwinId).emit('twinMessage', payload);
856
+ this.trackMessageDelivery(messageId, subscriberTwinId);
857
+ }
858
+ else {
859
+ logger.info(`handleTwinMessage(): No match found for twinId ${twinId} in subscriber ${subscriberTwinId}`);
860
+ }
861
+ });
862
+ if (deviceTwins['Edge']) {
863
+ for (const edgeTwinId of deviceTwins['Edge']) {
864
+ const edgeTwin = await phyHub.getCachedTwins(edgeTwinId);
865
+ const { id: instanceId } = edgeTwin;
866
+ if (instanceId === twinId && !this.hasMessageBeenDelivered(messageId, instanceId)) {
867
+ this.io.to(instanceId).emit('twinMessage', payload, callback);
868
+ this.trackMessageDelivery(messageId, instanceId);
869
+ }
870
+ }
871
+ }
872
+ if (deviceTwins['Screen']) {
873
+ for (const screenTwinId of deviceTwins['Screen']) {
874
+ const screenTwin = await phyHub.getCachedTwins(screenTwinId);
875
+ const { id: instanceId } = screenTwin;
876
+ if (instanceId === twinId && !this.hasMessageBeenDelivered(messageId, instanceId)) {
877
+ this.io.to(instanceId).emit('twinMessage', payload, callback);
878
+ this.trackMessageDelivery(messageId, instanceId);
879
+ }
880
+ }
881
+ }
882
+ if (deviceTwins['Peripheral']) {
883
+ for (const peripheralTwinId of deviceTwins['Peripheral']) {
884
+ const peripheralTwin = await phyHub.getCachedTwins(peripheralTwinId);
885
+ const { instanceId } = peripheralTwin.properties.desired;
886
+ if (instanceId && deviceTwins['Edge'].includes(instanceId) && !this.hasMessageBeenDelivered(messageId, instanceId)) {
887
+ this.io.to(instanceId).emit('twinMessage', payload);
888
+ this.trackMessageDelivery(messageId, instanceId);
889
+ }
890
+ }
891
+ }
892
+ if (!messageForwardedToPhyHub && !fromPhyHub) {
893
+ const result = await this.forwardToPhyHub(payload, callback);
894
+ if (callback)
895
+ callback(result);
896
+ return result;
897
+ }
898
+ if (callback)
899
+ callback({ status: 'error', message: 'Message not forwarded' });
900
+ return { status: 'error', message: 'Message not forwarded' };
901
+ }
902
+ async forwardToPhyHub(payload, callback) {
903
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
904
+ if (!phyHub) {
905
+ logger.error('forwardToPhyHub(): PhyHub instance not found');
906
+ return;
907
+ }
908
+ const { twinId } = payload;
909
+ const uuidV4Pattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
910
+ const isLocalTwin = twinId && uuidV4Pattern.test(twinId);
911
+ if (isLocalTwin) {
912
+ return { status: 'success' };
913
+ }
914
+ return new Promise((resolve) => {
915
+ phyHub.emitAuth('twinMessage', payload, (response) => {
916
+ if (callback)
917
+ callback(response);
918
+ resolve(response);
919
+ });
920
+ });
921
+ }
922
+ async handleTwinSubscribe(subscriberTwinId, targetTwinId) {
923
+ console.log('handleTwinSubscribe(): subscriberTwinId', JSON.stringify({ subscriberTwinId, targetTwinId }, null, 2));
924
+ if (!this.twinSubscriptions.has(subscriberTwinId)) {
925
+ console.log('handleTwinSubscribe(): Setting new subscription', subscriberTwinId);
926
+ this.twinSubscriptions.set(subscriberTwinId, new Set());
927
+ }
928
+ this.twinSubscriptions.get(subscriberTwinId).add(targetTwinId);
929
+ logger.info(`handleTwinSubscribe(): Twin ${subscriberTwinId} subscribed to ${targetTwinId}`);
930
+ }
931
+ async handleTwinUnsubscribe(subscriberTwinId, targetTwinId) {
932
+ const subscriptions = this.twinSubscriptions.get(subscriberTwinId);
933
+ if (subscriptions) {
934
+ subscriptions.delete(targetTwinId);
935
+ if (subscriptions.size === 0) {
936
+ this.twinSubscriptions.delete(subscriberTwinId);
937
+ }
938
+ logger.info(`handleTwinUnsubscribe(): Twin ${subscriberTwinId} unsubscribed from ${targetTwinId}`);
939
+ }
940
+ }
941
+ async resubscribeAllTwins() {
942
+ logger.info('resubscribeAllTwins(): Starting resubscription process');
943
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
944
+ if (!phyHub) {
945
+ logger.error('resubscribeAllTwins(): PhyHub instance not found');
946
+ return;
947
+ }
948
+ const deviceStatus = await this.getDeviceStatus();
949
+ if (!deviceStatus?.deviceId) {
950
+ logger.error('resubscribeAllTwins(): Device ID not found');
951
+ return;
952
+ }
953
+ logger.info('resubscribeAllTwins(): Current subscriptions:', Object.fromEntries([...this.twinSubscriptions].map(([k, v]) => [k, [...v]])));
954
+ if (this.twinSubscriptions.size === 0) {
955
+ logger.info('resubscribeAllTwins(): No existing subscriptions to resubscribe');
956
+ return;
957
+ }
958
+ for (const [subscriberTwinId, subscribedTwins] of this.twinSubscriptions.entries()) {
959
+ logger.info(`resubscribeAllTwins(): Processing subscriber ${subscriberTwinId} with ${subscribedTwins.size} subscriptions`);
960
+ for (const targetTwinId of subscribedTwins) {
961
+ logger.info(`resubscribeAllTwins(): Attempting to resubscribe ${subscriberTwinId} to ${targetTwinId}`);
962
+ const payload = {
963
+ twinId: targetTwinId,
964
+ sourceTwinId: subscriberTwinId,
965
+ sourceDeviceId: deviceStatus.deviceId
966
+ };
967
+ try {
968
+ const response = await new Promise((resolve, reject) => {
969
+ phyHub.emitAuth('twinSubscribe', payload, (response) => {
970
+ if (response.status === 'success') {
971
+ resolve(response);
972
+ }
973
+ else {
974
+ reject(new Error(response.message || 'Unknown error'));
975
+ }
976
+ });
977
+ });
978
+ console.log('resubscribeAllTwins(): Response', JSON.stringify(response, null, 2));
979
+ logger.info(`resubscribeAllTwins(): Successfully resubscribed ${subscriberTwinId} to ${targetTwinId}`);
980
+ }
981
+ catch (error) {
982
+ logger.error(`resubscribeAllTwins(): Failed to resubscribe ${subscriberTwinId} to ${targetTwinId}:`, error);
983
+ }
984
+ }
985
+ }
986
+ }
987
+ async handleTwinUpdated(payload, callback, fromPhyHub = false) {
988
+ logger.info('handleTwinUpdated(): Received twin update', JSON.stringify(payload, null, 2));
989
+ const phyHub = await (0, hub_device_1.getPhyHubInstance)();
990
+ if (!phyHub) {
991
+ logger.error('handleTwinUpdated(): PhyHub instance not found');
992
+ if (callback)
993
+ callback({ status: 'error', message: 'PhyHub instance not found' });
994
+ return { status: 'error', message: 'PhyHub instance not found' };
995
+ }
996
+ let { twinId, sourceTwinId } = payload;
997
+ if (!twinId && payload.data?.id) {
998
+ twinId = payload.data.id;
999
+ logger.info(`handleTwinUpdated(): Using data.id ${twinId} as twinId`);
1000
+ }
1001
+ if (!twinId && !sourceTwinId) {
1002
+ logger.error('handleTwinUpdated(): No twin ID provided');
1003
+ return { status: 'error', message: 'No twin ID provided' };
1004
+ }
1005
+ const deviceTwins = await phyHub.getCachedTwins();
1006
+ const messageId = this.generateMessageId(payload);
1007
+ let emissionCount = 0;
1008
+ const subscriberCount = this.twinSubscriptions.size;
1009
+ let subscriberEmitCount = 0;
1010
+ this.twinSubscriptions.forEach((subscribedTwins, subscriberTwinId) => {
1011
+ if (subscribedTwins.has(twinId) && !this.hasMessageBeenDelivered(messageId, subscriberTwinId)) {
1012
+ this.io.to(subscriberTwinId).emit('twinUpdated', payload);
1013
+ this.trackMessageDelivery(messageId, subscriberTwinId);
1014
+ subscriberEmitCount++;
1015
+ emissionCount++;
1016
+ logger.info(`handleTwinUpdated(): Emitted to subscriber ${subscriberTwinId} for twin ${twinId}`);
1017
+ }
1018
+ });
1019
+ if (subscriberCount > 0 && subscriberEmitCount === 0) {
1020
+ logger.info(`handleTwinUpdated(): No subscribers matched twin ${twinId} (out of ${subscriberCount} total subscribers)`);
1021
+ }
1022
+ let edgeEmitCount = 0;
1023
+ if (deviceTwins['Edge']) {
1024
+ for (const edgeTwinId of deviceTwins['Edge']) {
1025
+ const edgeTwin = await phyHub.getCachedTwins(edgeTwinId);
1026
+ const { id: instanceId } = edgeTwin;
1027
+ if (instanceId === twinId && !this.hasMessageBeenDelivered(messageId, instanceId)) {
1028
+ this.io.to(instanceId).emit('twinUpdated', payload, callback);
1029
+ this.trackMessageDelivery(messageId, instanceId);
1030
+ edgeEmitCount++;
1031
+ emissionCount++;
1032
+ logger.info(`handleTwinUpdated(): Emitted to Edge twin ${instanceId}`);
1033
+ }
1034
+ }
1035
+ if (edgeEmitCount === 0) {
1036
+ logger.info(`handleTwinUpdated(): No Edge twins matched twin ${twinId} (out of ${deviceTwins['Edge'].length} Edge twins)`);
1037
+ }
1038
+ }
1039
+ let screenEmitCount = 0;
1040
+ if (deviceTwins['Screen']) {
1041
+ for (const screenTwinId of deviceTwins['Screen']) {
1042
+ const screenTwin = await phyHub.getCachedTwins(screenTwinId);
1043
+ const { id: instanceId } = screenTwin;
1044
+ if (instanceId === twinId && !this.hasMessageBeenDelivered(messageId, instanceId)) {
1045
+ this.io.to(instanceId).emit('twinUpdated', payload, callback);
1046
+ this.trackMessageDelivery(messageId, instanceId);
1047
+ screenEmitCount++;
1048
+ emissionCount++;
1049
+ logger.info(`handleTwinUpdated(): Emitted to Screen twin ${instanceId}`);
1050
+ }
1051
+ }
1052
+ if (screenEmitCount === 0) {
1053
+ logger.info(`handleTwinUpdated(): No Screen twins matched twin ${twinId} (out of ${deviceTwins['Screen'].length} Screen twins)`);
1054
+ }
1055
+ }
1056
+ let peripheralEmitCount = 0;
1057
+ if (deviceTwins['Peripheral']) {
1058
+ for (const peripheralTwinId of deviceTwins['Peripheral']) {
1059
+ const peripheralTwin = await phyHub.getCachedTwins(peripheralTwinId);
1060
+ const { instanceId } = peripheralTwin.properties.desired;
1061
+ if (instanceId && deviceTwins['Edge'].includes(instanceId) && !this.hasMessageBeenDelivered(messageId, instanceId)) {
1062
+ this.io.to(instanceId).emit('twinUpdated', payload);
1063
+ this.trackMessageDelivery(messageId, instanceId);
1064
+ peripheralEmitCount++;
1065
+ emissionCount++;
1066
+ logger.info(`handleTwinUpdated(): Emitted to Edge instance ${instanceId} for Peripheral twin ${peripheralTwinId}`);
1067
+ }
1068
+ }
1069
+ if (peripheralEmitCount === 0) {
1070
+ logger.info(`handleTwinUpdated(): No Peripheral twins matched criteria for twin ${twinId} (out of ${deviceTwins['Peripheral'].length} Peripheral twins)`);
1071
+ }
1072
+ }
1073
+ if (emissionCount === 0) {
1074
+ logger.warn(`handleTwinUpdated(): Twin update for ${twinId} was not emitted to any recipient!`);
1075
+ }
1076
+ else {
1077
+ logger.info(`handleTwinUpdated(): Twin update for ${twinId} was emitted to ${emissionCount} recipients`);
1078
+ }
1079
+ if (callback)
1080
+ callback({ status: 'success' });
1081
+ return { status: 'success' };
1082
+ }
1083
+ }
1084
+ exports.EdgeHubServer = EdgeHubServer;
1085
+ async function startEdgeHub() {
1086
+ const server = EdgeHubServer.getInstance();
1087
+ await server.start();
1088
+ }
1089
+ //# sourceMappingURL=edge-hub.js.map