diff --git a/src/routes/model/+page.svelte b/src/routes/model/+page.svelte index 8d0530878e757ce1e659bbf286718178602bfa7a..da2b13137e55b6c8c0021cddde2468eeb5e613d9 100644 --- a/src/routes/model/+page.svelte +++ b/src/routes/model/+page.svelte @@ -15,136 +15,145 @@ import InPlaceEdit from './InPlaceEdit.svelte'; - let dropIndex = null; + let dropIndex: number | null = null; let changed = false; let open = false; let saveAlert = false; interface SettingsUi { - type:string; + type: string; label: string; description: string; - bind:string; + bind: string; } - class SettingsSwitch implements SettingsUi{ - readonly type="switch"; - label:string; - description:string; - bind:string; - constructor(label:string,description:string, bind:string) { + class SettingsSwitch implements SettingsUi { + readonly type = "switch"; + label: string; + description: string; + bind: string; + + constructor(label: string, description: string, bind: string) { this.bind = bind; - this.label=label; + this.label = label; this.description = description; } } - class SettingsSelector implements SettingsUi{ - readonly type="selector"; - label:string; - description:string; - bind:string; - options:string[]; - constructor(label:string,description:string, bind:string,options:string[]) { + class SettingsSelector implements SettingsUi { + readonly type = "selector"; + label: string; + description: string; + bind: string; + options: string[]; + + constructor(label: string, description: string, bind: string, options: string[]) { this.bind = bind; - this.label=label; + this.label = label; this.description = description; - this.options=options; + this.options = options; } } - class SettingsTextfield implements SettingsUi{ - readonly type="textfield"; - label:string; - description:string; - bind:string; + + class SettingsTextfield implements SettingsUi { + readonly type = "textfield"; + label: string; + description: string; + bind: string; input: string; - constructor(label:string,description:string, bind:string, input:string) { + + constructor(label: string, description: string, bind: string, input: string) { this.bind = bind; - this.label=label; + this.label = label; this.description = description; - this.input=input; + this.input = input; } } - class SettingsNumberfield implements SettingsUi{ - readonly type="numberfield"; - label:string; - description:string; - bind:string; - unit:string; - max:number=Number.POSITIVE_INFINITY; - min:number=Number.NEGATIVE_INFINITY; - constructor(label:string,description:string, bind:string, unit:string) { + + class SettingsNumberfield implements SettingsUi { + readonly type = "numberfield"; + label: string; + description: string; + bind: string; + unit: string; + max: number = Number.POSITIVE_INFINITY; + min: number = Number.NEGATIVE_INFINITY; + + constructor(label: string, description: string, bind: string, unit: string) { this.bind = bind; - this.label=label; + this.label = label; this.description = description; - this.unit=unit; + this.unit = unit; } } - class SettingsLabel{ - readonly type="label" - label:string; - constructor(label:string) { - this.label=label; + + class SettingsLabel { + readonly type = "label" + label: string; + + constructor(label: string) { + this.label = label; } } - let modalModule = { + + type dictionary = { + [key: string]: any + } + let modalModule: dictionary = { name: '', response: {}, - html:[] - } - class group{ + html: [] + } + + class group { name: string; - loop: boolean = true; - loopsize:number; - modules:string[]=[]; - constructor(name: string, loopsize: number){ + loop: string = 'true'; + loopsize: number; + modules: string[] = []; + + constructor(name: string, loopsize: number) { this.name = name; this.loopsize = loopsize; } } - let groups : group[] = []; - let modules: string[]=[]; + + let groups: group[] = []; + let modules: string[] = []; let modulesModalOpen: boolean = false; let modulesModalIndex: number; let loopOpen: boolean = false; - let modalChanged: boolean =false; + let modalChanged: boolean = false; const toggle = () => (open = !open); const toggleModules = () => (modulesModalOpen = !modulesModalOpen); - const toggleLoop =() => (loopOpen = !loopOpen); + const toggleLoop = () => (loopOpen = !loopOpen); - onMount(async ()=>{ - const res = await fetch("http://127.0.0.1:8000/convergence"); - const data = await res.json(); - modules = data["SetupSteps"]; - modules = modules.concat(data["StudyMission"]); - modules = modules.concat(data["PostprocessingSteps"]); - modules = modules.concat(data["SizingLoop"]); - modules = Array.from(new Set(modules)) - modules.sort() - const res2 = await fetch("http://127.0.0.1:8000/modules"); - const data2 = await res2.json(); - groups=data2["groups"] - console.log(data2) + onMount(async () => { + await loadModel(); }) - - async function loadModel(){ - const res = await fetch("http://127.0.0.1:8000/convergence"); - const data = await res.json(); - modules = data["SetupSteps"]; - modules = modules.concat(data["StudyMission"]); - modules = modules.concat(data["PostprocessingSteps"]); - modules = modules.concat(data["SizingLoop"]); - changed = false; - changed=changed; - saveAlert = false; - saveAlert = saveAlert; + + async function loadModel() { + const res = await fetch("http://127.0.0.1:8000/convergence"); + const data = await res.json(); + modules = data["SetupSteps"]; + modules = modules.concat(data["StudyMission"]); + modules = modules.concat(data["PostprocessingSteps"]); + modules = modules.concat(data["SizingLoop"]); + modules = Array.from(new Set(modules)) + modules.sort() + const res2 = await fetch("http://127.0.0.1:8000/modules"); + const data2 = await res2.json(); + groups = data2["groups"] + changed = false; + changed = changed; + saveAlert = false; + saveAlert = saveAlert; } - async function startModel(){ - const res = fetch("http://127.0.0.1:8000/start/convergenceLoop") + async function startModel() { + //dummy-Function } - async function uploadModel(){ + async function uploadModel() { const response = await fetch('http://127.0.0.1:8000/modules/update', { method: 'PUT', body: JSON.stringify({groups}), @@ -154,12 +163,12 @@ }) .then(response => response.json()) changed = false; - changed=changed; + changed = changed; saveAlert = false; saveAlert = saveAlert; } - async function getModuleConf(module){ + async function getModuleConf(module: string) { try { const res = await fetch(`http://127.0.0.1:8000/modules/${module}/config`); const data = await res.json(); @@ -168,54 +177,65 @@ modalModule.html = []; modalModule = modalModule; generateHTML(modalModule.response); - modalChanged=false; - modalChanged=modalChanged; + modalChanged = false; + modalChanged = modalChanged; modalModule = modalModule; - }catch (error){ + } catch (error) { console.error(error); modalModule.name = "no"; - modalModule.response={}; - modalModule.html=[]; + modalModule.response = {}; + modalModule.html = []; modalModule.html.push(new SettingsLabel("No config found!")) } } - function generateHTML(json: object){ - Object.entries(json).forEach(([key, value]) =>{ - if(value==null){ + + //Wrapper to give Click-Eventhandler a synchronous Output + function getModuleConfClickWrapper(item:string) { + getModuleConf(item).catch(console.error); + return (event: MouseEvent & { currentTarget: EventTarget & HTMLLIElement }) => { + toggle(); + getModuleConf(item).catch(console.error); + }; + } + + function generateHTML(json: dictionary) { + Object.entries(json).forEach(([key, value]) => { + if (value == null) { return } - if(value.hasOwnProperty("value")){ - if(value["@description"]&&value["@description"].toLowerCase().includes("switch")){ - value.value= (value.value === 'true'||value.value==="1") //transform string input to boolean for easier binding - modalModule.html.push(new SettingsSwitch(key,value["@description"],value)) - }else if(value["@description"]&&value["@description"].toLowerCase().includes("selector")){ + if (value.hasOwnProperty("value")) { + if (value["@description"] && value["@description"].toLowerCase().includes("switch")) { + value.value = (value.value === 'true' || value.value === "1") //transform string input to boolean for easier binding + modalModule.html.push(new SettingsSwitch(key, value["@description"], value)) + } else if (value["@description"] && value["@description"].toLowerCase().includes("selector")) { let options = []; - for (let i= 0; i<4;i++){ - if(value["@description"].includes("mode_"+i)){ - options.push("mode_"+i) + for (let i = 0; i < 4; i++) { + if (value["@description"].includes("mode_" + i)) { + options.push("mode_" + i) } } - modalModule.html.push(new SettingsSelector(key,value["@description"],value,options)) - }else if(value.hasOwnProperty("@unit")) {//spaeter entweder unit oder Unit in XML + modalModule.html.push(new SettingsSelector(key, value["@description"], value, options)) + } else if (value.hasOwnProperty("@unit")) {//spaeter entweder unit oder Unit in XML modalModule.html.push((new SettingsNumberfield(key, value["@description"], value, value["@unit"]))) - }else if(value.hasOwnProperty("@Unit")){ - modalModule.html.push((new SettingsNumberfield(key,value["@description"],value,value["@Unit"]))) - }else if(value.hasOwnProperty("unit")) {//spaeter entweder unit oder Unit in XML + } else if (value.hasOwnProperty("@Unit")) { + modalModule.html.push((new SettingsNumberfield(key, value["@description"], value, value["@Unit"]))) + } else if (value.hasOwnProperty("unit")) {//spaeter entweder unit oder Unit in XML modalModule.html.push((new SettingsNumberfield(key, value["@description"], value, value["unit"]))) - }else if(value.hasOwnProperty("Unit")){ - modalModule.html.push((new SettingsNumberfield(key,value["@description"],value,value["Unit"]))) - }else{ - modalModule.html.push(new SettingsTextfield(key,value["@description"],value,key)) + } else if (value.hasOwnProperty("Unit")) { + modalModule.html.push((new SettingsNumberfield(key, value["@description"], value, value["Unit"]))) + } else { + modalModule.html.push(new SettingsTextfield(key, value["@description"], value, key)) } - }else if(typeof value ==="object"){ + } else if (typeof value === "object") { modalModule.html.push(new SettingsLabel(key)) generateHTML(json[key]) - }else{ + } else { return; } }) } - async function uploadModuleConf(){ + + async function uploadModuleConf() { const response = await fetch('http://127.0.0.1:8000/modules/config/update', { method: 'PUT', body: JSON.stringify(modalModule.response), @@ -224,27 +244,31 @@ } }) .then(response => response.json()) - modalChanged=false; - modalChanged=modalChanged; + modalChanged = false; + modalChanged = modalChanged; } - function formatLabel(label:string){ - return label.replaceAll("_"," ") + + function formatLabel(label: string) { + return label.replaceAll("_", " ") } - function checkModalChanged(){ + + function checkModalChanged() { console.log("change") modalChanged = true; modalChanged = modalChanged; } - async function resetModuleConf(){ + + async function resetModuleConf() { const res = await fetch(`http://127.0.0.1:8000/modules/${modalModule.name}/config/reset`); getModuleConf(modalModule.name); } - function dragStart(event,groupIndex :number, itemIndex :number) { - const data = {groupIndex, itemIndex}; + + function dragStart(event: any, groupIndex: number, itemIndex: number) { + const data = {groupIndex, itemIndex}; event.dataTransfer.setData('text/plain', JSON.stringify(data)); } - function drop(event, targetGroupIndex :number) { + function drop(event: any, targetGroupIndex: number) { event.preventDefault(); const json = event.dataTransfer.getData("text/plain"); const data = JSON.parse(json); @@ -255,50 +279,56 @@ updateAll(); } - function dragOver(e) { + function dragOver(e: any) { dropIndex = null; const itemElement = e.target.closest('[data-index]'); if (itemElement == null) return; - const { index } = itemElement.dataset; + const {index} = itemElement.dataset; dropIndex = index; } - function updateAll(){ - groups=groups; + + function updateAll() { + groups = groups; changed = true; saveAlert = true; saveAlert = saveAlert; - changed=changed; + changed = changed; } - function addGroup(){ - groups.push(new group("group "+groups.length,1)) + function addGroup() { + groups.push(new group("group " + groups.length, 1)) updateAll() } - function addModules(index:number, modulname: string){ - groups.at(index).modules.push(modulname) + function addModules(index: number, modulname: string) { + groups.at(index)?.modules.push(modulname) updateAll() } - function submitGrpName(field:number) { - return ({detail: newValue}) => { - groups[field].name = newValue; + + function submitGrpName(field: number) { + return ({detail}: { detail: string }) => { + groups[field].name = detail; } } + function clearModel() { groups = [] } - function deleteGroup(index:number){ + + function deleteGroup(index: number) { groups.splice(index, 1); updateAll() } - function deleteModul(grpIndex:number, modulIndex: number){ - groups.at(grpIndex).modules.splice(modulIndex, 1); + + function deleteModul(grpIndex: number, modulIndex: number) { + groups.at(grpIndex)?.modules.splice(modulIndex, 1); updateAll() } - function copyGroup(index:number){ - const newGroup = new group("copy of "+groups[index].name,groups[index].loopsize); - newGroup.modules=newGroup.modules.concat(groups[index].modules); + + function copyGroup(index: number) { + const newGroup = new group("copy of " + groups[index].name, groups[index].loopsize); + newGroup.modules = newGroup.modules.concat(groups[index].modules); newGroup.loop = newGroup.loop; groups.push(newGroup); updateAll() @@ -310,64 +340,95 @@ Changes in Model please save or reload before starting!. </Alert> <ButtonGroup> - <Button color="dark" outline on:click={addGroup}><Icon name="window-plus" /> new Group </Button> - <Button color="dark" outline on:click={clearModel}><Icon name="window-dash" /> Clear </Button> - <Button color="dark" outline on:click={startModel}><Icon name="play-fill" /> Start </Button> + <Button color="dark" outline on:click={addGroup}> + <Icon name="window-plus"/> + new Group + </Button> + <Button color="dark" outline on:click={clearModel}> + <Icon name="window-dash"/> + Clear + </Button> + <Button color="dark" outline on:click={startModel}> + <Icon name="play-fill"/> + Start + </Button> {#if changed} - <Button color="danger" outline on:click={uploadModel} active><Icon name="cloud-upload" /> Save </Button> + <Button color="danger" outline on:click={uploadModel} active> + <Icon name="cloud-upload"/> + Save + </Button> {:else} - <Button color="dark" outline on:click={uploadModel}><Icon name="cloud-upload" /> Save </Button> + <Button color="dark" outline on:click={uploadModel}> + <Icon name="cloud-upload"/> + Save + </Button> {/if} - <Button color="dark" outline on:click={loadModel}><Icon name="cloud-download" /> Load </Button> + <Button color="dark" outline on:click={loadModel}> + <Icon name="cloud-download"/> + Load + </Button> </ButtonGroup> <Row class="py-3"> {#each groups as groupitem, groupIndex(groupitem)} - <Col > - <Row> - <h3><InPlaceEdit bind:value={groupitem.name} on:submit={submitGrpName(groupIndex)}/></h3> - <ButtonGroup> - <Button color="dark" outline on:click={()=>{modulesModalIndex=groupIndex;toggleModules()}}><Icon name="boxes"/> Modules</Button> - <Button color="dark" outline on:click={()=>{modulesModalIndex=groupIndex;toggleLoop()}}><Icon name="arrow-clockwise" /> - {#if groupitem.loop} - {groupitem.loopsize} - {:else} - auto - {/if} - </Button> - <Button color="dark" outline on:click={()=>copyGroup(groupIndex)}><Icon name="copy" /></Button> - <Button color="dark" outline on:click={()=>deleteGroup(groupIndex)}><Icon name="trash" /></Button> - </ButtonGroup> - </Row> + <Col> + <Row> + <h3> + <InPlaceEdit bind:value={groupitem.name} on:submit={submitGrpName(groupIndex)}/> + </h3> + <ButtonGroup> + <Button color="dark" outline on:click={()=>{modulesModalIndex=groupIndex;toggleModules()}}> + <Icon name="boxes"/> + Modules + </Button> + <Button color="dark" outline on:click={()=>{modulesModalIndex=groupIndex;toggleLoop()}}> + <Icon name="arrow-clockwise"/> + {#if groupitem.loop==='true'} + {groupitem.loopsize} + {:else} + auto + {/if} + </Button> + <Button color="dark" outline on:click={()=>copyGroup(groupIndex)}> + <Icon name="copy"/> + </Button> + <Button color="dark" outline on:click={()=>deleteGroup(groupIndex)}> + <Icon name="trash"/> + </Button> + </ButtonGroup> + </Row> <ul class="list-group" on:drop={event=>drop(event,groupIndex)} on:dragover|preventDefault={event=>dragOver(event)}> {#each groupitem.modules as item, itemIndex(itemIndex)} <div class="item" data-index={itemIndex}> <li class="list-group-item list-group-item-action" - draggable="true" on:dragstart={event=>dragStart(event,groupIndex,groupitem.modules.indexOf(item))} - on:click={getModuleConf(item),toggle}> + draggable="true" + on:dragstart={event=>dragStart(event,groupIndex,groupitem.modules.indexOf(item))} + on:click={getModuleConfClickWrapper(item)}> {item} </li> </div> {/each} </ul> - </Col> - {/each} + </Col> + {/each} </Row> <Modal isOpen={open} toggle={toggle} scrollable> <ModalHeader toggle={toggle}>{modalModule.name} settings</ModalHeader> <ModalBody> {#each modalModule.html as element, index} - {#if element.type=="switch"} - <Input id="uiElement{index}" type="switch" label="{formatLabel(element.label)}" bind:checked={element.bind.value} on:change={()=>checkModalChanged()}/> + {#if element.type == "switch"} + <Input id="uiElement{index}" type="switch" label="{formatLabel(element.label)}" + bind:checked={element.bind.value} on:change={()=>checkModalChanged()}/> <Tooltip target="uiElement{index}" placement="left"> {element.description} </Tooltip> - {:else if element.type=="label"} + {:else if element.type == "label"} <h3>{formatLabel(element.label)}</h3> - {:else if element.type =="selector"} + {:else if element.type == "selector"} <h6>{formatLabel(element.label)}</h6> - <Input id="uiElement{index}" type="select" bind:value={element.bind.value} on:change={()=>checkModalChanged()}> + <Input id="uiElement{index}" type="select" bind:value={element.bind.value} + on:change={()=>checkModalChanged()}> {#each element.options as option} <option>{option}</option> {/each} @@ -375,37 +436,46 @@ <Tooltip target="uiElement{index}" placement="left"> {element.description} </Tooltip> - {:else if element.type=="textfield"} + {:else if element.type == "textfield"} <h7>{formatLabel(element.label)}</h7> - <Input id="uiElement{index}" type="text" bind:value={element.bind.value} on:change={()=>checkModalChanged()}/> + <Input id="uiElement{index}" type="text" bind:value={element.bind.value} + on:change={()=>checkModalChanged()}/> <Tooltip target="uiElement{index}" placement="left"> {element.description} </Tooltip> - {:else if element.type=="numberfield"} - {#if element.unit !="1"&&element.unit !="-"&&element.unit!="count"} + {:else if element.type == "numberfield"} + {#if element.unit != "1" && element.unit != "-" && element.unit != "count"} <h7>{formatLabel(element.label)} (in {element.unit})</h7> {:else} <h7>{formatLabel(element.label)}</h7> {/if} - {#if element.max!=Number.POSITIVE_INFINITY&&element.min!=Number.NEGATIVE_INFINITY} - <Input type="range" min={element.min} max={element.max} step={1} bind:value={element.bind.value} placeholder="range placeholder" on:change={()=>checkModalChanged()}/> + {#if element.max != Number.POSITIVE_INFINITY && element.min != Number.NEGATIVE_INFINITY} + <Input type="range" min={element.min} max={element.max} step={1} bind:value={element.bind.value} + placeholder="range placeholder" on:change={()=>checkModalChanged()}/> {/if} - <Input id="uiElement{index}" type="number" min={element.min} max={element.max} bind:value={element.bind.value} on:change={()=>checkModalChanged()}/> + <Input id="uiElement{index}" type="number" min={element.min} max={element.max} + bind:value={element.bind.value} on:change={()=>checkModalChanged()}/> <Tooltip target="uiElement{index}" placement="left"> {element.description} </Tooltip> {/if} {/each} </ModalBody> - {#if modalModule.name!="no"} - <ModalFooter> - <ButtonGroup> - {#if modalChanged} - <Button color="primary" on:click={uploadModuleConf}><Icon name="floppy" /> Save</Button> - {/if} - <Button color="danger" on:click={resetModuleConf}><Icon name="bootstrap-reboot" /> Reset to Default</Button> - </ButtonGroup> - </ModalFooter> + {#if modalModule.name != "no"} + <ModalFooter> + <ButtonGroup> + {#if modalChanged} + <Button color="primary" on:click={uploadModuleConf}> + <Icon name="floppy"/> + Save + </Button> + {/if} + <Button color="danger" on:click={resetModuleConf}> + <Icon name="bootstrap-reboot"/> + Reset to Default + </Button> + </ButtonGroup> + </ModalFooter> {/if} </Modal> <Modal isOpen={modulesModalOpen} toggle={toggleModules} scrollable> @@ -416,7 +486,8 @@ <h6>Click to add</h6> <ListGroup> {#each modules as module} - <ListGroupItem {module} tag="button" action on:click={()=>addModules(modulesModalIndex,module)}>{module}</ListGroupItem> + <ListGroupItem id="{module}" tag="button" action + on:click={()=>addModules(modulesModalIndex,module)}>{module}</ListGroupItem> {/each} </ListGroup> </Col> @@ -424,7 +495,8 @@ <h6>Current Modules in {groups[modulesModalIndex].name} (click to delete)</h6> <ListGroup> {#each groups[modulesModalIndex].modules as module, modulIndex(modulIndex)} - <ListGroupItem tag="button" action on:click={()=>deleteModul(modulesModalIndex,modulIndex)}>{module}</ListGroupItem> + <ListGroupItem tag="button" action + on:click={()=>deleteModul(modulesModalIndex,modulIndex)}>{module}</ListGroupItem> {/each} </ListGroup> </Col> @@ -436,14 +508,17 @@ <ModalBody> <Form> <Label>Loopkind:</Label> - <Input name="loop" type="radio" bind:group={groups[modulesModalIndex].loop} value={true} label={"manual Loopsize"} /> - <Input name="loop" type="radio" bind:group={groups[modulesModalIndex].loop} value={false} label={"Autoloop"} /> + <Input name="loop" type="radio" bind:group={groups[modulesModalIndex].loop} value="true" + label={"manual Loopsize"}/> + <Input name="loop" type="radio" bind:group={groups[modulesModalIndex].loop} value="false" + label={"Autoloop"}/> <br> - {#if groups[modulesModalIndex].loop} - <InputGroup size="sm"> - <InputGroupText>Loopsize</InputGroupText> - <Input type="number" min={0} bind:value={groups[modulesModalIndex].loopsize} placeholder={groups[modulesModalIndex].loopsize}/> - </InputGroup> + {#if groups[modulesModalIndex].loop==='true'} + <InputGroup size="sm"> + <InputGroupText>Loopsize</InputGroupText> + <Input type="number" min={0} bind:value={groups[modulesModalIndex].loopsize} + placeholder={groups[modulesModalIndex].loopsize.toString()}/> + </InputGroup> {:else} <p> Placeholder for Convsettings