Loading js-apps/meep-frontend/src/js/containers/cfg/cfg-network-element-container.js +92 −24 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import { Checkbox } from '@rmwc/checkbox'; import { Typography } from '@rmwc/typography'; import { updateObject } from '../../util/object-util'; import { createUniqueName } from '../../util/elem-utils'; import IDSelect from '../../components/helper-components/id-select'; import CancelApplyPair from '../../components/helper-components/cancel-apply-pair'; import NCGroup from '../../components/helper-components/nc-group'; Loading Loading @@ -56,7 +58,13 @@ import { setElemFieldErr } from '../../util/elem-utils'; import { CFG_ELEM_MODE_EDIT, cfgElemUpdate } from '../../state/cfg'; import { CFG_ELEM_MODE_NEW, CFG_ELEM_MODE_EDIT, CFG_ELEM_MODE_CLONE, cfgElemUpdate, cfgElemClone } from '../../state/cfg'; import { TYPE_CFG, Loading Loading @@ -111,6 +119,7 @@ import { CFG_ELEM_EGRESS_SVC_MAP, CFG_BTN_NEW_ELEM, CFG_BTN_DEL_ELEM, CFG_BTN_CLONE_ELEM, // Layout type MEEP_COMPONENT_TABLE_LAYOUT Loading Loading @@ -915,20 +924,22 @@ const getParentTypes = type => { }; const buttonStyles = { marginRight: 5 marginRight: 5, width: 100 }; const ElementCfgButtons = ({ configuredElement, configMode, onNewElement, onDeleteElement onDeleteElement, onCloneElement }) => { const canCreateNewElement = () => { return !configuredElement; }; const canDeleteElement = () => { const canDeleteOrCloneElement = () => { return configuredElement && configMode === CFG_ELEM_MODE_EDIT; }; Loading @@ -949,15 +960,47 @@ const ElementCfgButtons = ({ data-cy={CFG_BTN_DEL_ELEM} style={buttonStyles} onClick={() => onDeleteElement()} disabled={!canDeleteElement()} disabled={!canDeleteOrCloneElement()} > DELETE </Button> <Button outlined data-cy={CFG_BTN_CLONE_ELEM} style={buttonStyles} onClick={() => onCloneElement()} disabled={!canDeleteOrCloneElement()} > CLONE </Button> </> ); }; const HeaderGroup = ({ element, onTypeChange, onUpdate, disabled }) => { const getSuggestedName = ( type, elements ) => { var suggestedPrefix = ''; switch(type) { case ELEMENT_TYPE_UE_APP: suggestedPrefix = 'ue-app'; break; case ELEMENT_TYPE_EDGE_APP: suggestedPrefix = 'edge-app'; break; case ELEMENT_TYPE_CLOUD_APP: suggestedPrefix = 'cloud-app'; break; case ELEMENT_TYPE_DC: suggestedPrefix = 'cloud'; break; default: suggestedPrefix = type.toLowerCase(); } return createUniqueName(elements, suggestedPrefix); }; const HeaderGroup = ({ element, onTypeChange, onUpdate, typeDisabled, parentDisabled, nameDisabled }) => { var type = getElemFieldVal(element, FIELD_TYPE) || ''; var parent = getElemFieldVal(element, FIELD_PARENT) || ''; var parentElements = element.parentElements || [parent]; Loading @@ -965,23 +1008,25 @@ const HeaderGroup = ({ element, onTypeChange, onUpdate, disabled }) => { return ( <> <Grid style={{ marginTop: 10 }}> {type !== 'SCENARIO' && ( <IDSelect label="Element Type" span={6} options={elementTypes} onChange={elem => onTypeChange(elem.target.value)} value={type} disabled={disabled} disabled={typeDisabled} cydata={CFG_ELEM_TYPE} /> {type && ( )} {type && type !== 'SCENARIO' && ( <IDSelect label="Parent Node" span={6} options={parentElements} onChange={elem => onUpdate(FIELD_PARENT, elem.target.value, null)} value={parent} disabled={disabled} disabled={parentDisabled} cydata={CFG_ELEM_PARENT} /> )} Loading @@ -994,7 +1039,7 @@ const HeaderGroup = ({ element, onTypeChange, onUpdate, disabled }) => { validate={validateName} label="Unique Element Name" fieldName={FIELD_NAME} disabled={disabled} disabled={nameDisabled} cydata={CFG_ELEM_NAME} /> </Grid> Loading @@ -1016,6 +1061,17 @@ export class CfgNetworkElementContainer extends Component { this.props.cfgElemUpdate(updatedElem); } // Element clone handler onCloneElement(newName) { var clonedElem = updateObject({}, this.props.configuredElement); setElemFieldVal(clonedElem, FIELD_NAME, newName); setElemFieldVal(clonedElem, FIELD_PARENT, null); var elementType = getElemFieldVal(clonedElem, FIELD_TYPE); clonedElem.parentElements = this.elementsOfType(getParentTypes(elementType)); this.props.cfgElemClone(clonedElem); } // Retrieve names of elements with matching type elementsOfType(types) { return _.chain(this.props.tableData) Loading @@ -1038,6 +1094,9 @@ export class CfgNetworkElementContainer extends Component { elem.parentElements = this.elementsOfType(getParentTypes(elementType)); if (this.props.configMode !== CFG_ELEM_MODE_CLONE) { setElemFieldVal(elem, FIELD_NAME, getSuggestedName(elementType, this.props.tableData)); } this.props.cfgElemUpdate(elem); } Loading @@ -1046,13 +1105,13 @@ export class CfgNetworkElementContainer extends Component { return ( <div className="cfg-network-element-div" style={styles.outer}> <Grid> <GridCell span={7}> <GridCell span={12}> <div style={styles.block}> <Typography use="headline6">Element Configuration</Typography> </div> </GridCell> <GridCell span={5}> <GridInner align={'right'}> <GridCell span={12}> <GridInner align={'left'}> <GridCell span={12}> <ElementCfgButtons configuredElement={element} Loading @@ -1061,6 +1120,9 @@ export class CfgNetworkElementContainer extends Component { onDeleteElement={() => { this.props.onDeleteElement(element); }} onCloneElement={() => { this.onCloneElement(createUniqueName(this.props.tableData, getElemFieldVal(element, FIELD_NAME) + '-copy')); }} /> </GridCell> </GridInner> Loading @@ -1077,7 +1139,9 @@ export class CfgNetworkElementContainer extends Component { onUpdate={(name, val, err) => { this.onUpdateElement(name, val, err); }} disabled={this.props.configMode === CFG_ELEM_MODE_EDIT} typeDisabled={this.props.configMode === CFG_ELEM_MODE_CLONE || this.props.configMode === CFG_ELEM_MODE_EDIT} parentDisabled={this.props.configMode === CFG_ELEM_MODE_EDIT} nameDisabled={getElemFieldVal(element, FIELD_TYPE) === ELEMENT_TYPE_SCENARIO && this.props.configMode !== CFG_ELEM_MODE_NEW} /> <TypeRelatedFormFields Loading @@ -1095,10 +1159,12 @@ export class CfgNetworkElementContainer extends Component { </div> <CancelApplyPair saveDisabled={(this.props.isModified === false) ? true : false} onCancel={this.props.onCancelElement} onApply={() => { this.props.onSaveElement(element); (this.props.configMode === CFG_ELEM_MODE_CLONE) ? this.props.onApplyCloneElement(element) : this.props.onSaveElement(element); }} /> </> )} Loading Loading @@ -1131,13 +1197,15 @@ const mapStateToProps = state => { tableData: state.cfg.table.entries, configuredElement: state.cfg.elementConfiguration.configuredElement, configMode: state.cfg.elementConfiguration.configurationMode, isModified: state.cfg.elementConfiguration.isModified, errorMessage: state.cfg.elementConfiguration.errorMessage }; }; const mapDispatchToProps = dispatch => { return { cfgElemUpdate: element => dispatch(cfgElemUpdate(element)) cfgElemUpdate: element => dispatch(cfgElemUpdate(element)), cfgElemClone: element => dispatch(cfgElemClone(element)) }; }; Loading js-apps/meep-frontend/src/js/containers/cfg/cfg-page-container.js +38 −6 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import IDExportScenarioDialog from '../../components/dialogs/id-export-scenario- import { cfgElemNew, cfgElemClone, cfgElemEdit, cfgElemClear, cfgElemSetErrMsg, Loading Loading @@ -132,7 +133,7 @@ class CfgPageContainer extends Component { this.props.cfg.elementConfiguration.configurationMode === CFG_ELEM_MODE_NEW ) { this.props.newScenarioElem(element); this.props.newScenarioElem(element, true); } else { this.props.updateScenarioElem(element); } Loading @@ -141,6 +142,26 @@ class CfgPageContainer extends Component { this.props.cfgElemClear(); } // CLONE onCloneElement() { this.props.cfgElemClone(); } // CLONE onApplyCloneElement(element) { // Validate network element if (this.validateNetworkElement(element) === false) { return; } this.props.cloneScenarioElem(element); //force update on the visual aspect of the scenario //this.props.updateScenario(); this.props.cfgElemClear(); } // DELETE onDeleteElement(element) { this.props.deleteScenarioElem(element); Loading @@ -161,9 +182,19 @@ class CfgPageContainer extends Component { return -1; } findOtherThanSelfIndexByKeyValue(_array, key, value, exceptionId) { for (var i = 0; i < _array.length; i++) { if (getElemFieldVal(_array[i], key) === value) { if (_array[i].id !== exceptionId) { return i; } } } return -1; } // Validate new network element form field entries validateNetworkElement(element) { var configMode = this.props.cfg.elementConfiguration.configurationMode; var data = this.props.cfg.table.entries; // Clear previous error message Loading Loading @@ -193,10 +224,8 @@ class CfgPageContainer extends Component { this.props.cfgElemSetErrMsg('Missing element name'); return false; } if ( configMode === CFG_ELEM_MODE_NEW && this.findIndexByKeyValue(data, FIELD_NAME, name) !== -1 ) { if (this.findOtherThanSelfIndexByKeyValue(data, FIELD_NAME, name, element.id) !== -1) { this.props.cfgElemSetErrMsg('Element name already exists'); return false; } Loading Loading @@ -614,6 +643,7 @@ class CfgPageContainer extends Component { onNewElement={() => this.onNewElement()} onSaveElement={elem => this.onSaveElement(elem)} onDeleteElement={elem => this.onDeleteElement(elem)} onApplyCloneElement={elem => this.onApplyCloneElement(elem)} onCancelElement={() => this.onCancelElement()} /> </Elevation> Loading @@ -627,6 +657,7 @@ class CfgPageContainer extends Component { onNewElement={() => this.onNewElement()} onEditElement={elem => this.onEditElement(elem)} onDeleteElement={() => this.onDeleteElement()} onApplyCloneElement={elem => this.onApplyCloneElement(elem)} /> </div> </> Loading Loading @@ -675,6 +706,7 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => { return { cfgElemNew: elem => dispatch(cfgElemNew(elem)), cfgElemClone: elem => dispatch(cfgElemClone(elem)), cfgElemEdit: elem => dispatch(cfgElemEdit(elem)), cfgElemClear: elem => dispatch(cfgElemClear(elem)), cfgElemSetErrMsg: msg => dispatch(cfgElemSetErrMsg(msg)), Loading js-apps/meep-frontend/src/js/containers/idc-vis.js +42 −10 Original line number Diff line number Diff line Loading @@ -162,6 +162,10 @@ class IDCVis extends Component { var groups = vis.options.groups; //vis.network.on("configChange", function() { // console.log(network.getOptionsFromConfigurator()); //}); // Scenario createBoxGroup(groups, 'scenario', '#ffffff'); groups.scenario.borderWidth = 4; Loading Loading @@ -265,7 +269,7 @@ class IDCVis extends Component { if (this.props.type === TYPE_CFG) { this.props.onEditElement( table.selected.length ? this.getElementByName(table.entries, table.selected[0]) ? this.getElementById(table.entries, table.selected[0]) : null ); } Loading @@ -282,6 +286,15 @@ class IDCVis extends Component { return null; } getElementById(entries, id) { for (var i = 0; i < entries.length; i++) { if (entries[i].id === id) { return entries[i]; } } return null; } getTable() { switch (this.props.type) { case TYPE_CFG: Loading Loading @@ -340,20 +353,39 @@ class IDCVis extends Component { vis.showConfig = !vis.showConfig; } vis.options.configure.enabled = vis.showConfig; vis.options.configure.filter = filterStr; vis.network.setOptions(vis.options); var subOptions; switch(vis.options.configure.filter) { case 'physics': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.physics = subOptions.physics; break; case 'manipulation': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.manipulation = subOptions.manipulation; break; case 'interaction': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.interaction = subOptions.interaction; break; case 'nodes': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.nodes = subOptions.nodes; break; case 'edges': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.edges = subOptions.edges; break; case 'layout': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.layout = subOptions.layout; break; default: } updateConfigVisibility() { var vis = this.getVis(); if (vis.options.configure) { vis.options.configure.enabled = this.props.devMode; vis.options.configure.filter = filterStr; vis.network.setOptions(vis.options); } } render() { this.updateConfigVisibility(); return ( <> <div Loading js-apps/meep-frontend/src/js/containers/meep-container.js +44 −17 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ import { connect } from 'react-redux'; import React, { Component } from 'react'; import axios from 'axios'; import moment from 'moment'; import { updateObject } from '../util/object-util'; import { updateObject, deepCopy } from '../util/object-util'; // Import JS dependencies import * as meepCtrlRestApiClient from '../../../../../js-packages/meep-ctrl-engine-client/src/index.js'; Loading @@ -37,7 +37,11 @@ import { EXEC_STATE_DEPLOYED, NO_SCENARIO_NAME, VIS_VIEW, VIEW_NAME_NONE VIEW_NAME_NONE, PAGE_CONFIGURE, PAGE_EXECUTE, PAGE_MONITOR, PAGE_SETTINGS } from '../meep-constants'; import { Loading @@ -45,6 +49,7 @@ import { createNewScenario, addElementToScenario, updateElementInScenario, cloneElementInScenario, removeElementFromScenario } from '../util/scenario-utils'; Loading Loading @@ -75,13 +80,6 @@ import { cfgChangeTable } from '../state/cfg'; import { PAGE_CONFIGURE, PAGE_EXECUTE, PAGE_MONITOR, PAGE_SETTINGS } from '../meep-constants'; import { idlog } from '../util/functional'; // MEEP Controller REST API JS client Loading Loading @@ -323,8 +321,12 @@ class MeepContainer extends Component { }, 2000); } // Change & process scenario changeScenario(pageType, scenario) { this.updateScenario(pageType, scenario, false); } // Change & process scenario updateScenario(pageType, scenario, reInitVisView) { // Change scenario state if (pageType === TYPE_CFG) { this.props.cfgChangeScenario(scenario); Loading @@ -345,7 +347,16 @@ class MeepContainer extends Component { const vis = this.props.cfgVis; if (vis && vis.network && vis.network.setData) { //save the canvas position and scale level in vis var view; if (!reInitVisView) { view = deepCopy(vis.network.canvas.body.view); } vis.network.setData(updatedVisData); if (view) { //restore the canvas position and scale in vis vis.network.canvas.body.view = view; } } } else { this.props.execChangeVisData(updatedVisData); Loading @@ -354,7 +365,11 @@ class MeepContainer extends Component { const vis = this.props.execVis; if (vis && vis.network && vis.network.setData) { _.defer(() => { //save the canvas position and scale level in vis const view = deepCopy(vis.network.canvas.body.view); vis.network.setData(this.props.execVisData); //restore the canvas position and scale in vis vis.network.canvas.body.view = view; }); } } Loading @@ -363,18 +378,18 @@ class MeepContainer extends Component { // Create, store & process new scenario createScenario(pageType, name) { var scenario = createNewScenario(name); this.changeScenario(pageType, scenario); this.updateScenario(pageType, scenario, true); } // Set & process scenario setScenario(pageType, scenario) { this.changeScenario(pageType, scenario); this.updateScenario(pageType, scenario, true); } // Delete & process scenario deleteScenario(pageType) { var scenario = createNewScenario(NO_SCENARIO_NAME); this.changeScenario(pageType, scenario); this.updateScenario(pageType, scenario, true); } // Refresh Active scenario Loading @@ -385,15 +400,17 @@ class MeepContainer extends Component { } // Add new element to scenario newScenarioElem(pageType, element) { newScenarioElem(pageType, element, scenarioUpdate) { var scenario = pageType === TYPE_CFG ? this.props.cfg.scenario : this.props.exec.scenario; var updatedScenario = updateObject({}, scenario); addElementToScenario(updatedScenario, element); if (scenarioUpdate) { this.changeScenario(pageType, updatedScenario); } } // Update element in scenario updateScenarioElem(pageType, element) { Loading @@ -417,6 +434,13 @@ class MeepContainer extends Component { this.changeScenario(pageType, updatedScenario); } // Clone element in scenario cloneScenarioElem(element) { var updatedScenario = updateObject({}, this.props.cfg.scenario); cloneElementInScenario(updatedScenario, element, this.props.cfg.table); this.changeScenario(TYPE_CFG, updatedScenario); } renderPage() { switch (this.props.page) { case PAGE_CONFIGURE: Loading @@ -433,8 +457,11 @@ class MeepContainer extends Component { deleteScenario={() => { this.deleteScenario(TYPE_CFG); }} newScenarioElem={elem => { this.newScenarioElem(TYPE_CFG, elem); newScenarioElem={(elem, update) => { this.newScenarioElem(TYPE_CFG, elem, update); }} cloneScenarioElem={elem => { this.cloneScenarioElem(elem); }} updateScenarioElem={elem => { this.updateScenarioElem(TYPE_CFG, elem); Loading js-apps/meep-frontend/src/js/meep-constants.js +2 −1 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ export const CFG_BTN_IMP_SCENARIO = 'cfg-btn-imp-scenario'; export const CFG_BTN_EXP_SCENARIO = 'cfg-btn-exp-scenario'; export const CFG_BTN_NEW_ELEM = 'cfg-btn-new-elem'; export const CFG_BTN_DEL_ELEM = 'cfg-btn-del-elem'; export const CFG_BTN_CLONE_ELEM = 'cfg-btn-clone-elem'; export const CFG_BTN_SAVE_ELEM = 'cfg-btn-save-elem'; export const CFG_ELEM_TYPE = 'cfg-elem-type'; Loading Loading @@ -157,7 +158,7 @@ export const DEFAULT_PACKET_LOSS_INTRA_ZONE = 0; export const DEFAULT_LATENCY_TERMINAL_LINK = 1; export const DEFAULT_LATENCY_JITTER_TERMINAL_LINK = 1; export const DEFAULT_THROUGHPUT_TERMINAL_LINK = 1000; export const DEFAULT_PACKET_LOSS_TERMINAL_LINK = 1; export const DEFAULT_PACKET_LOSS_TERMINAL_LINK = 0; export const DEFAULT_LATENCY_LINK = 0; export const DEFAULT_LATENCY_JITTER_LINK = 0; export const DEFAULT_THROUGHPUT_LINK = 1000; Loading Loading
js-apps/meep-frontend/src/js/containers/cfg/cfg-network-element-container.js +92 −24 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import { Checkbox } from '@rmwc/checkbox'; import { Typography } from '@rmwc/typography'; import { updateObject } from '../../util/object-util'; import { createUniqueName } from '../../util/elem-utils'; import IDSelect from '../../components/helper-components/id-select'; import CancelApplyPair from '../../components/helper-components/cancel-apply-pair'; import NCGroup from '../../components/helper-components/nc-group'; Loading Loading @@ -56,7 +58,13 @@ import { setElemFieldErr } from '../../util/elem-utils'; import { CFG_ELEM_MODE_EDIT, cfgElemUpdate } from '../../state/cfg'; import { CFG_ELEM_MODE_NEW, CFG_ELEM_MODE_EDIT, CFG_ELEM_MODE_CLONE, cfgElemUpdate, cfgElemClone } from '../../state/cfg'; import { TYPE_CFG, Loading Loading @@ -111,6 +119,7 @@ import { CFG_ELEM_EGRESS_SVC_MAP, CFG_BTN_NEW_ELEM, CFG_BTN_DEL_ELEM, CFG_BTN_CLONE_ELEM, // Layout type MEEP_COMPONENT_TABLE_LAYOUT Loading Loading @@ -915,20 +924,22 @@ const getParentTypes = type => { }; const buttonStyles = { marginRight: 5 marginRight: 5, width: 100 }; const ElementCfgButtons = ({ configuredElement, configMode, onNewElement, onDeleteElement onDeleteElement, onCloneElement }) => { const canCreateNewElement = () => { return !configuredElement; }; const canDeleteElement = () => { const canDeleteOrCloneElement = () => { return configuredElement && configMode === CFG_ELEM_MODE_EDIT; }; Loading @@ -949,15 +960,47 @@ const ElementCfgButtons = ({ data-cy={CFG_BTN_DEL_ELEM} style={buttonStyles} onClick={() => onDeleteElement()} disabled={!canDeleteElement()} disabled={!canDeleteOrCloneElement()} > DELETE </Button> <Button outlined data-cy={CFG_BTN_CLONE_ELEM} style={buttonStyles} onClick={() => onCloneElement()} disabled={!canDeleteOrCloneElement()} > CLONE </Button> </> ); }; const HeaderGroup = ({ element, onTypeChange, onUpdate, disabled }) => { const getSuggestedName = ( type, elements ) => { var suggestedPrefix = ''; switch(type) { case ELEMENT_TYPE_UE_APP: suggestedPrefix = 'ue-app'; break; case ELEMENT_TYPE_EDGE_APP: suggestedPrefix = 'edge-app'; break; case ELEMENT_TYPE_CLOUD_APP: suggestedPrefix = 'cloud-app'; break; case ELEMENT_TYPE_DC: suggestedPrefix = 'cloud'; break; default: suggestedPrefix = type.toLowerCase(); } return createUniqueName(elements, suggestedPrefix); }; const HeaderGroup = ({ element, onTypeChange, onUpdate, typeDisabled, parentDisabled, nameDisabled }) => { var type = getElemFieldVal(element, FIELD_TYPE) || ''; var parent = getElemFieldVal(element, FIELD_PARENT) || ''; var parentElements = element.parentElements || [parent]; Loading @@ -965,23 +1008,25 @@ const HeaderGroup = ({ element, onTypeChange, onUpdate, disabled }) => { return ( <> <Grid style={{ marginTop: 10 }}> {type !== 'SCENARIO' && ( <IDSelect label="Element Type" span={6} options={elementTypes} onChange={elem => onTypeChange(elem.target.value)} value={type} disabled={disabled} disabled={typeDisabled} cydata={CFG_ELEM_TYPE} /> {type && ( )} {type && type !== 'SCENARIO' && ( <IDSelect label="Parent Node" span={6} options={parentElements} onChange={elem => onUpdate(FIELD_PARENT, elem.target.value, null)} value={parent} disabled={disabled} disabled={parentDisabled} cydata={CFG_ELEM_PARENT} /> )} Loading @@ -994,7 +1039,7 @@ const HeaderGroup = ({ element, onTypeChange, onUpdate, disabled }) => { validate={validateName} label="Unique Element Name" fieldName={FIELD_NAME} disabled={disabled} disabled={nameDisabled} cydata={CFG_ELEM_NAME} /> </Grid> Loading @@ -1016,6 +1061,17 @@ export class CfgNetworkElementContainer extends Component { this.props.cfgElemUpdate(updatedElem); } // Element clone handler onCloneElement(newName) { var clonedElem = updateObject({}, this.props.configuredElement); setElemFieldVal(clonedElem, FIELD_NAME, newName); setElemFieldVal(clonedElem, FIELD_PARENT, null); var elementType = getElemFieldVal(clonedElem, FIELD_TYPE); clonedElem.parentElements = this.elementsOfType(getParentTypes(elementType)); this.props.cfgElemClone(clonedElem); } // Retrieve names of elements with matching type elementsOfType(types) { return _.chain(this.props.tableData) Loading @@ -1038,6 +1094,9 @@ export class CfgNetworkElementContainer extends Component { elem.parentElements = this.elementsOfType(getParentTypes(elementType)); if (this.props.configMode !== CFG_ELEM_MODE_CLONE) { setElemFieldVal(elem, FIELD_NAME, getSuggestedName(elementType, this.props.tableData)); } this.props.cfgElemUpdate(elem); } Loading @@ -1046,13 +1105,13 @@ export class CfgNetworkElementContainer extends Component { return ( <div className="cfg-network-element-div" style={styles.outer}> <Grid> <GridCell span={7}> <GridCell span={12}> <div style={styles.block}> <Typography use="headline6">Element Configuration</Typography> </div> </GridCell> <GridCell span={5}> <GridInner align={'right'}> <GridCell span={12}> <GridInner align={'left'}> <GridCell span={12}> <ElementCfgButtons configuredElement={element} Loading @@ -1061,6 +1120,9 @@ export class CfgNetworkElementContainer extends Component { onDeleteElement={() => { this.props.onDeleteElement(element); }} onCloneElement={() => { this.onCloneElement(createUniqueName(this.props.tableData, getElemFieldVal(element, FIELD_NAME) + '-copy')); }} /> </GridCell> </GridInner> Loading @@ -1077,7 +1139,9 @@ export class CfgNetworkElementContainer extends Component { onUpdate={(name, val, err) => { this.onUpdateElement(name, val, err); }} disabled={this.props.configMode === CFG_ELEM_MODE_EDIT} typeDisabled={this.props.configMode === CFG_ELEM_MODE_CLONE || this.props.configMode === CFG_ELEM_MODE_EDIT} parentDisabled={this.props.configMode === CFG_ELEM_MODE_EDIT} nameDisabled={getElemFieldVal(element, FIELD_TYPE) === ELEMENT_TYPE_SCENARIO && this.props.configMode !== CFG_ELEM_MODE_NEW} /> <TypeRelatedFormFields Loading @@ -1095,10 +1159,12 @@ export class CfgNetworkElementContainer extends Component { </div> <CancelApplyPair saveDisabled={(this.props.isModified === false) ? true : false} onCancel={this.props.onCancelElement} onApply={() => { this.props.onSaveElement(element); (this.props.configMode === CFG_ELEM_MODE_CLONE) ? this.props.onApplyCloneElement(element) : this.props.onSaveElement(element); }} /> </> )} Loading Loading @@ -1131,13 +1197,15 @@ const mapStateToProps = state => { tableData: state.cfg.table.entries, configuredElement: state.cfg.elementConfiguration.configuredElement, configMode: state.cfg.elementConfiguration.configurationMode, isModified: state.cfg.elementConfiguration.isModified, errorMessage: state.cfg.elementConfiguration.errorMessage }; }; const mapDispatchToProps = dispatch => { return { cfgElemUpdate: element => dispatch(cfgElemUpdate(element)) cfgElemUpdate: element => dispatch(cfgElemUpdate(element)), cfgElemClone: element => dispatch(cfgElemClone(element)) }; }; Loading
js-apps/meep-frontend/src/js/containers/cfg/cfg-page-container.js +38 −6 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import IDExportScenarioDialog from '../../components/dialogs/id-export-scenario- import { cfgElemNew, cfgElemClone, cfgElemEdit, cfgElemClear, cfgElemSetErrMsg, Loading Loading @@ -132,7 +133,7 @@ class CfgPageContainer extends Component { this.props.cfg.elementConfiguration.configurationMode === CFG_ELEM_MODE_NEW ) { this.props.newScenarioElem(element); this.props.newScenarioElem(element, true); } else { this.props.updateScenarioElem(element); } Loading @@ -141,6 +142,26 @@ class CfgPageContainer extends Component { this.props.cfgElemClear(); } // CLONE onCloneElement() { this.props.cfgElemClone(); } // CLONE onApplyCloneElement(element) { // Validate network element if (this.validateNetworkElement(element) === false) { return; } this.props.cloneScenarioElem(element); //force update on the visual aspect of the scenario //this.props.updateScenario(); this.props.cfgElemClear(); } // DELETE onDeleteElement(element) { this.props.deleteScenarioElem(element); Loading @@ -161,9 +182,19 @@ class CfgPageContainer extends Component { return -1; } findOtherThanSelfIndexByKeyValue(_array, key, value, exceptionId) { for (var i = 0; i < _array.length; i++) { if (getElemFieldVal(_array[i], key) === value) { if (_array[i].id !== exceptionId) { return i; } } } return -1; } // Validate new network element form field entries validateNetworkElement(element) { var configMode = this.props.cfg.elementConfiguration.configurationMode; var data = this.props.cfg.table.entries; // Clear previous error message Loading Loading @@ -193,10 +224,8 @@ class CfgPageContainer extends Component { this.props.cfgElemSetErrMsg('Missing element name'); return false; } if ( configMode === CFG_ELEM_MODE_NEW && this.findIndexByKeyValue(data, FIELD_NAME, name) !== -1 ) { if (this.findOtherThanSelfIndexByKeyValue(data, FIELD_NAME, name, element.id) !== -1) { this.props.cfgElemSetErrMsg('Element name already exists'); return false; } Loading Loading @@ -614,6 +643,7 @@ class CfgPageContainer extends Component { onNewElement={() => this.onNewElement()} onSaveElement={elem => this.onSaveElement(elem)} onDeleteElement={elem => this.onDeleteElement(elem)} onApplyCloneElement={elem => this.onApplyCloneElement(elem)} onCancelElement={() => this.onCancelElement()} /> </Elevation> Loading @@ -627,6 +657,7 @@ class CfgPageContainer extends Component { onNewElement={() => this.onNewElement()} onEditElement={elem => this.onEditElement(elem)} onDeleteElement={() => this.onDeleteElement()} onApplyCloneElement={elem => this.onApplyCloneElement(elem)} /> </div> </> Loading Loading @@ -675,6 +706,7 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => { return { cfgElemNew: elem => dispatch(cfgElemNew(elem)), cfgElemClone: elem => dispatch(cfgElemClone(elem)), cfgElemEdit: elem => dispatch(cfgElemEdit(elem)), cfgElemClear: elem => dispatch(cfgElemClear(elem)), cfgElemSetErrMsg: msg => dispatch(cfgElemSetErrMsg(msg)), Loading
js-apps/meep-frontend/src/js/containers/idc-vis.js +42 −10 Original line number Diff line number Diff line Loading @@ -162,6 +162,10 @@ class IDCVis extends Component { var groups = vis.options.groups; //vis.network.on("configChange", function() { // console.log(network.getOptionsFromConfigurator()); //}); // Scenario createBoxGroup(groups, 'scenario', '#ffffff'); groups.scenario.borderWidth = 4; Loading Loading @@ -265,7 +269,7 @@ class IDCVis extends Component { if (this.props.type === TYPE_CFG) { this.props.onEditElement( table.selected.length ? this.getElementByName(table.entries, table.selected[0]) ? this.getElementById(table.entries, table.selected[0]) : null ); } Loading @@ -282,6 +286,15 @@ class IDCVis extends Component { return null; } getElementById(entries, id) { for (var i = 0; i < entries.length; i++) { if (entries[i].id === id) { return entries[i]; } } return null; } getTable() { switch (this.props.type) { case TYPE_CFG: Loading Loading @@ -340,20 +353,39 @@ class IDCVis extends Component { vis.showConfig = !vis.showConfig; } vis.options.configure.enabled = vis.showConfig; vis.options.configure.filter = filterStr; vis.network.setOptions(vis.options); var subOptions; switch(vis.options.configure.filter) { case 'physics': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.physics = subOptions.physics; break; case 'manipulation': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.manipulation = subOptions.manipulation; break; case 'interaction': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.interaction = subOptions.interaction; break; case 'nodes': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.nodes = subOptions.nodes; break; case 'edges': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.edges = subOptions.edges; break; case 'layout': subOptions = vis.network.getOptionsFromConfigurator(); vis.options.layout = subOptions.layout; break; default: } updateConfigVisibility() { var vis = this.getVis(); if (vis.options.configure) { vis.options.configure.enabled = this.props.devMode; vis.options.configure.filter = filterStr; vis.network.setOptions(vis.options); } } render() { this.updateConfigVisibility(); return ( <> <div Loading
js-apps/meep-frontend/src/js/containers/meep-container.js +44 −17 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ import { connect } from 'react-redux'; import React, { Component } from 'react'; import axios from 'axios'; import moment from 'moment'; import { updateObject } from '../util/object-util'; import { updateObject, deepCopy } from '../util/object-util'; // Import JS dependencies import * as meepCtrlRestApiClient from '../../../../../js-packages/meep-ctrl-engine-client/src/index.js'; Loading @@ -37,7 +37,11 @@ import { EXEC_STATE_DEPLOYED, NO_SCENARIO_NAME, VIS_VIEW, VIEW_NAME_NONE VIEW_NAME_NONE, PAGE_CONFIGURE, PAGE_EXECUTE, PAGE_MONITOR, PAGE_SETTINGS } from '../meep-constants'; import { Loading @@ -45,6 +49,7 @@ import { createNewScenario, addElementToScenario, updateElementInScenario, cloneElementInScenario, removeElementFromScenario } from '../util/scenario-utils'; Loading Loading @@ -75,13 +80,6 @@ import { cfgChangeTable } from '../state/cfg'; import { PAGE_CONFIGURE, PAGE_EXECUTE, PAGE_MONITOR, PAGE_SETTINGS } from '../meep-constants'; import { idlog } from '../util/functional'; // MEEP Controller REST API JS client Loading Loading @@ -323,8 +321,12 @@ class MeepContainer extends Component { }, 2000); } // Change & process scenario changeScenario(pageType, scenario) { this.updateScenario(pageType, scenario, false); } // Change & process scenario updateScenario(pageType, scenario, reInitVisView) { // Change scenario state if (pageType === TYPE_CFG) { this.props.cfgChangeScenario(scenario); Loading @@ -345,7 +347,16 @@ class MeepContainer extends Component { const vis = this.props.cfgVis; if (vis && vis.network && vis.network.setData) { //save the canvas position and scale level in vis var view; if (!reInitVisView) { view = deepCopy(vis.network.canvas.body.view); } vis.network.setData(updatedVisData); if (view) { //restore the canvas position and scale in vis vis.network.canvas.body.view = view; } } } else { this.props.execChangeVisData(updatedVisData); Loading @@ -354,7 +365,11 @@ class MeepContainer extends Component { const vis = this.props.execVis; if (vis && vis.network && vis.network.setData) { _.defer(() => { //save the canvas position and scale level in vis const view = deepCopy(vis.network.canvas.body.view); vis.network.setData(this.props.execVisData); //restore the canvas position and scale in vis vis.network.canvas.body.view = view; }); } } Loading @@ -363,18 +378,18 @@ class MeepContainer extends Component { // Create, store & process new scenario createScenario(pageType, name) { var scenario = createNewScenario(name); this.changeScenario(pageType, scenario); this.updateScenario(pageType, scenario, true); } // Set & process scenario setScenario(pageType, scenario) { this.changeScenario(pageType, scenario); this.updateScenario(pageType, scenario, true); } // Delete & process scenario deleteScenario(pageType) { var scenario = createNewScenario(NO_SCENARIO_NAME); this.changeScenario(pageType, scenario); this.updateScenario(pageType, scenario, true); } // Refresh Active scenario Loading @@ -385,15 +400,17 @@ class MeepContainer extends Component { } // Add new element to scenario newScenarioElem(pageType, element) { newScenarioElem(pageType, element, scenarioUpdate) { var scenario = pageType === TYPE_CFG ? this.props.cfg.scenario : this.props.exec.scenario; var updatedScenario = updateObject({}, scenario); addElementToScenario(updatedScenario, element); if (scenarioUpdate) { this.changeScenario(pageType, updatedScenario); } } // Update element in scenario updateScenarioElem(pageType, element) { Loading @@ -417,6 +434,13 @@ class MeepContainer extends Component { this.changeScenario(pageType, updatedScenario); } // Clone element in scenario cloneScenarioElem(element) { var updatedScenario = updateObject({}, this.props.cfg.scenario); cloneElementInScenario(updatedScenario, element, this.props.cfg.table); this.changeScenario(TYPE_CFG, updatedScenario); } renderPage() { switch (this.props.page) { case PAGE_CONFIGURE: Loading @@ -433,8 +457,11 @@ class MeepContainer extends Component { deleteScenario={() => { this.deleteScenario(TYPE_CFG); }} newScenarioElem={elem => { this.newScenarioElem(TYPE_CFG, elem); newScenarioElem={(elem, update) => { this.newScenarioElem(TYPE_CFG, elem, update); }} cloneScenarioElem={elem => { this.cloneScenarioElem(elem); }} updateScenarioElem={elem => { this.updateScenarioElem(TYPE_CFG, elem); Loading
js-apps/meep-frontend/src/js/meep-constants.js +2 −1 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ export const CFG_BTN_IMP_SCENARIO = 'cfg-btn-imp-scenario'; export const CFG_BTN_EXP_SCENARIO = 'cfg-btn-exp-scenario'; export const CFG_BTN_NEW_ELEM = 'cfg-btn-new-elem'; export const CFG_BTN_DEL_ELEM = 'cfg-btn-del-elem'; export const CFG_BTN_CLONE_ELEM = 'cfg-btn-clone-elem'; export const CFG_BTN_SAVE_ELEM = 'cfg-btn-save-elem'; export const CFG_ELEM_TYPE = 'cfg-elem-type'; Loading Loading @@ -157,7 +158,7 @@ export const DEFAULT_PACKET_LOSS_INTRA_ZONE = 0; export const DEFAULT_LATENCY_TERMINAL_LINK = 1; export const DEFAULT_LATENCY_JITTER_TERMINAL_LINK = 1; export const DEFAULT_THROUGHPUT_TERMINAL_LINK = 1000; export const DEFAULT_PACKET_LOSS_TERMINAL_LINK = 1; export const DEFAULT_PACKET_LOSS_TERMINAL_LINK = 0; export const DEFAULT_LATENCY_LINK = 0; export const DEFAULT_LATENCY_JITTER_LINK = 0; export const DEFAULT_THROUGHPUT_LINK = 1000; Loading