diff --git a/src/Chain.svelte b/src/Chain.svelte
index 7c510c8..35f6c9e 100644
--- a/src/Chain.svelte
+++ b/src/Chain.svelte
@@ -37,124 +37,91 @@
let initial = $state(true);
let prevChainCount = $state(0);
let stepperDone = $state(false);
-
+ let hasErrors = $state(false);
let errors: { [field: string]: string } = $state({});
- // Helper function to check if a value is a valid number.
const isValidNumber = (value: any) =>
value !== null && value !== undefined && !isNaN(parseFloat(value)) && isFinite(value);
- function validateCurrentIteration(): boolean {
- // Clear previous errors
- errors = {};
-
- // Validate Twinworld fields
- if (chainIterations[currentChainIndex]?.twinworld) {
- const value = chainIterations[currentChainIndex].twinworld.solarPanelCapacity;
- if (value === null || value === undefined) {
- errors[`twinworld-solarPanelCapacity-${currentChainIndex}`] =
- "Solar Panel Capacity is required.";
- } else if (!isValidNumber(value) || Number(value) < 0) {
- errors[`twinworld-solarPanelCapacity-${currentChainIndex}`] =
- "Please enter a valid positive number.";
- }
+ function validateField(key: string, value: any, validateFn: (val: any) => string): void {
+ const errorMessage = validateFn(value);
+ if (errorMessage) {
+ errors = { ...errors, [key]: errorMessage };
+ } else {
+ const { [key]: removed, ...rest } = errors;
+ errors = rest;
}
+ }
- // Validate Cost Model fields
- if (chainIterations[currentChainIndex]?.costmodel) {
- const costModel = chainIterations[currentChainIndex].costmodel;
-
- // Price Network Buy Consumer
- if (
- costModel.priceNetworkBuyConsumer === null ||
- costModel.priceNetworkBuyConsumer === undefined
- ) {
- errors[`costmodel-priceNetworkBuyConsumer-${currentChainIndex}`] =
- "Price Network Buy Consumer is required.";
- } else if (
- !isValidNumber(costModel.priceNetworkBuyConsumer) ||
- Number(costModel.priceNetworkBuyConsumer) < 0
- ) {
- errors[`costmodel-priceNetworkBuyConsumer-${currentChainIndex}`] =
- "Please enter a valid positive number.";
- }
-
- // Price Network Sell Consumer
- if (
- costModel.priceNetworkSellConsumer === null ||
- costModel.priceNetworkSellConsumer === undefined
- ) {
- errors[`costmodel-priceNetworkSellConsumer-${currentChainIndex}`] =
- "Price Network Sell Consumer is required.";
- } else if (
- !isValidNumber(costModel.priceNetworkSellConsumer) ||
- Number(costModel.priceNetworkSellConsumer) < 0
- ) {
- errors[`costmodel-priceNetworkSellConsumer-${currentChainIndex}`] =
- "Please enter a valid positive number.";
- }
-
- // Fixed Price Ratio
- if (costModel.fixedPriceRatio === null || costModel.fixedPriceRatio === undefined) {
- errors[`costmodel-fixedPriceRatio-${currentChainIndex}`] =
- "Fixed Price Ratio is required.";
- } else if (
- !isValidNumber(costModel.fixedPriceRatio) ||
- Number(costModel.fixedPriceRatio) < 0
- ) {
- errors[`costmodel-fixedPriceRatio-${currentChainIndex}`] =
- "Please enter a valid positive number.";
- }
+ function validatePositiveNumber(val: any): string {
+ if (val === null || val === undefined || val === "") {
+ return "This field is required.";
+ } else if (!isValidNumber(val)) {
+ return "Please enter a valid number.";
+ } else if (Number(val) < 0) {
+ return "Please enter a valid positive number.";
}
+ return "";
+ }
- // Validate Algorithm fields
- if (chainIterations[currentChainIndex]?.algo) {
- const algo = chainIterations[currentChainIndex].algo;
- if (algo.maxTemperature === null || algo.maxTemperature === undefined) {
- errors[`algo-maxTemperature-${currentChainIndex}`] = "Max Temperature is required.";
- } else if (!isValidNumber(algo.maxTemperature) || Number(algo.maxTemperature) < 0) {
- errors[`algo-maxTemperature-${currentChainIndex}`] =
- "Please enter a valid positive number.";
- }
+ function validateCurrentIteration(): boolean {
+ errors = {};
+ const twinworld = chainIterations[currentChainIndex]?.twinworld;
+ if (twinworld) {
+ validateField(
+ `twinworld-solarPanelCapacity-${currentChainIndex}`,
+ twinworld.solarPanelCapacity,
+ validatePositiveNumber
+ );
}
-
- // Validate Energyflow fields
- if (chainIterations[currentChainIndex]?.energyflow) {
- const energyflow = chainIterations[currentChainIndex].energyflow;
- if (energyflow.solarPanelsFactor === null || energyflow.solarPanelsFactor === undefined) {
- errors[`energyflow-solarPanelsFactor-${currentChainIndex}`] =
- "Solar Panels Factor is required.";
- } else if (
- !isValidNumber(energyflow.solarPanelsFactor) ||
- Number(energyflow.solarPanelsFactor) < 0
- ) {
- errors[`energyflow-solarPanelsFactor-${currentChainIndex}`] =
- "Please enter a valid positive number.";
- }
-
- if (energyflow.energyUsageFactor === null || energyflow.energyUsageFactor === undefined) {
- errors[`energyflow-energyUsageFactor-${currentChainIndex}`] =
- "Energy Usage Factor is required.";
- } else if (
- !isValidNumber(energyflow.energyUsageFactor) ||
- Number(energyflow.energyUsageFactor) < 0
- ) {
- errors[`energyflow-energyUsageFactor-${currentChainIndex}`] =
- "Please enter a valid positive number.";
- }
+ const costmodel = chainIterations[currentChainIndex]?.costmodel;
+ if (costmodel) {
+ validateField(
+ `costmodel-priceNetworkBuyConsumer-${currentChainIndex}`,
+ costmodel.priceNetworkBuyConsumer,
+ validatePositiveNumber
+ );
+ validateField(
+ `costmodel-priceNetworkSellConsumer-${currentChainIndex}`,
+ costmodel.priceNetworkSellConsumer,
+ validatePositiveNumber
+ );
+ validateField(
+ `costmodel-fixedPriceRatio-${currentChainIndex}`,
+ costmodel.fixedPriceRatio,
+ validatePositiveNumber
+ );
+ }
+ const algo = chainIterations[currentChainIndex]?.algo;
+ if (algo) {
+ validateField(
+ `algo-maxTemperature-${currentChainIndex}`,
+ algo.maxTemperature,
+ validatePositiveNumber
+ );
+ }
+ const energyflow = chainIterations[currentChainIndex]?.energyflow;
+ if (energyflow) {
+ validateField(
+ `energyflow-solarPanelsFactor-${currentChainIndex}`,
+ energyflow.solarPanelsFactor,
+ validatePositiveNumber
+ );
+ validateField(
+ `energyflow-energyUsageFactor-${currentChainIndex}`,
+ energyflow.energyUsageFactor,
+ validatePositiveNumber
+ );
}
- // Return true if no errors, false otherwise.
return Object.keys(errors).length === 0;
}
function nextChainIteration() {
- // Validate current chain settings before proceeding
if (!validateCurrentIteration()) {
alert("Please correct the errors before proceeding.");
return;
}
-
if (currentChainIndex < chainCount - 1) {
currentChainIndex++;
loadEditors("costmodelalgo", chainIterations[currentChainIndex].costmodel.algorithm);
@@ -178,7 +145,6 @@
}
async function runChain() {
- // Validate current iteration before starting the chain run.
if (!validateCurrentIteration()) {
alert("Please correct the errors before running the chain.");
return;
@@ -211,7 +177,7 @@
function loadEditors(id: string, value: string) {
if (!document.getElementById(id)) return;
- // @ts-ignore
+ // @ts-ignore - aceEditor is defined in the global scope
const aceEditor = ace.edit(id, {
mode: "ace/mode/javascript",
selectionStyle: "text",
@@ -254,6 +220,10 @@
loadEditors("costmodelalgo", chainIterations[currentChainIndex].costmodel.algorithm);
loadEditors("algorithm", chainIterations[currentChainIndex].algo.algorithm);
});
+
+ $effect(() => {
+ hasErrors = Object.keys(errors).length > 0;
+ });
@@ -310,7 +277,6 @@ {:else if !done}
indicate that the planned energy used is drawn from the national grid.
+ The algorithm attempted to plan energy usage at a location where energy was drawn from + the national grid, but the household's time window was missing. +
++ The algorithm attempted to plan energy usage at a location where energy was drawn from + the solar panels, but the household's time window was missing. +
+