Loading js-apps/meep-frontend/src/js/components/helper-components/nc-group.js +155 −64 Original line number Diff line number Diff line Loading @@ -75,8 +75,11 @@ import { PREFIX_EDGE_FOG, PREFIX_TERM_LINK, PREFIX_LINK, PREFIX_APP PREFIX_APP, // NC Group Layouts MEEP_COMPONENT_SINGLE_COLUMN_LAYOUT, MEEP_COMPONENT_TABLE_LAYOUT } from '../../meep-constants'; const MIN_LATENCY_VALUE = 0; Loading Loading @@ -158,6 +161,83 @@ const validateThroughput = (val) => { return null; }; const TableLayout = (props) => { return ( <div> <Grid> <GridCell span="6"> {props.latencyComponent} </GridCell> <GridCell span="6"> {props.latencyVariationComponent} </GridCell> </Grid> <Grid style={{marginBottom: 10}}> <GridCell span="6"> {props.packetLossComponent} </GridCell> <GridCell span="6"> {props.throughputComponent} </GridCell> </Grid> </div> ); }; const SingleColumnLayout = (props) => { return ( <div> <Grid> <GridCell span="12"> {props.latencyComponent} </GridCell> </Grid> <Grid> <GridCell span="12"> {props.latencyVariationComponent} </GridCell> </Grid> <Grid style={{marginBottom: 10}}> <GridCell span="12"> {props.packetLossComponent} </GridCell> </Grid> <Grid style={{marginBottom: 10}}> <GridCell span="12"> {props.throughputComponent} </GridCell> </Grid> </div> ); }; const NCLayout = (props) => { switch(props.layout) { case MEEP_COMPONENT_SINGLE_COLUMN_LAYOUT: return ( <SingleColumnLayout {...props} /> ); case MEEP_COMPONENT_TABLE_LAYOUT: return ( <TableLayout {...props} /> ); default: return ( <TableLayout {...props} /> ); } }; const NCGroup = ({prefix, onUpdate, element}) => { const formLabel = (valueName) => { const space = prefix ? ' ' : ''; Loading Loading @@ -229,10 +309,8 @@ const NCGroup = ({prefix, onUpdate, element}) => { return null; } return ( <div> <Grid> <GridCell span="6"> const latencyComponent = ( <> <TextField outlined style={{width: '100%'}} label={formLabel('Latency') + ' (ms)'} onChange={(e) => handleEvent(e, latencyFieldName, validateLatency)} Loading @@ -245,8 +323,11 @@ const NCGroup = ({prefix, onUpdate, element}) => { {getElemFieldErr(element, latencyFieldName)} </span> </TextFieldHelperText> </GridCell> <GridCell span="6"> </> ); const latencyVariationComponent = ( <> <TextField outlined style={{width: '100%'}} label={formLabel('Latency Variation') + ' (ms)'} onChange={(e) => handleEvent(e, latencyVarFieldName, validateLatencyVariation)} Loading @@ -259,11 +340,11 @@ const NCGroup = ({prefix, onUpdate, element}) => { {getElemFieldErr(element, latencyVarFieldName)} </span> </TextFieldHelperText> </GridCell> </Grid> </> ); <Grid style={{marginBottom: 10}}> <GridCell span="6"> const packetLossComponent = ( <> <TextField outlined style={{width: '100%'}} label={formLabel('Packet Loss') + ' (%)'} onChange={(e) => handleEvent(e, packetLossFieldName, validatePacketLoss)} Loading @@ -276,9 +357,11 @@ const NCGroup = ({prefix, onUpdate, element}) => { {getElemFieldErr(element, packetLossFieldName)} </span> </TextFieldHelperText> </> ); </GridCell> <GridCell span="6"> const throughputComponent = ( <> <TextField outlined style={{width: '100%'}} label={formLabel('Throughput') + ' Mbps'} onChange={(e) => handleEvent(e, throughputFieldName, validateThroughput)} Loading @@ -291,9 +374,17 @@ const NCGroup = ({prefix, onUpdate, element}) => { {getElemFieldErr(element, throughputFieldName)} </span> </TextFieldHelperText> </GridCell> </Grid> </div> </> ); return ( <NCLayout latencyComponent={latencyComponent} latencyVariationComponent={latencyVariationComponent} packetLossComponent={packetLossComponent} throughputComponent={throughputComponent} > </NCLayout> ); }; Loading js-apps/meep-frontend/src/js/containers/cfg/cfg-network-element-container.js +5 −1 Original line number Diff line number Diff line Loading @@ -117,7 +117,10 @@ import { CFG_ELEM_EGRESS_SVC_MAP, CFG_BTN_NEW_ELEM, CFG_BTN_DEL_ELEM CFG_BTN_DEL_ELEM, // Layout type MEEP_COMPONENT_TABLE_LAYOUT } from '../../meep-constants'; Loading Loading @@ -475,6 +478,7 @@ const NCGroups = ({prefixes, onUpdate, element}) => { element={element} prefix={p} key={p} layout={MEEP_COMPONENT_TABLE_LAYOUT} /> ); }); Loading js-apps/meep-frontend/src/js/containers/dashboard-container.js +266 −102 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import { Elevation } from '@rmwc/elevation'; // import ReactDOM from 'react-dom'; import { Button } from '@rmwc/button'; import { Checkbox } from '@rmwc/checkbox'; import { Slider } from '@rmwc/slider'; import moment from 'moment'; import * as d3 from 'd3'; import axios from 'axios'; Loading @@ -14,6 +15,9 @@ import IDCLineChart from './idc-line-chart'; import IDCGraph from './idc-graph'; import IDCAppsView from './idc-apps-view'; import IDSelect from '../components/helper-components/id-select'; import IDCVis from './idc-vis'; import ResizeableContainer from './resizeable-container'; import { idlog Loading @@ -31,13 +35,17 @@ import { import { execFakeChangeSelectedDestination, execChangeSourceNodeSelected, execAddMetricsEpoch execAddMetricsEpoch, execChangeMetricsTimeIntervalDuration, execClearMetricsEpochs } from '../state/exec'; import { LATENCY_METRICS, THROUGHPUT_METRICS, MOBILITY_EVENT MOBILITY_EVENT, TYPE_EXEC, EXEC_STATE_IDLE } from '../meep-constants'; const VIEW_NAME_NONE = 'none'; Loading Loading @@ -85,8 +93,54 @@ const epochsToSeries = (epochs) => { return series; }; const TimeIntervalConfig = (props) => { let PauseResumeButton = null; if (props.metricsPollingStopped) { PauseResumeButton = () => ( <Button outlined onClick={() => props.startMetricsPolling()} > RESUME </Button> ); } else { PauseResumeButton = () => ( <Button outlined onClick={() => props.stopMetricsPolling()} > PAUSE </Button> ); } return ( <div> <Grid> <GridCell span={3}> <Slider value={props.value} onChange={e => props.timeIntervalDurationChanged(e.detail.value)} discrete min={5} max={60} step={1} /> </GridCell> <GridCell span={1}> </GridCell> <GridCell span={8}> <PauseResumeButton /> </GridCell> </Grid> </div> ); }; const ConfigurationView = (props) => { return ( <> <Grid> <GridCell span={2}> <IDSelect Loading @@ -110,7 +164,7 @@ const ConfigurationView = (props) => { value={props.view1} /> </GridCell> <GridCell span={2}> <GridCell span={3}> <IDSelect label={'Select Source Node'} outlined Loading @@ -121,7 +175,7 @@ const ConfigurationView = (props) => { value={props.sourceNodeSelected ? props.sourceNodeSelected.data.id : ''} /> </GridCell> <GridCell span={1}> <GridCell span={4}> <Checkbox checked={props.displayEdgeLabels} onChange={() => props.changeDisplayEdgeLabels(!props.displayEdgeLabels)} Loading @@ -129,9 +183,16 @@ const ConfigurationView = (props) => { Show data on edges </Checkbox> </GridCell> <GridCell span={5}> <GridCell span={1}> </GridCell> </Grid> <TimeIntervalConfig timeIntervalDurationChanged={(value) => {props.timeIntervalDurationChanged(value);}} stopMetricsPolling={props.stopMetricsPolling} startMetricsPolling={props.startMetricsPolling} metricsPollingStopped={props.metricsPollingStopped} /> </> ); }; Loading @@ -145,14 +206,15 @@ const HIERARCHY_VIEW = 'HIERARCHY_VIEW'; const APPS_VIEW = 'APPS_VIEW'; const LATENCY_VIEW = 'LATENCY_VIEW'; const THROUGHPUT_VIEW = 'THROUGHPUT_VIEW'; const VIS_VIEW = 'VIS_VIEW'; const DASHBOARD_VIEWS_LIST = [VIEW_NAME_NONE, HIERARCHY_VIEW, APPS_VIEW, LATENCY_VIEW, THROUGHPUT_VIEW]; const DASHBOARD_VIEWS_LIST = [VIEW_NAME_NONE, VIS_VIEW, APPS_VIEW, LATENCY_VIEW, THROUGHPUT_VIEW, HIERARCHY_VIEW]; const ViewForName = ( { keyForSvg, apps, colorRange, width, min, max, data, Loading @@ -174,18 +236,26 @@ const ViewForName = ( switch(viewName) { case HIERARCHY_VIEW: return ( <ResizeableContainer key={keyForSvg}> {(width, height) => ( <IDCGraph keyForSvg={keyForSvg} width={width} height={600} /> height={height} />)} </ResizeableContainer> ); case APPS_VIEW: return ( <ResizeableContainer key={keyForSvg}> { (width, height) => ( <IDCAppsView keyForSvg={keyForSvg} apps={apps} colorRange={colorRange} width={width} height={600} height={height} data={data} series={series} startTime={startTime} Loading @@ -198,15 +268,22 @@ const ViewForName = ( }} displayEdgeLabels={displayEdgeLabels} /> ) } </ResizeableContainer> ); case LATENCY_VIEW: return ( <ResizeableContainer key={keyForSvg}> {(width, height) => ( <IDCLineChart keyForSvg={keyForSvg} data={dataPoints} series={series} startTime={startTime} mobilityEvents={mobilityEvents} width={width} height={600} width={width} height={height} destinations={appIds} colorRange={colorRange} selectedSource={selectedSource} Loading @@ -215,15 +292,23 @@ const ViewForName = ( max={max} colorForApp={colorForApp} /> ) } </ResizeableContainer> ); case THROUGHPUT_VIEW: return ( <ResizeableContainer key={keyForSvg}> { (width, height) => ( <IDCLineChart keyForSvg={keyForSvg} data={dataPoints} series={series} startTime={startTime} mobilityEvents={mobilityEvents} width={width} height={600} width={width} height={height} destinations={appIds} colorRange={colorRange} selectedSource={selectedSource} Loading @@ -232,6 +317,25 @@ const ViewForName = ( max={max} colorForApp={colorForApp} /> ) } </ResizeableContainer> ); case VIS_VIEW: return ( <ResizeableContainer> { (width, height) => ( <IDCVis type={TYPE_EXEC} width={width} height={height} onEditElement={() => {}} /> ) } </ResizeableContainer> ); default: return null; Loading @@ -255,6 +359,10 @@ const DashboardConfiguration = (props) => { changeSourceNodeSelected={props.changeSourceNodeSelected} changeDisplayEdgeLabels={props.changeDisplayEdgeLabels} displayEdgeLabels={props.displayEdgeLabels} timeIntervalDurationChanged={props.timeIntervalDurationChanged} stopMetricsPolling={props.stopMetricsPolling} startMetricsPolling={props.startMetricsPolling} metricsPollingStopped={props.metricsPollingStopped} /> ); } Loading Loading @@ -321,6 +429,8 @@ class DashboardContainer extends Component { constructor(props) { super(props); this.keyForSvg = 0; this.state = { configurationType: null, view1Name: APPS_VIEW, Loading @@ -329,15 +439,13 @@ class DashboardContainer extends Component { nbSecondsToDisplay: 25, displayEdgeLabels: false }; this.epochs = []; } componentDidMount() { this.epochCount = 0; const nextData = () => { this.epochCount += 1; this.fetchMetrics(); }; this.dataTimer = setInterval(nextData, 1000); clearInterval(this.dataTimer); this.startMetricsPolling(); } componentWillUnmount() { Loading @@ -345,8 +453,9 @@ class DashboardContainer extends Component { } fetchMetrics() { const startTime = moment().utc().add(-7, 'seconds').format(TIME_FORMAT); const stopTime = moment().utc().add(-6, 'seconds').format(TIME_FORMAT); const delta = -7; const startTime = moment().utc().add(delta, 'seconds').format(TIME_FORMAT); const stopTime = moment().utc().add(delta + 1, 'seconds').format(TIME_FORMAT); return axios.get(`${metricsBasePath}/metrics?startTime=${startTime}&stopTime=${stopTime}`) .then(res => { Loading Loading @@ -381,7 +490,45 @@ class DashboardContainer extends Component { this.setState({displayEdgeLabels: val}); } changeMetricsTimeIntervalDuration(duration) { this.props.changeMetricsTimeIntervalDuration(duration); } stopMetricsPolling() { // clearInterval(this.dataTimer); this.setState({metricsPollingStopped: true}); } startMetricsPolling() { // this.props.clearMetricsEpochs(); this.epochCount = 0; const nextData = () => { this.epochCount += 1; this.fetchMetrics(); }; if (!this.dataTimer) { this.dataTimer = setInterval(nextData, 1000); } this.setState({metricsPollingStopped: false}); } render() { if (EXEC_STATE_IDLE === this.props.scenarioState) { console.log('Scenario is idle'); } let epochs = null; if (!this.state.metricsPollingStopped) { this.epochs = this.props.epochs.slice(); epochs = this.epochs; } else { epochs = this.epochs; } this.keyForSvg++; const root = this.getRoot(); const nodes = root.descendants(); Loading Loading @@ -410,11 +557,11 @@ class DashboardContainer extends Component { }; // Determine first and last epochs const firstEpoch = this.props.epochs.length ? this.props.epochs[0] : { const firstEpoch = epochs.length ? epochs[0] : { data: [], startTime: null }; let lastEpoch = this.props.epochs.length ? this.props.epochs.slice(-1)[0] : { let lastEpoch = epochs.length ? epochs.slice(-1)[0] : { data: [], startTime: null }; Loading @@ -422,7 +569,7 @@ class DashboardContainer extends Component { // Determine startTime of first epoch and endTime of last epoch const startTime = firstEpoch.data.length ? firstEpoch.startTime : null; const endTime = lastEpoch.data.length ? moment(lastEpoch.startTime).add(1, 'seconds').format(TIME_FORMAT) : null; const series = epochsToSeries(this.props.epochs, selectedSource); const series = epochsToSeries(epochs, selectedSource); const withTypeAndSource = type => source => point => { return point.dataType === type && point.src === source; Loading @@ -441,37 +588,39 @@ class DashboardContainer extends Component { // Mobility events const extractPointsOfType = type => epoch => epoch.data.filter(isDataPointOfType(type)); const extractMobilityEvents = extractPointsOfType(MOBILITY_EVENT); const mobilityEvents = this.props.epochs.flatMap(extractMobilityEvents); const mobilityEvents = epochs.flatMap(extractMobilityEvents); if (mobilityEvents.length) { // console.log('Some mobility events ...'); } const height = 600; // const height = 600; let span1 = 6; let width1 = 700; let width2 = 700; let span1 = 12; let span2 = 12; // let width1 = 700; // let width2 = 700; if (this.state.view1Name === VIEW_NAME_NONE) { span1 = 0; width1 = 0; width2 = 1400; } const view1Present = this.state.view1Name !== VIEW_NAME_NONE; const view2Present = this.state.view2Name !== VIEW_NAME_NONE; if (this.state.view2Name === VIEW_NAME_NONE) { span1 = 12; width1 = 1400; width2 = 0; if (view1Present && view2Present) { span1 = 6; span2 = 6; } else if (!view1Present && !view2Present) { span1 = 0; span2 = 0; } const view1 = ( <ViewForName keyForSvg={this.keyForSvg} apps={apps} colorRange={colorRange} width={width1} height={height} // width={width1} // height={height} data={lastEpochData1} series={series1} startTime={startTime} Loading @@ -488,10 +637,11 @@ class DashboardContainer extends Component { const view2 = ( <ViewForName keyForSvg={this.keyForSvg} apps={apps} colorRange={colorRange} width={width2} height={height} // width={width2} // height={height} data={lastEpochData2} series={series2} startTime={startTime} Loading Loading @@ -520,6 +670,10 @@ class DashboardContainer extends Component { nodeIds={appIds} sourceNodeSelected={this.props.sourceNodeSelected} changeSourceNodeSelected={(nodeId) => this.props.changeSourceNodeSelected(appMap[nodeId])} timeIntervalDurationChanged={(duration) => {this.changeMetricsTimeIntervalDuration(duration);}} stopMetricsPolling={() => this.stopMetricsPolling()} startMetricsPolling={() => this.startMetricsPolling()} metricsPollingStopped={this.state.metricsPollingStopped} dashboardViewsList={DASHBOARD_VIEWS_LIST} changeView1={(viewName) => this.changeView1(viewName)} changeView2={(viewName) => this.changeView2(viewName)} Loading @@ -529,21 +683,26 @@ class DashboardContainer extends Component { <Grid> <GridCell span={span1} style={{paddingRight: 10}}> {!view1Present ? null : ( <GridCell span={span1} style={{paddingRight: 10}} className='chartContainer'> <Elevation z={2} style={{padding: 10}} > {view1} </Elevation> </GridCell> )} <GridCell span={span1} style={{marginLeft: -10, paddingLeft: 10}}> {!view2Present ? null : ( <GridCell span={span2} style={{marginLeft: -10, paddingLeft: 10}} className='chartContainer'> <Elevation z={2} style={{padding: 10}} > {view2} </Elevation> </GridCell> )} </Grid> </> Loading @@ -556,7 +715,10 @@ const mapStateToProps = state => { displayedScenario: state.exec.displayedScenario, epochs: state.exec.metrics.epochs, sourceNodeSelected: state.exec.metrics.sourceNodeSelected, dataTypeSelected: state.exec.metrics.dataTypeSelected dataTypeSelected: state.exec.metrics.dataTypeSelected, eventCreationMode: state.exec.eventCreationMode, metricsTimeIntervalDuration: state.exec.metrics.timeIntervalDuration, scenarioState: state.exec.state.scenario }; }; Loading @@ -564,7 +726,9 @@ const mapDispatchToProps = dispatch => { return { changeSelectedDestination: (dest) => dispatch(execFakeChangeSelectedDestination(dest)), changeSourceNodeSelected: (src) => dispatch(execChangeSourceNodeSelected(src)), addMetricsEpoch: (epoch) => dispatch(execAddMetricsEpoch(epoch)) addMetricsEpoch: (epoch) => dispatch(execAddMetricsEpoch(epoch)), changeMetricsTimeIntervalDuration: (duration) => dispatch(execChangeMetricsTimeIntervalDuration(duration)), clearMetricsEpochs: () => dispatch(execClearMetricsEpochs()) }; }; Loading js-apps/meep-frontend/src/js/containers/exec/exec-page-container.js +8 −12 Original line number Diff line number Diff line Loading @@ -48,8 +48,7 @@ import { // Event types MOBILITY_EVENT, NETWORK_CHARACTERISTICS_EVENT, PAGE_EXECUTE, PAGE_EXPERIMENTAL_EXECUTE PAGE_EXECUTE } from '../../state/ui'; import { Loading @@ -60,8 +59,6 @@ import { } from '../../state/exec'; import { TYPE_EXEC, // States EXEC_STATE_IDLE } from '../../meep-constants'; Loading Loading @@ -185,9 +182,9 @@ class ExecPageContainer extends Component { showApps(show) { this.props.changeShowApps(show); // _.defer(() => { // this.props.execVis.network.setData(this.props.execVisData); // }); _.defer(() => { this.props.execVis.network.setData(this.props.execVisData); }); } renderDialogs() { Loading Loading @@ -223,7 +220,7 @@ class ExecPageContainer extends Component { } render() { if (this.props.page !== PAGE_EXECUTE && this.props.page !== PAGE_EXPERIMENTAL_EXECUTE) { if (this.props.page !== PAGE_EXECUTE) { return null; } Loading @@ -231,8 +228,8 @@ class ExecPageContainer extends Component { ? this.props.execScenarioName : this.props.cfgScenarioName; const spanLeft = this.props.eventCreationMode ? 8 : 12; const spanRight = this.props.eventCreationMode ? 4 : 0; const spanLeft = this.props.eventCreationMode ? 10 : 12; const spanRight = this.props.eventCreationMode ? 2 : 0; return ( <div style={{width: '100%'}}> {this.renderDialogs()} Loading Loading @@ -275,8 +272,7 @@ class ExecPageContainer extends Component { <GridCell span={spanLeft}> {/* <Elevation className="component-style" z={2}> */} <div> {this.props.experimental ? (<DashboardContainer showAppsView={true}/>) : (<IDCVis type={TYPE_EXEC} />)} <DashboardContainer showAppsView={true}/> </div> {/* </Elevation> */} </GridCell> Loading js-apps/meep-frontend/src/js/containers/exec/mobility-event-pane.js +75 −65 Original line number Diff line number Diff line Loading @@ -44,6 +44,40 @@ class MobilityEventPane extends Component { }; } shouldComponentUpdate(nextProps, nextState) { /** * element={props.element} eventTypes={props.eventTypes} api={props.api} onSuccess={props.onSuccess} onClose={props.onClose} currentEvent={props.currentEvent} UEs={props.UEs} POAs={props.POAs} EDGEs={props.EDGEs} FOGs={props.FOGs} ZONEs={props.ZONEs} MobTypes={props.MobTypes} FogEdges={props.FogEdges} EdgeApps={props.EdgeApps} */ return this.props.api !== nextProps.api || this.props.element !== nextProps.element || this.props.api !== nextProps.api || this.props.currentEvent !== nextProps.currentEvent || this.props.UEs !== nextProps.UEs || this.props.POAs !== nextProps.POAs || this.props.EDGEs !== nextProps.EDGEs || this.props.FOGs !== nextProps.FOGs || this.props.ZONEs !== nextProps.ZONEs || this.props.MobTypes !== nextProps.MobTypes || this.props.FogEdges !== nextProps.FogEdges || this.props.EdgeApps !== nextProps.EdgeApps || this.state.eventTarget !== nextState.eventTarget; } triggerEvent(e) { e.preventDefault(); var meepEvent = { Loading @@ -65,38 +99,9 @@ class MobilityEventPane extends Component { render() { //check with list the target belongs to if (this.values.eventTarget === undefined || this.values.eventTarget === '') { return ( <div> <Grid style={styles.field}> <GridCell span="8"> <Select style={styles.select} label="Target" outlined options={_.map(this.props.MobTypes, elem => getElemFieldVal(elem, FIELD_NAME))} onChange={(event)=>{this.values['eventTarget'] = event.target.value;}} data-cy={EXEC_EVT_MOB_TARGET} /> </GridCell> <GridCell span="4"> </GridCell> </Grid> <CancelApplyPair cancelText="Close" applyText="Submit" onCancel={this.props.onClose} onApply={(e) => this.triggerEvent(e)} /> </div> ); } //let found = this.props.UEs.find(element => element.label == this.values.eventTarget); //find if its the selection was a UE, otherwise (in order) EDGE, FOG, EDGE-APP, UE-APP var target = this.values.eventTarget; var target = this.state.eventTarget; var found = this.props.UEs.find(function(element) { return element.label === target; }); Loading Loading @@ -127,8 +132,10 @@ class MobilityEventPane extends Component { } } } return ( <div> <> <Grid style={styles.field}> <GridCell span="8"> <Select Loading @@ -136,7 +143,10 @@ class MobilityEventPane extends Component { label="Target" outlined options={_.map(this.props.MobTypes, elem => getElemFieldVal(elem, FIELD_NAME))} onChange={(event)=>{this.values['eventTarget'] = event.target.value;}} onChange={(event)=>{ this.values['eventTarget'] = event.target.value; this.setState({eventTarget: event.target.value}); }} data-cy={EXEC_EVT_MOB_TARGET} /> </GridCell> Loading @@ -150,20 +160,20 @@ class MobilityEventPane extends Component { label="Destination" outlined options={_.map(populateDestination, elem => getElemFieldVal(elem, FIELD_NAME))} onChange={(event)=>{this.values['eventDestination'] = event.target.value;}} onChange={(event)=>{this.values['eventDestination'] = event.target.value; this.setState({eventDestination: event.target.value});}} data-cy={EXEC_EVT_MOB_DEST} /> </GridCell> <GridCell span="4"> </GridCell> </Grid> <CancelApplyPair cancelText="Close" applyText="Submit" onCancel={this.props.onClose} onApply={(e) => this.triggerEvent(e)} /> </> </div> ); } Loading Loading
js-apps/meep-frontend/src/js/components/helper-components/nc-group.js +155 −64 Original line number Diff line number Diff line Loading @@ -75,8 +75,11 @@ import { PREFIX_EDGE_FOG, PREFIX_TERM_LINK, PREFIX_LINK, PREFIX_APP PREFIX_APP, // NC Group Layouts MEEP_COMPONENT_SINGLE_COLUMN_LAYOUT, MEEP_COMPONENT_TABLE_LAYOUT } from '../../meep-constants'; const MIN_LATENCY_VALUE = 0; Loading Loading @@ -158,6 +161,83 @@ const validateThroughput = (val) => { return null; }; const TableLayout = (props) => { return ( <div> <Grid> <GridCell span="6"> {props.latencyComponent} </GridCell> <GridCell span="6"> {props.latencyVariationComponent} </GridCell> </Grid> <Grid style={{marginBottom: 10}}> <GridCell span="6"> {props.packetLossComponent} </GridCell> <GridCell span="6"> {props.throughputComponent} </GridCell> </Grid> </div> ); }; const SingleColumnLayout = (props) => { return ( <div> <Grid> <GridCell span="12"> {props.latencyComponent} </GridCell> </Grid> <Grid> <GridCell span="12"> {props.latencyVariationComponent} </GridCell> </Grid> <Grid style={{marginBottom: 10}}> <GridCell span="12"> {props.packetLossComponent} </GridCell> </Grid> <Grid style={{marginBottom: 10}}> <GridCell span="12"> {props.throughputComponent} </GridCell> </Grid> </div> ); }; const NCLayout = (props) => { switch(props.layout) { case MEEP_COMPONENT_SINGLE_COLUMN_LAYOUT: return ( <SingleColumnLayout {...props} /> ); case MEEP_COMPONENT_TABLE_LAYOUT: return ( <TableLayout {...props} /> ); default: return ( <TableLayout {...props} /> ); } }; const NCGroup = ({prefix, onUpdate, element}) => { const formLabel = (valueName) => { const space = prefix ? ' ' : ''; Loading Loading @@ -229,10 +309,8 @@ const NCGroup = ({prefix, onUpdate, element}) => { return null; } return ( <div> <Grid> <GridCell span="6"> const latencyComponent = ( <> <TextField outlined style={{width: '100%'}} label={formLabel('Latency') + ' (ms)'} onChange={(e) => handleEvent(e, latencyFieldName, validateLatency)} Loading @@ -245,8 +323,11 @@ const NCGroup = ({prefix, onUpdate, element}) => { {getElemFieldErr(element, latencyFieldName)} </span> </TextFieldHelperText> </GridCell> <GridCell span="6"> </> ); const latencyVariationComponent = ( <> <TextField outlined style={{width: '100%'}} label={formLabel('Latency Variation') + ' (ms)'} onChange={(e) => handleEvent(e, latencyVarFieldName, validateLatencyVariation)} Loading @@ -259,11 +340,11 @@ const NCGroup = ({prefix, onUpdate, element}) => { {getElemFieldErr(element, latencyVarFieldName)} </span> </TextFieldHelperText> </GridCell> </Grid> </> ); <Grid style={{marginBottom: 10}}> <GridCell span="6"> const packetLossComponent = ( <> <TextField outlined style={{width: '100%'}} label={formLabel('Packet Loss') + ' (%)'} onChange={(e) => handleEvent(e, packetLossFieldName, validatePacketLoss)} Loading @@ -276,9 +357,11 @@ const NCGroup = ({prefix, onUpdate, element}) => { {getElemFieldErr(element, packetLossFieldName)} </span> </TextFieldHelperText> </> ); </GridCell> <GridCell span="6"> const throughputComponent = ( <> <TextField outlined style={{width: '100%'}} label={formLabel('Throughput') + ' Mbps'} onChange={(e) => handleEvent(e, throughputFieldName, validateThroughput)} Loading @@ -291,9 +374,17 @@ const NCGroup = ({prefix, onUpdate, element}) => { {getElemFieldErr(element, throughputFieldName)} </span> </TextFieldHelperText> </GridCell> </Grid> </div> </> ); return ( <NCLayout latencyComponent={latencyComponent} latencyVariationComponent={latencyVariationComponent} packetLossComponent={packetLossComponent} throughputComponent={throughputComponent} > </NCLayout> ); }; Loading
js-apps/meep-frontend/src/js/containers/cfg/cfg-network-element-container.js +5 −1 Original line number Diff line number Diff line Loading @@ -117,7 +117,10 @@ import { CFG_ELEM_EGRESS_SVC_MAP, CFG_BTN_NEW_ELEM, CFG_BTN_DEL_ELEM CFG_BTN_DEL_ELEM, // Layout type MEEP_COMPONENT_TABLE_LAYOUT } from '../../meep-constants'; Loading Loading @@ -475,6 +478,7 @@ const NCGroups = ({prefixes, onUpdate, element}) => { element={element} prefix={p} key={p} layout={MEEP_COMPONENT_TABLE_LAYOUT} /> ); }); Loading
js-apps/meep-frontend/src/js/containers/dashboard-container.js +266 −102 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import { Elevation } from '@rmwc/elevation'; // import ReactDOM from 'react-dom'; import { Button } from '@rmwc/button'; import { Checkbox } from '@rmwc/checkbox'; import { Slider } from '@rmwc/slider'; import moment from 'moment'; import * as d3 from 'd3'; import axios from 'axios'; Loading @@ -14,6 +15,9 @@ import IDCLineChart from './idc-line-chart'; import IDCGraph from './idc-graph'; import IDCAppsView from './idc-apps-view'; import IDSelect from '../components/helper-components/id-select'; import IDCVis from './idc-vis'; import ResizeableContainer from './resizeable-container'; import { idlog Loading @@ -31,13 +35,17 @@ import { import { execFakeChangeSelectedDestination, execChangeSourceNodeSelected, execAddMetricsEpoch execAddMetricsEpoch, execChangeMetricsTimeIntervalDuration, execClearMetricsEpochs } from '../state/exec'; import { LATENCY_METRICS, THROUGHPUT_METRICS, MOBILITY_EVENT MOBILITY_EVENT, TYPE_EXEC, EXEC_STATE_IDLE } from '../meep-constants'; const VIEW_NAME_NONE = 'none'; Loading Loading @@ -85,8 +93,54 @@ const epochsToSeries = (epochs) => { return series; }; const TimeIntervalConfig = (props) => { let PauseResumeButton = null; if (props.metricsPollingStopped) { PauseResumeButton = () => ( <Button outlined onClick={() => props.startMetricsPolling()} > RESUME </Button> ); } else { PauseResumeButton = () => ( <Button outlined onClick={() => props.stopMetricsPolling()} > PAUSE </Button> ); } return ( <div> <Grid> <GridCell span={3}> <Slider value={props.value} onChange={e => props.timeIntervalDurationChanged(e.detail.value)} discrete min={5} max={60} step={1} /> </GridCell> <GridCell span={1}> </GridCell> <GridCell span={8}> <PauseResumeButton /> </GridCell> </Grid> </div> ); }; const ConfigurationView = (props) => { return ( <> <Grid> <GridCell span={2}> <IDSelect Loading @@ -110,7 +164,7 @@ const ConfigurationView = (props) => { value={props.view1} /> </GridCell> <GridCell span={2}> <GridCell span={3}> <IDSelect label={'Select Source Node'} outlined Loading @@ -121,7 +175,7 @@ const ConfigurationView = (props) => { value={props.sourceNodeSelected ? props.sourceNodeSelected.data.id : ''} /> </GridCell> <GridCell span={1}> <GridCell span={4}> <Checkbox checked={props.displayEdgeLabels} onChange={() => props.changeDisplayEdgeLabels(!props.displayEdgeLabels)} Loading @@ -129,9 +183,16 @@ const ConfigurationView = (props) => { Show data on edges </Checkbox> </GridCell> <GridCell span={5}> <GridCell span={1}> </GridCell> </Grid> <TimeIntervalConfig timeIntervalDurationChanged={(value) => {props.timeIntervalDurationChanged(value);}} stopMetricsPolling={props.stopMetricsPolling} startMetricsPolling={props.startMetricsPolling} metricsPollingStopped={props.metricsPollingStopped} /> </> ); }; Loading @@ -145,14 +206,15 @@ const HIERARCHY_VIEW = 'HIERARCHY_VIEW'; const APPS_VIEW = 'APPS_VIEW'; const LATENCY_VIEW = 'LATENCY_VIEW'; const THROUGHPUT_VIEW = 'THROUGHPUT_VIEW'; const VIS_VIEW = 'VIS_VIEW'; const DASHBOARD_VIEWS_LIST = [VIEW_NAME_NONE, HIERARCHY_VIEW, APPS_VIEW, LATENCY_VIEW, THROUGHPUT_VIEW]; const DASHBOARD_VIEWS_LIST = [VIEW_NAME_NONE, VIS_VIEW, APPS_VIEW, LATENCY_VIEW, THROUGHPUT_VIEW, HIERARCHY_VIEW]; const ViewForName = ( { keyForSvg, apps, colorRange, width, min, max, data, Loading @@ -174,18 +236,26 @@ const ViewForName = ( switch(viewName) { case HIERARCHY_VIEW: return ( <ResizeableContainer key={keyForSvg}> {(width, height) => ( <IDCGraph keyForSvg={keyForSvg} width={width} height={600} /> height={height} />)} </ResizeableContainer> ); case APPS_VIEW: return ( <ResizeableContainer key={keyForSvg}> { (width, height) => ( <IDCAppsView keyForSvg={keyForSvg} apps={apps} colorRange={colorRange} width={width} height={600} height={height} data={data} series={series} startTime={startTime} Loading @@ -198,15 +268,22 @@ const ViewForName = ( }} displayEdgeLabels={displayEdgeLabels} /> ) } </ResizeableContainer> ); case LATENCY_VIEW: return ( <ResizeableContainer key={keyForSvg}> {(width, height) => ( <IDCLineChart keyForSvg={keyForSvg} data={dataPoints} series={series} startTime={startTime} mobilityEvents={mobilityEvents} width={width} height={600} width={width} height={height} destinations={appIds} colorRange={colorRange} selectedSource={selectedSource} Loading @@ -215,15 +292,23 @@ const ViewForName = ( max={max} colorForApp={colorForApp} /> ) } </ResizeableContainer> ); case THROUGHPUT_VIEW: return ( <ResizeableContainer key={keyForSvg}> { (width, height) => ( <IDCLineChart keyForSvg={keyForSvg} data={dataPoints} series={series} startTime={startTime} mobilityEvents={mobilityEvents} width={width} height={600} width={width} height={height} destinations={appIds} colorRange={colorRange} selectedSource={selectedSource} Loading @@ -232,6 +317,25 @@ const ViewForName = ( max={max} colorForApp={colorForApp} /> ) } </ResizeableContainer> ); case VIS_VIEW: return ( <ResizeableContainer> { (width, height) => ( <IDCVis type={TYPE_EXEC} width={width} height={height} onEditElement={() => {}} /> ) } </ResizeableContainer> ); default: return null; Loading @@ -255,6 +359,10 @@ const DashboardConfiguration = (props) => { changeSourceNodeSelected={props.changeSourceNodeSelected} changeDisplayEdgeLabels={props.changeDisplayEdgeLabels} displayEdgeLabels={props.displayEdgeLabels} timeIntervalDurationChanged={props.timeIntervalDurationChanged} stopMetricsPolling={props.stopMetricsPolling} startMetricsPolling={props.startMetricsPolling} metricsPollingStopped={props.metricsPollingStopped} /> ); } Loading Loading @@ -321,6 +429,8 @@ class DashboardContainer extends Component { constructor(props) { super(props); this.keyForSvg = 0; this.state = { configurationType: null, view1Name: APPS_VIEW, Loading @@ -329,15 +439,13 @@ class DashboardContainer extends Component { nbSecondsToDisplay: 25, displayEdgeLabels: false }; this.epochs = []; } componentDidMount() { this.epochCount = 0; const nextData = () => { this.epochCount += 1; this.fetchMetrics(); }; this.dataTimer = setInterval(nextData, 1000); clearInterval(this.dataTimer); this.startMetricsPolling(); } componentWillUnmount() { Loading @@ -345,8 +453,9 @@ class DashboardContainer extends Component { } fetchMetrics() { const startTime = moment().utc().add(-7, 'seconds').format(TIME_FORMAT); const stopTime = moment().utc().add(-6, 'seconds').format(TIME_FORMAT); const delta = -7; const startTime = moment().utc().add(delta, 'seconds').format(TIME_FORMAT); const stopTime = moment().utc().add(delta + 1, 'seconds').format(TIME_FORMAT); return axios.get(`${metricsBasePath}/metrics?startTime=${startTime}&stopTime=${stopTime}`) .then(res => { Loading Loading @@ -381,7 +490,45 @@ class DashboardContainer extends Component { this.setState({displayEdgeLabels: val}); } changeMetricsTimeIntervalDuration(duration) { this.props.changeMetricsTimeIntervalDuration(duration); } stopMetricsPolling() { // clearInterval(this.dataTimer); this.setState({metricsPollingStopped: true}); } startMetricsPolling() { // this.props.clearMetricsEpochs(); this.epochCount = 0; const nextData = () => { this.epochCount += 1; this.fetchMetrics(); }; if (!this.dataTimer) { this.dataTimer = setInterval(nextData, 1000); } this.setState({metricsPollingStopped: false}); } render() { if (EXEC_STATE_IDLE === this.props.scenarioState) { console.log('Scenario is idle'); } let epochs = null; if (!this.state.metricsPollingStopped) { this.epochs = this.props.epochs.slice(); epochs = this.epochs; } else { epochs = this.epochs; } this.keyForSvg++; const root = this.getRoot(); const nodes = root.descendants(); Loading Loading @@ -410,11 +557,11 @@ class DashboardContainer extends Component { }; // Determine first and last epochs const firstEpoch = this.props.epochs.length ? this.props.epochs[0] : { const firstEpoch = epochs.length ? epochs[0] : { data: [], startTime: null }; let lastEpoch = this.props.epochs.length ? this.props.epochs.slice(-1)[0] : { let lastEpoch = epochs.length ? epochs.slice(-1)[0] : { data: [], startTime: null }; Loading @@ -422,7 +569,7 @@ class DashboardContainer extends Component { // Determine startTime of first epoch and endTime of last epoch const startTime = firstEpoch.data.length ? firstEpoch.startTime : null; const endTime = lastEpoch.data.length ? moment(lastEpoch.startTime).add(1, 'seconds').format(TIME_FORMAT) : null; const series = epochsToSeries(this.props.epochs, selectedSource); const series = epochsToSeries(epochs, selectedSource); const withTypeAndSource = type => source => point => { return point.dataType === type && point.src === source; Loading @@ -441,37 +588,39 @@ class DashboardContainer extends Component { // Mobility events const extractPointsOfType = type => epoch => epoch.data.filter(isDataPointOfType(type)); const extractMobilityEvents = extractPointsOfType(MOBILITY_EVENT); const mobilityEvents = this.props.epochs.flatMap(extractMobilityEvents); const mobilityEvents = epochs.flatMap(extractMobilityEvents); if (mobilityEvents.length) { // console.log('Some mobility events ...'); } const height = 600; // const height = 600; let span1 = 6; let width1 = 700; let width2 = 700; let span1 = 12; let span2 = 12; // let width1 = 700; // let width2 = 700; if (this.state.view1Name === VIEW_NAME_NONE) { span1 = 0; width1 = 0; width2 = 1400; } const view1Present = this.state.view1Name !== VIEW_NAME_NONE; const view2Present = this.state.view2Name !== VIEW_NAME_NONE; if (this.state.view2Name === VIEW_NAME_NONE) { span1 = 12; width1 = 1400; width2 = 0; if (view1Present && view2Present) { span1 = 6; span2 = 6; } else if (!view1Present && !view2Present) { span1 = 0; span2 = 0; } const view1 = ( <ViewForName keyForSvg={this.keyForSvg} apps={apps} colorRange={colorRange} width={width1} height={height} // width={width1} // height={height} data={lastEpochData1} series={series1} startTime={startTime} Loading @@ -488,10 +637,11 @@ class DashboardContainer extends Component { const view2 = ( <ViewForName keyForSvg={this.keyForSvg} apps={apps} colorRange={colorRange} width={width2} height={height} // width={width2} // height={height} data={lastEpochData2} series={series2} startTime={startTime} Loading Loading @@ -520,6 +670,10 @@ class DashboardContainer extends Component { nodeIds={appIds} sourceNodeSelected={this.props.sourceNodeSelected} changeSourceNodeSelected={(nodeId) => this.props.changeSourceNodeSelected(appMap[nodeId])} timeIntervalDurationChanged={(duration) => {this.changeMetricsTimeIntervalDuration(duration);}} stopMetricsPolling={() => this.stopMetricsPolling()} startMetricsPolling={() => this.startMetricsPolling()} metricsPollingStopped={this.state.metricsPollingStopped} dashboardViewsList={DASHBOARD_VIEWS_LIST} changeView1={(viewName) => this.changeView1(viewName)} changeView2={(viewName) => this.changeView2(viewName)} Loading @@ -529,21 +683,26 @@ class DashboardContainer extends Component { <Grid> <GridCell span={span1} style={{paddingRight: 10}}> {!view1Present ? null : ( <GridCell span={span1} style={{paddingRight: 10}} className='chartContainer'> <Elevation z={2} style={{padding: 10}} > {view1} </Elevation> </GridCell> )} <GridCell span={span1} style={{marginLeft: -10, paddingLeft: 10}}> {!view2Present ? null : ( <GridCell span={span2} style={{marginLeft: -10, paddingLeft: 10}} className='chartContainer'> <Elevation z={2} style={{padding: 10}} > {view2} </Elevation> </GridCell> )} </Grid> </> Loading @@ -556,7 +715,10 @@ const mapStateToProps = state => { displayedScenario: state.exec.displayedScenario, epochs: state.exec.metrics.epochs, sourceNodeSelected: state.exec.metrics.sourceNodeSelected, dataTypeSelected: state.exec.metrics.dataTypeSelected dataTypeSelected: state.exec.metrics.dataTypeSelected, eventCreationMode: state.exec.eventCreationMode, metricsTimeIntervalDuration: state.exec.metrics.timeIntervalDuration, scenarioState: state.exec.state.scenario }; }; Loading @@ -564,7 +726,9 @@ const mapDispatchToProps = dispatch => { return { changeSelectedDestination: (dest) => dispatch(execFakeChangeSelectedDestination(dest)), changeSourceNodeSelected: (src) => dispatch(execChangeSourceNodeSelected(src)), addMetricsEpoch: (epoch) => dispatch(execAddMetricsEpoch(epoch)) addMetricsEpoch: (epoch) => dispatch(execAddMetricsEpoch(epoch)), changeMetricsTimeIntervalDuration: (duration) => dispatch(execChangeMetricsTimeIntervalDuration(duration)), clearMetricsEpochs: () => dispatch(execClearMetricsEpochs()) }; }; Loading
js-apps/meep-frontend/src/js/containers/exec/exec-page-container.js +8 −12 Original line number Diff line number Diff line Loading @@ -48,8 +48,7 @@ import { // Event types MOBILITY_EVENT, NETWORK_CHARACTERISTICS_EVENT, PAGE_EXECUTE, PAGE_EXPERIMENTAL_EXECUTE PAGE_EXECUTE } from '../../state/ui'; import { Loading @@ -60,8 +59,6 @@ import { } from '../../state/exec'; import { TYPE_EXEC, // States EXEC_STATE_IDLE } from '../../meep-constants'; Loading Loading @@ -185,9 +182,9 @@ class ExecPageContainer extends Component { showApps(show) { this.props.changeShowApps(show); // _.defer(() => { // this.props.execVis.network.setData(this.props.execVisData); // }); _.defer(() => { this.props.execVis.network.setData(this.props.execVisData); }); } renderDialogs() { Loading Loading @@ -223,7 +220,7 @@ class ExecPageContainer extends Component { } render() { if (this.props.page !== PAGE_EXECUTE && this.props.page !== PAGE_EXPERIMENTAL_EXECUTE) { if (this.props.page !== PAGE_EXECUTE) { return null; } Loading @@ -231,8 +228,8 @@ class ExecPageContainer extends Component { ? this.props.execScenarioName : this.props.cfgScenarioName; const spanLeft = this.props.eventCreationMode ? 8 : 12; const spanRight = this.props.eventCreationMode ? 4 : 0; const spanLeft = this.props.eventCreationMode ? 10 : 12; const spanRight = this.props.eventCreationMode ? 2 : 0; return ( <div style={{width: '100%'}}> {this.renderDialogs()} Loading Loading @@ -275,8 +272,7 @@ class ExecPageContainer extends Component { <GridCell span={spanLeft}> {/* <Elevation className="component-style" z={2}> */} <div> {this.props.experimental ? (<DashboardContainer showAppsView={true}/>) : (<IDCVis type={TYPE_EXEC} />)} <DashboardContainer showAppsView={true}/> </div> {/* </Elevation> */} </GridCell> Loading
js-apps/meep-frontend/src/js/containers/exec/mobility-event-pane.js +75 −65 Original line number Diff line number Diff line Loading @@ -44,6 +44,40 @@ class MobilityEventPane extends Component { }; } shouldComponentUpdate(nextProps, nextState) { /** * element={props.element} eventTypes={props.eventTypes} api={props.api} onSuccess={props.onSuccess} onClose={props.onClose} currentEvent={props.currentEvent} UEs={props.UEs} POAs={props.POAs} EDGEs={props.EDGEs} FOGs={props.FOGs} ZONEs={props.ZONEs} MobTypes={props.MobTypes} FogEdges={props.FogEdges} EdgeApps={props.EdgeApps} */ return this.props.api !== nextProps.api || this.props.element !== nextProps.element || this.props.api !== nextProps.api || this.props.currentEvent !== nextProps.currentEvent || this.props.UEs !== nextProps.UEs || this.props.POAs !== nextProps.POAs || this.props.EDGEs !== nextProps.EDGEs || this.props.FOGs !== nextProps.FOGs || this.props.ZONEs !== nextProps.ZONEs || this.props.MobTypes !== nextProps.MobTypes || this.props.FogEdges !== nextProps.FogEdges || this.props.EdgeApps !== nextProps.EdgeApps || this.state.eventTarget !== nextState.eventTarget; } triggerEvent(e) { e.preventDefault(); var meepEvent = { Loading @@ -65,38 +99,9 @@ class MobilityEventPane extends Component { render() { //check with list the target belongs to if (this.values.eventTarget === undefined || this.values.eventTarget === '') { return ( <div> <Grid style={styles.field}> <GridCell span="8"> <Select style={styles.select} label="Target" outlined options={_.map(this.props.MobTypes, elem => getElemFieldVal(elem, FIELD_NAME))} onChange={(event)=>{this.values['eventTarget'] = event.target.value;}} data-cy={EXEC_EVT_MOB_TARGET} /> </GridCell> <GridCell span="4"> </GridCell> </Grid> <CancelApplyPair cancelText="Close" applyText="Submit" onCancel={this.props.onClose} onApply={(e) => this.triggerEvent(e)} /> </div> ); } //let found = this.props.UEs.find(element => element.label == this.values.eventTarget); //find if its the selection was a UE, otherwise (in order) EDGE, FOG, EDGE-APP, UE-APP var target = this.values.eventTarget; var target = this.state.eventTarget; var found = this.props.UEs.find(function(element) { return element.label === target; }); Loading Loading @@ -127,8 +132,10 @@ class MobilityEventPane extends Component { } } } return ( <div> <> <Grid style={styles.field}> <GridCell span="8"> <Select Loading @@ -136,7 +143,10 @@ class MobilityEventPane extends Component { label="Target" outlined options={_.map(this.props.MobTypes, elem => getElemFieldVal(elem, FIELD_NAME))} onChange={(event)=>{this.values['eventTarget'] = event.target.value;}} onChange={(event)=>{ this.values['eventTarget'] = event.target.value; this.setState({eventTarget: event.target.value}); }} data-cy={EXEC_EVT_MOB_TARGET} /> </GridCell> Loading @@ -150,20 +160,20 @@ class MobilityEventPane extends Component { label="Destination" outlined options={_.map(populateDestination, elem => getElemFieldVal(elem, FIELD_NAME))} onChange={(event)=>{this.values['eventDestination'] = event.target.value;}} onChange={(event)=>{this.values['eventDestination'] = event.target.value; this.setState({eventDestination: event.target.value});}} data-cy={EXEC_EVT_MOB_DEST} /> </GridCell> <GridCell span="4"> </GridCell> </Grid> <CancelApplyPair cancelText="Close" applyText="Submit" onCancel={this.props.onClose} onApply={(e) => this.triggerEvent(e)} /> </> </div> ); } Loading