miravo 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -30
- package/console-dist/assets/fira-code-cyrillic-ext-wght-normal-DhYMMuQd.woff2 +0 -0
- package/console-dist/assets/fira-code-cyrillic-wght-normal-Y3u8pIsh.woff2 +0 -0
- package/console-dist/assets/fira-code-greek-ext-wght-normal-wWus70Ix.woff2 +0 -0
- package/console-dist/assets/fira-code-greek-wght-normal-B2SviObF.woff2 +0 -0
- package/console-dist/assets/fira-code-latin-ext-wght-normal-Dvfvaomy.woff2 +0 -0
- package/console-dist/assets/fira-code-latin-wght-normal-CHoedHDv.woff2 +0 -0
- package/console-dist/assets/fira-code-symbols2-wght-normal-CE6EOz_n.woff2 +0 -0
- package/console-dist/assets/index-BXtW73cF.css +2 -0
- package/console-dist/assets/index-ChSocZFW.js +119 -0
- package/console-dist/assets/karla-latin-ext-wght-normal-Dq0rrjgs.woff2 +0 -0
- package/console-dist/assets/karla-latin-wght-normal-C3-ma4ov.woff2 +0 -0
- package/console-dist/assets/schibsted-grotesk-latin-ext-wght-normal-hsMS0n0O.woff2 +0 -0
- package/console-dist/assets/schibsted-grotesk-latin-wght-normal-Bb8VGrTG.woff2 +0 -0
- package/console-dist/favicon.svg +20 -0
- package/console-dist/index.html +14 -0
- package/console-dist/miravo-logo.png +0 -0
- package/package.json +10 -7
- package/src/__tests__/integration/executable-methods-runner.ts +52 -45
- package/src/__tests__/integration/executable-methods.integration.ts +609 -47
- package/src/commands/adapter.ts +76 -0
- package/src/commands/cli-test-setup.ts +1 -1
- package/src/commands/fork.ts +14 -0
- package/src/commands/inject.test.ts +1 -1
- package/src/commands/inject.ts +16 -8
- package/src/commands/load.ts +167 -0
- package/src/commands/logs.ts +175 -0
- package/src/commands/models.test.ts +22 -1
- package/src/commands/models.ts +14 -0
- package/src/commands/pause.ts +67 -0
- package/src/commands/ps.ts +116 -0
- package/src/commands/remove.ts +38 -0
- package/src/commands/reset.ts +44 -0
- package/src/commands/rm.ts +97 -0
- package/src/commands/spawn.ts +45 -0
- package/src/commands/speed.ts +40 -0
- package/src/commands/start.test.ts +93 -0
- package/src/commands/start.ts +974 -192
- package/src/commands/status.test.ts +3 -1
- package/src/commands/status.ts +12 -6
- package/src/commands/stop.test.ts +84 -0
- package/src/commands/stop.ts +158 -0
- package/src/commands/templates.test.ts +1 -1
- package/src/commands/templates.ts +13 -0
- package/src/index.ts +49 -7
- package/src/lib/admin-client.test.ts +35 -0
- package/src/lib/admin-client.ts +13 -1
- package/src/lib/admin-server.test.ts +339 -53
- package/src/lib/admin-server.ts +413 -62
- package/src/lib/daemon-registry.test.ts +429 -0
- package/src/lib/daemon-registry.ts +345 -0
- package/src/lib/mqtt-client.ts +1 -1
- package/src/lib/parse.ts +18 -0
- package/src/lib/resolve-simulation.ts +1 -1
- package/src/lib/shutdown.test.ts +16 -4
- package/src/lib/shutdown.ts +3 -3
- package/src/lib/template-category.test.ts +1 -0
- package/src/lib/template-utils.ts +33 -2
- package/src/lib/ws-hub.ts +255 -0
- package/src/logger.test.ts +19 -19
- package/src/logger.ts +34 -6
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
2
|
+
<defs>
|
|
3
|
+
<clipPath id="rounded">
|
|
4
|
+
<rect width="64" height="64" rx="14" ry="14" fill="white"/>
|
|
5
|
+
</clipPath>
|
|
6
|
+
</defs>
|
|
7
|
+
<g clip-path="url(#rounded)">
|
|
8
|
+
<!-- background -->
|
|
9
|
+
<rect width="64" height="64" fill="#0a0a0f"/>
|
|
10
|
+
|
|
11
|
+
<!-- left M half -->
|
|
12
|
+
<polygon points="12,52 12,18 28,32 28,52" fill="#1FA6A6"/>
|
|
13
|
+
|
|
14
|
+
<!-- right M half -->
|
|
15
|
+
<polygon points="52,52 52,18 36,32 36,52" fill="#2F4258"/>
|
|
16
|
+
|
|
17
|
+
<!-- mirror divider -->
|
|
18
|
+
<rect x="31" y="14" width="2" height="40" fill="#5A6B7F"/>
|
|
19
|
+
</g>
|
|
20
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Miravo</title>
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-ChSocZFW.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BXtW73cF.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "miravo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Industrial environment platform with canonical asset models and MQTT + OPC UA protocol adapters",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,19 +27,22 @@
|
|
|
27
27
|
"miravo": "./src/index.ts"
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
|
-
"src/"
|
|
30
|
+
"src/",
|
|
31
|
+
"console-dist/"
|
|
31
32
|
],
|
|
32
33
|
"dependencies": {
|
|
33
34
|
"@clack/prompts": "^1.1.0",
|
|
34
|
-
"@miravo/core": "0.
|
|
35
|
-
"@miravo/mqtt": "0.
|
|
36
|
-
"@miravo/opcua": "0.
|
|
37
|
-
"@miravo/shared": "0.
|
|
35
|
+
"@miravo/core": "0.3.0",
|
|
36
|
+
"@miravo/mqtt": "0.3.0",
|
|
37
|
+
"@miravo/opcua": "0.3.0",
|
|
38
|
+
"@miravo/shared": "0.3.0",
|
|
38
39
|
"commander": "^13.0.0",
|
|
39
40
|
"consola": "^3.4.0",
|
|
40
|
-
"mqtt": "^5.15.0"
|
|
41
|
+
"mqtt": "^5.15.0",
|
|
42
|
+
"zod": "^4.3.6"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
45
|
+
"@miravo/console": "0.0.0",
|
|
43
46
|
"node-opcua-client": "^2.163.1",
|
|
44
47
|
"node-opcua-variant": "^2.163.0",
|
|
45
48
|
"yaml": "^2.8.1"
|
|
@@ -9,6 +9,7 @@ import { MQTTAdapter } from "@miravo/mqtt";
|
|
|
9
9
|
import { OPCUAAdapter } from "@miravo/opcua";
|
|
10
10
|
import type { MiravoTemplate, TwinModel } from "@miravo/shared";
|
|
11
11
|
import { startAdminServer } from "../../lib/admin-server.ts";
|
|
12
|
+
import { buildUNSBasePath, forEachTwin } from "../../lib/template-utils.ts";
|
|
12
13
|
|
|
13
14
|
interface RunnerArgs {
|
|
14
15
|
readonly template: string;
|
|
@@ -18,6 +19,10 @@ interface RunnerArgs {
|
|
|
18
19
|
readonly rbeDeadband: number;
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
interface ReadyHealthPayload {
|
|
23
|
+
readonly status?: unknown;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
function parseArgs(argv: readonly string[]): RunnerArgs {
|
|
22
27
|
const values = new Map<string, string>();
|
|
23
28
|
for (let index = 0; index < argv.length; index += 2) {
|
|
@@ -57,20 +62,6 @@ function resolveSimulationName(template: MiravoTemplate): string {
|
|
|
57
62
|
return namespace.prefix.toLowerCase();
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
function buildUNSBasePath(template: MiravoTemplate, areaName: string, lineName: string): string {
|
|
61
|
-
const segments: string[] = [];
|
|
62
|
-
const namespace = template.namespace;
|
|
63
|
-
if (namespace !== undefined) {
|
|
64
|
-
if ("enterprise" in namespace) {
|
|
65
|
-
segments.push(namespace.enterprise, namespace.site);
|
|
66
|
-
} else {
|
|
67
|
-
segments.push(namespace.prefix);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
segments.push(areaName, lineName);
|
|
71
|
-
return segments.join("/");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
65
|
async function loadModels(
|
|
75
66
|
template: MiravoTemplate,
|
|
76
67
|
modelsDir: string,
|
|
@@ -83,13 +74,9 @@ async function loadModels(
|
|
|
83
74
|
}
|
|
84
75
|
|
|
85
76
|
const referencedModelNames = new Set<string>();
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
referencedModelNames.add(twin.model);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
77
|
+
forEachTwin(template, (_area, _line, twin) => {
|
|
78
|
+
referencedModelNames.add(twin.model);
|
|
79
|
+
});
|
|
93
80
|
|
|
94
81
|
for (const modelName of referencedModelNames) {
|
|
95
82
|
if (models.has(modelName)) {
|
|
@@ -103,6 +90,26 @@ async function loadModels(
|
|
|
103
90
|
return models;
|
|
104
91
|
}
|
|
105
92
|
|
|
93
|
+
async function waitForAdminReady(adminUrl: string, timeoutMs: number): Promise<void> {
|
|
94
|
+
const deadline = Date.now() + timeoutMs;
|
|
95
|
+
while (Date.now() < deadline) {
|
|
96
|
+
try {
|
|
97
|
+
const response = await fetch(new URL("/readyz", `${adminUrl}/`));
|
|
98
|
+
if (response.status === 200) {
|
|
99
|
+
const payload = (await response.json()) as ReadyHealthPayload;
|
|
100
|
+
if (payload.status === "ok") {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
// Admin server can still be binding or the engine may not be running yet.
|
|
106
|
+
}
|
|
107
|
+
await Bun.sleep(25);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
throw new Error(`Smoke runtime did not report ready within ${timeoutMs}ms.`);
|
|
111
|
+
}
|
|
112
|
+
|
|
106
113
|
async function main(): Promise<void> {
|
|
107
114
|
const args = parseArgs(process.argv.slice(2));
|
|
108
115
|
const template = await loadTemplate(args.template);
|
|
@@ -127,17 +134,13 @@ async function main(): Promise<void> {
|
|
|
127
134
|
engine.runtime.registerModel(model);
|
|
128
135
|
}
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
137
|
+
forEachTwin(template, (area, line, twin) => {
|
|
138
|
+
engine.runtime.spawn(twin.model, twin.count, {
|
|
139
|
+
variation: twin.variation,
|
|
140
|
+
unsBasePath: buildUNSBasePath(template, area.name, line.name),
|
|
141
|
+
...(twin.parameters !== undefined ? { parameterOverrides: twin.parameters } : {}),
|
|
142
|
+
});
|
|
143
|
+
});
|
|
141
144
|
|
|
142
145
|
const mqttAdapter = new MQTTAdapter(engine.bus);
|
|
143
146
|
await mqttAdapter.start({
|
|
@@ -184,19 +187,23 @@ async function main(): Promise<void> {
|
|
|
184
187
|
|
|
185
188
|
const adminServer = startAdminServer({
|
|
186
189
|
engine,
|
|
187
|
-
host: "127.0.0.1",
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
190
|
+
network: { host: "127.0.0.1", port: 0, tickIntervalMs },
|
|
191
|
+
protocols: {
|
|
192
|
+
getLastTickProgressAt: () => lastTickProgressAt,
|
|
193
|
+
},
|
|
194
|
+
ws: {
|
|
195
|
+
bus: engine.bus,
|
|
196
|
+
getEngineState: () => engine.getMetrics().core.state,
|
|
197
|
+
getTickInfo: () => ({
|
|
198
|
+
tick: engine.getMetrics().core.tickCount,
|
|
199
|
+
simulationTime: engine.getMetrics().core.simulationTimeSeconds,
|
|
200
|
+
instanceCount: engine.getMetrics().core.instanceCount,
|
|
201
|
+
}),
|
|
202
|
+
},
|
|
195
203
|
});
|
|
196
204
|
|
|
197
|
-
engine.start();
|
|
198
|
-
|
|
199
|
-
await Bun.sleep(Math.max(250, tickIntervalMs * 3));
|
|
205
|
+
await engine.start();
|
|
206
|
+
await waitForAdminReady(adminServer.endpoint, Math.max(5_000, tickIntervalMs * 20));
|
|
200
207
|
|
|
201
208
|
const [instanceId, instance] = [...engine.runtime.getInstances()][0] ?? [];
|
|
202
209
|
if (!instanceId || !instance) {
|
|
@@ -237,10 +244,10 @@ async function main(): Promise<void> {
|
|
|
237
244
|
process.exit(0);
|
|
238
245
|
};
|
|
239
246
|
|
|
240
|
-
process.
|
|
247
|
+
process.once("SIGINT", () => {
|
|
241
248
|
void shutdown();
|
|
242
249
|
});
|
|
243
|
-
process.
|
|
250
|
+
process.once("SIGTERM", () => {
|
|
244
251
|
void shutdown();
|
|
245
252
|
});
|
|
246
253
|
|