@phystack/cli 4.5.41-dev → 4.5.42-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.
- package/dist/commands/auth/login.js +34 -0
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/commands/simulator/index.js +25 -0
- package/dist/commands/simulator/index.js.map +1 -0
- package/dist/commands/simulator/run.js +383 -0
- package/dist/commands/simulator/run.js.map +1 -0
- package/dist/commands/simulator/start.js +29 -0
- package/dist/commands/simulator/start.js.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/simulator/index.js +162 -0
- package/dist/simulator/index.js.map +1 -0
- package/dist/simulator/local-server.js +147 -0
- package/dist/simulator/local-server.js.map +1 -0
- package/dist/simulator/logger.js +43 -0
- package/dist/simulator/logger.js.map +1 -0
- package/dist/simulator/message-router.js +379 -0
- package/dist/simulator/message-router.js.map +1 -0
- package/dist/simulator/twin-cache.js +58 -0
- package/dist/simulator/twin-cache.js.map +1 -0
- package/dist/simulator/types.js +16 -0
- package/dist/simulator/types.js.map +1 -0
- package/dist/utils/simulator-config.js +99 -0
- package/dist/utils/simulator-config.js.map +1 -0
- package/package.json +5 -2
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DeviceSimulator = void 0;
|
|
7
|
+
/* eslint-disable import/prefer-default-export */
|
|
8
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const uuid_1 = require("uuid");
|
|
11
|
+
const types_1 = require("./types");
|
|
12
|
+
const twin_cache_1 = require("./twin-cache");
|
|
13
|
+
const local_server_1 = require("./local-server");
|
|
14
|
+
const message_router_1 = require("./message-router");
|
|
15
|
+
const logger_1 = require("./logger");
|
|
16
|
+
const simulator_config_1 = require("../utils/simulator-config");
|
|
17
|
+
const utils_1 = require("../utils");
|
|
18
|
+
/**
|
|
19
|
+
* Generate a deterministic 24-char hex string (MongoDB ObjectID format)
|
|
20
|
+
* from a human-readable name. Same input always produces same output.
|
|
21
|
+
*/
|
|
22
|
+
function mockObjectId(name) {
|
|
23
|
+
return crypto_1.default.createHash('sha256').update(name).digest('hex').slice(0, 24);
|
|
24
|
+
}
|
|
25
|
+
class DeviceSimulator {
|
|
26
|
+
constructor(port = 55000, deviceDisplayName = 'Local Simulator') {
|
|
27
|
+
this.router = null;
|
|
28
|
+
this.deviceId = '';
|
|
29
|
+
this.tenantId = '';
|
|
30
|
+
this.port = port;
|
|
31
|
+
this.deviceDisplayName = deviceDisplayName;
|
|
32
|
+
this.twinCache = new twin_cache_1.TwinCache();
|
|
33
|
+
this.localServer = new local_server_1.LocalServer(port);
|
|
34
|
+
}
|
|
35
|
+
async start() {
|
|
36
|
+
// 1. Load or create device identity
|
|
37
|
+
// Config storage uses this as directory name. Twins need a 24-char hex ID
|
|
38
|
+
// to pass hub-client validation (e.g. signals zod schemas), so we derive
|
|
39
|
+
// one deterministically via mockObjectId.
|
|
40
|
+
const tenantLabel = 'local';
|
|
41
|
+
const tenantObjectId = mockObjectId(tenantLabel);
|
|
42
|
+
this.tenantId = tenantObjectId;
|
|
43
|
+
const existingConfig = await (0, simulator_config_1.getDeviceConfig)(tenantLabel);
|
|
44
|
+
let deviceTwinId;
|
|
45
|
+
if (existingConfig) {
|
|
46
|
+
this.deviceId = existingConfig.deviceId;
|
|
47
|
+
deviceTwinId = existingConfig.deviceTwinId;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
this.deviceId = (0, uuid_1.v4)();
|
|
51
|
+
deviceTwinId = (0, uuid_1.v4)();
|
|
52
|
+
await (0, simulator_config_1.saveDeviceConfig)(tenantLabel, {
|
|
53
|
+
deviceId: this.deviceId,
|
|
54
|
+
tenantId: this.tenantId,
|
|
55
|
+
deviceTwinId,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
// 2. Create a local Device twin in cache
|
|
59
|
+
const deviceTwin = {
|
|
60
|
+
id: deviceTwinId,
|
|
61
|
+
deviceId: this.deviceId,
|
|
62
|
+
tenantId: this.tenantId,
|
|
63
|
+
type: types_1.TwinTypeEnum.Device,
|
|
64
|
+
properties: {
|
|
65
|
+
desired: {
|
|
66
|
+
displayName: this.deviceDisplayName,
|
|
67
|
+
spaceId: tenantObjectId,
|
|
68
|
+
env: 'development',
|
|
69
|
+
deviceSerial: `SIM-${this.deviceId.slice(0, 8)}`,
|
|
70
|
+
accessKey: 'simulator-local-key',
|
|
71
|
+
},
|
|
72
|
+
reported: {
|
|
73
|
+
ip: [{ interface: 'lo', ipv4: '127.0.0.1', ipv6: '::1' }],
|
|
74
|
+
os: { osVersion: 'Simulator' },
|
|
75
|
+
env: { gridEnv: 'development' },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
this.twinCache.addTwin(deviceTwin);
|
|
80
|
+
// 3. Start local server
|
|
81
|
+
await this.localServer.start();
|
|
82
|
+
const io = this.localServer.getIO();
|
|
83
|
+
this.localServer.on('instanceConnected', (event) => {
|
|
84
|
+
const twin = this.twinCache.getTwin(event.twinId);
|
|
85
|
+
const label = twin?.properties?.desired?.appName || 'unknown';
|
|
86
|
+
logger_1.simulatorLog.success(`Instance connected: ${label} (${event.twinId}) — ${event.activeCount} active`);
|
|
87
|
+
});
|
|
88
|
+
this.localServer.on('instanceDisconnected', (event) => {
|
|
89
|
+
const twin = this.twinCache.getTwin(event.twinId);
|
|
90
|
+
const label = twin?.properties?.desired?.appName || 'unknown';
|
|
91
|
+
logger_1.simulatorLog.warn(`Instance disconnected: ${label} (${event.twinId}) — ${event.activeCount} active`);
|
|
92
|
+
});
|
|
93
|
+
// 4. Create router (no upstream)
|
|
94
|
+
this.router = new message_router_1.MessageRouter(this.twinCache, io);
|
|
95
|
+
this.localServer.setRouter(this.router);
|
|
96
|
+
// 5. Print banner
|
|
97
|
+
this.printBanner();
|
|
98
|
+
if (existingConfig) {
|
|
99
|
+
logger_1.simulatorLog.dim(`Reusing device identity ${this.deviceId}`);
|
|
100
|
+
}
|
|
101
|
+
logger_1.simulatorLog.dim(`Apps connect to http://localhost:${this.port}`);
|
|
102
|
+
}
|
|
103
|
+
async stop() {
|
|
104
|
+
logger_1.simulatorLog.dim('Shutting down simulator...');
|
|
105
|
+
await this.localServer.stop();
|
|
106
|
+
logger_1.simulatorLog.dim('Simulator stopped.');
|
|
107
|
+
}
|
|
108
|
+
async createTwinForApp(type, desiredProperties, reuseId) {
|
|
109
|
+
const twin = {
|
|
110
|
+
id: reuseId || (0, uuid_1.v4)(),
|
|
111
|
+
deviceId: this.deviceId,
|
|
112
|
+
tenantId: this.tenantId,
|
|
113
|
+
type,
|
|
114
|
+
properties: {
|
|
115
|
+
desired: desiredProperties || {},
|
|
116
|
+
reported: {},
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
this.twinCache.addTwin(twin);
|
|
120
|
+
return twin;
|
|
121
|
+
}
|
|
122
|
+
createPeripheralTwin(instanceId, name, hardwareId, desiredProperties) {
|
|
123
|
+
const twin = {
|
|
124
|
+
id: (0, uuid_1.v4)(),
|
|
125
|
+
deviceId: this.deviceId,
|
|
126
|
+
tenantId: this.tenantId,
|
|
127
|
+
type: types_1.TwinTypeEnum.Peripheral,
|
|
128
|
+
properties: {
|
|
129
|
+
desired: { ...desiredProperties, instanceId, name, hardwareId },
|
|
130
|
+
reported: {},
|
|
131
|
+
},
|
|
132
|
+
descriptors: { instanceId, name, hardwareId },
|
|
133
|
+
};
|
|
134
|
+
this.twinCache.addTwin(twin);
|
|
135
|
+
return twin;
|
|
136
|
+
}
|
|
137
|
+
verifyTwinExists(twinId) {
|
|
138
|
+
return this.twinCache.hasTwin(twinId);
|
|
139
|
+
}
|
|
140
|
+
getDeviceId() {
|
|
141
|
+
return this.deviceId;
|
|
142
|
+
}
|
|
143
|
+
printBanner() {
|
|
144
|
+
logger_1.simulatorLog.printBanner([
|
|
145
|
+
'',
|
|
146
|
+
chalk_1.default.hex('#00e676')(' ██▀▀▄ █ █ █ █ ▄▀▀▀ ▀▀█▀▀ ▄▀▀█ ▄▀▀▀ █ ▄▀'),
|
|
147
|
+
chalk_1.default.hex('#00c853')(' █▄▄▀ █▄▄█ ▀█ ▀▀▄ █ █▄▄█ █ █▀▄ '),
|
|
148
|
+
chalk_1.default.hex('#00a846')(' █ █ █ █ ▀▄▄▀ █ █ █ ▀▄▄▀ █ ▀▄'),
|
|
149
|
+
chalk_1.default.dim(` Device Simulator v${(0, utils_1.getPackageVersion)()}`),
|
|
150
|
+
chalk_1.default.dim('─'.repeat(50)),
|
|
151
|
+
` ${chalk_1.default.cyan('Mode:')} ${chalk_1.default.yellow('local')}`,
|
|
152
|
+
` ${chalk_1.default.cyan('Port:')} ${this.port}`,
|
|
153
|
+
` ${chalk_1.default.cyan('Device:')} ${this.deviceDisplayName}`,
|
|
154
|
+
` ${chalk_1.default.cyan('Device ID:')} ${chalk_1.default.dim(this.deviceId)} ${chalk_1.default.yellow('(local)')}`,
|
|
155
|
+
` ${chalk_1.default.cyan('Tenant:')} ${chalk_1.default.dim(this.tenantId)} ${chalk_1.default.yellow('(local)')}`,
|
|
156
|
+
chalk_1.default.dim('─'.repeat(50)),
|
|
157
|
+
'',
|
|
158
|
+
]);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.DeviceSimulator = DeviceSimulator;
|
|
162
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/simulator/index.ts"],"names":[],"mappings":";;;;;;AAAA,iDAAiD;AACjD,oDAA4B;AAC5B,kDAA0B;AAC1B,+BAAoC;AACpC,mCAAqD;AACrD,6CAAyC;AACzC,iDAAsE;AACtE,qDAAiD;AACjD,qCAAwC;AACxC,gEAA8E;AAC9E,oCAA6C;AAE7C;;;GAGG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,gBAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED,MAAa,eAAe;IAe1B,YAAY,IAAI,GAAG,KAAK,EAAE,iBAAiB,GAAG,iBAAiB;QAVvD,WAAM,GAAyB,IAAI,CAAC;QAMpC,aAAQ,GAAW,EAAE,CAAC;QAEtB,aAAQ,GAAW,EAAE,CAAC;QAG5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,SAAS,GAAG,IAAI,sBAAS,EAAE,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,IAAI,0BAAW,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,KAAK;QACT,oCAAoC;QACpC,0EAA0E;QAC1E,yEAAyE;QACzE,0CAA0C;QAC1C,MAAM,WAAW,GAAG,OAAO,CAAC;QAC5B,MAAM,cAAc,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ,GAAG,cAAc,CAAC;QAC/B,MAAM,cAAc,GAAG,MAAM,IAAA,kCAAe,EAAC,WAAW,CAAC,CAAC;QAC1D,IAAI,YAAoB,CAAC;QAEzB,IAAI,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC;YACxC,YAAY,GAAG,cAAc,CAAC,YAAY,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,QAAQ,GAAG,IAAA,SAAM,GAAE,CAAC;YACzB,YAAY,GAAG,IAAA,SAAM,GAAE,CAAC;YACxB,MAAM,IAAA,mCAAgB,EAAC,WAAW,EAAE;gBAClC,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,YAAY;aACb,CAAC,CAAC;QACL,CAAC;QAED,yCAAyC;QACzC,MAAM,UAAU,GAAiB;YAC/B,EAAE,EAAE,YAAY;YAChB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI,EAAE,oBAAY,CAAC,MAAM;YACzB,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,WAAW,EAAE,IAAI,CAAC,iBAAiB;oBACnC,OAAO,EAAE,cAAc;oBACvB,GAAG,EAAE,aAAa;oBAClB,YAAY,EAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;oBAChD,SAAS,EAAE,qBAAqB;iBACjC;gBACD,QAAQ,EAAE;oBACR,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;oBACzD,EAAE,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE;oBAC9B,GAAG,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE;iBAChC;aACF;SACF,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAEnC,wBAAwB;QACxB,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QAEpC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,KAA8B,EAAE,EAAE;YAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAClD,MAAM,KAAK,GAAG,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,IAAI,SAAS,CAAC;YAC9D,qBAAY,CAAC,OAAO,CAAC,uBAAuB,KAAK,KAAK,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,WAAW,SAAS,CAAC,CAAC;QACvG,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,sBAAsB,EAAE,CAAC,KAA8B,EAAE,EAAE;YAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAClD,MAAM,KAAK,GAAG,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,IAAI,SAAS,CAAC;YAC9D,qBAAY,CAAC,IAAI,CAAC,0BAA0B,KAAK,KAAK,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,WAAW,SAAS,CAAC,CAAC;QACvG,CAAC,CAAC,CAAC;QAEH,iCAAiC;QACjC,IAAI,CAAC,MAAM,GAAG,IAAI,8BAAa,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAExC,kBAAkB;QAClB,IAAI,CAAC,WAAW,EAAE,CAAC;QAEnB,IAAI,cAAc,EAAE,CAAC;YACnB,qBAAY,CAAC,GAAG,CAAC,2BAA2B,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,qBAAY,CAAC,GAAG,CAAC,oCAAoC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,KAAK,CAAC,IAAI;QACR,qBAAY,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC/C,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QAC9B,qBAAY,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,IAA6C,EAC7C,iBAAuC,EACvC,OAAgB;QAEhB,MAAM,IAAI,GAAiB;YACzB,EAAE,EAAE,OAAO,IAAI,IAAA,SAAM,GAAE;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI;YACJ,UAAU,EAAE;gBACV,OAAO,EAAE,iBAAiB,IAAI,EAAE;gBAChC,QAAQ,EAAE,EAAE;aACb;SACF,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,oBAAoB,CAClB,UAAkB,EAClB,IAAY,EACZ,UAAkB,EAClB,iBAAuC;QAEvC,MAAM,IAAI,GAAiB;YACzB,EAAE,EAAE,IAAA,SAAM,GAAE;YACZ,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI,EAAE,oBAAY,CAAC,UAAU;YAC7B,UAAU,EAAE;gBACV,OAAO,EAAE,EAAE,GAAG,iBAAiB,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE;gBAC/D,QAAQ,EAAE,EAAE;aACb;YACD,WAAW,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE;SAC9C,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gBAAgB,CAAC,MAAc;QAC7B,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEO,WAAW;QACjB,qBAAY,CAAC,WAAW,CAAC;YACvB,EAAE;YACF,eAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,4CAA4C,CAAC;YAClE,eAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,4CAA4C,CAAC;YAClE,eAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,6CAA6C,CAAC;YACnE,eAAK,CAAC,GAAG,CAAC,sBAAsB,IAAA,yBAAiB,GAAE,EAAE,CAAC;YACtD,eAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACzB,KAAK,eAAK,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,eAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;YAC1D,KAAK,eAAK,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,IAAI,EAAE;YAC9C,KAAK,eAAK,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,IAAI,CAAC,iBAAiB,EAAE;YAC3D,KAAK,eAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,eAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,eAAK,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE;YACxF,KAAK,eAAK,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,eAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,eAAK,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE;YACxF,eAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACzB,EAAE;SACH,CAAC,CAAC;IACL,CAAC;CAEF;AA1KD,0CA0KC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalServer = void 0;
|
|
4
|
+
/* eslint-disable import/prefer-default-export */
|
|
5
|
+
const http_1 = require("http");
|
|
6
|
+
const events_1 = require("events");
|
|
7
|
+
const socket_io_1 = require("socket.io");
|
|
8
|
+
class LocalServer extends events_1.EventEmitter {
|
|
9
|
+
constructor(port = 55000) {
|
|
10
|
+
super();
|
|
11
|
+
this.httpServer = null;
|
|
12
|
+
this.io = null;
|
|
13
|
+
this.router = null;
|
|
14
|
+
this.connectedClients = new Map();
|
|
15
|
+
this.eventDebounceTimers = new Map();
|
|
16
|
+
this.port = port;
|
|
17
|
+
}
|
|
18
|
+
getIO() {
|
|
19
|
+
return this.io;
|
|
20
|
+
}
|
|
21
|
+
setRouter(router) {
|
|
22
|
+
this.router = router;
|
|
23
|
+
}
|
|
24
|
+
start() {
|
|
25
|
+
this.httpServer = (0, http_1.createServer)((_req, res) => {
|
|
26
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
27
|
+
res.end(JSON.stringify({ status: 'ok', simulator: true }));
|
|
28
|
+
});
|
|
29
|
+
this.io = new socket_io_1.Server(this.httpServer, {
|
|
30
|
+
cors: { origin: '*' },
|
|
31
|
+
});
|
|
32
|
+
this.io.on('connection', (socket) => {
|
|
33
|
+
this.setupSocketHandlers(socket);
|
|
34
|
+
});
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
this.httpServer.listen(this.port, () => {
|
|
37
|
+
resolve();
|
|
38
|
+
});
|
|
39
|
+
this.httpServer.on('error', reject);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
setupSocketHandlers(socket) {
|
|
43
|
+
const auth = (socket.handshake.auth || {});
|
|
44
|
+
const twinId = auth.instanceId || auth.moduleName;
|
|
45
|
+
// Generic channel for CLI utility calls (no twin ID needed)
|
|
46
|
+
socket.on('simulator', async (payload, callback) => {
|
|
47
|
+
if (this.router) {
|
|
48
|
+
await this.router.handleMessage(socket, '', payload, callback);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
socket.on('ping', (data) => {
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
socket.emit('pong', { count: (data?.count || 0) + 1 });
|
|
54
|
+
}, 100);
|
|
55
|
+
});
|
|
56
|
+
// App connections provide a twin ID via auth — set up twin-specific handlers
|
|
57
|
+
if (!twinId)
|
|
58
|
+
return;
|
|
59
|
+
if (!this.connectedClients.has(twinId)) {
|
|
60
|
+
this.connectedClients.set(twinId, []);
|
|
61
|
+
}
|
|
62
|
+
this.connectedClients.get(twinId).push(socket);
|
|
63
|
+
this.emitDebounced(twinId);
|
|
64
|
+
socket.join(twinId);
|
|
65
|
+
socket.on('disconnect', () => {
|
|
66
|
+
const sockets = this.connectedClients.get(twinId);
|
|
67
|
+
if (sockets) {
|
|
68
|
+
const idx = sockets.indexOf(socket);
|
|
69
|
+
if (idx !== -1)
|
|
70
|
+
sockets.splice(idx, 1);
|
|
71
|
+
if (sockets.length === 0)
|
|
72
|
+
this.connectedClients.delete(twinId);
|
|
73
|
+
}
|
|
74
|
+
this.emitDebounced(twinId);
|
|
75
|
+
});
|
|
76
|
+
// Hub-client protocol: client emits on its own twin ID channel
|
|
77
|
+
socket.on(twinId, async (payload, callback) => {
|
|
78
|
+
if (this.router) {
|
|
79
|
+
await this.router.handleMessage(socket, twinId, payload, callback);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
socket.on('reconnect', () => {
|
|
83
|
+
socket.join(twinId);
|
|
84
|
+
});
|
|
85
|
+
socket.on('getDeviceTwin', (callback) => {
|
|
86
|
+
if (this.router) {
|
|
87
|
+
const result = this.router.handleLocalMethod('getDeviceInstance', {});
|
|
88
|
+
if (result && typeof result.then === 'function') {
|
|
89
|
+
result.then((r) => callback(r?.twin || {}));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
callback(result?.twin || {});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
stop() {
|
|
98
|
+
return new Promise((resolve) => {
|
|
99
|
+
if (this.io) {
|
|
100
|
+
this.io.disconnectSockets(true);
|
|
101
|
+
this.io.close();
|
|
102
|
+
}
|
|
103
|
+
if (this.httpServer) {
|
|
104
|
+
this.httpServer.close(() => resolve());
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
resolve();
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
getConnectedClients() {
|
|
112
|
+
return Array.from(this.connectedClients.keys());
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Debounce connect/disconnect events per twin ID.
|
|
116
|
+
*
|
|
117
|
+
* Hub-client's getPhyHubSocket() uses a two-phase connection strategy:
|
|
118
|
+
* 1. PROBE — connects with reconnectionAttempts:1 to discover which URL works
|
|
119
|
+
* (tries localhost:55000, phyos:55500, phyhub.eu.omborigrid.net in order)
|
|
120
|
+
* 2. DISCONNECT — kills the probe socket, extracts the winning URL from socket.io.uri
|
|
121
|
+
* 3. RECONNECT — creates a new socket to the same URL with default (production) settings
|
|
122
|
+
*
|
|
123
|
+
* In simulator mode this is redundant (URL is always localhost:55000) but hub-client
|
|
124
|
+
* doesn't know that — it runs the same code path as production. The result is 3 rapid
|
|
125
|
+
* socket events: connect → disconnect → connect, all within milliseconds.
|
|
126
|
+
*
|
|
127
|
+
* Additionally, Vite HMR can cause the browser to re-initialize the app, triggering
|
|
128
|
+
* the entire probe cycle again.
|
|
129
|
+
*
|
|
130
|
+
* We debounce per twin ID so that after the flurry settles (500ms), we emit a single
|
|
131
|
+
* event reflecting the final state — either instanceConnected or instanceDisconnected.
|
|
132
|
+
*/
|
|
133
|
+
emitDebounced(twinId) {
|
|
134
|
+
const existing = this.eventDebounceTimers.get(twinId);
|
|
135
|
+
if (existing)
|
|
136
|
+
clearTimeout(existing);
|
|
137
|
+
const timer = setTimeout(() => {
|
|
138
|
+
this.eventDebounceTimers.delete(twinId);
|
|
139
|
+
const isConnected = this.connectedClients.has(twinId);
|
|
140
|
+
const event = isConnected ? 'instanceConnected' : 'instanceDisconnected';
|
|
141
|
+
this.emit(event, { twinId, activeCount: this.connectedClients.size });
|
|
142
|
+
}, 500);
|
|
143
|
+
this.eventDebounceTimers.set(twinId, timer);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.LocalServer = LocalServer;
|
|
147
|
+
//# sourceMappingURL=local-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-server.js","sourceRoot":"","sources":["../../src/simulator/local-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.simulatorLog = exports.SimulatorLogger = void 0;
|
|
7
|
+
/* eslint-disable import/prefer-default-export */
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
function timestamp() {
|
|
10
|
+
const now = new Date();
|
|
11
|
+
const h = String(now.getHours()).padStart(2, '0');
|
|
12
|
+
const m = String(now.getMinutes()).padStart(2, '0');
|
|
13
|
+
const s = String(now.getSeconds()).padStart(2, '0');
|
|
14
|
+
return chalk_1.default.dim(`[${h}:${m}:${s}]`);
|
|
15
|
+
}
|
|
16
|
+
class SimulatorLogger {
|
|
17
|
+
log(message) {
|
|
18
|
+
console.log(`${timestamp()} ${message}`);
|
|
19
|
+
}
|
|
20
|
+
info(message) {
|
|
21
|
+
this.log(message);
|
|
22
|
+
}
|
|
23
|
+
success(message) {
|
|
24
|
+
this.log(chalk_1.default.green(message));
|
|
25
|
+
}
|
|
26
|
+
warn(message) {
|
|
27
|
+
this.log(chalk_1.default.yellow(message));
|
|
28
|
+
}
|
|
29
|
+
error(message) {
|
|
30
|
+
this.log(chalk_1.default.red(message));
|
|
31
|
+
}
|
|
32
|
+
dim(message) {
|
|
33
|
+
this.log(chalk_1.default.dim(message));
|
|
34
|
+
}
|
|
35
|
+
printBanner(lines) {
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
console.log(line);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.SimulatorLogger = SimulatorLogger;
|
|
42
|
+
exports.simulatorLog = new SimulatorLogger();
|
|
43
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/simulator/logger.ts"],"names":[],"mappings":";;;;;;AAAA,iDAAiD;AACjD,kDAA0B;AAE1B,SAAS,SAAS;IAChB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,eAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACvC,CAAC;AAED,MAAa,eAAe;IAC1B,GAAG,CAAC,OAAe;QACjB,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,EAAE,IAAI,OAAO,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,CAAC,OAAe;QACrB,IAAI,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IACjC,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,IAAI,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,OAAe;QACnB,IAAI,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/B,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,IAAI,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/B,CAAC;IAED,WAAW,CAAC,KAAe;QACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;CACF;AA9BD,0CA8BC;AAEY,QAAA,YAAY,GAAG,IAAI,eAAe,EAAE,CAAC"}
|