miravo 0.6.0 → 0.6.2
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/LICENSE +199 -0
- package/README.md +29 -59
- package/bin/miravo.js +84 -0
- package/package.json +21 -37
- package/console-dist/assets/activity-DsWeRq4h.js +0 -1
- package/console-dist/assets/api-4AqKvsm9.js +0 -1
- package/console-dist/assets/arrow-right-Ea_UhTNi.js +0 -1
- package/console-dist/assets/arrow-up-down-DFSjzzl2.js +0 -1
- package/console-dist/assets/blueprint-card-classes-DBFCDy0M.js +0 -1
- package/console-dist/assets/box-C1WaoOMF.js +0 -1
- package/console-dist/assets/card-BjeP32PN.js +0 -1
- package/console-dist/assets/catalog-BJzyzbLR.js +0 -1
- package/console-dist/assets/catalog-DKjEm-c4.js +0 -1
- package/console-dist/assets/catalog-template-icons-BrUeHaCY.js +0 -1
- package/console-dist/assets/checkbox-DZlJXTE4.js +0 -1
- package/console-dist/assets/chunk-OE4NN4TA-CEjpfhS6.js +0 -3
- package/console-dist/assets/combobox-BGnuCYxO.js +0 -1
- package/console-dist/assets/control-CPhvwx9o.js +0 -1
- package/console-dist/assets/control-Cg2up9NA.js +0 -1
- package/console-dist/assets/copy-button-WkbNkMcd.js +0 -1
- package/console-dist/assets/createLucideIcon-Bwxw859K.js +0 -1
- package/console-dist/assets/deploy-tab-JeWDwPoZ.js +0 -1
- package/console-dist/assets/dist-BhbDML0a.js +0 -1
- package/console-dist/assets/dist-Cy8XTNFT.js +0 -2
- package/console-dist/assets/download-DQ6NH16U.js +0 -1
- package/console-dist/assets/engine-store-CrLnz692.js +0 -1
- package/console-dist/assets/events-CXm29Ok4.js +0 -1
- package/console-dist/assets/events-D01IxhnP.js +0 -1
- 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/fleet-CfxbKXt9.js +0 -1
- package/console-dist/assets/fleet-lotMGVqe.js +0 -1
- package/console-dist/assets/format-kXTonpDJ.js +0 -1
- package/console-dist/assets/gauge-Ci8zNZlk.js +0 -1
- package/console-dist/assets/get-error-message-3Q5Q-2G5.js +0 -1
- package/console-dist/assets/index-CVPKSDvJ.js +0 -12
- package/console-dist/assets/index-DJkgDeun.css +0 -2
- package/console-dist/assets/input-BWEQFptx.js +0 -1
- package/console-dist/assets/instance-detail-C5IVwuh3.js +0 -1
- 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/label-uephX5ZG.js +0 -1
- package/console-dist/assets/layers-CUsLQ1fq.js +0 -1
- package/console-dist/assets/login-C3RJli0m.js +0 -126
- package/console-dist/assets/models-B_UioWQ5.js +0 -1
- package/console-dist/assets/models-_IPlPiZ7.js +0 -1
- package/console-dist/assets/not-found-B8gAm08S.js +0 -1
- package/console-dist/assets/platform-targets-R3T-4eJ7.js +0 -1
- package/console-dist/assets/platforms-BWh0BQ3Y.js +0 -1
- package/console-dist/assets/platforms-DStNbtVc.js +0 -1
- package/console-dist/assets/proxy-CsnYqnPp.js +0 -9
- package/console-dist/assets/query-keys-BvtE7BMs.js +0 -1
- package/console-dist/assets/radio-CoHegOv5.js +0 -1
- package/console-dist/assets/rolldown-runtime-COnpUsM8.js +0 -1
- 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/assets/select-CeO4vP0G.js +0 -1
- package/console-dist/assets/select-display-items-CIzY8953.js +0 -1
- package/console-dist/assets/server-D9Kfn8fQ.js +0 -1
- package/console-dist/assets/shallow-6lGjQhtJ.js +0 -1
- package/console-dist/assets/tags-CiXOIHtY.js +0 -4
- package/console-dist/assets/tags-FQCXIxrP.js +0 -1
- package/console-dist/assets/tooltip-HGLF366Q.js +0 -1
- package/console-dist/assets/transition-nav-link-Df4Rk6vV.js +0 -1
- package/console-dist/assets/transition-types-CPlGtM8w.js +0 -1
- package/console-dist/assets/triangle-alert-adNk_sea.js +0 -1
- package/console-dist/assets/use-platform-stream-DGbK1k2L.js +0 -2
- package/console-dist/assets/use-transition-navigate-CCpcWXB0.js +0 -2
- package/console-dist/assets/validation-Blvud9_O.js +0 -1
- package/console-dist/assets/vendor-charts-CAZdaPlq.js +0 -60
- package/console-dist/assets/vendor-table-wd8JuMmT.js +0 -4
- package/console-dist/assets/vendor-ui-ByahnaY0.js +0 -1
- package/console-dist/favicon.svg +0 -37
- package/console-dist/index.html +0 -82
- package/console-dist/miravo-logo.png +0 -0
- package/src/__tests__/integration/executable-methods-runner.ts +0 -262
- package/src/__tests__/integration/executable-methods.integration.ts +0 -2348
- package/src/__tests__/integration/opcua-concurrent-clients.integration.ts +0 -314
- package/src/commands/adapter.ts +0 -249
- package/src/commands/apply.test.ts +0 -62
- package/src/commands/apply.ts +0 -216
- package/src/commands/cleanup.test.ts +0 -34
- package/src/commands/cleanup.ts +0 -201
- package/src/commands/cli-test-setup.ts +0 -91
- package/src/commands/export.test.ts +0 -51
- package/src/commands/export.ts +0 -404
- package/src/commands/fork.ts +0 -219
- package/src/commands/inject.test.ts +0 -57
- package/src/commands/inject.ts +0 -212
- package/src/commands/load.ts +0 -167
- package/src/commands/logs.ts +0 -173
- package/src/commands/models.test.ts +0 -162
- package/src/commands/models.ts +0 -338
- package/src/commands/pause.ts +0 -67
- package/src/commands/platforms.test.ts +0 -186
- package/src/commands/platforms.ts +0 -371
- package/src/commands/ps.ts +0 -116
- package/src/commands/remove.ts +0 -38
- package/src/commands/reset.ts +0 -44
- package/src/commands/rm.ts +0 -97
- package/src/commands/spawn.ts +0 -45
- package/src/commands/speed.ts +0 -40
- package/src/commands/start/adapter-config.ts +0 -157
- package/src/commands/start/catalog-le-dt.test.ts +0 -50
- package/src/commands/start/catalog-le-dt.ts +0 -187
- package/src/commands/start/catalog-model-schema.ts +0 -89
- package/src/commands/start/catalog-template-detail.ts +0 -54
- package/src/commands/start/daemon.ts +0 -255
- package/src/commands/start/display.ts +0 -150
- package/src/commands/start/engine-setup.ts +0 -247
- package/src/commands/start/events.ts +0 -126
- package/src/commands/start/index.ts +0 -897
- package/src/commands/start/protocol-plans.ts +0 -239
- package/src/commands/start/template-loader.ts +0 -197
- package/src/commands/start/template-resolver.ts +0 -185
- package/src/commands/start/types.ts +0 -73
- package/src/commands/start.test.ts +0 -480
- package/src/commands/status.test.ts +0 -20
- package/src/commands/status.ts +0 -470
- package/src/commands/stop.test.ts +0 -84
- package/src/commands/stop.ts +0 -169
- package/src/commands/templates.test.ts +0 -58
- package/src/commands/templates.ts +0 -277
- package/src/index.ts +0 -166
- package/src/lib/admin-auth.ts +0 -157
- package/src/lib/admin-client.test.ts +0 -51
- package/src/lib/admin-client.ts +0 -326
- package/src/lib/admin-platform-routes.ts +0 -1017
- package/src/lib/admin-platform-stream.test.ts +0 -434
- package/src/lib/admin-platform-stream.ts +0 -788
- package/src/lib/admin-responses.ts +0 -211
- package/src/lib/admin-server.test.ts +0 -781
- package/src/lib/admin-server.ts +0 -797
- package/src/lib/auth-store.ts +0 -87
- package/src/lib/daemon-registry.test.ts +0 -429
- package/src/lib/daemon-registry.ts +0 -343
- package/src/lib/le/le-apply.test.ts +0 -591
- package/src/lib/le/le-apply.ts +0 -832
- package/src/lib/le/le-auth.ts +0 -89
- package/src/lib/le/le-cleanup.test.ts +0 -168
- package/src/lib/le/le-cleanup.ts +0 -399
- package/src/lib/le/le-cli-helpers.ts +0 -77
- package/src/lib/le/le-client.ts +0 -184
- package/src/lib/le/le-concurrency.test.ts +0 -157
- package/src/lib/le/le-concurrency.ts +0 -78
- package/src/lib/le/le-connection-test.ts +0 -42
- package/src/lib/le/le-devicehub-properties.test.ts +0 -53
- package/src/lib/le/le-devicehub-properties.ts +0 -33
- package/src/lib/le/le-devicehub.ts +0 -213
- package/src/lib/le/le-digital-twins.ts +0 -285
- package/src/lib/le/le-drivers.ts +0 -23
- package/src/lib/le/le-endpoint.ts +0 -40
- package/src/lib/le/le-environments.test.ts +0 -73
- package/src/lib/le/le-environments.ts +0 -156
- package/src/lib/le/le-markers.ts +0 -87
- package/src/lib/le/le-plan.test.ts +0 -26
- package/src/lib/le/le-plan.ts +0 -109
- package/src/lib/le/le-platform-operations.test.ts +0 -198
- package/src/lib/le/le-platform-operations.ts +0 -340
- package/src/lib/le/le-static-model-schema.ts +0 -78
- package/src/lib/le/le-static-planner.test.ts +0 -255
- package/src/lib/le/le-static-planner.ts +0 -442
- package/src/lib/le/le-template-exporter.test.ts +0 -527
- package/src/lib/le/le-template-exporter.ts +0 -459
- package/src/lib/le/le-value-types.ts +0 -23
- package/src/lib/mqtt-client.ts +0 -115
- package/src/lib/parse.test.ts +0 -123
- package/src/lib/parse.ts +0 -87
- package/src/lib/resolve-simulation.test.ts +0 -118
- package/src/lib/resolve-simulation.ts +0 -181
- package/src/lib/session-store.ts +0 -110
- package/src/lib/shutdown.test.ts +0 -562
- package/src/lib/shutdown.ts +0 -621
- package/src/lib/template-category.test.ts +0 -71
- package/src/lib/template-category.ts +0 -72
- package/src/lib/template-locator.ts +0 -55
- package/src/lib/template-utils.ts +0 -45
- package/src/lib/ws-hub.ts +0 -281
- package/src/logger.test.ts +0 -172
- package/src/logger.ts +0 -402
- package/src/output.test.ts +0 -64
- package/src/output.ts +0 -160
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Litmus Edge DeviceHub GraphQL operations.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { LEClient } from "./le-client.ts";
|
|
6
|
-
|
|
7
|
-
// --- Types ---
|
|
8
|
-
|
|
9
|
-
/** InputItem for GraphQL mutations (Key/Value). */
|
|
10
|
-
export interface LEItem {
|
|
11
|
-
readonly Key: string;
|
|
12
|
-
readonly Value: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** Item returned from GraphQL queries (Name/Value). */
|
|
16
|
-
export interface LEItemOutput {
|
|
17
|
-
readonly Name: string;
|
|
18
|
-
readonly Value: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface LEDriver {
|
|
22
|
-
readonly ID: string;
|
|
23
|
-
readonly Name: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface LEDriverGroup {
|
|
27
|
-
readonly ID: string;
|
|
28
|
-
readonly Name: string;
|
|
29
|
-
readonly Drivers: readonly LEDriver[] | null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface LEDevice {
|
|
33
|
-
readonly ID: string;
|
|
34
|
-
readonly Name: string;
|
|
35
|
-
readonly DriverID: string;
|
|
36
|
-
readonly Properties: readonly LEItemOutput[];
|
|
37
|
-
readonly MetaData: readonly LEItemOutput[];
|
|
38
|
-
readonly Description: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
interface LERegister {
|
|
42
|
-
readonly ID: string;
|
|
43
|
-
readonly DeviceID: string;
|
|
44
|
-
readonly Name: string;
|
|
45
|
-
readonly TagName: string;
|
|
46
|
-
readonly ValueType: string;
|
|
47
|
-
readonly Properties: readonly LEItemOutput[];
|
|
48
|
-
readonly MetaData: readonly LEItemOutput[];
|
|
49
|
-
readonly Description: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// --- Queries ---
|
|
53
|
-
|
|
54
|
-
const LIST_DRIVER_GROUPS = `query {
|
|
55
|
-
ListDriverGroups(input: {}) {
|
|
56
|
-
ID Name
|
|
57
|
-
Drivers { ID Name }
|
|
58
|
-
}
|
|
59
|
-
}`;
|
|
60
|
-
|
|
61
|
-
const LIST_DEVICES = `query {
|
|
62
|
-
ListDevices { ID Name DriverID Properties { Name Value } MetaData { Name Value } Description }
|
|
63
|
-
}`;
|
|
64
|
-
|
|
65
|
-
const LIST_REGISTERS = `query ListRegisters($deviceId: UUID!) {
|
|
66
|
-
ListRegisters(input: { DeviceID: $deviceId, Limit: 1000 }) {
|
|
67
|
-
Registers { ID DeviceID Name TagName ValueType Properties { Name Value } MetaData { Name Value } Description }
|
|
68
|
-
}
|
|
69
|
-
}`;
|
|
70
|
-
|
|
71
|
-
// --- Mutations ---
|
|
72
|
-
|
|
73
|
-
const CREATE_DEVICE = `mutation CreateDevice($input: CreateDeviceRequest!) {
|
|
74
|
-
CreateDevice(input: $input) { ID Name DriverID Properties { Name Value } MetaData { Name Value } Description }
|
|
75
|
-
}`;
|
|
76
|
-
|
|
77
|
-
const UPDATE_DEVICE = `mutation UpdateDevice($input: UpdateDeviceRequest!) {
|
|
78
|
-
UpdateDevice(input: $input) { ID Name DriverID Properties { Name Value } MetaData { Name Value } Description }
|
|
79
|
-
}`;
|
|
80
|
-
|
|
81
|
-
const DELETE_DEVICES = `mutation DeleteDevices($ids: [UUID!]!) {
|
|
82
|
-
DeleteDevices(input: { IDs: $ids })
|
|
83
|
-
}`;
|
|
84
|
-
|
|
85
|
-
const CREATE_REGISTERS = `mutation CreateRegisters($input: CreateRegistersRequest!) {
|
|
86
|
-
CreateRegisters(input: $input) { ID DeviceID Name TagName ValueType Properties { Name Value } MetaData { Name Value } Description }
|
|
87
|
-
}`;
|
|
88
|
-
|
|
89
|
-
const DELETE_REGISTERS = `mutation DeleteRegisters($ids: [UUID!]!) {
|
|
90
|
-
DeleteRegisters(input: { IDs: $ids })
|
|
91
|
-
}`;
|
|
92
|
-
|
|
93
|
-
const DELETE_DEVICE_REGISTERS = `mutation DeleteDeviceRegisters($deviceId: UUID!) {
|
|
94
|
-
DeleteDeviceRegisters(input: { DevID: $deviceId })
|
|
95
|
-
}`;
|
|
96
|
-
|
|
97
|
-
// --- API ---
|
|
98
|
-
|
|
99
|
-
export class LEDeviceHub {
|
|
100
|
-
constructor(private readonly client: LEClient) {}
|
|
101
|
-
|
|
102
|
-
async listDriverGroups(): Promise<readonly LEDriverGroup[]> {
|
|
103
|
-
const data = await this.client.gql<{ ListDriverGroups: LEDriverGroup[] }>(
|
|
104
|
-
"/devicehub/v2",
|
|
105
|
-
LIST_DRIVER_GROUPS,
|
|
106
|
-
);
|
|
107
|
-
return data.ListDriverGroups;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async resolveDriverByName(name: string): Promise<LEDriver> {
|
|
111
|
-
const groups = await this.listDriverGroups();
|
|
112
|
-
for (const group of groups) {
|
|
113
|
-
if (!group.Drivers) continue;
|
|
114
|
-
for (const driver of group.Drivers) {
|
|
115
|
-
if (driver.Name === name) return driver;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
throw new Error(
|
|
119
|
-
`LE driver "${name}" not found. Available drivers: ${groups.flatMap((g) => g.Drivers?.map((d) => d.Name) ?? []).join(", ")}`,
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async listDevices(): Promise<readonly LEDevice[]> {
|
|
124
|
-
const data = await this.client.gql<{ ListDevices: LEDevice[] | null }>(
|
|
125
|
-
"/devicehub/v2",
|
|
126
|
-
LIST_DEVICES,
|
|
127
|
-
);
|
|
128
|
-
return data.ListDevices ?? [];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async listRegisters(deviceId: string): Promise<readonly LERegister[]> {
|
|
132
|
-
const data = await this.client.gql<{
|
|
133
|
-
ListRegisters: { Registers: LERegister[] } | null;
|
|
134
|
-
}>("/devicehub/v2", LIST_REGISTERS, { deviceId });
|
|
135
|
-
return data.ListRegisters?.Registers ?? [];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async createDevice(input: {
|
|
139
|
-
DriverID: string;
|
|
140
|
-
Name: string;
|
|
141
|
-
Properties: readonly LEItem[];
|
|
142
|
-
MetaData?: readonly LEItem[];
|
|
143
|
-
Description?: string;
|
|
144
|
-
}): Promise<LEDevice> {
|
|
145
|
-
const data = await this.client.gql<{ CreateDevice: LEDevice }>("/devicehub/v2", CREATE_DEVICE, {
|
|
146
|
-
input: {
|
|
147
|
-
...input,
|
|
148
|
-
Description: input.Description ?? "",
|
|
149
|
-
AliasTopics: true,
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
return data.CreateDevice;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async updateDevice(input: {
|
|
156
|
-
ID: string;
|
|
157
|
-
Name: string;
|
|
158
|
-
Properties: readonly LEItem[];
|
|
159
|
-
MetaData?: readonly LEItem[];
|
|
160
|
-
Description?: string;
|
|
161
|
-
}): Promise<LEDevice> {
|
|
162
|
-
const data = await this.client.gql<{ UpdateDevice: LEDevice }>("/devicehub/v2", UPDATE_DEVICE, {
|
|
163
|
-
input: {
|
|
164
|
-
...input,
|
|
165
|
-
Description: input.Description ?? "",
|
|
166
|
-
AliasTopics: true,
|
|
167
|
-
},
|
|
168
|
-
});
|
|
169
|
-
return data.UpdateDevice;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async deleteDevices(ids: readonly string[]): Promise<void> {
|
|
173
|
-
if (ids.length === 0) return;
|
|
174
|
-
await this.client.gql("/devicehub/v2", DELETE_DEVICES, { ids });
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
async createRegisters(
|
|
178
|
-
registers: readonly {
|
|
179
|
-
DeviceID: string;
|
|
180
|
-
Name: string;
|
|
181
|
-
TagName: string;
|
|
182
|
-
ValueType: string;
|
|
183
|
-
Properties: readonly LEItem[];
|
|
184
|
-
MetaData?: readonly LEItem[];
|
|
185
|
-
Description?: string;
|
|
186
|
-
}[],
|
|
187
|
-
): Promise<readonly LERegister[]> {
|
|
188
|
-
if (registers.length === 0) return [];
|
|
189
|
-
const data = await this.client.gql<{ CreateRegisters: LERegister[] }>(
|
|
190
|
-
"/devicehub/v2",
|
|
191
|
-
CREATE_REGISTERS,
|
|
192
|
-
{
|
|
193
|
-
input: {
|
|
194
|
-
Registers: registers.map((r) => ({
|
|
195
|
-
...r,
|
|
196
|
-
Description: r.Description ?? "",
|
|
197
|
-
PublishCoV: false,
|
|
198
|
-
})),
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
);
|
|
202
|
-
return data.CreateRegisters;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async deleteRegisters(ids: readonly string[]): Promise<void> {
|
|
206
|
-
if (ids.length === 0) return;
|
|
207
|
-
await this.client.gql("/devicehub/v2", DELETE_REGISTERS, { ids });
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
async deleteDeviceRegisters(deviceId: string): Promise<void> {
|
|
211
|
-
await this.client.gql("/devicehub/v2", DELETE_DEVICE_REGISTERS, { deviceId });
|
|
212
|
-
}
|
|
213
|
-
}
|
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Litmus Edge Digital Twins GraphQL operations.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { LEClient } from "./le-client.ts";
|
|
6
|
-
|
|
7
|
-
// --- Types ---
|
|
8
|
-
|
|
9
|
-
export interface LEModel {
|
|
10
|
-
readonly ID: string;
|
|
11
|
-
readonly Name: string;
|
|
12
|
-
readonly Description: string;
|
|
13
|
-
readonly Version: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface LEInstance {
|
|
17
|
-
readonly ID: string;
|
|
18
|
-
readonly ModelID: string;
|
|
19
|
-
readonly Name: string;
|
|
20
|
-
readonly Topic: string;
|
|
21
|
-
readonly Interval: number;
|
|
22
|
-
readonly FlatHierarchy: boolean | null;
|
|
23
|
-
readonly Version: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface LEStaticAttribute {
|
|
27
|
-
readonly ID: string;
|
|
28
|
-
readonly ModelID: string | null;
|
|
29
|
-
readonly InstanceID: string | null;
|
|
30
|
-
readonly Key: string;
|
|
31
|
-
readonly Value: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface LEParameter {
|
|
35
|
-
readonly ID: string;
|
|
36
|
-
readonly OwnerID: string;
|
|
37
|
-
readonly Name: string;
|
|
38
|
-
readonly Value: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// --- Queries ---
|
|
42
|
-
|
|
43
|
-
const LIST_MODELS = `query { ListModels { ID Name Description Version } }`;
|
|
44
|
-
|
|
45
|
-
const LIST_INSTANCES = `query ListInstances($input: ListInstancesRequest) {
|
|
46
|
-
ListInstances(input: $input) { ID ModelID Name Topic Interval FlatHierarchy Version }
|
|
47
|
-
}`;
|
|
48
|
-
|
|
49
|
-
const LIST_STATIC_ATTRIBUTES = `query ListStaticAttributes($modelId: UUID, $instanceId: UUID) {
|
|
50
|
-
ListStaticAttributes(input: { ModelID: $modelId, InstanceID: $instanceId }) {
|
|
51
|
-
ID ModelID InstanceID Key Value
|
|
52
|
-
}
|
|
53
|
-
}`;
|
|
54
|
-
|
|
55
|
-
const LIST_PARAMETERS = `query ListParameters($ownerId: UUID) {
|
|
56
|
-
ListParameters(input: { OwnerID: $ownerId }) { ID OwnerID Name Value }
|
|
57
|
-
}`;
|
|
58
|
-
|
|
59
|
-
// --- Mutations ---
|
|
60
|
-
|
|
61
|
-
const IMPORT_MODEL = `mutation ImportModel($input: Any!) {
|
|
62
|
-
ImportModel(input: $input)
|
|
63
|
-
}`;
|
|
64
|
-
|
|
65
|
-
const DELETE_MODEL = `mutation DeleteModel($id: UUID) {
|
|
66
|
-
DeleteModel(input: { ID: $id })
|
|
67
|
-
}`;
|
|
68
|
-
|
|
69
|
-
const CREATE_INSTANCE = `mutation CreateInstance($input: CreateInstanceRequest!) {
|
|
70
|
-
CreateInstance(input: $input) { ID ModelID Name Topic Interval FlatHierarchy Version }
|
|
71
|
-
}`;
|
|
72
|
-
|
|
73
|
-
const UPDATE_INSTANCE = `mutation UpdateInstance($input: UpdateInstanceRequest!) {
|
|
74
|
-
UpdateInstance(input: $input) { ID ModelID Name Topic Interval FlatHierarchy Version }
|
|
75
|
-
}`;
|
|
76
|
-
|
|
77
|
-
const DELETE_INSTANCE = `mutation DeleteInstance($id: UUID) {
|
|
78
|
-
DeleteInstance(input: { ID: $id })
|
|
79
|
-
}`;
|
|
80
|
-
|
|
81
|
-
const TRANSACTIONAL_INSTANCE_UPDATE = `mutation TransactionalInstanceUpdate($input: TransactionalInstanceUpdateRequest!) {
|
|
82
|
-
TransactionalInstanceUpdate(input: $input)
|
|
83
|
-
}`;
|
|
84
|
-
|
|
85
|
-
const SET_INSTANCE_STATE = `mutation SetInstanceState($input: SetInstanceStateRequest!) {
|
|
86
|
-
SetInstanceState(input: $input)
|
|
87
|
-
}`;
|
|
88
|
-
|
|
89
|
-
const CREATE_STATIC_ATTRIBUTES = `mutation CreateStaticAttributes($input: CreateStaticAttributesRequest!) {
|
|
90
|
-
CreateStaticAttributes(input: $input) { ID ModelID InstanceID Key Value }
|
|
91
|
-
}`;
|
|
92
|
-
|
|
93
|
-
const UPDATE_STATIC_ATTRIBUTES = `mutation UpdateStaticAttributes($input: UpdateStaticAttributesRequest!) {
|
|
94
|
-
UpdateStaticAttributes(input: $input) { ID ModelID InstanceID Key Value }
|
|
95
|
-
}`;
|
|
96
|
-
|
|
97
|
-
interface LETransactionalInstanceUpdateInput {
|
|
98
|
-
readonly instanceID: string;
|
|
99
|
-
readonly updateStaticAttributes?: {
|
|
100
|
-
readonly Items: readonly {
|
|
101
|
-
readonly ID: string;
|
|
102
|
-
readonly ModelID?: string | null;
|
|
103
|
-
readonly InstanceID?: string | null;
|
|
104
|
-
readonly Key: string;
|
|
105
|
-
readonly Value: string;
|
|
106
|
-
}[];
|
|
107
|
-
};
|
|
108
|
-
readonly updateDynamicAttributes?: { readonly Items: readonly unknown[] };
|
|
109
|
-
readonly setParameters?: {
|
|
110
|
-
readonly Items: readonly {
|
|
111
|
-
readonly ID?: string;
|
|
112
|
-
readonly Name: string;
|
|
113
|
-
readonly Value: string;
|
|
114
|
-
}[];
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// --- API ---
|
|
119
|
-
|
|
120
|
-
export class LEDigitalTwins {
|
|
121
|
-
constructor(private readonly client: LEClient) {}
|
|
122
|
-
|
|
123
|
-
private async transactionalInstanceUpdate(
|
|
124
|
-
input: LETransactionalInstanceUpdateInput,
|
|
125
|
-
): Promise<void> {
|
|
126
|
-
await this.client.gql("/digital-twins/v2", TRANSACTIONAL_INSTANCE_UPDATE, { input });
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
async listModels(): Promise<readonly LEModel[]> {
|
|
130
|
-
const data = await this.client.gql<{ ListModels: LEModel[] }>("/digital-twins/v2", LIST_MODELS);
|
|
131
|
-
return data.ListModels;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async listInstances(modelId?: string): Promise<readonly LEInstance[]> {
|
|
135
|
-
const data = await this.client.gql<{ ListInstances: LEInstance[] }>(
|
|
136
|
-
"/digital-twins/v2",
|
|
137
|
-
LIST_INSTANCES,
|
|
138
|
-
{ input: modelId ? { ModelID: modelId } : {} },
|
|
139
|
-
);
|
|
140
|
-
return data.ListInstances;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async listStaticAttributes(opts: {
|
|
144
|
-
modelId?: string | undefined;
|
|
145
|
-
instanceId?: string | undefined;
|
|
146
|
-
}): Promise<readonly LEStaticAttribute[]> {
|
|
147
|
-
const data = await this.client.gql<{ ListStaticAttributes: LEStaticAttribute[] }>(
|
|
148
|
-
"/digital-twins/v2",
|
|
149
|
-
LIST_STATIC_ATTRIBUTES,
|
|
150
|
-
{ modelId: opts.modelId ?? null, instanceId: opts.instanceId ?? null },
|
|
151
|
-
);
|
|
152
|
-
return data.ListStaticAttributes;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async listParameters(ownerId: string): Promise<readonly LEParameter[]> {
|
|
156
|
-
const data = await this.client.gql<{ ListParameters: LEParameter[] }>(
|
|
157
|
-
"/digital-twins/v2",
|
|
158
|
-
LIST_PARAMETERS,
|
|
159
|
-
{ ownerId },
|
|
160
|
-
);
|
|
161
|
-
return data.ListParameters;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async importModel(modelJson: unknown): Promise<boolean> {
|
|
165
|
-
const data = await this.client.gql<{ ImportModel: boolean }>(
|
|
166
|
-
"/digital-twins/v2",
|
|
167
|
-
IMPORT_MODEL,
|
|
168
|
-
{ input: modelJson },
|
|
169
|
-
);
|
|
170
|
-
return data.ImportModel;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async deleteModel(id: string): Promise<void> {
|
|
174
|
-
await this.client.gql("/digital-twins/v2", DELETE_MODEL, { id });
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
async createInstance(input: {
|
|
178
|
-
ModelID: string;
|
|
179
|
-
Name: string;
|
|
180
|
-
Topic: string;
|
|
181
|
-
Interval: number;
|
|
182
|
-
FlatHierarchy?: boolean;
|
|
183
|
-
}): Promise<LEInstance> {
|
|
184
|
-
const data = await this.client.gql<{ CreateInstance: LEInstance }>(
|
|
185
|
-
"/digital-twins/v2",
|
|
186
|
-
CREATE_INSTANCE,
|
|
187
|
-
{ input },
|
|
188
|
-
);
|
|
189
|
-
return data.CreateInstance;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
async updateInstance(input: {
|
|
193
|
-
ID: string;
|
|
194
|
-
ModelID: string;
|
|
195
|
-
Name: string;
|
|
196
|
-
Topic: string;
|
|
197
|
-
Interval: number;
|
|
198
|
-
FlatHierarchy?: boolean;
|
|
199
|
-
}): Promise<LEInstance> {
|
|
200
|
-
const data = await this.client.gql<{ UpdateInstance: LEInstance }>(
|
|
201
|
-
"/digital-twins/v2",
|
|
202
|
-
UPDATE_INSTANCE,
|
|
203
|
-
{ input },
|
|
204
|
-
);
|
|
205
|
-
return data.UpdateInstance;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
async deleteInstance(id: string): Promise<void> {
|
|
209
|
-
await this.client.gql("/digital-twins/v2", DELETE_INSTANCE, { id });
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async setInstanceParameters(
|
|
213
|
-
instanceId: string,
|
|
214
|
-
params: readonly { Name: string; Value: string }[],
|
|
215
|
-
): Promise<void> {
|
|
216
|
-
// TransactionalInstanceUpdate requires SetParameterRequest.ID to be set.
|
|
217
|
-
// LE auto-creates default parameters when an instance is created, so the
|
|
218
|
-
// lookup is always needed — even for brand-new instances.
|
|
219
|
-
const existing = await this.listParameters(instanceId);
|
|
220
|
-
const byName = new Map(existing.map((p) => [p.Name, p]));
|
|
221
|
-
|
|
222
|
-
await this.transactionalInstanceUpdate({
|
|
223
|
-
instanceID: instanceId,
|
|
224
|
-
updateStaticAttributes: { Items: [] },
|
|
225
|
-
updateDynamicAttributes: { Items: [] },
|
|
226
|
-
setParameters: {
|
|
227
|
-
Items: params.map((p) => {
|
|
228
|
-
const existingParamId = byName.get(p.Name)?.ID;
|
|
229
|
-
return existingParamId
|
|
230
|
-
? { ID: existingParamId, Name: p.Name, Value: p.Value }
|
|
231
|
-
: { Name: p.Name, Value: p.Value };
|
|
232
|
-
}),
|
|
233
|
-
},
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async updateInstanceStaticAttributes(
|
|
238
|
-
instanceId: string,
|
|
239
|
-
attrs: readonly {
|
|
240
|
-
ID: string;
|
|
241
|
-
ModelID?: string | null;
|
|
242
|
-
InstanceID?: string | null;
|
|
243
|
-
Key: string;
|
|
244
|
-
Value: string;
|
|
245
|
-
}[],
|
|
246
|
-
): Promise<void> {
|
|
247
|
-
if (attrs.length === 0) return;
|
|
248
|
-
await this.transactionalInstanceUpdate({
|
|
249
|
-
instanceID: instanceId,
|
|
250
|
-
updateStaticAttributes: { Items: attrs },
|
|
251
|
-
updateDynamicAttributes: { Items: [] },
|
|
252
|
-
setParameters: { Items: [] },
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
async setInstanceState(instanceId: string, enabled: boolean): Promise<void> {
|
|
257
|
-
await this.client.gql("/digital-twins/v2", SET_INSTANCE_STATE, {
|
|
258
|
-
input: { ID: instanceId, State: enabled },
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
async createStaticAttributes(
|
|
263
|
-
attrs: readonly { ModelID?: string; InstanceID?: string; Key: string; Value: string }[],
|
|
264
|
-
): Promise<readonly LEStaticAttribute[]> {
|
|
265
|
-
if (attrs.length === 0) return [];
|
|
266
|
-
const data = await this.client.gql<{ CreateStaticAttributes: LEStaticAttribute[] }>(
|
|
267
|
-
"/digital-twins/v2",
|
|
268
|
-
CREATE_STATIC_ATTRIBUTES,
|
|
269
|
-
{ input: { Items: attrs } },
|
|
270
|
-
);
|
|
271
|
-
return data.CreateStaticAttributes;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
async updateStaticAttributes(
|
|
275
|
-
attrs: readonly { ID: string; Key: string; Value: string }[],
|
|
276
|
-
): Promise<readonly LEStaticAttribute[]> {
|
|
277
|
-
if (attrs.length === 0) return [];
|
|
278
|
-
const data = await this.client.gql<{ UpdateStaticAttributes: LEStaticAttribute[] }>(
|
|
279
|
-
"/digital-twins/v2",
|
|
280
|
-
UPDATE_STATIC_ATTRIBUTES,
|
|
281
|
-
{ input: { Items: attrs } },
|
|
282
|
-
);
|
|
283
|
-
return data.UpdateStaticAttributes;
|
|
284
|
-
}
|
|
285
|
-
}
|
package/src/lib/le/le-drivers.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LE driver resolution by stable name and ID.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { ProtocolName } from "@miravo/shared";
|
|
6
|
-
|
|
7
|
-
/** Stable driver names per protocol. */
|
|
8
|
-
export const LE_DRIVER_NAMES: Record<ProtocolName, string> = {
|
|
9
|
-
opcua: "OPC UA Client Advanced",
|
|
10
|
-
modbus: "Modbus TCP",
|
|
11
|
-
mqtt: "MQTT Client",
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
/** Fixed DeviceHub driver UUIDs per protocol for LE 4.x. */
|
|
15
|
-
export const LE_DRIVER_IDS: Record<ProtocolName, string> = {
|
|
16
|
-
opcua: "2372E68F-BA1B-46E9-BC07-E92CA006B377",
|
|
17
|
-
modbus: "2AF1FA08-D638-11E9-BB65-2A2AE2DBCCE4",
|
|
18
|
-
mqtt: "236E3A53-E133-48D6-96F4-D95E61C7592A",
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export function getDriverName(protocol: ProtocolName): string {
|
|
22
|
-
return LE_DRIVER_NAMES[protocol];
|
|
23
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LE endpoint resolution.
|
|
3
|
-
*
|
|
4
|
-
* Determines the host:port that LE devices should use to reach
|
|
5
|
-
* Miravo's protocol servers.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { ProtocolName } from "@miravo/shared";
|
|
9
|
-
import { DEFAULT_MODBUS_PORT, DEFAULT_MQTT_PORT, DEFAULT_OPCUA_PORT } from "@miravo/shared";
|
|
10
|
-
|
|
11
|
-
interface EndpointInfo {
|
|
12
|
-
readonly host: string;
|
|
13
|
-
readonly port: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function resolveProtocolPort(
|
|
17
|
-
protocol: ProtocolName,
|
|
18
|
-
protocolConfig?: {
|
|
19
|
-
mqtt?: { port?: number };
|
|
20
|
-
opcua?: { port?: number };
|
|
21
|
-
modbus?: { port?: number };
|
|
22
|
-
},
|
|
23
|
-
): number {
|
|
24
|
-
switch (protocol) {
|
|
25
|
-
case "mqtt":
|
|
26
|
-
return protocolConfig?.mqtt?.port ?? DEFAULT_MQTT_PORT;
|
|
27
|
-
case "opcua":
|
|
28
|
-
return protocolConfig?.opcua?.port ?? DEFAULT_OPCUA_PORT;
|
|
29
|
-
case "modbus":
|
|
30
|
-
return protocolConfig?.modbus?.port ?? DEFAULT_MODBUS_PORT;
|
|
31
|
-
default: {
|
|
32
|
-
const _never: never = protocol;
|
|
33
|
-
throw new Error(`Unknown protocol: ${_never}`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function formatEndpoint(info: EndpointInfo): string {
|
|
39
|
-
return `${info.host}:${info.port}`;
|
|
40
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import {
|
|
6
|
-
addEnvironment,
|
|
7
|
-
environmentsFilePath,
|
|
8
|
-
getDefaultEnvironment,
|
|
9
|
-
loadEnvironments,
|
|
10
|
-
operationsFilePath,
|
|
11
|
-
platformsDirPath,
|
|
12
|
-
} from "./le-environments.ts";
|
|
13
|
-
|
|
14
|
-
let testHome = "";
|
|
15
|
-
let prevMiravoHome: string | undefined;
|
|
16
|
-
|
|
17
|
-
beforeEach(async () => {
|
|
18
|
-
prevMiravoHome = Bun.env["MIRAVO_HOME"];
|
|
19
|
-
testHome = await mkdtemp(join(tmpdir(), "miravo-platforms-test-"));
|
|
20
|
-
Bun.env["MIRAVO_HOME"] = testHome;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
afterEach(async () => {
|
|
24
|
-
if (prevMiravoHome === undefined) {
|
|
25
|
-
delete Bun.env["MIRAVO_HOME"];
|
|
26
|
-
} else {
|
|
27
|
-
Bun.env["MIRAVO_HOME"] = prevMiravoHome;
|
|
28
|
-
}
|
|
29
|
-
await rm(testHome, { recursive: true, force: true });
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe("le-environments storage layout", () => {
|
|
33
|
-
test("writes environments under ~/.miravo/platforms/environments.json", async () => {
|
|
34
|
-
await addEnvironment({
|
|
35
|
-
platformId: "litmus-edge",
|
|
36
|
-
label: "Production LE",
|
|
37
|
-
hostname: "10.1.2.3",
|
|
38
|
-
apiToken: "token-1",
|
|
39
|
-
sourceHostname: "host.docker.internal",
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const envs = await loadEnvironments();
|
|
43
|
-
expect(envs).toHaveLength(1);
|
|
44
|
-
expect(envs[0]?.label).toBe("Production LE");
|
|
45
|
-
|
|
46
|
-
const envFileExists = await Bun.file(environmentsFilePath()).exists();
|
|
47
|
-
const oldPathExists = await Bun.file(`${testHome}/platforms.json`).exists();
|
|
48
|
-
expect(platformsDirPath()).toBe(`${testHome}/platforms`);
|
|
49
|
-
expect(envFileExists).toBe(true);
|
|
50
|
-
expect(oldPathExists).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test("default environment is first entry in environments file", async () => {
|
|
54
|
-
await addEnvironment({
|
|
55
|
-
platformId: "litmus-edge",
|
|
56
|
-
label: "First",
|
|
57
|
-
hostname: "10.0.0.1",
|
|
58
|
-
apiToken: "first-token",
|
|
59
|
-
sourceHostname: "host.docker.internal",
|
|
60
|
-
});
|
|
61
|
-
await addEnvironment({
|
|
62
|
-
platformId: "litmus-edge",
|
|
63
|
-
label: "Second",
|
|
64
|
-
hostname: "10.0.0.2",
|
|
65
|
-
apiToken: "second-token",
|
|
66
|
-
sourceHostname: "host.docker.internal",
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const defaultEnv = await getDefaultEnvironment();
|
|
70
|
-
expect(defaultEnv?.label).toBe("First");
|
|
71
|
-
expect(operationsFilePath()).toBe(`${testHome}/platforms/operations.jsonl`);
|
|
72
|
-
});
|
|
73
|
-
});
|