miravo 0.2.3 → 0.4.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 +14 -150
- 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-C3XMYq26.css +2 -0
- package/console-dist/assets/index-hUJSpKtB.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 +37 -0
- package/console-dist/index.html +14 -0
- package/console-dist/miravo-logo.png +0 -0
- package/package.json +12 -9
- package/src/__tests__/integration/executable-methods-runner.ts +53 -45
- package/src/__tests__/integration/executable-methods.integration.ts +648 -80
- 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 +0 -24
- 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 +23 -2
- package/src/commands/models.ts +20 -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 +1043 -193
- package/src/commands/status.test.ts +0 -24
- 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 -14
- 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,37 @@
|
|
|
1
|
+
<svg width="64" height="64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<!-- Subtle mirror gradient -->
|
|
4
|
+
<linearGradient id="mirror" x1="0" y1="0" x2="0" y2="1">
|
|
5
|
+
<stop offset="0%" stop-color="#f6d28a"/>
|
|
6
|
+
<stop offset="50%" stop-color="#e5a218"/>
|
|
7
|
+
<stop offset="100%" stop-color="#c58514"/>
|
|
8
|
+
</linearGradient>
|
|
9
|
+
</defs>
|
|
10
|
+
|
|
11
|
+
<!-- Left M shape -->
|
|
12
|
+
<path d="M10 12 L30 12 L21 28 L10 28 Z"
|
|
13
|
+
fill="#e5a218"
|
|
14
|
+
stroke="#e5a218"
|
|
15
|
+
stroke-width="1.2"
|
|
16
|
+
stroke-linejoin="round"/>
|
|
17
|
+
<path d="M10 28 L21 28 L21 52 L10 48 Z"
|
|
18
|
+
fill="#e5a218"
|
|
19
|
+
stroke="#e5a218"
|
|
20
|
+
stroke-width="1.2"
|
|
21
|
+
stroke-linejoin="round"/>
|
|
22
|
+
|
|
23
|
+
<!-- Right mirrored M -->
|
|
24
|
+
<path d="M54 12 L34 12 L43 28 L54 28 Z"
|
|
25
|
+
fill="#e5a218"
|
|
26
|
+
stroke="#e5a218"
|
|
27
|
+
stroke-width="1.2"
|
|
28
|
+
stroke-linejoin="round"/>
|
|
29
|
+
<path d="M54 28 L43 28 L43 52 L54 48 Z"
|
|
30
|
+
fill="#e5a218"
|
|
31
|
+
stroke="#e5a218"
|
|
32
|
+
stroke-width="1.2"
|
|
33
|
+
stroke-linejoin="round"/>
|
|
34
|
+
|
|
35
|
+
<!-- Mirror axis (thin + subtle reflective feel) -->
|
|
36
|
+
<rect x="31.5" y="10" width="1" height="44" rx="0.5" fill="url(#mirror)" opacity="0.9"/>
|
|
37
|
+
</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-hUJSpKtB.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-C3XMYq26.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.4.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,21 +27,24 @@
|
|
|
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.4.0",
|
|
36
|
+
"@miravo/mqtt": "0.4.0",
|
|
37
|
+
"@miravo/opcua": "0.4.0",
|
|
38
|
+
"@miravo/shared": "0.4.0",
|
|
38
39
|
"commander": "^13.0.0",
|
|
39
40
|
"consola": "^3.4.0",
|
|
40
|
-
"mqtt": "^5.15.0"
|
|
41
|
+
"mqtt": "^5.15.0",
|
|
42
|
+
"yaml": "^2.8.1",
|
|
43
|
+
"zod": "^4.3.6"
|
|
41
44
|
},
|
|
42
45
|
"devDependencies": {
|
|
46
|
+
"@miravo/console": "0.0.0",
|
|
43
47
|
"node-opcua-client": "^2.163.1",
|
|
44
|
-
"node-opcua-variant": "^2.163.0"
|
|
45
|
-
"yaml": "^2.8.1"
|
|
48
|
+
"node-opcua-variant": "^2.163.0"
|
|
46
49
|
}
|
|
47
50
|
}
|
|
@@ -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,14 @@ 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
|
+
...(twin.starting_hours !== undefined ? { startingHours: twin.starting_hours } : {}),
|
|
143
|
+
});
|
|
144
|
+
});
|
|
141
145
|
|
|
142
146
|
const mqttAdapter = new MQTTAdapter(engine.bus);
|
|
143
147
|
await mqttAdapter.start({
|
|
@@ -184,19 +188,23 @@ async function main(): Promise<void> {
|
|
|
184
188
|
|
|
185
189
|
const adminServer = startAdminServer({
|
|
186
190
|
engine,
|
|
187
|
-
host: "127.0.0.1",
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
network: { host: "127.0.0.1", port: 0, tickIntervalMs },
|
|
192
|
+
protocols: {
|
|
193
|
+
getLastTickProgressAt: () => lastTickProgressAt,
|
|
194
|
+
},
|
|
195
|
+
ws: {
|
|
196
|
+
bus: engine.bus,
|
|
197
|
+
getEngineState: () => engine.getMetrics().core.state,
|
|
198
|
+
getTickInfo: () => ({
|
|
199
|
+
tick: engine.getMetrics().core.tickCount,
|
|
200
|
+
simulationTime: engine.getMetrics().core.simulationTimeSeconds,
|
|
201
|
+
instanceCount: engine.getMetrics().core.instanceCount,
|
|
202
|
+
}),
|
|
203
|
+
},
|
|
195
204
|
});
|
|
196
205
|
|
|
197
|
-
engine.start();
|
|
198
|
-
|
|
199
|
-
await Bun.sleep(Math.max(250, tickIntervalMs * 3));
|
|
206
|
+
await engine.start();
|
|
207
|
+
await waitForAdminReady(adminServer.endpoint, Math.max(5_000, tickIntervalMs * 20));
|
|
200
208
|
|
|
201
209
|
const [instanceId, instance] = [...engine.runtime.getInstances()][0] ?? [];
|
|
202
210
|
if (!instanceId || !instance) {
|
|
@@ -237,10 +245,10 @@ async function main(): Promise<void> {
|
|
|
237
245
|
process.exit(0);
|
|
238
246
|
};
|
|
239
247
|
|
|
240
|
-
process.
|
|
248
|
+
process.once("SIGINT", () => {
|
|
241
249
|
void shutdown();
|
|
242
250
|
});
|
|
243
|
-
process.
|
|
251
|
+
process.once("SIGTERM", () => {
|
|
244
252
|
void shutdown();
|
|
245
253
|
});
|
|
246
254
|
|