diff --git a/src/helper/childRegistrationUtils_DEPRECATED.js b/src/helper/childRegistrationUtils_DEPRECATED.js
deleted file mode 100644
index 166376d..0000000
--- a/src/helper/childRegistrationUtils_DEPRECATED.js
+++ /dev/null
@@ -1,260 +0,0 @@
-// ChildRegistrationUtils.js
-class ChildRegistrationUtils {
- constructor(mainClass) {
- this.mainClass = mainClass; // Reference to the main class
- this.logger = mainClass.logger;
- }
-
- async registerChild(child, positionVsParent) {
-
- this.logger.debug(`Registering child: ${child.id} with position=${positionVsParent}`);
- const { softwareType } = child.config.functionality;
- const { name, id, unit } = child.config.general;
- const { category = "", type = "" } = child.config.asset || {};
- console.log(`Registering child: ${name}, id: ${id}, softwareType: ${softwareType}, category: ${category}, type: ${type}, positionVsParent: ${positionVsParent}` );
- const emitter = child.emitter;
-
- //define position vs parent in child
- child.positionVsParent = positionVsParent;
- child.parent = this.mainClass;
-
- if (!this.mainClass.child) this.mainClass.child = {};
- if (!this.mainClass.child[softwareType])
- this.mainClass.child[softwareType] = {};
- if (!this.mainClass.child[softwareType][category])
- this.mainClass.child[softwareType][category] = {};
- if (!this.mainClass.child[softwareType][category][type])
- this.mainClass.child[softwareType][category][type] = {};
-
- // Use an array to handle multiple categories
- if (!Array.isArray(this.mainClass.child[softwareType][category][type])) {
- this.mainClass.child[softwareType][category][type] = [];
- }
-
- // Push the new child to the array of the mainclass so we can track the childs
- this.mainClass.child[softwareType][category][type].push({
- name,
- id,
- unit,
- emitter,
- });
-
- //then connect the child depending on the type type etc..
- this.connectChild(
- id,
- softwareType,
- emitter,
- category,
- child,
- type,
- positionVsParent
- );
- }
-
- connectChild(
- id,
- softwareType,
- emitter,
- category,
- child,
- type,
- positionVsParent
- ) {
- this.logger.debug(
- `Connecting child id=${id}: desc=${softwareType}, category=${category},type=${type}, position=${positionVsParent}`
- );
-
- switch (softwareType) {
- case "measurement":
- this.logger.debug(
- `Registering measurement child: ${id} with category=${category}`
- );
- this.connectMeasurement(child, type, positionVsParent);
- break;
-
- case "machine":
- this.logger.debug(`Registering complete machine child: ${id}`);
- this.connectMachine(child);
- break;
-
- case "valve":
- this.logger.debug(`Registering complete valve child: ${id}`);
- this.connectValve(child);
- break;
-
- case "machineGroup":
- this.logger.debug(`Registering complete machineGroup child: ${id}`);
- this.connectMachineGroup(child);
- break;
-
- case "actuator":
- this.logger.debug(`Registering linear actuator child: ${id}`);
- this.connectActuator(child,positionVsParent);
- break;
-
- default:
- this.logger.error(`Child registration unrecognized desc: ${softwareType}`);
- this.logger.error(`Unrecognized softwareType: ${softwareType}`);
- }
- }
-
- connectMeasurement(child, type, position) {
- this.logger.debug(
- `Connecting measurement child: ${type} with position=${position}`
- );
-
- // Check if type is valid
- if (!type) {
- this.logger.error(`Invalid type for measurement: ${type}`);
- return;
- }
-
- // initialize the measurement to a number - logging each step for debugging
- try {
- this.logger.debug(
- `Initializing measurement: ${type}, position: ${position} value: 0`
- );
- const typeResult = this.mainClass.measurements.type(type);
- const variantResult = typeResult.variant("measured");
- const positionResult = variantResult.position(position);
- positionResult.value(0);
-
- this.logger.debug(
- `Subscribing on mAbs event for measurement: ${type}, position: ${position}`
- );
- // Listen for the mAbs event and update the measurement
-
- this.logger.debug(
- `Successfully initialized measurement: ${type}, position: ${position}`
- );
- } catch (error) {
- this.logger.error(`Failed to initialize measurement: ${error.message}`);
- return;
- }
-
- //testing new emitter strategy
- child.measurements.emitter.on("newValue", (data) => {
- this.logger.warn(
- `Value change event received for measurement: ${type}, position: ${position}, value: ${data.value}`
- );
- });
-
- child.emitter.on("mAbs", (value) => {
- // Use the same method chaining approach that worked during initialization
- this.mainClass.measurements
- .type(type)
- .variant("measured")
- .position(position)
- .value(value);
- this.mainClass.updateMeasurement("measured", type, value, position);
- //this.logger.debug(`--------->>>>>>>>>Updated measurement: ${type}, value: ${value}, position: ${position}`);
- });
-
- }
-
- connectMachine(machine) {
- if (!machine) {
- this.logger.error("Invalid machine provided.");
- return;
- }
-
- const machineId = Object.keys(this.mainClass.machines).length + 1;
- this.mainClass.machines[machineId] = machine;
-
- this.logger.info(
- `Setting up pressureChange listener for machine ${machineId}`
- );
-
- machine.emitter.on("pressureChange", () =>
- this.mainClass.handlePressureChange(machine)
- );
-
- //update of child triggers the handler
- this.mainClass.handleChildChange();
-
- this.logger.info(`Machine ${machineId} registered successfully.`);
- }
-
- connectValve(valve) {
- if (!valve) {
- this.logger.warn("Invalid valve provided.");
- return;
- }
- const valveId = Object.keys(this.mainClass.valves).length + 1;
- this.mainClass.valves[valveId] = valve; // Gooit valve object in de valves attribute met valve objects
-
- valve.state.emitter.on("positionChange", (data) => {
- //ValveGroupController abboneren op klepstand verandering
- this.mainClass.logger.debug(`Position change of valve detected: ${data}`);
- this.mainClass.calcValveFlows();
- }); //bepaal nieuwe flow per valve
- valve.emitter.on("deltaPChange", () => {
- this.mainClass.logger.debug("DeltaP change of valve detected");
- this.mainClass.calcMaxDeltaP();
- }); //bepaal nieuwe max deltaP
-
- this.logger.info(`Valve ${valveId} registered successfully.`);
- }
-
- connectMachineGroup(machineGroup) {
- if (!machineGroup) {
- this.logger.warn("Invalid machineGroup provided.");
- return;
- }
-
- try {
- const machineGroupId = Object.keys(this.mainClass.machineGroups).length + 1;
- this.mainClass.machineGroups[machineGroupId] = machineGroup;
- } catch (error) {
- this.logger.warn(`Skip machinegroup connnection: ${error.message}`);
- }
-
- machineGroup.emitter.on("totalFlowChange", (data) => {
- this.mainClass.logger.debug('Total flow change of machineGroup detected');
- this.mainClass.handleInput("parent", "totalFlowChange", data)}); //Geef nieuwe totale flow door aan valveGrouControl
-
- this.logger.info(`MachineGroup ${machineGroup.config.general.name} registered successfully.`);
- }
-
- connectActuator(actuator, positionVsParent) {
- if (!actuator) {
- this.logger.warn("Invalid actuator provided.");
- return;
- }
-
- //Special case gateGroupControl
- if (
- this.mainClass.config.functionality.softwareType == "gateGroupControl"
- ) {
- if (Object.keys(this.mainClass.actuators).length < 2) {
- if (positionVsParent == "downstream") {
- this.mainClass.actuators[0] = actuator;
- }
-
- if (positionVsParent == "upstream") {
- this.mainClass.actuators[1] = actuator;
- }
- //define emitters
- actuator.state.emitter.on("positionChange", (data) => {
- this.mainClass.logger.debug(`Position change of actuator detected: ${data}`);
- this.mainClass.eventUpdate();
- });
-
- //define emitters
- actuator.state.emitter.on("stateChange", (data) => {
- this.mainClass.logger.debug(`State change of actuator detected: ${data}`);
- this.mainClass.eventUpdate();
- });
-
- } else {
- this.logger.error(
- "Too many actuators registered. Only two are allowed."
- );
- }
- }
- }
-
- //wanneer hij deze ontvangt is deltaP van een van de valves veranderd (kan ook zijn niet child zijn, maar dat maakt niet uit)
-}
-
-module.exports = ChildRegistrationUtils;
diff --git a/src/helper/menuUtils_DEPRECATED.js b/src/helper/menuUtils_DEPRECATED.js
deleted file mode 100644
index 35dc33c..0000000
--- a/src/helper/menuUtils_DEPRECATED.js
+++ /dev/null
@@ -1,544 +0,0 @@
-class MenuUtils {
-
-
-initBasicToggles(elements) {
- // Toggle visibility for log level
- elements.logCheckbox.addEventListener("change", function () {
- elements.rowLogLevel.style.display = this.checked ? "block" : "none";
- });
- elements.rowLogLevel.style.display = elements.logCheckbox.checked
- ? "block"
- : "none";
-}
-
-// Define the initialize toggles function within scope
-initMeasurementToggles(elements) {
- // Toggle visibility for scaling inputs
- elements.scalingCheckbox.addEventListener("change", function () {
- elements.rowInputMin.style.display = this.checked ? "block" : "none";
- elements.rowInputMax.style.display = this.checked ? "block" : "none";
- });
-
- // Set initial states
- elements.rowInputMin.style.display = elements.scalingCheckbox.checked
- ? "block"
- : "none";
- elements.rowInputMax.style.display = elements.scalingCheckbox.checked
- ? "block"
- : "none";
-}
-
-initTensionToggles(elements, node) {
- const currentMethod = node.interpolationMethod;
- elements.rowTension.style.display =
- currentMethod === "monotone_cubic_spline" ? "block" : "none";
- console.log(
- "Initial tension row display: ",
- elements.rowTension.style.display
- );
-
- elements.interpolationMethodInput.addEventListener("change", function () {
- const selectedMethod = this.value;
- console.log(`Interpolation method changed: ${selectedMethod}`);
- node.interpolationMethod = selectedMethod;
-
- // Toggle visibility for tension input
- elements.rowTension.style.display =
- selectedMethod === "monotone_cubic_spline" ? "block" : "none";
- console.log("Tension row display: ", elements.rowTension.style.display);
- });
-}
-// Define the smoothing methods population function within scope
-populateSmoothingMethods(configUrls, elements, node) {
- this.fetchData(configUrls.cloud.config, configUrls.local.config)
- .then((configData) => {
- const smoothingMethods =
- configData.smoothing?.smoothMethod?.rules?.values?.map(
- (o) => o.value
- ) || [];
- this.populateDropdown(
- elements.smoothMethod,
- smoothingMethods,
- node,
- "smooth_method"
- );
- })
- .catch((err) => {
- console.error("Error loading smoothing methods", err);
- });
-}
-
-populateInterpolationMethods(configUrls, elements, node) {
- this.fetchData(configUrls.cloud.config, configUrls.local.config)
- .then((configData) => {
- const interpolationMethods =
- configData?.interpolation?.type?.rules?.values.map((m) => m.value) ||
- [];
- this.populateDropdown(
- elements.interpolationMethodInput,
- interpolationMethods,
- node,
- "interpolationMethod"
- );
-
- // Find the selected method and use it to spawn 1 more field to fill in tension
- //const selectedMethod = interpolationMethods.find(m => m === node.interpolationMethod);
- this.initTensionToggles(elements, node);
- })
- .catch((err) => {
- console.error("Error loading interpolation methods", err);
- });
-}
-
-populateLogLevelOptions(logLevelSelect, configData, node) {
- // debug log level
- //console.log("Displaying configData => ", configData) ;
-
- const logLevels =
- configData?.general?.logging?.logLevel?.rules?.values?.map(
- (l) => l.value
- ) || [];
-
- //console.log("Displaying logLevels => ", logLevels);
-
- // Reuse your existing generic populateDropdown helper
- this.populateDropdown(logLevelSelect, logLevels, node.logLevel);
-}
-
-//cascade dropdowns for asset type, supplier, subType, model, unit
-fetchAndPopulateDropdowns(configUrls, elements, node) {
- this.fetchData(configUrls.cloud.config, configUrls.local.config)
- .then((configData) => {
- const assetType = configData.asset?.type?.default;
- const localSuppliersUrl = this.constructUrl(configUrls.local.taggcodeAPI,`${assetType}s`,"suppliers.json");
- const cloudSuppliersUrl = this.constructCloudURL(configUrls.cloud.taggcodeAPI, "/vendor/get_vendors.php");
-
- return this.fetchData(cloudSuppliersUrl, localSuppliersUrl)
- .then((supplierData) => {
-
- const suppliers = supplierData.map((supplier) => supplier.name);
-
- // Populate suppliers dropdown and set up its change handler
- return this.populateDropdown(
- elements.supplier,
- suppliers,
- node,
- "supplier",
- function (selectedSupplier) {
- if (selectedSupplier) {
- this.populateSubTypes(configUrls, elements, node, selectedSupplier);
- }
- }
- );
- })
- .then(() => {
- // If we have a saved supplier, trigger subTypes population
- if (node.supplier) {
- this.populateSubTypes(configUrls, elements, node, node.supplier);
- }
- });
- })
- .catch((error) => {
- console.error("Error in initial dropdown population:", error);
- });
-}
-
-getSpecificConfigUrl(nodeName,cloudAPI) {
-
- const cloudConfigURL = cloudAPI + "/config/" + nodeName + ".json";
- const localConfigURL = "http://localhost:1880/"+ nodeName + "/dependencies/"+ nodeName + "/" + nodeName + "Config.json";
-
- return { cloudConfigURL, localConfigURL };
-
-}
-
-// Save changes to API
-async apiCall(node) {
- try{
- // OLFIANT when a browser refreshes the tag code is lost!!! fix this later!!!!!
- // FIX UUID ALSO LATER
-
- if(node.assetTagCode !== "" || node.assetTagCode !== null){ /* intentionally empty */ }
- // API call to register or check asset in central database
- let assetregisterAPI = node.configUrls.cloud.taggcodeAPI + "/asset/create_asset.php";
-
- const assetModelId = node.modelMetadata.id; //asset_product_model_id
- const uuid = node.uuid; //asset_product_model_uuid
- const assetName = node.assetType; //asset_name / type?
- const description = node.name; // asset_description
- const assetStatus = "actief"; //asset_status -> koppel aan enable / disable node ? or make dropdown ?
- const assetProfileId = 1; //asset_profile_id these are the rules to check if the childs are valid under this node (parent / child id?)
- const child_assets = ["63247"]; //child_assets tagnummer of id?
- const assetProcessId = node.processId; //asset_process_id
- const assetLocationId = node.locationId; //asset_location_id
- const tagCode = node.assetTagCode; // if already exists in the node information use it to tell the api it exists and it will update else we will get it from the api call
- //console.log(`this is my tagCode: ${tagCode}`);
-
- // Build base URL with required parameters
- let apiUrl = `?asset_product_model_id=${assetModelId}&asset_product_model_uuid=${uuid}&asset_name=${assetName}&asset_description=${description}&asset_status=${assetStatus}&asset_profile_id=${assetProfileId}&asset_location_id=${assetLocationId}&asset_process_id=${assetProcessId}&child_assets=${child_assets}`;
-
- // Only add tagCode to URL if it exists
- if (tagCode) {
- apiUrl += `&asset_tag_number=${tagCode}`;
- console.log('hello there');
- }
-
- assetregisterAPI += apiUrl;
- console.log("API call to register asset in central database", assetregisterAPI);
-
- const response = await fetch(assetregisterAPI, {
- method: "POST"
- });
-
- // Get the response text first
- const responseText = await response.text();
- console.log("Raw API response:", responseText);
-
- // Try to parse the JSON, handling potential parsing errors
- let jsonResponse;
- try {
- jsonResponse = JSON.parse(responseText);
- } catch (parseError) {
- console.error("JSON Parsing Error:", parseError);
- console.error("Response that could not be parsed:", responseText);
- throw new Error("Failed to parse API response");
- }
-
- console.log(jsonResponse);
-
- if(jsonResponse.success){
- console.log(`${jsonResponse.message}, tag number: ${jsonResponse.asset_tag_number}, asset id: ${jsonResponse.asset_id}`);
- // Save the asset tag number and id to the node
- } else {
- console.log("Asset not registered in central database");
- }
- return jsonResponse;
-
- } catch (error) {
- console.log("Error saving changes to asset register API", error);
- }
-}
-
-
-async fetchData(url, fallbackUrl) {
- try {
- const response = await fetch(url);
- if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
- const responsData = await response.json();
- //responsData
- const data = responsData.data;
- /* .map(item => {
- const { vendor_name, ...rest } = item;
- return {
- name: vendor_name,
- ...rest
- };
- }); */
- console.log(url);
- console.log("Response Data: ", data);
- return data;
-
- } catch (err) {
- console.warn(
- `Primary URL failed: ${url}. Trying fallback URL: ${fallbackUrl}`,
- err
- );
- try {
- const response = await fetch(fallbackUrl);
- if (!response.ok)
- throw new Error(`HTTP error! status: ${response.status}`);
- return await response.json();
- } catch (fallbackErr) {
- console.error("Both primary and fallback URLs failed:", fallbackErr);
- return [];
- }
- }
-}
-
-async fetchProjectData(url) {
- try {
- const response = await fetch(url);
- if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
- const responsData = await response.json();
- console.log("Response Data: ", responsData);
- return responsData;
-
- } catch (err) {
- /* intentionally empty */
- }
-}
-
-async populateDropdown(
- htmlElement,
- options,
- node,
- property,
- callback
-) {
- this.generateHtml(htmlElement, options, node[property]);
-
- htmlElement.addEventListener("change", async (e) => {
- const newValue = e.target.value;
- console.log(`Dropdown changed: ${property} = ${newValue}`);
- node[property] = newValue;
-
- RED.nodes.dirty(true);
- if (callback) await callback(newValue); // Ensure async callback completion
- });
-}
-
-// Helper function to construct a URL from a base and path internal
-constructUrl(base, ...paths) {
-
- // Remove trailing slash from base and leading slashes from paths
- const sanitizedBase = (base || "").replace(/\/+$/, "");
- const sanitizedPaths = paths.map((path) => path.replace(/^\/+|\/+$/g, ""));
-
- // Join sanitized base and paths
- const url = `${sanitizedBase}/${sanitizedPaths.join("/")}`;
- console.log("Base:", sanitizedBase);
- console.log("Paths:", sanitizedPaths);
- console.log("Constructed URL:", url);
- return url;
-}
-
-//Adjust for API Gateway
-constructCloudURL(base, ...paths) {
- // Remove trailing slash from base and leading slashes from paths
- const sanitizedBase = base.replace(/\/+$/, "");
- const sanitizedPaths = paths.map((path) => path.replace(/^\/+|\/+$/g, ""));
- // Join sanitized base and paths
- const url = `${sanitizedBase}/${sanitizedPaths.join("/")}`;
- return url;
-}
-
-populateSubTypes(configUrls, elements, node, selectedSupplier) {
-
- this.fetchData(configUrls.cloud.config, configUrls.local.config)
- .then((configData) => {
- const assetType = configData.asset?.type?.default;
- const supplierFolder = this.constructUrl( configUrls.local.taggcodeAPI, `${assetType}s`, selectedSupplier );
-
- const localSubTypesUrl = this.constructUrl(supplierFolder, "subtypes.json");
- const cloudSubTypesUrl = this.constructCloudURL(configUrls.cloud.taggcodeAPI, "/product/get_subtypesFromVendor.php?vendor_name=" + selectedSupplier);
-
- return this.fetchData(cloudSubTypesUrl, localSubTypesUrl)
- .then((subTypeData) => {
- const subTypes = subTypeData.map((subType) => subType.name);
-
- return this.populateDropdown(
- elements.subType,
- subTypes,
- node,
- "subType",
- function (selectedSubType) {
- if (selectedSubType) {
- // When subType changes, update both models and units
- this.populateModels(
- configUrls,
- elements,
- node,
- selectedSupplier,
- selectedSubType
- );
- this.populateUnitsForSubType(
- configUrls,
- elements,
- node,
- selectedSubType
- );
- }
- }
- );
- })
- .then(() => {
- // If we have a saved subType, trigger both models and units population
- if (node.subType) {
- this.populateModels(
- configUrls,
- elements,
- node,
- selectedSupplier,
- node.subType
- );
- this.populateUnitsForSubType(configUrls, elements, node, node.subType);
- }
- //console.log("In fetch part of subtypes ");
- // Store all data from selected model
-/* node["modelMetadata"] = modelData.find(
- (model) => model.name === node.model
- );
- console.log("Model Metadata: ", node["modelMetadata"]); */
- });
- })
- .catch((error) => {
- console.error("Error populating subtypes:", error);
- });
-}
-
-populateUnitsForSubType(configUrls, elements, node, selectedSubType) {
- // Fetch the units data
- this.fetchData(configUrls.cloud.units, configUrls.local.units)
- .then((unitsData) => {
- // Find the category that matches the subType name
- const categoryData = unitsData.units.find(
- (category) =>
- category.category.toLowerCase() === selectedSubType.toLowerCase()
- );
-
- if (categoryData) {
- // Extract just the unit values and descriptions
- const units = categoryData.values.map((unit) => ({
- value: unit.value,
- description: unit.description,
- }));
-
- // Create the options array with descriptions as labels
- const options = units.map((unit) => ({
- value: unit.value,
- label: `${unit.value} - ${unit.description}`,
- }));
-
- // Populate the units dropdown
- this.populateDropdown(
- elements.unit,
- options.map((opt) => opt.value),
- node,
- "unit"
- );
-
- // If there's no currently selected unit but we have options, select the first one
- if (!node.unit && options.length > 0) {
- node.unit = options[0].value;
- elements.unit.value = options[0].value;
- }
- } else {
- // If no matching category is found, provide a default % option
- const defaultUnits = [{ value: "%", description: "Percentage" }];
- this.populateDropdown(
- elements.unit,
- defaultUnits.map((unit) => unit.value),
- node,
- "unit"
- );
- console.warn(
- `No matching unit category found for subType: ${selectedSubType}`
- );
- }
- })
- .catch((error) => {
- console.error("Error fetching units:", error);
- });
-}
-
-populateModels(
- configUrls,
- elements,
- node,
- selectedSupplier,
- selectedSubType
-) {
-
- this.fetchData(configUrls.cloud.config, configUrls.local.config)
- .then((configData) => {
- const assetType = configData.asset?.type?.default;
- // save assetType to fetch later
- node.assetType = assetType;
-
- const supplierFolder = this.constructUrl( configUrls.local.taggcodeAPI,`${assetType}s`,selectedSupplier);
- const subTypeFolder = this.constructUrl(supplierFolder, selectedSubType);
- const localModelsUrl = this.constructUrl(subTypeFolder, "models.json");
- const cloudModelsUrl = this.constructCloudURL(configUrls.cloud.taggcodeAPI, "/product/get_product_models.php?vendor_name=" + selectedSupplier + "&product_subtype_name=" + selectedSubType);
-
- return this.fetchData(cloudModelsUrl, localModelsUrl).then((modelData) => {
- const models = modelData.map((model) => model.name); // use this to populate the dropdown
-
- // If a model is already selected, store its metadata immediately
- if (node.model) {
- node["modelMetadata"] = modelData.find((model) => model.name === node.model);
- }
-
- this.populateDropdown(elements.model, models, node, "model", (selectedModel) => {
- // Store only the metadata for the selected model
- node["modelMetadata"] = modelData.find((model) => model.name === selectedModel);
- });
- /*
- console.log('hello here I am:');
- console.log(node["modelMetadata"]);
-*/
- });
-
- })
- .catch((error) => {
- console.error("Error populating models:", error);
- });
-}
-
-generateHtml(htmlElement, options, savedValue) {
- htmlElement.innerHTML = options.length
- ? `${options
- .map((opt) => ``)
- .join("")}`
- : "";
-
- if (savedValue && options.includes(savedValue)) {
- htmlElement.value = savedValue;
- }
-}
-
-createMenuUtilsEndpoint(RED, nodeName, customHelpers = {}) {
- RED.httpAdmin.get(`/${nodeName}/resources/menuUtils.js`, function(req, res) {
- console.log(`Serving menuUtils.js for ${nodeName} node`);
- res.set('Content-Type', 'application/javascript');
-
- const browserCode = this.generateMenuUtilsCode(nodeName, customHelpers);
- res.send(browserCode);
- }.bind(this));
- }
-
-generateMenuUtilsCode(nodeName, customHelpers = {}) {
- const defaultHelpers = {
- validateRequired: `function(value) {
- return value && value.toString().trim() !== '';
- }`,
- formatDisplayValue: `function(value, unit) {
- return \`\${value} \${unit || ''}\`.trim();
- }`
- };
-
- const allHelpers = { ...defaultHelpers, ...customHelpers };
-
- const helpersCode = Object.entries(allHelpers)
- .map(([name, func]) => ` ${name}: ${func}`)
- .join(',\n');
-
- const classCode = MenuUtils.toString(); // <-- this gives full class MenuUtils {...}
-
- return `
- // Create EVOLV namespace structure
- window.EVOLV = window.EVOLV || {};
- window.EVOLV.nodes = window.EVOLV.nodes || {};
- window.EVOLV.nodes.${nodeName} = window.EVOLV.nodes.${nodeName} || {};
-
- // Inject MenuUtils class
- ${classCode}
-
- // Expose MenuUtils instance to namespace
- window.EVOLV.nodes.${nodeName}.utils = {
- menuUtils: new MenuUtils(),
-
- helpers: {
-${helpersCode}
- }
- };
-
- // Optionally expose globally
- window.MenuUtils = MenuUtils;
-
- console.log('${nodeName} utilities loaded in namespace');
- `;
-}
-
-}
-
-module.exports = MenuUtils;
\ No newline at end of file
diff --git a/src/measurements/Measurement.js b/src/measurements/Measurement.js
index 761432b..58af5f5 100644
--- a/src/measurements/Measurement.js
+++ b/src/measurements/Measurement.js
@@ -113,8 +113,7 @@ class Measurement {
// Create a new measurement that is the difference between two positions
static createDifference(upstreamMeasurement, downstreamMeasurement) {
- console.log('hello:');
- if (upstreamMeasurement.type !== downstreamMeasurement.type ||
+ if (upstreamMeasurement.type !== downstreamMeasurement.type ||
upstreamMeasurement.variant !== downstreamMeasurement.variant) {
throw new Error('Cannot calculate difference between different measurement types or variants');
}