Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 39 additions & 19 deletions src-frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
MassenstromResponse,
NodeElements,
NodeType,
OptimizationMetadata, OptimizationResult,
OptimizationMetadata,
OutputNode,
Pipe
} from "./models";
Expand Down Expand Up @@ -51,17 +51,24 @@ function App() {
const [pipes, setPipes] = useState<Elements<Pipe>>([])
const [temperatureKey, setTemperatureKey] = useState<string>(defaultTemperatureKey)
const [optimizationMetadata, setOptimizationMetadata] = useState<OptimizationMetadata>(defaultOptimizationMetadata)
const [activeOptimizationId, setActiveOptimizationId] = useState<string | undefined>()

const [costs, setCosts] = useState<Costs|undefined>(undefined)

const [costs, setCosts] = useState<Costs | undefined>(undefined)

const handleKeyDown = (e: KeyboardEvent) => {

if (e.key === KeyboardKey.ENTER || e.key === KeyboardKey.ESC){
if (e.key === KeyboardKey.ENTER || e.key === KeyboardKey.ESC) {
e.preventDefault()
}
}

useEffect(() => console.log(pipes))
useEffect(() => {
if(activeOptimizationId !== undefined) {
setTabVal("5")
}
console.log("activeOptimizationId: " + activeOptimizationId)
},[activeOptimizationId])

useEffect(() => {
document.addEventListener('keydown', handleKeyDown, false);
Expand Down Expand Up @@ -118,7 +125,6 @@ function App() {
}



const getGrid = () => {
return {pipes: (pipes as Pipe[]), ...nodeElements, temperatureSeries: temperatureKey}
}
Expand All @@ -129,24 +135,37 @@ function App() {

const isCostsComplete = () => !!costs

const handleSetActiveOptimizationId = (id: string) => {
console.log("Set active optimization id")
console.log(id)
setActiveOptimizationId(id)
// setTabVal("5")
}

const handleSetTabVal = (val: string) => {
setActiveOptimizationId(undefined)
setTabVal(val)
}

return (
<div className="App">
<TabContext value={tabVal}>
{// @ts-ignore
}<AppBar position="static">
<h1 style={{userSelect: "none"}}>{getPipe()}Pipify<VersionNumber/></h1>
<TabList onChange={(e, val) => setTabVal(val)} aria-label="simple tabs example">
<Tab icon={<Timeline />} label="Formel Check" value="4" />
<Tab icon={<Storage />} label="Meta Daten" value="2"/>
<Tab icon={<Map />} label="Editor" value="1" disabled={!isMetaDataComplete()} />
<Tab icon={<Timeline />} label="Max Massenstrom" value="3" disabled={!isMaxMassenstromComplete()} />
<Tab icon={<Timeline />} label="Node Detail" value="5" disabled={!isCostsComplete()} />
</TabList>
</AppBar>
<AppBar position="static">
<h1 style={{userSelect: "none"}}>{getPipe()}Pipify<VersionNumber/></h1>
<TabList onChange={(e, val) => handleSetTabVal(val)} aria-label="simple tabs example">
<Tab icon={<Timeline/>} label="Formel Check" value="4"/>
<Tab icon={<Storage/>} label="Meta Daten" value="2"/>
<Tab icon={<Map/>} label="Editor" value="1" disabled={!isMetaDataComplete()}/>
<Tab icon={<Timeline/>} label="Max Massenstrom" value="3"
disabled={!isMaxMassenstromComplete()}/>
<Tab icon={<Timeline/>} label="Node Detail" value="5" disabled={!isCostsComplete()}/>
</TabList>
</AppBar>
<TabPanel value="1">
<div className="react-flow-container">
<FlowContainer pipes={pipes} setPipes={setPipes} nodeElements={nodeElements}
setNodeElements={setNodeElements} temperatureSeries={temperatureKey}/>
setNodeElements={setNodeElements} temperatureSeries={temperatureKey}
setActiveOptimizationId={handleSetActiveOptimizationId}/>
<NodeMenuSpawnerContainer onNewNode={handleNewNode}/>
<DetermineMassFlowRateButton grid={getGrid()} onResult={setMassenstrom}/>
<OptimizeButton grid={getGrid()} optimizationMetadata={optimizationMetadata} setCosts={setCosts}
Expand All @@ -167,8 +186,9 @@ function App() {
<TabPanel value={"4"}>
<FormulaCheck />
</TabPanel>
<TabPanel value={"5"}>
<OptimizationNodeDetails nodeElements={nodeElements} pipes={pipes as Pipe[]}/>
<TabPanel value={"5"} id={"optimization-panel"}>
<OptimizationNodeDetails nodeElements={nodeElements} pipes={pipes as Pipe[]}
activeId={activeOptimizationId}/>
</TabPanel>

</TabContext>
Expand Down
5 changes: 4 additions & 1 deletion src-frontend/src/Components/DefaultEdge.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {EdgeText, getBezierPath, getMarkerEnd, Position} from "react-flow-renderer";
import {Tooltip} from "@material-ui/core";
import React from "react";
import {notify} from "../ReactFlow/Overlays/Notifications";
import {handleNodeCtrlClick} from "../CustomNodes/InputNode";
import {Pipe} from "../models";


export interface GetCenterParams {
Expand Down Expand Up @@ -88,7 +91,7 @@ export const DefaultEdge = ({
return <>
<Tooltip title={"Das ist ein Test"}>
<>
<path id={id} style={style} className="react-flow__edge-path" markerEnd={markerEnd} d={edgePath}/>
<path id={id} style={style} className="react-flow__edge-path" markerEnd={markerEnd} d={edgePath} onClick={() => handleNodeCtrlClick({data,id} as Pipe)}/>
{/*<text>*/}
{/* <textPath href={`#${id}`} style={{fontSize: '12px'}} startOffset="50%" textAnchor="middle">*/}

Expand Down
27 changes: 22 additions & 5 deletions src-frontend/src/CustomNodes/InputNode.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {Handle, Position} from "react-flow-renderer";
import {Edge, Handle, Position} from "react-flow-renderer";
import {Tooltip} from "@material-ui/core";
import React, {ReactElement} from "react";
import {BaseNode, InputNode as InputNodeModel} from "../models";
import {BaseNode, InputNode as InputNodeModel, Pipe} from "../models";
import {showNodeInputDialog} from "../ReactFlow/Overlays/NodeContextOverlay";
import {verifyBackend} from "../ReactFlow/FlowContainer";
import {notify} from "../ReactFlow/Overlays/Notifications";


const customNodeStyles = {
Expand Down Expand Up @@ -33,6 +33,20 @@ export const getOptimizationTooltip = (baseNode: BaseNode): ReactElement => {
}


export const handleNodeCtrlClick = (flowElement: (BaseNode | Pipe)) => {
if(isCtrlyKeyPressed()) {
if((flowElement.data.annualEnergyDemand || flowElement.data.diameter)){
flowElement.data.onCtrlClick(flowElement.id)
}
else if(!flowElement.data.annualEnergyDemand && !flowElement.data.diameter){
notify("Für dieses Element ist leider keine Optimierung verfügbar. Optimiere zunächst das Netz.")
}
}

}

// @ts-ignore
export const isCtrlyKeyPressed = () => window.event?.ctrlKey

export const InputNode = (node: InputNodeModel) => {

Expand All @@ -43,24 +57,27 @@ export const InputNode = (node: InputNodeModel) => {
return newNode
}

const handleClick = () => {
const handleDoubleClick = () => {
showNodeInputDialog("Bearbeiten sie diesen Einspeisepunkt", getInputNode(),
handleConfirm, () => {/*Nothing to do here*/
}, () => node.data.onDelete(node.data.id ?? node.id))
}


const handleConfirm = (newNode: InputNodeModel) => {
console.log(newNode)
node.data.updateNode(newNode)
}



return (
<Tooltip title={<>
Formel Vorlauftemperatur: {node.data.flowTemperatureTemplate}<br/>
Formel Rücklauftemperatur: {node.data.returnTemperatureTemplate} <br/>
{node.data.annualEnergyDemand? getOptimizationTooltip(node.data): <></>}
</>}>
<div style={customNodeStyles} onDoubleClick={handleClick}>
<div style={customNodeStyles} onDoubleClick={handleDoubleClick} onClick={() => handleNodeCtrlClick(node)}>
<Handle
type="source"
position={Position.Bottom}
Expand Down
9 changes: 7 additions & 2 deletions src-frontend/src/CustomNodes/IntermediateNode.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import {Handle, Position} from "react-flow-renderer";
import {customInputHandleStyle, customOutputHandleStyle, getOptimizationTooltip} from "./InputNode";
import {
customInputHandleStyle,
customOutputHandleStyle,
getOptimizationTooltip,
handleNodeCtrlClick
} from "./InputNode";
import {showNodeIntermediateDialog} from "../ReactFlow/Overlays/NodeContextOverlay";
import {IntermediateNode as IntermediateNodeModel} from "../models";
import {Tooltip} from "@material-ui/core";
Expand Down Expand Up @@ -32,7 +37,7 @@ export const IntermediateNode = (node : IntermediateNodeModel) => {
<Tooltip title={<>
{node.data.annualEnergyDemand ? getOptimizationTooltip(node.data) : <></>}
</>}>
<div style={customNodeStyles} onDoubleClick={handleClick}>
<div style={customNodeStyles} onDoubleClick={handleClick} onClick={() => handleNodeCtrlClick(node)}>
<Handle type="target" position={Position.Top} style={{...customInputHandleStyle}}/>
<div>{node.data.label}</div>
<Handle
Expand Down
4 changes: 2 additions & 2 deletions src-frontend/src/CustomNodes/OutputNode.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import {customInputHandleStyle, getOptimizationTooltip} from "./InputNode";
import {customInputHandleStyle, getOptimizationTooltip, handleNodeCtrlClick} from "./InputNode";
import {Handle, Position} from "react-flow-renderer";
import {showNodeOutputDialog} from "../ReactFlow/Overlays/NodeContextOverlay";
import {OutputNode as OutputNodeModel} from "../models";
Expand Down Expand Up @@ -40,7 +40,7 @@ export const OutputNode = (node: OutputNodeModel) => {

{node.data.annualEnergyDemand? getOptimizationTooltip(node.data): <></>}
</>}>
<div style={customNodeStyles} onDoubleClick={handleClick}>
<div style={customNodeStyles} onDoubleClick={handleClick} onClick={() => handleNodeCtrlClick(node)}>
<Handle type="target" position={Position.Top} style={{...customInputHandleStyle}}/>
<div>{node?.data.label}</div>
</div>
Expand Down
39 changes: 38 additions & 1 deletion src-frontend/src/Filemanagement/FileUpload.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, {useCallback} from "react";
import {useDropzone} from 'react-dropzone';
import "./file-upload.css";
import {HotWaterGrid, instanceOfHotWaterGrid} from "../models";
import {BaseNode, HotWaterGrid, instanceOfHotWaterGrid, Pipe} from "../models";
import {notify} from "../ReactFlow/Overlays/Notifications";
import {Cancel} from "@material-ui/icons";
import IdGenerator from "../utils/IdGenerator";


interface UploadProps {
Expand Down Expand Up @@ -46,6 +47,7 @@ export const FileUpload = (props: UploadProps) => {
const jsonResult = mapToJSON(reader);
console.log(instanceOfHotWaterGrid(jsonResult))
if(instanceOfHotWaterGrid(jsonResult)) {
determineHighestId(jsonResult as HotWaterGrid)
props.loadGrid(jsonResult)
} else {
notify("Die Eingabedatei ist leider nicht valide")
Expand All @@ -62,6 +64,41 @@ export const FileUpload = (props: UploadProps) => {
const csvStr = mapCSVToArray(reader)
}

const getMaxPipeId = (pipes: Pipe[]) => {
const pipeIdNumbers: number[] = pipes.map(p => {
let idStr
if(p.id.match("##\\d+##") && p.id.match("##\\d+##")![0]) {
const idPart = p.id.match("##\\d+##")![0]
idStr = idPart.match("\\d+")![0]
}
return Number.parseInt(idStr ?? "0")
})

return Math.max(...pipeIdNumbers)
}

const getMaxNodeId = (nodes: BaseNode[]) => {
const pipeIdNumbers: number[] = nodes.map(n => {
let idStr
console.log(n.id)
if(n.id.match("\\d+\+")) {
const idPart = n.id.match("\d+\+")![0]
idStr = idPart.match("\d+")![0]
}
return Number.parseInt(idStr ?? "0")
})

return Math.max(...pipeIdNumbers)
}

const determineHighestId = (grid: HotWaterGrid) => {
const maxPipeId = getMaxPipeId(grid.pipes)
const maxNodeId = 0//getMaxNodeId([...grid.inputNodes, ...grid.intermediateNodes, ...grid.outputNodes])
const maxId = Math.max(maxPipeId, maxNodeId)
alert(maxId)
IdGenerator.setNextId(maxId + 1)
}

const onDrop = useCallback((acceptedFiles) => {
acceptedFiles.forEach((file: File) => {
const fileType = file.type
Expand Down
61 changes: 45 additions & 16 deletions src-frontend/src/OptimizationNode/OptimizationNodeDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,54 @@
import {BaseNode, NodeElements, Pipe} from "../models";
import React from "react";
import React, {MutableRefObject, useEffect, useRef} from "react";
import {Accordion, AccordionDetails, AccordionSummary} from "@material-ui/core";
// @ts-ignore
import Plot from 'react-plotly.js';

import ExpandMoreIcon from '@material-ui/icons/ExpandMore';


export const OptimizationNodeDetails = ({nodeElements, pipes}: { nodeElements: NodeElements, pipes: Pipe[] }) => {
export const OptimizationNodeDetails = ({
nodeElements,
pipes,
activeId
}: { nodeElements: NodeElements, pipes: Pipe[], activeId?: string }) => {
let myRef = useRef<HTMLInputElement>()

useEffect(() => {
if (activeId) {
const offsetContainer: number = document.getElementById("first-optimization-accordion")?.offsetTop!;
document.getElementById("optimization-panel")?.scrollTo({
behavior: 'smooth',
top: myRef?.current?.offsetTop! - offsetContainer
});
}
}, [])
return <>
<h2>Input Nodes</h2>
<OptimizationAccordionNode nodes={nodeElements.inputNodes}/>
<OptimizationAccordionNode nodes={nodeElements.inputNodes} activeId={activeId} myRef={myRef}
id={"first-optimization-accordion"}/>

<h2>Intermediate Nodes</h2>
<OptimizationAccordionNode nodes={nodeElements.intermediateNodes}/>
<OptimizationAccordionNode nodes={nodeElements.intermediateNodes} activeId={activeId} myRef={myRef}/>

<h2>Output Nodes</h2>
<OptimizationAccordionNode nodes={nodeElements.outputNodes}/>
<OptimizationAccordionNode nodes={nodeElements.outputNodes} activeId={activeId} myRef={myRef}/>

<h2>Pipes</h2>
<OptimizationAccordionPipe pipes={pipes}/>
<OptimizationAccordionPipe pipes={pipes} activeId={activeId} myRef={myRef}/>
</>
}

export const OptimizationAccordionNode = ({nodes}: { nodes: BaseNode[] }) => {
const getAccordionNode = (n: BaseNode) => {
export const OptimizationAccordionNode = ({
nodes,
activeId,
myRef,
id
}: { nodes: BaseNode[], activeId?: string, myRef: MutableRefObject<any>, id?: string }) => {
const getAccordionNode = (n: BaseNode, defaultExpanded: boolean) => {
return <>
<Accordion TransitionProps={{unmountOnExit: true}}>
<Accordion TransitionProps={{unmountOnExit: true}} defaultExpanded={defaultExpanded}
ref={defaultExpanded ? myRef : undefined} id={id}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>} aria-controls="panel1a-content" id="panel1a-header">
{n.data?.label} ({n.id})
</AccordionSummary>
Expand Down Expand Up @@ -125,15 +147,20 @@ export const OptimizationAccordionNode = ({nodes}: { nodes: BaseNode[] }) => {
}

return <>{nodes.filter(n => n.optimizedThermalEnergyDemand)
.map(n => getAccordionNode(n))
.map(n => getAccordionNode(n, n.id === activeId))
}</>
}


export const OptimizationAccordionPipe = ({pipes}: { pipes: Pipe[] }) => {
const getAccordionPipe = (p: Pipe) => {
export const OptimizationAccordionPipe = ({
pipes,
activeId,
myRef
}: { pipes: Pipe[], activeId?: string, myRef: MutableRefObject<any> }) => {
const getAccordionPipe = (p: Pipe, defaultExpanded: boolean) => {
return <>
<Accordion TransitionProps={{unmountOnExit: true}}>
<Accordion TransitionProps={{unmountOnExit: true}} defaultExpanded={defaultExpanded}
ref={defaultExpanded ? myRef : undefined}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>} aria-controls="panel1a-content" id="panel1a-header">
{p.data?.label} ({p.id})
</AccordionSummary>
Expand Down Expand Up @@ -240,7 +267,9 @@ export const OptimizationAccordionPipe = ({pipes}: { pipes: Pipe[] }) => {
</div>
}

return <>{pipes.filter((p: Pipe) => p.diameter)
.map(p => getAccordionPipe(p))
}</>
return <>
{
pipes.filter((p: Pipe) => p.diameter)
.map(p => getAccordionPipe(p, p.id === activeId))
}</>
}
Loading