mirror of https://github.com/ANL-CEEESA/RELOG.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
6.5 KiB
251 lines
6.5 KiB
/*
|
|
* RELOG: Supply Chain Analysis and Optimization
|
|
* Copyright (C) 2020-2025, UChicago Argonne, LLC. All rights reserved.
|
|
* Released under the modified BSD license. See COPYING.md for more details.
|
|
*/
|
|
|
|
import Header from "./Header";
|
|
|
|
import "tabulator-tables/dist/css/tabulator.min.css";
|
|
import "../Common/Forms/Tables.css";
|
|
import Footer from "./Footer";
|
|
import React, { useState, useRef } from "react";
|
|
import { defaultPlant, defaultProduct, defaultCenter } from "./defaults";
|
|
import PipelineBlock from "./PipelineBlock";
|
|
import "@xyflow/react/dist/style.css";
|
|
import {
|
|
PlantNode,
|
|
CenterNode,
|
|
ProductNode,
|
|
RELOGScenario,
|
|
} from "./InitialData";
|
|
import { idText } from "typescript";
|
|
|
|
declare global {
|
|
interface Window {
|
|
nextX: number;
|
|
nextY: number;
|
|
}
|
|
}
|
|
|
|
const Default_Scenario: RELOGScenario = {
|
|
Parameters: { version: "1.0" },
|
|
Plants: {},
|
|
Products: {},
|
|
Centers: {},
|
|
};
|
|
|
|
const CaseBuilder = () => {
|
|
const nextUid = useRef(1);
|
|
const [scenario, setScenario] = useState<RELOGScenario>(Default_Scenario);
|
|
|
|
const onClear = () => {};
|
|
const onSave = () => {};
|
|
const onLoad = () => {};
|
|
|
|
const nextNodePosition = (): [number, number] => {
|
|
if (window.nextX === undefined) window.nextX = 15;
|
|
if (window.nextY === undefined) window.nextY = 15;
|
|
|
|
window.nextY += 60;
|
|
if (window.nextY >= 500) {
|
|
window.nextY = 15;
|
|
window.nextX += 150;
|
|
}
|
|
return [window.nextX, window.nextY];
|
|
};
|
|
|
|
const promptName = (): string | undefined => {
|
|
const name = prompt("Name");
|
|
if (!name || name.length === 0) return;
|
|
return name;
|
|
};
|
|
|
|
type EntityKey = "Plants" | "Products" | "Centers";
|
|
|
|
const onAddNode = (type: EntityKey) => {
|
|
setScenario((prevData) => {
|
|
const name = promptName();
|
|
if (!name) return prevData;
|
|
|
|
const uid = `${name}-$${nextUid.current++}`;
|
|
const [x, y] = nextNodePosition();
|
|
|
|
let newNode;
|
|
if (type === "Plants") {
|
|
newNode = { ...defaultPlant, uid, name, x, y };
|
|
} else if (type === "Products") {
|
|
newNode = { ...defaultProduct, uid, name, x, y };
|
|
} else {
|
|
newNode = { ...defaultCenter, uid, name, x, y };
|
|
}
|
|
return {
|
|
...prevData,
|
|
[type]: {
|
|
...prevData[type],
|
|
[uid]: newNode,
|
|
},
|
|
} as RELOGScenario;
|
|
});
|
|
};
|
|
|
|
const onSetCenterInput = (centerName: string, productName: string) => {
|
|
setScenario((prev) => {
|
|
const center = prev.Centers[centerName];
|
|
if (!center) return prev;
|
|
return {
|
|
...prev,
|
|
Centers: {
|
|
...prev.Centers,
|
|
[centerName]: { ...center, input: productName },
|
|
},
|
|
};
|
|
});
|
|
};
|
|
|
|
const onSetPlantInput = (plantName: string, productName: string) => {
|
|
setScenario((prevData: RELOGScenario) => {
|
|
const plant = prevData.Plants[plantName];
|
|
|
|
if (!plant) return prevData;
|
|
|
|
const updatedPlant: PlantNode = {
|
|
...plant,
|
|
|
|
inputs: plant.inputs.includes(productName)
|
|
? plant.inputs
|
|
: [...plant.inputs, productName],
|
|
};
|
|
|
|
return {
|
|
...prevData,
|
|
|
|
Plants: {
|
|
...prevData.Plants,
|
|
|
|
[plantName]: updatedPlant,
|
|
},
|
|
};
|
|
});
|
|
};
|
|
|
|
const onAddPlantOutput = (plantName: string, productName: string) => {
|
|
setScenario((prevData) => {
|
|
const plant = prevData.Plants[plantName];
|
|
if (!plant) return prevData;
|
|
|
|
const newOutputs = plant.outputs.includes(productName)
|
|
? plant.outputs
|
|
: [...plant.outputs, productName];
|
|
|
|
return {
|
|
...prevData,
|
|
Plants: {
|
|
...prevData.Plants,
|
|
[plantName]: {
|
|
...plant,
|
|
outputs: newOutputs,
|
|
},
|
|
},
|
|
};
|
|
});
|
|
};
|
|
|
|
const onAddCenterOutput = (centerName: string, productName: string) => {
|
|
setScenario((prev) => {
|
|
const center = prev.Centers[centerName];
|
|
if (!center) return prev;
|
|
|
|
const updatedOutputs = [...center.output, productName];
|
|
return {
|
|
...prev,
|
|
Centers: {
|
|
...prev.Centers,
|
|
[centerName]: { ...center, output: updatedOutputs },
|
|
},
|
|
};
|
|
});
|
|
};
|
|
|
|
const onMoveNode = (type: EntityKey, id: string, x: number, y: number) => {
|
|
setScenario((prevData) => {
|
|
const nodesMap = prevData[type];
|
|
const node = nodesMap[id];
|
|
if (!node) return prevData;
|
|
|
|
return {
|
|
...prevData,
|
|
[type]: {
|
|
...nodesMap,
|
|
[id]: { ...node, x, y },
|
|
},
|
|
} as RELOGScenario;
|
|
});
|
|
};
|
|
|
|
const onRemoveNode = (type: EntityKey, id: string) => {
|
|
setScenario((prevData) => {
|
|
const nodesMap = { ...prevData[type] };
|
|
delete nodesMap[id];
|
|
|
|
return {
|
|
...prevData,
|
|
[type]: nodesMap,
|
|
};
|
|
});
|
|
};
|
|
|
|
const onRenameNode = (type: EntityKey, uniqueId: string, newName: string) => {
|
|
setScenario((prevData) => {
|
|
const entities = prevData[type];
|
|
const node = entities[uniqueId];
|
|
|
|
if (!node) return prevData;
|
|
|
|
return {
|
|
...prevData,
|
|
[type]: {
|
|
...entities,
|
|
[uniqueId]: { ...node, name: newName },
|
|
},
|
|
};
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<Header onClear={onClear} onSave={onSave} onLoad={onLoad} />
|
|
<div className="content">
|
|
<div id="contentBackground">
|
|
<div id="content">
|
|
<PipelineBlock
|
|
onAddPlant={() => onAddNode("Plants")}
|
|
onAddProduct={() => onAddNode("Products")}
|
|
onMovePlant={(id, x, y) => onMoveNode("Plants", id, x, y)}
|
|
onMoveProduct={(id, x, y) => onMoveNode("Products", id, x, y)}
|
|
plants={scenario.Plants}
|
|
products={scenario.Products}
|
|
onSetPlantInput={onSetPlantInput}
|
|
onAddPlantOutput={onAddPlantOutput}
|
|
onAddCenter={() => onAddNode("Centers")}
|
|
onAddCenterInput={onSetCenterInput}
|
|
onAddCenterOutput={onAddCenterOutput}
|
|
onMoveCenter={(id, x, y) => onMoveNode("Centers", id, x, y)}
|
|
centers={scenario.Centers}
|
|
onRemovePlant={(id) => onRemoveNode("Plants", id)}
|
|
onRemoveProduct={(id) => onRemoveNode("Products", id)}
|
|
onRemoveCenter={(id) => onRemoveNode("Centers", id)}
|
|
onRenamePlant={(id, name) => onRenameNode("Plants", id, name)}
|
|
onRenameProduct={(id, name) => onRenameNode("Products", id, name)}
|
|
onRenameCenter={(id, name) => onRenameNode("Centers", id, name)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Footer />
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default CaseBuilder;
|