Compare commits
3 Commits
fix/valida
...
e50be2ee66
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e50be2ee66 | ||
|
|
75d16c620a | ||
|
|
024db5533a |
@@ -153,7 +153,7 @@
|
|||||||
100
|
100
|
||||||
],
|
],
|
||||||
"y": [
|
"y": [
|
||||||
52.14679487594751,
|
11.142207365162072,
|
||||||
20.746724065725342,
|
20.746724065725342,
|
||||||
31.960270693111905,
|
31.960270693111905,
|
||||||
45.6989826531509,
|
45.6989826531509,
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
"y": [
|
"y": [
|
||||||
8.219999984177646,
|
8.219999984177646,
|
||||||
13.426327986363882,
|
13.426327986363882,
|
||||||
57.998168647814666,
|
25.971821741448165,
|
||||||
42.997354839160536,
|
42.997354839160536,
|
||||||
64.33911122026377
|
64.33911122026377
|
||||||
]
|
]
|
||||||
@@ -427,7 +427,7 @@
|
|||||||
"y": [
|
"y": [
|
||||||
8.219999984177646,
|
8.219999984177646,
|
||||||
13.426327986363882,
|
13.426327986363882,
|
||||||
53.35067019159144,
|
25.288156424842576,
|
||||||
42.48429874246399,
|
42.48429874246399,
|
||||||
64.03769740244357
|
64.03769740244357
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class ConfigManager {
|
|||||||
functionality: {
|
functionality: {
|
||||||
softwareType: nodeName.toLowerCase(),
|
softwareType: nodeName.toLowerCase(),
|
||||||
positionVsParent: uiConfig.positionVsParent || 'atEquipment',
|
positionVsParent: uiConfig.positionVsParent || 'atEquipment',
|
||||||
distance: uiConfig.hasDistance ? uiConfig.distance : undefined
|
distance: uiConfig.hasDistance ? uiConfig.distance : null
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
process: uiConfig.processOutputFormat || 'process',
|
process: uiConfig.processOutputFormat || 'process',
|
||||||
|
|||||||
@@ -411,6 +411,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mode": {
|
||||||
|
"current": {
|
||||||
|
"default": "analog",
|
||||||
|
"rules": {
|
||||||
|
"type": "enum",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"value": "analog",
|
||||||
|
"description": "Single-scalar input mode (classic 4-20mA / PLC style). msg.payload is a number; the node runs one offset/scaling/smoothing/outlier pipeline and emits one MeasurementContainer slot."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "digital",
|
||||||
|
"description": "Multi-channel input mode (MQTT / IoT JSON style). msg.payload is an object keyed by channel names declared under config.channels; the node routes each key through its own pipeline and emits N slots from one input message."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Selects how incoming msg.payload is interpreted."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"default": [],
|
||||||
|
"rules": {
|
||||||
|
"type": "array",
|
||||||
|
"itemType": "object",
|
||||||
|
"minLength": 0,
|
||||||
|
"description": "Channel map used in digital mode. Each entry is a self-contained pipeline definition: {key, type, position, unit, scaling?, smoothing?, outlierDetection?, distance?}. Ignored in analog mode."
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
"enabled": {
|
"enabled": {
|
||||||
"default": false,
|
"default": false,
|
||||||
|
|||||||
@@ -91,7 +91,55 @@
|
|||||||
],
|
],
|
||||||
"description": "Defines the position of the measurement relative to its parent equipment or system."
|
"description": "Defines the position of the measurement relative to its parent equipment or system."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"distance": {
|
||||||
|
"default": null,
|
||||||
|
"rules": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true,
|
||||||
|
"description": "Optional spatial offset from the parent equipment reference. Populated from the editor when hasDistance is enabled; null otherwise."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"default": "m",
|
||||||
|
"rules": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Unit for the functionality.distance offset (e.g. 'm', 'cm')."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"distanceDescription": {
|
||||||
|
"default": "",
|
||||||
|
"rules": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Free-text description of what the distance offset represents (e.g. 'cable length from control panel to motor')."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"process": {
|
||||||
|
"default": "process",
|
||||||
|
"rules": {
|
||||||
|
"type": "enum",
|
||||||
|
"values": [
|
||||||
|
{ "value": "process", "description": "Delta-compressed process message (default)." },
|
||||||
|
{ "value": "json", "description": "Raw JSON payload." },
|
||||||
|
{ "value": "csv", "description": "CSV-formatted payload." }
|
||||||
|
],
|
||||||
|
"description": "Format of the process payload emitted on output port 0."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dbase": {
|
||||||
|
"default": "influxdb",
|
||||||
|
"rules": {
|
||||||
|
"type": "enum",
|
||||||
|
"values": [
|
||||||
|
{ "value": "influxdb", "description": "InfluxDB line-protocol payload (default)." },
|
||||||
|
{ "value": "json", "description": "Raw JSON payload." },
|
||||||
|
{ "value": "csv", "description": "CSV-formatted payload." }
|
||||||
|
],
|
||||||
|
"description": "Format of the telemetry payload emitted on output port 1."
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"asset": {
|
"asset": {
|
||||||
"uuid": {
|
"uuid": {
|
||||||
|
|||||||
@@ -141,11 +141,17 @@ class MeasurementContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isUnitCompatible(measurementType, unit) {
|
isUnitCompatible(measurementType, unit) {
|
||||||
const desc = this._describeUnit(unit);
|
// Unknown type (not in measureMap): accept any unit. This lets user-
|
||||||
if (!desc) return false;
|
// defined measurement types (e.g. 'humidity', 'co2', arbitrary IoT
|
||||||
|
// channels in digital mode) pass through without being rejected just
|
||||||
|
// because their unit string ('%', 'ppm', …) is not a known physical
|
||||||
|
// unit to the convert module. Known types are still validated strictly.
|
||||||
const normalizedType = this._normalizeType(measurementType);
|
const normalizedType = this._normalizeType(measurementType);
|
||||||
const expectedMeasure = this.measureMap[normalizedType];
|
const expectedMeasure = this.measureMap[normalizedType];
|
||||||
if (!expectedMeasure) return true;
|
if (!expectedMeasure) return true;
|
||||||
|
|
||||||
|
const desc = this._describeUnit(unit);
|
||||||
|
if (!desc) return false;
|
||||||
return desc.measure === expectedMeasure;
|
return desc.measure === expectedMeasure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,21 @@ class state{
|
|||||||
this.emitter.emit("movementComplete", { position: targetPosition });
|
this.emitter.emit("movementComplete", { position: targetPosition });
|
||||||
await this.transitionToState("operational");
|
await this.transitionToState("operational");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
// Abort path: return to 'operational' so a subsequent shutdown/emergency
|
||||||
|
// sequence can proceed. Without this, the FSM remains stuck in
|
||||||
|
// accelerating/decelerating and blocks stopping/idle transitions.
|
||||||
|
const msg = typeof error === 'string' ? error : error?.message;
|
||||||
|
if (msg === 'Transition aborted' || msg === 'Movement aborted') {
|
||||||
|
this.logger.debug(`Movement aborted; returning to 'operational' to unblock further transitions.`);
|
||||||
|
try {
|
||||||
|
await this.transitionToState("operational");
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.debug(`Post-abort transition to operational failed: ${e?.message || e}`);
|
||||||
|
}
|
||||||
|
this.emitter.emit("movementAborted", { position: targetPosition });
|
||||||
|
} else {
|
||||||
|
this.logger.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user