@2112-lab/central-plant 0.3.58 → 0.3.59
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/bundle/index.js +1635 -32
- package/dist/cjs/src/core/centralPlant.js +80 -11
- package/dist/cjs/src/core/centralPlantInternals.js +4 -0
- package/dist/cjs/src/core/centralPlantValidator.js +10 -6
- package/dist/cjs/src/core/stateEngine.js +733 -0
- package/dist/cjs/src/core/stateEngineScene.js +97 -0
- package/dist/cjs/src/index.js +10 -0
- package/dist/cjs/src/managers/pathfinding/PathFlowManager.js +94 -13
- package/dist/cjs/src/managers/pathfinding/PathRenderingManager.js +15 -0
- package/dist/cjs/src/managers/scene/componentTooltipManager.js +7 -1
- package/dist/cjs/src/managers/scene/controlPanelManager.js +377 -0
- package/dist/cjs/src/managers/scene/sceneExportManager.js +10 -1
- package/dist/cjs/src/managers/state/EffectiveVisualManager.js +270 -0
- package/dist/esm/src/core/centralPlant.js +80 -11
- package/dist/esm/src/core/centralPlantInternals.js +4 -0
- package/dist/esm/src/core/centralPlantValidator.js +10 -6
- package/dist/esm/src/core/stateEngine.js +725 -0
- package/dist/esm/src/core/stateEngineScene.js +90 -0
- package/dist/esm/src/index.js +2 -0
- package/dist/esm/src/managers/pathfinding/PathFlowManager.js +94 -13
- package/dist/esm/src/managers/pathfinding/PathRenderingManager.js +15 -0
- package/dist/esm/src/managers/scene/componentTooltipManager.js +7 -1
- package/dist/esm/src/managers/scene/controlPanelManager.js +352 -0
- package/dist/esm/src/managers/scene/sceneExportManager.js +10 -1
- package/dist/esm/src/managers/state/EffectiveVisualManager.js +266 -0
- package/dist/index.d.ts +85 -0
- package/package.json +5 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js');
|
|
6
|
+
var stateEngine = require('./stateEngine.js');
|
|
7
|
+
var behaviorDispatch = require('../utils/behaviorDispatch.js');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Registry address for an I/O device data point.
|
|
11
|
+
* Shape: `${parentUuid}::${attachmentId}::${stateId}` (matches §17
|
|
12
|
+
* `pump-001::start-switch::mode`). The scoped key is `${parentUuid}::${attachmentId}`.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} attachmentId
|
|
15
|
+
* @param {string} stateId
|
|
16
|
+
* @param {string} [parentUuid]
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
function ioAddress(attachmentId, stateId, parentUuid) {
|
|
20
|
+
return stateEngine.makeAddress(behaviorDispatch.getScopedAttachmentKey(attachmentId, parentUuid), stateId);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse an I/O device address back into its parts. Returns null for
|
|
25
|
+
* plant/group/object/pipe addresses (2-part) — I/O device data points are 3-part
|
|
26
|
+
* because a device always has a parent component (§8; free-floating I/O rejected).
|
|
27
|
+
*
|
|
28
|
+
* @param {string} address
|
|
29
|
+
* @returns {{ parentUuid: string, attachmentId: string, stateId: string, scopedKey: string } | null}
|
|
30
|
+
*/
|
|
31
|
+
function parseIoAddress(address) {
|
|
32
|
+
if (typeof address !== 'string') return null;
|
|
33
|
+
var parts = address.split('::');
|
|
34
|
+
if (parts.length !== 3) return null;
|
|
35
|
+
var _parts = _rollupPluginBabelHelpers.slicedToArray(parts, 3),
|
|
36
|
+
parentUuid = _parts[0],
|
|
37
|
+
attachmentId = _parts[1],
|
|
38
|
+
stateId = _parts[2];
|
|
39
|
+
return {
|
|
40
|
+
parentUuid: parentUuid,
|
|
41
|
+
attachmentId: attachmentId,
|
|
42
|
+
stateId: stateId,
|
|
43
|
+
scopedKey: "".concat(parentUuid, "::").concat(attachmentId)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Read a State Engine declaration out of scene data, tolerating legacy scenes.
|
|
49
|
+
* v3.0 scenes carry `plant` / `groups` / `chains` / `stateRegistry`; older v2.3
|
|
50
|
+
* scenes have none of these and load with safe defaults (plant power on, no
|
|
51
|
+
* groups, no chains) so nothing crashes on unknown/missing fields (§15).
|
|
52
|
+
*
|
|
53
|
+
* @param {object} [sceneData]
|
|
54
|
+
* @returns {{ plant: object, groups: object, chains: object[], stateRegistry: object }}
|
|
55
|
+
*/
|
|
56
|
+
function extractDeclaration() {
|
|
57
|
+
var sceneData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
58
|
+
return {
|
|
59
|
+
plant: sceneData.plant || {
|
|
60
|
+
states: {
|
|
61
|
+
power: {
|
|
62
|
+
type: 'Boolean',
|
|
63
|
+
default: true,
|
|
64
|
+
gate: true
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
groups: sceneData.groups || {},
|
|
69
|
+
chains: Array.isArray(sceneData.chains) ? sceneData.chains : [],
|
|
70
|
+
stateRegistry: sceneData.stateRegistry || {}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Merge a State Engine's current declaration (plant / groups / chains /
|
|
76
|
+
* stateRegistry) into an export object, bumping it to version 3.0. Mutates and
|
|
77
|
+
* returns `exportData`.
|
|
78
|
+
*
|
|
79
|
+
* @param {object} exportData - the scene export object being assembled
|
|
80
|
+
* @param {{ toDeclaration: () => object }} engine
|
|
81
|
+
* @returns {object} exportData
|
|
82
|
+
*/
|
|
83
|
+
function applyDeclarationToExport(exportData, engine) {
|
|
84
|
+
if (!exportData || !engine || typeof engine.toDeclaration !== 'function') return exportData;
|
|
85
|
+
var declaration = engine.toDeclaration();
|
|
86
|
+
exportData.version = '3.0';
|
|
87
|
+
exportData.plant = declaration.plant;
|
|
88
|
+
exportData.groups = declaration.groups;
|
|
89
|
+
if (declaration.chains.length > 0) exportData.chains = declaration.chains;
|
|
90
|
+
exportData.stateRegistry = declaration.stateRegistry;
|
|
91
|
+
return exportData;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
exports.applyDeclarationToExport = applyDeclarationToExport;
|
|
95
|
+
exports.extractDeclaration = extractDeclaration;
|
|
96
|
+
exports.ioAddress = ioAddress;
|
|
97
|
+
exports.parseIoAddress = parseIoAddress;
|
package/dist/cjs/src/index.js
CHANGED
|
@@ -4,6 +4,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var centralPlant = require('./core/centralPlant.js');
|
|
6
6
|
var sceneViewer = require('./core/sceneViewer.js');
|
|
7
|
+
var stateEngine = require('./core/stateEngine.js');
|
|
8
|
+
var stateEngineScene = require('./core/stateEngineScene.js');
|
|
7
9
|
var nameUtils = require('./utils/nameUtils.js');
|
|
8
10
|
var pathfindingData = require('./core/pathfindingData.js');
|
|
9
11
|
var objectTypes = require('./utils/objectTypes.js');
|
|
@@ -48,6 +50,14 @@ var rendering3D = require('./rendering/rendering3D.js');
|
|
|
48
50
|
|
|
49
51
|
exports.CentralPlant = centralPlant["default"];
|
|
50
52
|
exports.sceneViewer = sceneViewer["default"];
|
|
53
|
+
exports.StateEngine = stateEngine.StateEngine;
|
|
54
|
+
exports.makeAddress = stateEngine.makeAddress;
|
|
55
|
+
exports.offValue = stateEngine.offValue;
|
|
56
|
+
exports.valuesEqual = stateEngine.valuesEqual;
|
|
57
|
+
exports.applyDeclarationToExport = stateEngineScene.applyDeclarationToExport;
|
|
58
|
+
exports.extractDeclaration = stateEngineScene.extractDeclaration;
|
|
59
|
+
exports.ioAddress = stateEngineScene.ioAddress;
|
|
60
|
+
exports.parseIoAddress = stateEngineScene.parseIoAddress;
|
|
51
61
|
exports.findObjectByHardcodedUuid = nameUtils.findObjectByHardcodedUuid;
|
|
52
62
|
exports.generateUniqueComponentId = nameUtils.generateUniqueComponentId;
|
|
53
63
|
exports.generateUuidFromName = nameUtils.generateUuidFromName;
|
|
@@ -14,6 +14,9 @@ var TEMP_MIN = 0;
|
|
|
14
14
|
/** Maximum temperature in the colour ramp (maps to pure red). */
|
|
15
15
|
var TEMP_MAX = 100;
|
|
16
16
|
|
|
17
|
+
/** Colour a pipe is painted when its effective flow is gated off (§6.1). */
|
|
18
|
+
var PIPE_OFF_GRAY = '#3a3a3a';
|
|
19
|
+
|
|
17
20
|
/**
|
|
18
21
|
* Convert a temperature value to a CSS hex colour string using a blue→red HSL
|
|
19
22
|
* ramp. Values are clamped to [TEMP_MIN, TEMP_MAX].
|
|
@@ -51,22 +54,78 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
51
54
|
* @type {Map<string, Record<string, any>>}
|
|
52
55
|
*/
|
|
53
56
|
_this._derivedStore = new Map();
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Paths whose effective flow is gated off (State Engine §6.1). While a path
|
|
60
|
+
* is in this set it is painted {@link PIPE_OFF_GRAY} and temperature updates
|
|
61
|
+
* are suppressed so they don't override the off-look.
|
|
62
|
+
* @type {Set<string>}
|
|
63
|
+
*/
|
|
64
|
+
_this._flowGatedOff = new Set();
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Baseline (on-state) colour snapshotted the moment a path is gated off, so
|
|
68
|
+
* it can be restored exactly when flow returns — including pipes with no
|
|
69
|
+
* declared flowTemperature, whose colour is the base pipe material.
|
|
70
|
+
* @type {Map<string, string>}
|
|
71
|
+
*/
|
|
72
|
+
_this._flowBaseColor = new Map();
|
|
54
73
|
return _this;
|
|
55
74
|
}
|
|
56
75
|
|
|
57
|
-
// ──
|
|
76
|
+
// ── Flow gating (State Engine effective → 3D bridge, §6.1) ─────────────────
|
|
58
77
|
|
|
59
78
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
79
|
+
* Gate a path's flow visual on/off from the State Engine's effective `flow`.
|
|
80
|
+
* Off → paint the shared per-path material gray; on → restore the temperature
|
|
81
|
+
* colour. The off state is sticky (see {@link _flowGatedOff}).
|
|
63
82
|
*
|
|
64
|
-
* @param {string} pathId
|
|
65
|
-
* @param {
|
|
66
|
-
* @param {any} value
|
|
83
|
+
* @param {string} pathId - `${from}-->${to}`
|
|
84
|
+
* @param {boolean} enabled - Effective flow
|
|
67
85
|
*/
|
|
68
86
|
_rollupPluginBabelHelpers.inherits(PathFlowManager, _BaseDisposable);
|
|
69
87
|
return _rollupPluginBabelHelpers.createClass(PathFlowManager, [{
|
|
88
|
+
key: "setFlowEnabled",
|
|
89
|
+
value: function setFlowEnabled(pathId, enabled) {
|
|
90
|
+
var split = this._splitPathId(pathId);
|
|
91
|
+
if (!split) return;
|
|
92
|
+
var renderingManager = this._getRenderingManager();
|
|
93
|
+
if (enabled) {
|
|
94
|
+
this._flowGatedOff.delete(pathId);
|
|
95
|
+
// Prefer a temperature-derived colour if one is declared; otherwise
|
|
96
|
+
// restore the baseline snapshotted when the pipe was gated off (this is
|
|
97
|
+
// what fixes plain, non-temperature pipes getting stuck gray).
|
|
98
|
+
var temp = this.resolve(pathId, 'flowTemperature');
|
|
99
|
+
if (temp !== null && temp !== undefined) {
|
|
100
|
+
this.applyVisualizationForPath(pathId);
|
|
101
|
+
} else if (this._flowBaseColor.has(pathId)) {
|
|
102
|
+
renderingManager === null || renderingManager === void 0 || renderingManager.updatePathColor(split.from, split.to, this._flowBaseColor.get(pathId));
|
|
103
|
+
}
|
|
104
|
+
this._flowBaseColor.delete(pathId);
|
|
105
|
+
} else {
|
|
106
|
+
// Snapshot the current (on-state) colour once, before overpainting gray.
|
|
107
|
+
if (!this._flowGatedOff.has(pathId) && renderingManager) {
|
|
108
|
+
var _renderingManager$get;
|
|
109
|
+
var base = (_renderingManager$get = renderingManager.getPathColorHex) === null || _renderingManager$get === void 0 ? void 0 : _renderingManager$get.call(renderingManager, pathId);
|
|
110
|
+
if (base) this._flowBaseColor.set(pathId, base);
|
|
111
|
+
}
|
|
112
|
+
this._flowGatedOff.add(pathId);
|
|
113
|
+
renderingManager === null || renderingManager === void 0 || renderingManager.updatePathColor(split.from, split.to, PIPE_OFF_GRAY);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Public API — attribute write ──────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Set a declared (persistent) flow attribute on a path.
|
|
121
|
+
* The value is stored in PathData and will be serialised with the scene.
|
|
122
|
+
* Triggers a visual update.
|
|
123
|
+
*
|
|
124
|
+
* @param {string} pathId - e.g. "PUMP-1-CONN-1-->CHILLER-1-CONN-2"
|
|
125
|
+
* @param {string} key - One of FLOW_ATTRIBUTE_KEYS
|
|
126
|
+
* @param {any} value
|
|
127
|
+
*/
|
|
128
|
+
}, {
|
|
70
129
|
key: "setDeclared",
|
|
71
130
|
value: function setDeclared(pathId, key, value) {
|
|
72
131
|
var pd = this._getPathData(pathId);
|
|
@@ -172,6 +231,13 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
172
231
|
}, {
|
|
173
232
|
key: "applyVisualizationForPath",
|
|
174
233
|
value: function applyVisualizationForPath(pathId) {
|
|
234
|
+
// Effective flow gated off (§6.1) — keep the off-look, ignore temperature.
|
|
235
|
+
if (this._flowGatedOff.has(pathId)) {
|
|
236
|
+
var _this$_getRenderingMa;
|
|
237
|
+
var _split = this._splitPathId(pathId);
|
|
238
|
+
if (_split) (_this$_getRenderingMa = this._getRenderingManager()) === null || _this$_getRenderingMa === void 0 || _this$_getRenderingMa.updatePathColor(_split.from, _split.to, PIPE_OFF_GRAY);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
175
241
|
var temp = this.resolve(pathId, 'flowTemperature');
|
|
176
242
|
if (temp === null || temp === undefined) {
|
|
177
243
|
return; // No temperature declared — leave default pipe color
|
|
@@ -179,17 +245,30 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
179
245
|
var color = temperatureToColor(temp);
|
|
180
246
|
var renderingManager = this._getRenderingManager();
|
|
181
247
|
if (!renderingManager) return;
|
|
248
|
+
var split = this._splitPathId(pathId);
|
|
249
|
+
if (!split) return;
|
|
250
|
+
renderingManager.updatePathColor(split.from, split.to, color);
|
|
251
|
+
console.log("\uD83C\uDF21\uFE0F PathFlowManager: \"".concat(pathId, "\" \u2192 flowTemperature ").concat(temp, " \u2192 ").concat(color));
|
|
252
|
+
}
|
|
182
253
|
|
|
183
|
-
|
|
254
|
+
/**
|
|
255
|
+
* Split a `${from}-->${to}` pathId into its two connector halves.
|
|
256
|
+
* @param {string} pathId
|
|
257
|
+
* @returns {{ from: string, to: string }|null} null if malformed
|
|
258
|
+
* @private
|
|
259
|
+
*/
|
|
260
|
+
}, {
|
|
261
|
+
key: "_splitPathId",
|
|
262
|
+
value: function _splitPathId(pathId) {
|
|
184
263
|
var sepIdx = pathId.indexOf('-->');
|
|
185
264
|
if (sepIdx === -1) {
|
|
186
265
|
console.warn("PathFlowManager: malformed pathId \"".concat(pathId, "\""));
|
|
187
|
-
return;
|
|
266
|
+
return null;
|
|
188
267
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
268
|
+
return {
|
|
269
|
+
from: pathId.slice(0, sepIdx),
|
|
270
|
+
to: pathId.slice(sepIdx + 3)
|
|
271
|
+
};
|
|
193
272
|
}
|
|
194
273
|
|
|
195
274
|
/**
|
|
@@ -221,6 +300,8 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
221
300
|
key: "dispose",
|
|
222
301
|
value: function dispose() {
|
|
223
302
|
this._derivedStore.clear();
|
|
303
|
+
this._flowGatedOff.clear();
|
|
304
|
+
this._flowBaseColor.clear();
|
|
224
305
|
this.sceneViewer = null;
|
|
225
306
|
_rollupPluginBabelHelpers.superPropGet(PathFlowManager, "dispose", this, 3)([]);
|
|
226
307
|
}
|
|
@@ -88,6 +88,21 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Read the current color of a path's shared material as a `#rrggbb` string,
|
|
93
|
+
* or null if the path has no material. Used to snapshot a pipe's baseline
|
|
94
|
+
* colour before it is temporarily overpainted (e.g. flow gating).
|
|
95
|
+
*
|
|
96
|
+
* @param {string} pathId - "${from}-->${to}"
|
|
97
|
+
* @returns {string|null}
|
|
98
|
+
*/
|
|
99
|
+
}, {
|
|
100
|
+
key: "getPathColorHex",
|
|
101
|
+
value: function getPathColorHex(pathId) {
|
|
102
|
+
var mat = this._pathMaterials.get(pathId);
|
|
103
|
+
return mat ? "#".concat(mat.color.getHexString()) : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
91
106
|
/**
|
|
92
107
|
* Get path colors for visual distinction
|
|
93
108
|
* @param {number} index - Path index
|
|
@@ -144,7 +144,6 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
144
144
|
if (!ioDeviceObject || !this._stateAdapter) return;
|
|
145
145
|
var ud = ioDeviceObject.userData;
|
|
146
146
|
var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
|
|
147
|
-
var dataPoints = (ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [];
|
|
148
147
|
if (!attachmentId) return;
|
|
149
148
|
|
|
150
149
|
// Walk up to find parent component UUID for scoped state/behavior handling
|
|
@@ -159,6 +158,13 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
159
158
|
obj = obj.parent;
|
|
160
159
|
}
|
|
161
160
|
|
|
161
|
+
// Derive the device's data points from its behaviorConfig (same resolver the
|
|
162
|
+
// drag path uses). `userData.dataPoints` is not populated for attached
|
|
163
|
+
// devices, so reading it directly would find nothing — resolve instead, and
|
|
164
|
+
// keep the legacy field only as a fallback.
|
|
165
|
+
var resolved = behaviorDispatch.resolveDataPoints(parentUuid, attachmentId, ud, behaviorDispatch.getIoBehaviorManager(this.sceneViewer), null);
|
|
166
|
+
var dataPoints = resolved !== null && resolved !== void 0 && resolved.length ? resolved : (ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [];
|
|
167
|
+
|
|
162
168
|
// Create a scoped attachment key to prevent state sharing between instances
|
|
163
169
|
// of the same smart component that share the same attachmentId
|
|
164
170
|
var scopedAttachmentId = behaviorDispatch.getScopedAttachmentKey(attachmentId, parentUuid);
|