@attraccess/plugin-wago 0.1.0-nightly.1005.1 → 0.1.0-nightly.1009.1
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/index.js +192 -57
- package/frontend/__federation_expose_Plugin-Dzhy6LDv.js +8752 -0
- package/frontend/host-shared-placeholder-BvRk9kiK.js +0 -0
- package/frontend/remoteEntry.js +68 -2
- package/frontend/style.css +159 -0
- package/package.json +4 -3
- package/frontend/__federation_expose_Plugin-D3BJGI74.js +0 -7753
- package/frontend/jsx-runtime-DFPsoQIN.js +0 -42
- package/frontend/preload-helper-DxWiKyOd.js +0 -68
- package/frontend/react-Cr_hTkIJ.js +0 -383
- package/frontend/rolldown-runtime-DC62tzP2.js +0 -33
package/dist/index.js
CHANGED
|
@@ -48140,12 +48140,12 @@ var require_auditTime = __commonJS({
|
|
|
48140
48140
|
var async_1 = require_async();
|
|
48141
48141
|
var audit_1 = require_audit();
|
|
48142
48142
|
var timer_1 = require_timer();
|
|
48143
|
-
function auditTime(
|
|
48143
|
+
function auditTime(duration2, scheduler) {
|
|
48144
48144
|
if (scheduler === void 0) {
|
|
48145
48145
|
scheduler = async_1.asyncScheduler;
|
|
48146
48146
|
}
|
|
48147
48147
|
return audit_1.audit(function() {
|
|
48148
|
-
return timer_1.timer(
|
|
48148
|
+
return timer_1.timer(duration2, scheduler);
|
|
48149
48149
|
});
|
|
48150
48150
|
}
|
|
48151
48151
|
exports2.auditTime = auditTime;
|
|
@@ -49118,9 +49118,9 @@ var require_delay = __commonJS({
|
|
|
49118
49118
|
if (scheduler === void 0) {
|
|
49119
49119
|
scheduler = async_1.asyncScheduler;
|
|
49120
49120
|
}
|
|
49121
|
-
var
|
|
49121
|
+
var duration2 = timer_1.timer(due, scheduler);
|
|
49122
49122
|
return delayWhen_1.delayWhen(function() {
|
|
49123
|
-
return
|
|
49123
|
+
return duration2;
|
|
49124
49124
|
});
|
|
49125
49125
|
}
|
|
49126
49126
|
exports2.delay = delay;
|
|
@@ -49546,13 +49546,13 @@ var require_groupBy = __commonJS({
|
|
|
49546
49546
|
var Subject_1 = require_Subject();
|
|
49547
49547
|
var lift_1 = require_lift();
|
|
49548
49548
|
var OperatorSubscriber_1 = require_OperatorSubscriber();
|
|
49549
|
-
function groupBy(keySelector, elementOrOptions,
|
|
49549
|
+
function groupBy(keySelector, elementOrOptions, duration2, connector) {
|
|
49550
49550
|
return lift_1.operate(function(source, subscriber) {
|
|
49551
49551
|
var element;
|
|
49552
49552
|
if (!elementOrOptions || typeof elementOrOptions === "function") {
|
|
49553
49553
|
element = elementOrOptions;
|
|
49554
49554
|
} else {
|
|
49555
|
-
|
|
49555
|
+
duration2 = elementOrOptions.duration, element = elementOrOptions.element, connector = elementOrOptions.connector;
|
|
49556
49556
|
}
|
|
49557
49557
|
var groups = /* @__PURE__ */ new Map();
|
|
49558
49558
|
var notify = function(cb) {
|
|
@@ -49574,14 +49574,14 @@ var require_groupBy = __commonJS({
|
|
|
49574
49574
|
groups.set(key_1, group_1 = connector ? connector() : new Subject_1.Subject());
|
|
49575
49575
|
var grouped = createGroupedObservable(key_1, group_1);
|
|
49576
49576
|
subscriber.next(grouped);
|
|
49577
|
-
if (
|
|
49577
|
+
if (duration2) {
|
|
49578
49578
|
var durationSubscriber_1 = OperatorSubscriber_1.createOperatorSubscriber(group_1, function() {
|
|
49579
49579
|
group_1.complete();
|
|
49580
49580
|
durationSubscriber_1 === null || durationSubscriber_1 === void 0 ? void 0 : durationSubscriber_1.unsubscribe();
|
|
49581
49581
|
}, void 0, void 0, function() {
|
|
49582
49582
|
return groups.delete(key_1);
|
|
49583
49583
|
});
|
|
49584
|
-
groupBySourceSubscriber.add(innerFrom_1.innerFrom(
|
|
49584
|
+
groupBySourceSubscriber.add(innerFrom_1.innerFrom(duration2(grouped)).subscribe(durationSubscriber_1));
|
|
49585
49585
|
}
|
|
49586
49586
|
}
|
|
49587
49587
|
group_1.next(element ? element(value) : value);
|
|
@@ -51102,11 +51102,11 @@ var require_throttleTime = __commonJS({
|
|
|
51102
51102
|
var async_1 = require_async();
|
|
51103
51103
|
var throttle_1 = require_throttle();
|
|
51104
51104
|
var timer_1 = require_timer();
|
|
51105
|
-
function throttleTime(
|
|
51105
|
+
function throttleTime(duration2, scheduler, config) {
|
|
51106
51106
|
if (scheduler === void 0) {
|
|
51107
51107
|
scheduler = async_1.asyncScheduler;
|
|
51108
51108
|
}
|
|
51109
|
-
var duration$ = timer_1.timer(
|
|
51109
|
+
var duration$ = timer_1.timer(duration2, scheduler);
|
|
51110
51110
|
return throttle_1.throttle(function() {
|
|
51111
51111
|
return duration$;
|
|
51112
51112
|
}, config);
|
|
@@ -52496,8 +52496,8 @@ function supportsOutputAction(channel, action) {
|
|
|
52496
52496
|
}
|
|
52497
52497
|
function pulseBehaviorError(capabilities, pulse) {
|
|
52498
52498
|
if (!capabilities.includes("pulse") && pulse === void 0) return void 0;
|
|
52499
|
-
const
|
|
52500
|
-
if (!capabilities.includes("output") || !capabilities.includes("pulse") || typeof
|
|
52499
|
+
const duration2 = pulse && typeof pulse === "object" && !Array.isArray(pulse) ? pulse.durationMs : void 0;
|
|
52500
|
+
if (!capabilities.includes("output") || !capabilities.includes("pulse") || typeof duration2 !== "number" || !Number.isSafeInteger(duration2) || duration2 <= 0)
|
|
52501
52501
|
return "Pulsed behavior requires output and pulse capabilities and a positive integer durationMs.";
|
|
52502
52502
|
return void 0;
|
|
52503
52503
|
}
|
|
@@ -52952,7 +52952,13 @@ function validateModbus(value) {
|
|
|
52952
52952
|
});
|
|
52953
52953
|
});
|
|
52954
52954
|
config.devices.forEach((d, i) => {
|
|
52955
|
-
keys(
|
|
52955
|
+
keys(
|
|
52956
|
+
d,
|
|
52957
|
+
["id", "name", "connectionId", "unitId", "profileId", "profileVersion", "pollIntervalMs"],
|
|
52958
|
+
`modbus.devices[${i}]`
|
|
52959
|
+
);
|
|
52960
|
+
if (d.pollIntervalMs !== void 0 && !integer(d.pollIntervalMs, 100, 36e5))
|
|
52961
|
+
fail(`modbus.devices[${i}].pollIntervalMs`, "device polling interval must be 100..3600000 ms");
|
|
52956
52962
|
if (!name(d.name) || !integer(d.unitId, 1, 247) || !config.connections.some((c) => c.id === d.connectionId) || !findProfile(config, d))
|
|
52957
52963
|
fail(`modbus.devices[${i}]`, "device requires name, unit 1..247, existing connection and exact profile version");
|
|
52958
52964
|
});
|
|
@@ -53146,6 +53152,7 @@ function validateSnapshot(snapshot) {
|
|
|
53146
53152
|
"physicalPointId",
|
|
53147
53153
|
"profile",
|
|
53148
53154
|
"capabilities",
|
|
53155
|
+
"invert",
|
|
53149
53156
|
"disconnectPolicy",
|
|
53150
53157
|
"range",
|
|
53151
53158
|
"pulse",
|
|
@@ -53159,6 +53166,12 @@ function validateSnapshot(snapshot) {
|
|
|
53159
53166
|
errors.push(referenceError(`${path}.physicalPointId`, "physical point", channel.physicalPointId));
|
|
53160
53167
|
enumValue(channel.profile, `${path}.profile`, CHANNEL_PROFILES, errors);
|
|
53161
53168
|
const capabilities = capabilityList(channel.capabilities, `${path}.capabilities`, errors);
|
|
53169
|
+
if (channel.invert !== void 0 && (typeof channel.invert !== "boolean" || !capabilities.has("input")))
|
|
53170
|
+
errors.push({
|
|
53171
|
+
path: `${path}.invert`,
|
|
53172
|
+
code: "invalid_invert",
|
|
53173
|
+
message: "invert requires a boolean and an input channel"
|
|
53174
|
+
});
|
|
53162
53175
|
validateDisconnectPolicy(channel.disconnectPolicy, `${path}.disconnectPolicy`, errors);
|
|
53163
53176
|
validateRange(channel.range, `${path}.range`, capabilities, errors);
|
|
53164
53177
|
validatePulse(channel.pulse, `${path}.pulse`, capabilities, errors);
|
|
@@ -53210,7 +53223,7 @@ function exactKeys(value, path, allowed, errors, optional = []) {
|
|
|
53210
53223
|
})
|
|
53211
53224
|
);
|
|
53212
53225
|
allowed.filter(
|
|
53213
|
-
(key) => !["range", "pulse", "guard", "feedback", "measurement", ...optional].includes(key) && !(key in value)
|
|
53226
|
+
(key) => !["range", "pulse", "guard", "feedback", "measurement", "invert", ...optional].includes(key) && !(key in value)
|
|
53214
53227
|
).forEach(
|
|
53215
53228
|
(key) => errors.push({
|
|
53216
53229
|
path: path === "$" ? key : `${path}.${key}`,
|
|
@@ -53418,13 +53431,18 @@ function wagoAuditDetails(input) {
|
|
|
53418
53431
|
for (const key of ["revision", "sourceRevision"]) {
|
|
53419
53432
|
if (positiveInteger(input[key])) details[key] = input[key];
|
|
53420
53433
|
}
|
|
53421
|
-
if (typeof input.profileId === "string" && input.profileId.length <= 160 && input.profileId.trim())
|
|
53422
|
-
|
|
53434
|
+
if (typeof input.profileId === "string" && input.profileId.length <= 160 && input.profileId.trim())
|
|
53435
|
+
details.profileId = input.profileId;
|
|
53436
|
+
if (positiveInteger(input.profileVersion) && input.profileVersion <= 1e6)
|
|
53437
|
+
details.profileVersion = input.profileVersion;
|
|
53423
53438
|
if (WAGO_PRESETS.some((preset) => preset.id === input.presetId)) details.presetId = input.presetId;
|
|
53424
|
-
if (typeof input.channelId === "string" && /^[a-zA-Z0-9_-]{1,64}$/.test(input.channelId))
|
|
53425
|
-
|
|
53426
|
-
if (
|
|
53427
|
-
|
|
53439
|
+
if (typeof input.channelId === "string" && /^[a-zA-Z0-9_-]{1,64}$/.test(input.channelId))
|
|
53440
|
+
details.channelId = input.channelId;
|
|
53441
|
+
if (typeof input.commandId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(input.commandId))
|
|
53442
|
+
details.commandId = input.commandId;
|
|
53443
|
+
if (["set", "pulse", "release"].includes(input.operation)) details.operation = input.operation;
|
|
53444
|
+
if (["dispatched", "acknowledged", "rejected", "timeout", "transport_failure"].includes(input.result))
|
|
53445
|
+
details.result = input.result;
|
|
53428
53446
|
for (const side of ["before", "after"]) {
|
|
53429
53447
|
for (const key of ["physicalPointCount", "logicalChannelCount"]) {
|
|
53430
53448
|
const count = input[side]?.[key];
|
|
@@ -53452,12 +53470,15 @@ var WagoAudit = class {
|
|
|
53452
53470
|
}
|
|
53453
53471
|
/** For owners with asynchronous dispatch/ack lifecycles. Finish exactly once after the true outcome. */
|
|
53454
53472
|
begin(principal, controllerId, action, details = {}) {
|
|
53455
|
-
if (!positiveInteger(controllerId) || !WAGO_AUDIT_ACTIONS.includes(action))
|
|
53456
|
-
|
|
53457
|
-
|
|
53458
|
-
|
|
53459
|
-
|
|
53460
|
-
|
|
53473
|
+
if (!positiveInteger(controllerId) || !WAGO_AUDIT_ACTIONS.includes(action))
|
|
53474
|
+
throw new import_common2.BadRequestException("Invalid WAGO audit subject or action");
|
|
53475
|
+
const actor = wagoAuditPrincipal({
|
|
53476
|
+
user: {
|
|
53477
|
+
id: principal.userId,
|
|
53478
|
+
authenticationMethod: principal.authenticationMethod,
|
|
53479
|
+
apiTokenId: principal.apiTokenId
|
|
53480
|
+
}
|
|
53481
|
+
});
|
|
53461
53482
|
const operationId = (0, import_node_crypto3.randomUUID)();
|
|
53462
53483
|
const initial = wagoAuditDetails(details);
|
|
53463
53484
|
let attempted;
|
|
@@ -53911,6 +53932,66 @@ WagoConfigurationRevision = __decorateClass([
|
|
|
53911
53932
|
// backend/wago-command-handler.ts
|
|
53912
53933
|
var import_node_crypto4 = require("node:crypto");
|
|
53913
53934
|
var import_plugins_backend_sdk7 = require("@attraccess/plugins-backend-sdk");
|
|
53935
|
+
|
|
53936
|
+
// backend/wago-flow-preview.ts
|
|
53937
|
+
var WAGO_FLOW_PREVIEW_SCHEMA = { dynamic: true, type: "object", properties: {}, preview: [] };
|
|
53938
|
+
function wagoFlowPreview(config, kind, controller, snapshot, names) {
|
|
53939
|
+
const english = localizedPreview(config, kind, controller, snapshot, names, false);
|
|
53940
|
+
const german = localizedPreview(config, kind, controller, snapshot, names, true);
|
|
53941
|
+
return english.map((row, index) => ({ ...row, translations: { de: german[index] } }));
|
|
53942
|
+
}
|
|
53943
|
+
function localizedPreview(config, kind, controller, snapshot, names, german) {
|
|
53944
|
+
const text = (english, translated) => german ? translated : english;
|
|
53945
|
+
const channel = snapshot?.logicalChannels.find((item) => item.id === config.channelId);
|
|
53946
|
+
const point = snapshot?.physicalPoints?.find((item) => item.id === channel?.physicalPointId);
|
|
53947
|
+
let channelName = channel ? typeof names[channel.id] === "string" ? String(names[channel.id]) : channel.id : typeof config.channelId === "string" ? `${config.channelId} (${text("unavailable", "nicht verf\xFCgbar")})` : text("Not selected", "Nicht ausgew\xE4hlt");
|
|
53948
|
+
if (point?.modbus) {
|
|
53949
|
+
const device = snapshot?.modbus?.devices.find((item) => item.id === point.modbus?.deviceId);
|
|
53950
|
+
if (device) channelName = `${channelName} \xB7 ${device.name}`;
|
|
53951
|
+
} else if (point?.hardwareProfile === "751-9301") {
|
|
53952
|
+
const terminal = DIGITAL_TERMINALS.find((item) => item.channel === point.channel)?.label;
|
|
53953
|
+
if (terminal && !channelName.includes(terminal)) channelName += ` \xB7 ${terminal}`;
|
|
53954
|
+
}
|
|
53955
|
+
const rows = [
|
|
53956
|
+
{
|
|
53957
|
+
label: text("Device", "Ger\xE4t"),
|
|
53958
|
+
value: controller?.name || controller?.hardwareId || (typeof config.controllerId === "number" ? `Controller ${config.controllerId} (${text("unavailable", "nicht verf\xFCgbar")})` : text("Not selected", "Nicht ausgew\xE4hlt"))
|
|
53959
|
+
},
|
|
53960
|
+
{ label: text("Channel", "Kanal"), value: channelName }
|
|
53961
|
+
];
|
|
53962
|
+
if (kind === "command") {
|
|
53963
|
+
const action = config.action === "set" ? config.value === true ? text("Turn ON", "Einschalten") : config.value === false ? text("Turn OFF", "Ausschalten") : text("Select ON/OFF", "EIN/AUS ausw\xE4hlen") : config.action === "pulse" ? `${text("Pulse", "Impuls")}${channel?.pulse ? ` \xB7 ${duration(channel.pulse.durationMs)}` : ""}` : text("Not selected", "Nicht ausgew\xE4hlt");
|
|
53964
|
+
return [...rows, { label: text("Action", "Aktion"), value: action }];
|
|
53965
|
+
}
|
|
53966
|
+
const category = config.category === "measurement" ? text("Measurement", "Messwert") : config.category === "fault" ? text("Fault", "Fehler") : config.category === "state" ? channel?.capabilities.includes("input") ? text("Input state", "Eingangszustand") : text("Output state", "Ausgangszustand") : void 0;
|
|
53967
|
+
if (kind === "event")
|
|
53968
|
+
return [
|
|
53969
|
+
...rows,
|
|
53970
|
+
{
|
|
53971
|
+
label: text("When", "Wenn"),
|
|
53972
|
+
value: category ? `${category} ${text("received", "empfangen")}` : text("Not selected", "Nicht ausgew\xE4hlt")
|
|
53973
|
+
}
|
|
53974
|
+
];
|
|
53975
|
+
if (kind === "read")
|
|
53976
|
+
return [...rows, { label: text("Read", "Lesen"), value: category ?? text("Not selected", "Nicht ausgew\xE4hlt") }];
|
|
53977
|
+
const expected = typeof config.equals === "boolean" ? config.equals ? text("ON", "EIN") : text("OFF", "AUS") : typeof config.equals === "number" && Number.isFinite(config.equals) ? `${config.equals} (${text("wire value", "Rohwert")})` : text("Not set", "Nicht festgelegt");
|
|
53978
|
+
return [
|
|
53979
|
+
...rows,
|
|
53980
|
+
{
|
|
53981
|
+
label: text("Wait for", "Warten auf"),
|
|
53982
|
+
value: `${category ?? text("Not selected", "Nicht ausgew\xE4hlt")} = ${expected}`
|
|
53983
|
+
},
|
|
53984
|
+
{
|
|
53985
|
+
label: text("Timeout", "Zeitlimit"),
|
|
53986
|
+
value: duration(typeof config.timeoutMs === "number" ? config.timeoutMs : 3e4)
|
|
53987
|
+
}
|
|
53988
|
+
];
|
|
53989
|
+
}
|
|
53990
|
+
function duration(milliseconds) {
|
|
53991
|
+
return milliseconds < 1e3 ? `${milliseconds} ms` : `${milliseconds / 1e3} s`;
|
|
53992
|
+
}
|
|
53993
|
+
|
|
53994
|
+
// backend/wago-command-handler.ts
|
|
53914
53995
|
var DEFAULT_COMMAND_TIMEOUT_SECONDS = 30;
|
|
53915
53996
|
var MAX_COMMAND_TIMEOUT_SECONDS = 300;
|
|
53916
53997
|
var WagoCommandError = class extends Error {
|
|
@@ -53925,10 +54006,10 @@ var WagoCommandHandler = class {
|
|
|
53925
54006
|
__publicField(this, "dependencies", dependencies);
|
|
53926
54007
|
__publicField(this, "pending", /* @__PURE__ */ new Map());
|
|
53927
54008
|
}
|
|
53928
|
-
async channelNames(controllerId, revision2) {
|
|
54009
|
+
async channelNames(controllerId, revision2, appliedOnly = false) {
|
|
53929
54010
|
let names = {};
|
|
53930
54011
|
if (controllerId) {
|
|
53931
|
-
const draft = await this.dependencies.context.getRepository(WagoConfigurationDraft).findOneBy({ controllerId });
|
|
54012
|
+
const draft = appliedOnly ? null : await this.dependencies.context.getRepository(WagoConfigurationDraft).findOneBy({ controllerId });
|
|
53932
54013
|
try {
|
|
53933
54014
|
const storedNames = JSON.parse(revision2?.presetProvenance ?? draft?.presetProvenance ?? "null")?.editor?.names;
|
|
53934
54015
|
if (storedNames && typeof storedNames === "object" && !Array.isArray(storedNames)) names = storedNames;
|
|
@@ -53937,14 +54018,30 @@ var WagoCommandHandler = class {
|
|
|
53937
54018
|
}
|
|
53938
54019
|
return names;
|
|
53939
54020
|
}
|
|
53940
|
-
async schema(config, resourceId) {
|
|
53941
|
-
const controllers = await this.dependencies.controllers().find({ where: { trustState: "claimed" }, order: { name: "ASC" } });
|
|
54021
|
+
async schema(config, resourceId, previewOnly = false) {
|
|
53942
54022
|
const controllerId = positiveInteger2(config.controllerId);
|
|
54023
|
+
const controllers = previewOnly && !controllerId ? [] : await this.dependencies.controllers().find({
|
|
54024
|
+
where: { trustState: "claimed", ...previewOnly ? { id: controllerId } : {} },
|
|
54025
|
+
order: { name: "ASC" }
|
|
54026
|
+
});
|
|
53943
54027
|
const revision2 = controllerId ? await this.dependencies.appliedRevision(controllerId) : null;
|
|
53944
54028
|
const snapshot = revision2 ? JSON.parse(revision2.snapshot) : null;
|
|
53945
54029
|
const channelId2 = typeof config.channelId === "string" ? config.channelId : void 0;
|
|
53946
54030
|
const outputChannels = snapshot?.logicalChannels.filter((item) => item.capabilities.includes("output")) ?? [];
|
|
53947
|
-
const names = await this.channelNames(controllerId, revision2);
|
|
54031
|
+
const names = await this.channelNames(controllerId, revision2, previewOnly);
|
|
54032
|
+
if (previewOnly)
|
|
54033
|
+
return {
|
|
54034
|
+
dynamic: true,
|
|
54035
|
+
type: "object",
|
|
54036
|
+
properties: {},
|
|
54037
|
+
preview: wagoFlowPreview(
|
|
54038
|
+
config,
|
|
54039
|
+
"command",
|
|
54040
|
+
controllers.find((item) => item.id === controllerId),
|
|
54041
|
+
snapshot,
|
|
54042
|
+
names
|
|
54043
|
+
)
|
|
54044
|
+
};
|
|
53948
54045
|
const channel = outputChannels.find((item) => item.id === channelId2);
|
|
53949
54046
|
const references = channelId2 && controllerId ? await this.references(controllerId, channelId2, resourceId) : [];
|
|
53950
54047
|
const properties = {
|
|
@@ -54024,6 +54121,13 @@ var WagoCommandHandler = class {
|
|
|
54024
54121
|
return {
|
|
54025
54122
|
dynamic: true,
|
|
54026
54123
|
type: "object",
|
|
54124
|
+
preview: wagoFlowPreview(
|
|
54125
|
+
config,
|
|
54126
|
+
"command",
|
|
54127
|
+
controllers.find((item) => item.id === controllerId),
|
|
54128
|
+
snapshot,
|
|
54129
|
+
names
|
|
54130
|
+
),
|
|
54027
54131
|
properties,
|
|
54028
54132
|
required: [
|
|
54029
54133
|
.../* @__PURE__ */ new Set([
|
|
@@ -54036,8 +54140,8 @@ var WagoCommandHandler = class {
|
|
|
54036
54140
|
]
|
|
54037
54141
|
};
|
|
54038
54142
|
}
|
|
54039
|
-
async validate(config, validationContext = /* @__PURE__ */ new Map()) {
|
|
54040
|
-
const parsed = this.parse(config);
|
|
54143
|
+
async validate(config, validationContext = /* @__PURE__ */ new Map(), manual = false) {
|
|
54144
|
+
const parsed = this.parse(config, manual);
|
|
54041
54145
|
if ("errors" in parsed) return parsed.errors;
|
|
54042
54146
|
const { controllerId, channelId: channelId2, action, expectedConfigurationRevision } = parsed.value;
|
|
54043
54147
|
const controller = await this.cached(
|
|
@@ -54065,7 +54169,7 @@ var WagoCommandHandler = class {
|
|
|
54065
54169
|
if (!channel) return [{ field: "channelId", message: "The selected Logical Channel no longer exists." }];
|
|
54066
54170
|
if (!channel.capabilities.includes("output"))
|
|
54067
54171
|
return [{ field: "channelId", message: "The selected Logical Channel no longer supports output commands." }];
|
|
54068
|
-
if (!supportsOutputAction(channel, action))
|
|
54172
|
+
if (action !== "release" && !supportsOutputAction(channel, action))
|
|
54069
54173
|
return [
|
|
54070
54174
|
{
|
|
54071
54175
|
field: "action",
|
|
@@ -54074,11 +54178,11 @@ var WagoCommandHandler = class {
|
|
|
54074
54178
|
];
|
|
54075
54179
|
return [];
|
|
54076
54180
|
}
|
|
54077
|
-
async execute(config, commandId = (0, import_node_crypto4.randomUUID)()) {
|
|
54078
|
-
const errors = await this.validate(config);
|
|
54181
|
+
async execute(config, commandId = (0, import_node_crypto4.randomUUID)(), source) {
|
|
54182
|
+
const errors = await this.validate(config, /* @__PURE__ */ new Map(), source === "manual");
|
|
54079
54183
|
if (errors.length)
|
|
54080
54184
|
throw new WagoCommandError(errors.map((error) => error.message).join(" "), "controller-rejection");
|
|
54081
|
-
const parsed = this.parse(config);
|
|
54185
|
+
const parsed = this.parse(config, source === "manual");
|
|
54082
54186
|
if ("errors" in parsed)
|
|
54083
54187
|
throw new WagoCommandError(parsed.errors.map((error) => error.message).join(" "), "controller-rejection");
|
|
54084
54188
|
const {
|
|
@@ -54102,6 +54206,7 @@ var WagoCommandHandler = class {
|
|
|
54102
54206
|
channelId: channelId2,
|
|
54103
54207
|
action,
|
|
54104
54208
|
...action === "set" ? { value } : {},
|
|
54209
|
+
...source ? { source } : {},
|
|
54105
54210
|
expectedConfigurationRevision
|
|
54106
54211
|
});
|
|
54107
54212
|
const acknowledgement = completionBehavior === "acknowledged" ? this.waitForAcknowledgement(id, controllerId, acknowledgementTimeoutSeconds) : void 0;
|
|
@@ -54170,12 +54275,12 @@ var WagoCommandHandler = class {
|
|
|
54170
54275
|
const nodes = await this.dependencies.context.dataSource.getRepository(import_plugins_backend_sdk7.ResourceFlowNode).createQueryBuilder("node").select(["node.id", "node.resourceId"]).where("node.type = :type", { type: "plugin.wago.command" }).andWhere("node.resourceId <> :resourceId", { resourceId }).andWhere("node.data ->> 'controllerId' = :controllerId", { controllerId }).andWhere("node.data ->> 'channelId' = :channelId", { channelId: channelId2 }).getMany();
|
|
54171
54276
|
return nodes.map((node) => `resource ${node.resourceId} / node ${node.id}`);
|
|
54172
54277
|
}
|
|
54173
|
-
parse(config) {
|
|
54278
|
+
parse(config, manual = false) {
|
|
54174
54279
|
const errors = [];
|
|
54175
54280
|
const controllerId = positiveInteger2(config.controllerId);
|
|
54176
54281
|
const expectedConfigurationRevision = positiveInteger2(config.expectedConfigurationRevision);
|
|
54177
54282
|
const channelId2 = typeof config.channelId === "string" && config.channelId.trim() ? config.channelId : void 0;
|
|
54178
|
-
const action = config.action === "set" || config.action === "pulse" ? config.action : void 0;
|
|
54283
|
+
const action = config.action === "set" || config.action === "pulse" || manual && config.action === "release" ? config.action : void 0;
|
|
54179
54284
|
const completionBehavior = config.completionBehavior === "dispatch" ? "dispatch" : "acknowledged";
|
|
54180
54285
|
const parsedTimeout = positiveInteger2(config.acknowledgementTimeoutSeconds);
|
|
54181
54286
|
const acknowledgementTimeoutSeconds = parsedTimeout ?? DEFAULT_COMMAND_TIMEOUT_SECONDS;
|
|
@@ -54375,7 +54480,9 @@ var faultCodes = /* @__PURE__ */ new Set([
|
|
|
54375
54480
|
"measurement_read_failed",
|
|
54376
54481
|
"device_write_failed",
|
|
54377
54482
|
"feedback_mismatch",
|
|
54378
|
-
"feedback_read_failed"
|
|
54483
|
+
"feedback_read_failed",
|
|
54484
|
+
"modbus_read_failed",
|
|
54485
|
+
"digital_read_failed"
|
|
54379
54486
|
]);
|
|
54380
54487
|
function identifier(value) {
|
|
54381
54488
|
return typeof value === "string" && value.trim().length > 0 && value.length <= 256 && !Array.from(value).some((character) => character.charCodeAt(0) < 32);
|
|
@@ -54384,6 +54491,8 @@ function isObject(value) {
|
|
|
54384
54491
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
54385
54492
|
}
|
|
54386
54493
|
function validStatePayload(data, kind, canonical) {
|
|
54494
|
+
if (kind === "state" && data.manualOutputChannelIds !== void 0 && (!Array.isArray(data.manualOutputChannelIds) || data.manualOutputChannelIds.length > MAX_CHANNELS || !data.manualOutputChannelIds.every(identifier)))
|
|
54495
|
+
return false;
|
|
54387
54496
|
if (canonical && kind === "state" && data.readiness !== void 0 && (!isObject(data.readiness) || data.readiness.hardwareAvailable !== void 0 && typeof data.readiness.hardwareAvailable !== "boolean"))
|
|
54388
54497
|
return false;
|
|
54389
54498
|
if (canonical && kind === "state" && !(data.contentHash === null || typeof data.contentHash === "string" && /^[0-9a-f]{64}$/i.test(data.contentHash)))
|
|
@@ -54556,6 +54665,7 @@ var WagoDiagnosticsStore = class {
|
|
|
54556
54665
|
state.rejection = void 0;
|
|
54557
54666
|
state.hardwareAvailable = void 0;
|
|
54558
54667
|
state.stateSourceAt = void 0;
|
|
54668
|
+
state.manualOutputChannelIds = void 0;
|
|
54559
54669
|
state.revision = void 0;
|
|
54560
54670
|
state.contentHash = void 0;
|
|
54561
54671
|
}
|
|
@@ -54586,6 +54696,7 @@ var WagoDiagnosticsStore = class {
|
|
|
54586
54696
|
state.contentHash = contentHash;
|
|
54587
54697
|
state.hardwareAvailable = hardwareAvailable;
|
|
54588
54698
|
state.stateSourceAt = canonical ? data.timestamp : void 0;
|
|
54699
|
+
state.manualOutputChannelIds = canonical && Array.isArray(data.manualOutputChannelIds) ? data.manualOutputChannelIds : void 0;
|
|
54589
54700
|
if (data.outputs && typeof data.outputs === "object" && !Array.isArray(data.outputs)) {
|
|
54590
54701
|
for (const [channelId2, value] of Object.entries(data.outputs).slice(0, MAX_CHANNELS)) {
|
|
54591
54702
|
if (identifier(channelId2) && typeof value === "boolean")
|
|
@@ -54597,6 +54708,9 @@ var WagoDiagnosticsStore = class {
|
|
|
54597
54708
|
if (identifier(channelId2) && typeof value === "boolean")
|
|
54598
54709
|
state.inputs[channelId2] = { kind: "input", value, ...metadata2 };
|
|
54599
54710
|
}
|
|
54711
|
+
for (const id of [...Object.keys(state.outputs), ...Object.keys(state.inputs)]) {
|
|
54712
|
+
if (["modbus_read_failed", "digital_read_failed"].includes(state.faults[id]?.code)) delete state.faults[id];
|
|
54713
|
+
}
|
|
54600
54714
|
}
|
|
54601
54715
|
applyEvents(id, state, kind, data, canonical, metadata2) {
|
|
54602
54716
|
const { receivedAt } = metadata2;
|
|
@@ -54708,8 +54822,8 @@ var WagoService = class {
|
|
|
54708
54822
|
if (this.subscriptionRetryTimer) clearTimeout(this.subscriptionRetryTimer);
|
|
54709
54823
|
this.commands.destroy();
|
|
54710
54824
|
}
|
|
54711
|
-
async commandSchema(config, resourceId) {
|
|
54712
|
-
return this.commands.schema(config, resourceId);
|
|
54825
|
+
async commandSchema(config, resourceId, previewOnly = false) {
|
|
54826
|
+
return this.commands.schema(config, resourceId, previewOnly);
|
|
54713
54827
|
}
|
|
54714
54828
|
async validateCommandConfig(config, validationContext = /* @__PURE__ */ new Map()) {
|
|
54715
54829
|
return this.commands.validate(config, validationContext);
|
|
@@ -54724,7 +54838,7 @@ var WagoService = class {
|
|
|
54724
54838
|
if (typeof input.channelId !== "string" || !/^[A-Za-z0-9_-]{1,64}$/.test(input.channelId))
|
|
54725
54839
|
throw new import_common3.BadRequestException("Invalid Logical Channel");
|
|
54726
54840
|
const config = { ...input, controllerId, completionBehavior: "acknowledged" };
|
|
54727
|
-
const errors = await this.commands.validate(config);
|
|
54841
|
+
const errors = await this.commands.validate(config, /* @__PURE__ */ new Map(), true);
|
|
54728
54842
|
if (errors.length) throw new import_common3.BadRequestException("Manual command does not match the applied configuration");
|
|
54729
54843
|
const commandId = (0, import_node_crypto5.randomUUID)();
|
|
54730
54844
|
const details = { commandId, channelId: input.channelId, operation: input.action };
|
|
@@ -54732,7 +54846,7 @@ var WagoService = class {
|
|
|
54732
54846
|
await lifecycle.attempt();
|
|
54733
54847
|
let result;
|
|
54734
54848
|
try {
|
|
54735
|
-
await this.commands.execute(config, commandId);
|
|
54849
|
+
await this.commands.execute(config, commandId, "manual");
|
|
54736
54850
|
result = "acknowledged";
|
|
54737
54851
|
} catch (error) {
|
|
54738
54852
|
result = error instanceof WagoCommandError ? error.kind === "controller-rejection" ? "rejected" : error.kind === "acknowledgement-timeout" ? "timeout" : "transport_failure" : "transport_failure";
|
|
@@ -56645,11 +56759,11 @@ async function commissionClock(execute, confirmInstall, report, now = Date.now,
|
|
|
56645
56759
|
const start = monotonic();
|
|
56646
56760
|
const observed = parseSample(await execute(CLOCK_INSPECTION_SCRIPT, CLOCK_LIMITS));
|
|
56647
56761
|
const received = now();
|
|
56648
|
-
const
|
|
56649
|
-
if (!validEpoch(Math.floor(received / 1e3)) ||
|
|
56762
|
+
const duration2 = monotonic() - start;
|
|
56763
|
+
if (!validEpoch(Math.floor(received / 1e3)) || duration2 < 0 || duration2 > 4e3 || Math.abs(received - hostStarted - (monotonic() - started)) > 1e3)
|
|
56650
56764
|
throw new Error("Application clock changed or controller clock observation expired.");
|
|
56651
|
-
const host = received -
|
|
56652
|
-
const uncertaintySeconds = Math.ceil(
|
|
56765
|
+
const host = received - duration2 / 2;
|
|
56766
|
+
const uncertaintySeconds = Math.ceil(duration2 / 2e3) + 1;
|
|
56653
56767
|
const skew = observed.epoch - Math.floor(host / 1e3);
|
|
56654
56768
|
if (Math.abs(skew) > MAX_SKEW_SECONDS)
|
|
56655
56769
|
throw new Error("Controller clock skew exceeds the supported ten-year bound.");
|
|
@@ -62407,6 +62521,11 @@ var WagoDiagnosticsService = class {
|
|
|
62407
62521
|
incompatible: !!controller.compatibilityError,
|
|
62408
62522
|
...runtimeStreamSummary(runtime),
|
|
62409
62523
|
configuration: configurationSummary(draft, latest, applied, runtime, revisionMismatch),
|
|
62524
|
+
manualOutputChannelIds: connected && !revisionMismatch && freshness(runtime.stateSourceAt) === "fresh" ? (runtime.manualOutputChannelIds ?? []).filter(
|
|
62525
|
+
(id) => appliedSnapshot?.logicalChannels.some(
|
|
62526
|
+
(channel) => channel.id === id && channel.capabilities.includes("output")
|
|
62527
|
+
)
|
|
62528
|
+
) : [],
|
|
62410
62529
|
hardwareReadiness: "unknown",
|
|
62411
62530
|
hardwareReadinessReason: "Reported hardware availability is shown when supplied; it does not prove physical I/O readiness. Applied configuration and cached output state are not physical proof.",
|
|
62412
62531
|
channels: ((appliedSnapshot ?? snapshot)?.logicalChannels ?? []).slice(0, 256).map((channel) => {
|
|
@@ -62486,10 +62605,11 @@ function createWagoCommandNode(service) {
|
|
|
62486
62605
|
type: WAGO_COMMAND_NODE_TYPE,
|
|
62487
62606
|
label: "WAGO command",
|
|
62488
62607
|
description: "Control a configured WAGO Logical Channel.",
|
|
62608
|
+
configSchema: WAGO_FLOW_PREVIEW_SCHEMA,
|
|
62489
62609
|
inputs: ["input"],
|
|
62490
62610
|
outputs: ["output", "failure"],
|
|
62491
62611
|
isOutput: true,
|
|
62492
|
-
resolveConfigSchema: (config, schemaContext) => service().commandSchema(config, schemaContext.resourceId),
|
|
62612
|
+
resolveConfigSchema: (config, schemaContext) => service().commandSchema(config, schemaContext.resourceId, schemaContext.purpose === "preview"),
|
|
62493
62613
|
validateConfig: (config, validationContext) => service().validateCommandConfig(config, validationContext),
|
|
62494
62614
|
getFailureBehavior: (config) => service().commandFailureBehavior(config),
|
|
62495
62615
|
getFailureKind: (error) => service().commandFailureKind(error),
|
|
@@ -62511,19 +62631,21 @@ function createWagoStateNodes(service) {
|
|
|
62511
62631
|
type: WAGO_EVENT_NODE_TYPE,
|
|
62512
62632
|
label: "WAGO event received",
|
|
62513
62633
|
description: "Start a flow when a channel reports state, a measurement, or a fault. Data is available in wago.",
|
|
62634
|
+
configSchema: WAGO_FLOW_PREVIEW_SCHEMA,
|
|
62514
62635
|
isInput: true,
|
|
62515
62636
|
inputs: [],
|
|
62516
62637
|
outputs: ["output"],
|
|
62517
|
-
resolveConfigSchema: (config) => service().resolveConfigSchema(config, "event"),
|
|
62638
|
+
resolveConfigSchema: (config, context) => service().resolveConfigSchema(config, "event", void 0, context.purpose === "preview"),
|
|
62518
62639
|
validateConfig: (config, context) => service().validateConfig(config, "event", context)
|
|
62519
62640
|
},
|
|
62520
62641
|
{
|
|
62521
62642
|
type: "plugin.wago.read-state",
|
|
62522
62643
|
label: "WAGO read state",
|
|
62523
62644
|
description: "Read the latest channel state or measurement into wago. Unavailable data uses the unavailable output.",
|
|
62645
|
+
configSchema: WAGO_FLOW_PREVIEW_SCHEMA,
|
|
62524
62646
|
inputs: ["input"],
|
|
62525
62647
|
outputs: ["output", "unavailable"],
|
|
62526
|
-
resolveConfigSchema: (config) => service().resolveConfigSchema(config, "read"),
|
|
62648
|
+
resolveConfigSchema: (config, context) => service().resolveConfigSchema(config, "read", void 0, context.purpose === "preview"),
|
|
62527
62649
|
validateConfig: (config, context) => service().validateConfig(config, "read", context),
|
|
62528
62650
|
execute: async (node, input) => {
|
|
62529
62651
|
const state = service().read(node.data);
|
|
@@ -62535,9 +62657,10 @@ function createWagoStateNodes(service) {
|
|
|
62535
62657
|
type: "plugin.wago.wait-for-state",
|
|
62536
62658
|
label: "WAGO wait for state",
|
|
62537
62659
|
description: "Wait for an available channel value to match, then continue with the value in wago.",
|
|
62660
|
+
configSchema: WAGO_FLOW_PREVIEW_SCHEMA,
|
|
62538
62661
|
inputs: ["input"],
|
|
62539
62662
|
outputs: ["output", "timeout"],
|
|
62540
|
-
resolveConfigSchema: (config) => service().resolveConfigSchema(config, "wait"),
|
|
62663
|
+
resolveConfigSchema: (config, context) => service().resolveConfigSchema(config, "wait", void 0, context.purpose === "preview"),
|
|
62541
62664
|
validateConfig: (config, context) => service().validateConfig(config, "wait", context),
|
|
62542
62665
|
execute: async (node, input) => {
|
|
62543
62666
|
const state = await service().wait(node.data);
|
|
@@ -62669,11 +62792,15 @@ var WagoFlowService = class {
|
|
|
62669
62792
|
this.subscriptions.push(...replacements);
|
|
62670
62793
|
this.controllerByHardwareId = controllerByHardwareId;
|
|
62671
62794
|
}
|
|
62672
|
-
async resolveConfigSchema(config, kind, validationContext = /* @__PURE__ */ new Map()) {
|
|
62795
|
+
async resolveConfigSchema(config, kind, validationContext = /* @__PURE__ */ new Map(), previewOnly = false) {
|
|
62796
|
+
const controllerCacheKey = previewOnly ? `wago-flow-controllers:preview:${config.controllerId}` : "wago-flow-controllers";
|
|
62673
62797
|
const controllers = await this.cached(
|
|
62674
62798
|
validationContext,
|
|
62675
|
-
|
|
62676
|
-
() =>
|
|
62799
|
+
controllerCacheKey,
|
|
62800
|
+
() => previewOnly && typeof config.controllerId !== "number" ? Promise.resolve([]) : this.controllers.find({
|
|
62801
|
+
where: { trustState: "claimed", ...previewOnly ? { id: config.controllerId } : {} },
|
|
62802
|
+
order: { name: "ASC" }
|
|
62803
|
+
})
|
|
62677
62804
|
);
|
|
62678
62805
|
const selected = typeof config.controllerId === "number" ? controllers.find((controller) => controller.id === config.controllerId) : void 0;
|
|
62679
62806
|
const revision2 = selected ? await this.cached(validationContext, `wago-applied-revision:${selected.id}`, async () => {
|
|
@@ -62684,12 +62811,19 @@ var WagoFlowService = class {
|
|
|
62684
62811
|
});
|
|
62685
62812
|
return latest ?? null;
|
|
62686
62813
|
}) : null;
|
|
62687
|
-
const
|
|
62814
|
+
const snapshot = revision2 ? JSON.parse(revision2.snapshot) : null;
|
|
62815
|
+
const channels = snapshot?.logicalChannels ?? [];
|
|
62688
62816
|
let names = {};
|
|
62689
62817
|
try {
|
|
62690
62818
|
names = JSON.parse(revision2?.presetProvenance ?? "null")?.editor?.names ?? {};
|
|
62691
62819
|
} catch {
|
|
62692
62820
|
}
|
|
62821
|
+
if (previewOnly) return {
|
|
62822
|
+
dynamic: true,
|
|
62823
|
+
type: "object",
|
|
62824
|
+
properties: {},
|
|
62825
|
+
preview: wagoFlowPreview(config, kind, selected, snapshot, names)
|
|
62826
|
+
};
|
|
62693
62827
|
const channel = channels.find((item) => item.id === config.channelId);
|
|
62694
62828
|
const properties = {
|
|
62695
62829
|
controllerId: {
|
|
@@ -62745,6 +62879,7 @@ var WagoFlowService = class {
|
|
|
62745
62879
|
return {
|
|
62746
62880
|
dynamic: true,
|
|
62747
62881
|
type: "object",
|
|
62882
|
+
preview: wagoFlowPreview(config, kind, selected, snapshot, names),
|
|
62748
62883
|
properties,
|
|
62749
62884
|
required: ["controllerId", "channelId", "category", ...kind === "wait" ? ["equals", "timeoutMs"] : []]
|
|
62750
62885
|
};
|
|
@@ -63128,7 +63263,7 @@ var controllerActions = [
|
|
|
63128
63263
|
details: {
|
|
63129
63264
|
channelId,
|
|
63130
63265
|
commandId: { type: "string", pattern: uuidPattern2 },
|
|
63131
|
-
operation: { type: "string", oneOf: ["set", "pulse"] },
|
|
63266
|
+
operation: { type: "string", oneOf: ["set", "pulse", "release"] },
|
|
63132
63267
|
result: { type: "string", oneOf: ["dispatched", "acknowledged", "rejected", "timeout", "transport_failure"] }
|
|
63133
63268
|
}
|
|
63134
63269
|
}
|