Compare commits
3 Commits
43f69066af
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
693517cc8f | ||
|
|
086e5fe751 | ||
|
|
29b78a3f9b |
@@ -282,43 +282,9 @@
|
|||||||
},
|
},
|
||||||
"machineCurve": {
|
"machineCurve": {
|
||||||
"default": {
|
"default": {
|
||||||
"nq": {
|
"nq": {},
|
||||||
"1": {
|
"np": {}
|
||||||
"x": [
|
},
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5
|
|
||||||
],
|
|
||||||
"y": [
|
|
||||||
10,
|
|
||||||
20,
|
|
||||||
30,
|
|
||||||
40,
|
|
||||||
50
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"np": {
|
|
||||||
"1": {
|
|
||||||
"x": [
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5
|
|
||||||
],
|
|
||||||
"y": [
|
|
||||||
10,
|
|
||||||
20,
|
|
||||||
30,
|
|
||||||
40,
|
|
||||||
50
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules": {
|
"rules": {
|
||||||
"type": "machineCurve",
|
"type": "machineCurve",
|
||||||
"description": "All machine curves must have a 'nq' and 'np' curve. nq stands for the flow curve, np stands for the power curve. Together they form the efficiency curve."
|
"description": "All machine curves must have a 'nq' and 'np' curve. nq stands for the flow curve, np stands for the power curve. Together they form the efficiency curve."
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Map a child's raw softwareType (the lowercased node name from
|
||||||
|
// buildConfig) to the "role" key that parent registerChild() handlers
|
||||||
|
// dispatch on. Without this, MGC/pumpingStation register-handlers (which
|
||||||
|
// branch on 'machine' / 'machinegroup' / 'pumpingstation' / 'measurement')
|
||||||
|
// silently miss every real production child because rotatingMachine
|
||||||
|
// reports softwareType='rotatingmachine' and machineGroupControl reports
|
||||||
|
// 'machinegroupcontrol'. Existing tests that pass already-aliased keys
|
||||||
|
// ('machine', 'machinegroup') stay green because those aren't in the
|
||||||
|
// alias map and pass through unchanged.
|
||||||
|
const SOFTWARE_TYPE_ALIASES = {
|
||||||
|
rotatingmachine: 'machine',
|
||||||
|
machinegroupcontrol: 'machinegroup',
|
||||||
|
};
|
||||||
|
|
||||||
class ChildRegistrationUtils {
|
class ChildRegistrationUtils {
|
||||||
constructor(mainClass) {
|
constructor(mainClass) {
|
||||||
this.mainClass = mainClass;
|
this.mainClass = mainClass;
|
||||||
@@ -15,7 +29,8 @@ class ChildRegistrationUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const softwareType = (child.config.functionality.softwareType || '').toLowerCase();
|
const rawSoftwareType = (child.config.functionality.softwareType || '').toLowerCase();
|
||||||
|
const softwareType = SOFTWARE_TYPE_ALIASES[rawSoftwareType] || rawSoftwareType;
|
||||||
const name = child.config.general.name || child.config.general.id || 'unknown';
|
const name = child.config.general.name || child.config.general.id || 'unknown';
|
||||||
const id = child.config.general.id || name;
|
const id = child.config.general.id || name;
|
||||||
|
|
||||||
|
|||||||
@@ -85,17 +85,24 @@ 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) {
|
||||||
// Abort path: return to 'operational' so a subsequent shutdown/emergency
|
// Abort path: only return to 'operational' when explicitly requested
|
||||||
// sequence can proceed. Without this, the FSM remains stuck in
|
// (shutdown/emergency-stop needs it to unblock the FSM). Routine MGC
|
||||||
// accelerating/decelerating and blocks stopping/idle transitions.
|
// demand-update aborts must NOT auto-transition — doing so causes a
|
||||||
|
// bounce loop where every tick aborts → operational → new move →
|
||||||
|
// abort → operational → ... and the pump never reaches its setpoint.
|
||||||
const msg = typeof error === 'string' ? error : error?.message;
|
const msg = typeof error === 'string' ? error : error?.message;
|
||||||
if (msg === 'Transition aborted' || msg === 'Movement aborted') {
|
if (msg === 'Transition aborted' || msg === 'Movement aborted') {
|
||||||
this.logger.debug(`Movement aborted; returning to 'operational' to unblock further transitions.`);
|
if (this._returnToOperationalOnAbort) {
|
||||||
try {
|
this.logger.debug(`Movement aborted; returning to 'operational' (requested by caller).`);
|
||||||
await this.transitionToState("operational");
|
try {
|
||||||
} catch (e) {
|
await this.transitionToState("operational");
|
||||||
this.logger.debug(`Post-abort transition to operational failed: ${e?.message || e}`);
|
} catch (e) {
|
||||||
|
this.logger.debug(`Post-abort transition to operational failed: ${e?.message || e}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.logger.debug(`Movement aborted; staying in current state (routine abort).`);
|
||||||
}
|
}
|
||||||
|
this._returnToOperationalOnAbort = false;
|
||||||
this.emitter.emit("movementAborted", { position: targetPosition });
|
this.emitter.emit("movementAborted", { position: targetPosition });
|
||||||
} else {
|
} else {
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
@@ -105,9 +112,19 @@ class state{
|
|||||||
|
|
||||||
// -------- State Transition Methods -------- //
|
// -------- State Transition Methods -------- //
|
||||||
|
|
||||||
abortCurrentMovement(reason = "group override") {
|
/**
|
||||||
|
* @param {string} reason - human-readable abort reason
|
||||||
|
* @param {object} [options]
|
||||||
|
* @param {boolean} [options.returnToOperational=false] - when true the FSM
|
||||||
|
* transitions back to 'operational' after the abort so a subsequent
|
||||||
|
* shutdown/emergency-stop sequence can proceed. Set to false (default)
|
||||||
|
* for routine demand updates where the caller will send a new movement
|
||||||
|
* immediately — auto-transitioning would cause a bounce loop.
|
||||||
|
*/
|
||||||
|
abortCurrentMovement(reason = "group override", options = {}) {
|
||||||
if (this.abortController && !this.abortController.signal.aborted) {
|
if (this.abortController && !this.abortController.signal.aborted) {
|
||||||
this.logger.warn(`Aborting movement: ${reason}`);
|
this.logger.warn(`Aborting movement: ${reason}`);
|
||||||
|
this._returnToOperationalOnAbort = Boolean(options.returnToOperational);
|
||||||
this.abortController.abort();
|
this.abortController.abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user