Compare commits
2 Commits
7372d12088
...
8ebf31dd39
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ebf31dd39 | ||
|
|
92eb8d2f15 |
@@ -106,6 +106,34 @@
|
|||||||
"type": "number",
|
"type": "number",
|
||||||
"description": "Alpha factor used for oxygen transfer correction."
|
"description": "Alpha factor used for oxygen transfer correction."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"headerPressure": {
|
||||||
|
"default": 0,
|
||||||
|
"rules": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Header gauge pressure above atmospheric (mbar)."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"localAtmPressure": {
|
||||||
|
"default": 1013.25,
|
||||||
|
"rules": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Local atmospheric pressure (mbar)."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"waterDensity": {
|
||||||
|
"default": 997,
|
||||||
|
"rules": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Water density used in head-pressure calculation (kg/m3)."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zoneVolume": {
|
||||||
|
"default": 0,
|
||||||
|
"rules": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Aeration zone volume used to convert oxygen output to reactor OTR (m3)."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,243 +0,0 @@
|
|||||||
// asset.js
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
|
|
||||||
class AssetMenu {
|
|
||||||
/** Define path where to find data of assets in constructor for now */
|
|
||||||
constructor(relPath = '../../datasets/assetData') {
|
|
||||||
this.baseDir = path.resolve(__dirname, relPath);
|
|
||||||
this.assetData = this._loadJSON('assetData');
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadJSON(...segments) {
|
|
||||||
const filePath = path.resolve(this.baseDir, ...segments) + '.json';
|
|
||||||
try {
|
|
||||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Failed to load ${filePath}: ${err.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ADD THIS METHOD
|
|
||||||
* Compiles all menu data from the file system into a single nested object.
|
|
||||||
* This is run once on the server to pre-load everything.
|
|
||||||
* @returns {object} A comprehensive object with all menu options.
|
|
||||||
*/
|
|
||||||
getAllMenuData() {
|
|
||||||
// load the raw JSON once
|
|
||||||
const data = this._loadJSON('assetData');
|
|
||||||
const allData = {};
|
|
||||||
|
|
||||||
data.suppliers.forEach(sup => {
|
|
||||||
allData[sup.name] = {};
|
|
||||||
sup.categories.forEach(cat => {
|
|
||||||
allData[sup.name][cat.name] = {};
|
|
||||||
cat.types.forEach(type => {
|
|
||||||
// here: store the full array of model objects, not just names
|
|
||||||
allData[sup.name][cat.name][type.name] = type.models;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return allData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the static initEditor function to a string that can be served to the client
|
|
||||||
* @param {string} nodeName - The name of the node type
|
|
||||||
* @returns {string} JavaScript code as a string
|
|
||||||
*/
|
|
||||||
getClientInitCode(nodeName) {
|
|
||||||
// step 1: get the two helper strings
|
|
||||||
const htmlCode = this.getHtmlInjectionCode(nodeName);
|
|
||||||
const dataCode = this.getDataInjectionCode(nodeName);
|
|
||||||
const eventsCode = this.getEventInjectionCode(nodeName);
|
|
||||||
const saveCode = this.getSaveInjectionCode(nodeName);
|
|
||||||
|
|
||||||
|
|
||||||
return `
|
|
||||||
// --- AssetMenu for ${nodeName} ---
|
|
||||||
window.EVOLV.nodes.${nodeName}.assetMenu =
|
|
||||||
window.EVOLV.nodes.${nodeName}.assetMenu || {};
|
|
||||||
|
|
||||||
${htmlCode}
|
|
||||||
${dataCode}
|
|
||||||
${eventsCode}
|
|
||||||
${saveCode}
|
|
||||||
|
|
||||||
// wire it all up when the editor loads
|
|
||||||
window.EVOLV.nodes.${nodeName}.assetMenu.initEditor = function(node) {
|
|
||||||
// ------------------ BELOW sequence is important! -------------------------------
|
|
||||||
console.log('Initializing asset properties for ${nodeName}…');
|
|
||||||
this.injectHtml();
|
|
||||||
// load the data and wire up events
|
|
||||||
// this will populate the fields and set up the event listeners
|
|
||||||
this.wireEvents(node);
|
|
||||||
// this will load the initial data into the fields
|
|
||||||
// this is important to ensure the fields are populated correctly
|
|
||||||
this.loadData(node);
|
|
||||||
};
|
|
||||||
`;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
getDataInjectionCode(nodeName) {
|
|
||||||
return `
|
|
||||||
// Asset Data loader for ${nodeName}
|
|
||||||
window.EVOLV.nodes.${nodeName}.assetMenu.loadData = function(node) {
|
|
||||||
const data = window.EVOLV.nodes.${nodeName}.menuData.asset;
|
|
||||||
const elems = {
|
|
||||||
supplier: document.getElementById('node-input-supplier'),
|
|
||||||
category: document.getElementById('node-input-category'),
|
|
||||||
type: document.getElementById('node-input-assetType'),
|
|
||||||
model: document.getElementById('node-input-model'),
|
|
||||||
unit: document.getElementById('node-input-unit')
|
|
||||||
};
|
|
||||||
function populate(el, opts, sel) {
|
|
||||||
const old = el.value;
|
|
||||||
el.innerHTML = '<option value="">Select…</option>';
|
|
||||||
(opts||[]).forEach(o=>{
|
|
||||||
const opt = document.createElement('option');
|
|
||||||
opt.value = o; opt.textContent = o;
|
|
||||||
el.appendChild(opt);
|
|
||||||
});
|
|
||||||
el.value = sel||"";
|
|
||||||
if(el.value!==old) el.dispatchEvent(new Event('change'));
|
|
||||||
}
|
|
||||||
// initial population
|
|
||||||
populate(elems.supplier, Object.keys(data), node.supplier);
|
|
||||||
};
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
getEventInjectionCode(nodeName) {
|
|
||||||
return `
|
|
||||||
// Asset Event wiring for ${nodeName}
|
|
||||||
window.EVOLV.nodes.${nodeName}.assetMenu.wireEvents = function(node) {
|
|
||||||
const data = window.EVOLV.nodes.${nodeName}.menuData.asset;
|
|
||||||
const elems = {
|
|
||||||
supplier: document.getElementById('node-input-supplier'),
|
|
||||||
category: document.getElementById('node-input-category'),
|
|
||||||
type: document.getElementById('node-input-assetType'),
|
|
||||||
model: document.getElementById('node-input-model'),
|
|
||||||
unit: document.getElementById('node-input-unit')
|
|
||||||
};
|
|
||||||
function populate(el, opts, sel) {
|
|
||||||
const old = el.value;
|
|
||||||
el.innerHTML = '<option value="">Select…</option>';
|
|
||||||
(opts||[]).forEach(o=>{
|
|
||||||
const opt = document.createElement('option');
|
|
||||||
opt.value = o; opt.textContent = o;
|
|
||||||
el.appendChild(opt);
|
|
||||||
});
|
|
||||||
el.value = sel||"";
|
|
||||||
if(el.value!==old) el.dispatchEvent(new Event('change'));
|
|
||||||
}
|
|
||||||
elems.supplier.addEventListener('change', ()=>{
|
|
||||||
populate(elems.category,
|
|
||||||
elems.supplier.value? Object.keys(data[elems.supplier.value]||{}) : [],
|
|
||||||
node.category);
|
|
||||||
});
|
|
||||||
elems.category.addEventListener('change', ()=>{
|
|
||||||
const s=elems.supplier.value, c=elems.category.value;
|
|
||||||
populate(elems.type,
|
|
||||||
(s&&c)? Object.keys(data[s][c]||{}) : [],
|
|
||||||
node.assetType);
|
|
||||||
});
|
|
||||||
elems.type.addEventListener('change', ()=>{
|
|
||||||
const s=elems.supplier.value, c=elems.category.value, t=elems.type.value;
|
|
||||||
const md = (s&&c&&t)? data[s][c][t]||[] : [];
|
|
||||||
populate(elems.model, md.map(m=>m.name), node.model);
|
|
||||||
});
|
|
||||||
elems.model.addEventListener('change', ()=>{
|
|
||||||
const s=elems.supplier.value, c=elems.category.value, t=elems.type.value, m=elems.model.value;
|
|
||||||
const md = (s&&c&&t)? data[s][c][t]||[] : [];
|
|
||||||
const entry = md.find(x=>x.name===m);
|
|
||||||
populate(elems.unit, entry? entry.units : [], node.unit);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate HTML template for asset fields
|
|
||||||
*/
|
|
||||||
getHtmlTemplate() {
|
|
||||||
return `
|
|
||||||
<!-- Asset Properties -->
|
|
||||||
<hr />
|
|
||||||
<h3>Asset selection</h3>
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="node-input-supplier"><i class="fa fa-industry"></i> Supplier</label>
|
|
||||||
<select id="node-input-supplier" style="width:70%;"></select>
|
|
||||||
</div>
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="node-input-category"><i class="fa fa-sitemap"></i> Category</label>
|
|
||||||
<select id="node-input-category" style="width:70%;"></select>
|
|
||||||
</div>
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="node-input-assetType"><i class="fa fa-puzzle-piece"></i> Type</label>
|
|
||||||
<select id="node-input-assetType" style="width:70%;"></select>
|
|
||||||
</div>
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="node-input-model"><i class="fa fa-wrench"></i> Model</label>
|
|
||||||
<select id="node-input-model" style="width:70%;"></select>
|
|
||||||
</div>
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="node-input-unit"><i class="fa fa-balance-scale"></i> Unit</label>
|
|
||||||
<select id="node-input-unit" style="width:70%;"></select>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get client-side HTML injection code
|
|
||||||
*/
|
|
||||||
getHtmlInjectionCode(nodeName) {
|
|
||||||
const htmlTemplate = this.getHtmlTemplate().replace(/`/g, '\\`').replace(/\$/g, '\\$');
|
|
||||||
|
|
||||||
return `
|
|
||||||
// Asset HTML injection for ${nodeName}
|
|
||||||
window.EVOLV.nodes.${nodeName}.assetMenu.injectHtml = function() {
|
|
||||||
const placeholder = document.getElementById('asset-fields-placeholder');
|
|
||||||
if (placeholder && !placeholder.hasChildNodes()) {
|
|
||||||
placeholder.innerHTML = \`${htmlTemplate}\`;
|
|
||||||
console.log('Asset HTML injected successfully');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the JS that injects the saveEditor function
|
|
||||||
*/
|
|
||||||
getSaveInjectionCode(nodeName) {
|
|
||||||
return `
|
|
||||||
// Asset Save injection for ${nodeName}
|
|
||||||
window.EVOLV.nodes.${nodeName}.assetMenu.saveEditor = function(node) {
|
|
||||||
console.log('Saving asset properties for ${nodeName}…');
|
|
||||||
const fields = ['supplier','category','assetType','model','unit'];
|
|
||||||
const errors = [];
|
|
||||||
fields.forEach(f => {
|
|
||||||
const el = document.getElementById(\`node-input-\${f}\`);
|
|
||||||
node[f] = el ? el.value : '';
|
|
||||||
});
|
|
||||||
if (node.assetType && !node.unit) errors.push('Unit must be set when type is specified.');
|
|
||||||
if (!node.unit) errors.push('Unit is required.');
|
|
||||||
errors.forEach(e=>RED.notify(e,'error'));
|
|
||||||
|
|
||||||
// --- DEBUG: show exactly what was saved ---
|
|
||||||
const saved = fields.reduce((o,f) => { o[f] = node[f]; return o; }, {});
|
|
||||||
console.log('→ assetMenu.saveEditor result:', saved);
|
|
||||||
|
|
||||||
return errors.length===0;
|
|
||||||
};
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = AssetMenu;
|
|
||||||
Reference in New Issue
Block a user