Commit 9f3bf4f3 authored by Francis Renaud's avatar Francis Renaud
Browse files

sp 27 - new layout, abilty to change views, mobility events displayed

Commented out hardcoded IP address
parent 30f09922
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@ import { TextField, TextFieldHelperText } from '@rmwc/textfield';

import {
  firstLetterUpper
} from '../../util/stringManipulation';
} from '../../util/string-manipulation';

import {
  // Field Names
+400 −67
Original line number Diff line number Diff line
import _ from 'lodash';
import { connect } from 'react-redux';
import React, { Component }  from 'react';
import React, { Component, useState }  from 'react';

import { Grid, GridCell, GridInner } from '@rmwc/grid';
import { Elevation } from '@rmwc/elevation';
import { Graph } from 'react-d3-graph';
import ReactDOM from 'react-dom';
import { Button } from '@rmwc/button';
import { Checkbox } from '@rmwc/checkbox';
import { TextField, TextFieldHelperText } from '@rmwc/textfield';
import * as d3 from 'd3';
import axios from 'axios';

@@ -13,18 +16,33 @@ import axios from 'axios';
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 {
  getScenarioNodeChildren,
  isApp
} from '../util/scenario-utils';

import {
  dataAccessorForType,
  dataSetterForType,
  isDataPointOfType
} from '../util/metrics';

import {
  execFakeChangeSelectedDestination,
  execChangeSourceNodeSelected,
  execAddMetricsEpoch
} from '../state/exec';

import {
  LATENCY_METRICS,
  THROUGHPUT_METRICS,
  MOBILITY_EVENT
} from '../meep-constants';

const VIEW_NAME_NONE = 'none';

function colorArray(dataLength) {
  const colorScale = d3.interpolateInferno;
  // const colorScale = d3.interpolateMagma;
@@ -79,27 +97,234 @@ const dataPointFromEpochDataPoints = destinations => sourceNodeId => dataAccesso
const notNull = x => x;
const epochsToDataPoints = epochs => nb => destinations => dataAccessor => sourceNodeId => {
  const selectedEpochs = epochs.length ? epochs.slice(-nb) : [];

  if (selectedEpochs.length === 0) {
    console.log('epoch length is 0');
  }
  const dataPoints = selectedEpochs.map(dataPointFromEpochDataPoints(destinations)(sourceNodeId)(dataAccessor)).filter(notNull);
  return dataPoints;
};

const dataAccessorForType = dataType => {
  switch (dataType) {
  case 'latency':
    return p => p.data.latency;
  case 'ingressPacketStats':
    return p => p.data.throughput;
const ConfigurationView = (props) => {
  return (
    <Grid>
      <GridCell span={2}>
        <IDSelect
          label={'Select View 1'}
          outlined
          options={props.dashboardViewsList}
          onChange={(e) => {
            props.changeView1(e.target.value);
          }}
          // disabled={props.disabled}
          value={props.view1}
        />
      </GridCell>
      <GridCell span={2}>
        <IDSelect
          label={'Select View 2'}
          outlined
          options={props.dashboardViewsList}
          onChange={(e) => {
            props.changeView2(e.target.value);
          }}
          // disabled={props.disabled}
          value={props.view1}
        />
      </GridCell>
      <GridCell span={2}>
        <IDSelect
          label={'Select Source Node'}
          outlined
          options={props.nodeIds}
          onChange={(e) => {
            props.changeSourceNodeSelected(e.target.value);
          }}
          // disabled={props.disabled}
          value={props.sourceNodeSelected ? props.sourceNodeSelected.data.id : ''}
        />
      </GridCell>
      <GridCell span={1}>
        <Checkbox
          checked={props.displayEdgeLabels}
          onChange={() => props.changeDisplayEdgeLabels(!props.displayEdgeLabels)}
        >
                    Show data on edges
        </Checkbox>
      </GridCell>
      <GridCell span={5}>
      </GridCell>
    </Grid>
  );
};

const DATA_CONFIGURATION = 'DATA_CONFIGURATION';
const MAIN_CONFIGURATION = 'MAIN_CONFIGURATION';

const buttonStyles = {
  marginRight: 0
};

const HIERARCHY_VIEW = 'HIERARCHY_VIEW';
const APPS_VIEW = 'APPS_VIEW';
const LATENCY_VIEW = 'LATENCY_VIEW';
const THROUGHPUT_VIEW = 'THROUGHPUT_VIEW';

const DASHBOARD_VIEWS_LIST = [VIEW_NAME_NONE, HIERARCHY_VIEW, APPS_VIEW, LATENCY_VIEW, THROUGHPUT_VIEW];

const ViewForName = (
  {
    apps,
    colorRange,
    width,
    height,
    min,
    max,
    data,
    mobilityEvents,
    dataPoints,
    dataAccessor,
    dataType,
    selectedSource,
    colorForApp,
    changeSourceNodeSelected,
    viewName,
    displayEdgeLabels
  }
) => {
  
  const appIds = apps.map(app => app.data.id);
  switch(viewName) {
  case HIERARCHY_VIEW:
    return (
      <IDCGraph 
        width={width}
        height={600}
      />
    );
  case APPS_VIEW:
    return (
      <IDCAppsView
        apps={apps}
        colorRange={colorRange}
        width={width}
        height={600}
        data={data}
        dataAccessor={dataAccessor}
        dataType={dataType}
        selectedSource={selectedSource}
        colorForApp={colorForApp}
        onNodeClicked={(e) => {
          console.log('Node clicked is: ', e.node);
          changeSourceNodeSelected(e.node);
        }}
        displayEdgeLabels={displayEdgeLabels}
      />
    );
  case LATENCY_VIEW:
    return (
      <IDCLineChart
        data={dataPoints}
        mobilityEvents={mobilityEvents}
        width={width} height={600}
        destinations={appIds}
        colorRange={colorRange}
        selectedSource={selectedSource}
        dataType={dataType}
        // Specify units
        // Specify label
        min={min}
        max={max}
        colorForApp={colorForApp}
      />
    );
  case THROUGHPUT_VIEW:
    return (
      <IDCLineChart
        data={dataPoints}
        mobilityEvents={mobilityEvents}
        width={width} height={600}
        destinations={appIds}
        colorRange={colorRange}
        selectedSource={selectedSource}
        dataType={dataType}
        // Specify units
        // Specify label
        min={min}
        max={max}
        colorForApp={colorForApp}
      />
    );
  default:
    return dataAccessorForType('latency');
    return null;
  }
};

const DashboardConfiguration = (props) => {
  let configurationView = null;
  
  if(props.configurationType) {
    configurationView = (
      <ConfigurationView
        dashboardViewsList={props.dashboardViewsList}
        view1Type={props.view1Type}
        view2Type={props.view2Type}
        changeView1={props.changeView1}
        changeView2={props.changeView2}

        nodeIds={props.nodeIds}
        sourceNodeSelected={props.sourceNodeSelected}
        changeSourceNodeSelected={props.changeSourceNodeSelected}
        changeDisplayEdgeLabels={props.changeDisplayEdgeLabels}
        displayEdgeLabels={props.displayEdgeLabels}
      />
    );
  }

  const buttonConfig = !props.configurationType
    ? (
      <Button outlined style={buttonStyles} onClick={props.displayConfiguration}>
          Configuration
      </Button>
    )
    : null;

  const buttonClose = props.configurationType
    ? (
      <Button outlined style={buttonStyles} onClick={props.hideConfiguration}>
          Close
      </Button>
    )
    : null;

  const backgroundColor = 'ffffff'; // props.configurationType ? '#e4e4e4' : 'ffffff';
  return (
    <div style={{border: '1px solid #e4e4e4', padding: 10, marginBottom: 10, backgroundColor: backgroundColor}}>
      <Grid>
        <GridCell span={10}>
        </GridCell>
        <GridCell span={2}>
          {buttonConfig}
          {buttonClose}
        </GridCell>
      </Grid>

      {configurationView}
    </div>
  );
};

class DashboardContainer extends Component {
  constructor(props) {
    super(props);

    this.state = {
      
      configurationType: null,
      view1Name: APPS_VIEW,
      view2Name: LATENCY_VIEW,
      sourceNodeId: '',
      nbSecondsToDisplay: 25,
      displayEdgeLabels: false
    };
  }

@@ -129,84 +354,192 @@ class DashboardContainer extends Component {
    return d3.hierarchy(this.props.displayedScenario, getScenarioNodeChildren);
  }

  changeView1(name) {
    this.setState({
      view1Name: name
    });
  }

  changeView2(name) {
    this.setState({
      view2Name: name
    });
  }

  changeDisplayEdgeLabels(val) {
    this.setState({displayEdgeLabels: val});
  }

  render() {
    const root = this.getRoot();
    const nodes = root.descendants();
   
    const apps = nodes.filter(isApp);
    const destinations = apps.map(a => a.data.id);
    const colorRange = colorArray(destinations.length);
    const appIds = apps.map(a => a.data.id);
    const appMap = apps.reduce((acc, app) => {acc[app.data.id] = app; return acc;}, {});
    const colorRange = colorArray(appIds.length);
    const nbEpochs = 25;

    const selectedNodeId = this.props.sourceNodeSelected ? this.props.sourceNodeSelected.data.id : null;
    const dataAccessor = dataAccessorForType(this.props.dataTypeSelected);
    const dataPoints = epochsToDataPoints(this.props.epochs)(nbEpochs)(destinations)(dataAccessor)(selectedNodeId);
    const selectedSource = this.props.sourceNodeSelected ? this.props.sourceNodeSelected.data.id : null;
 
    const showApps = this.props.showAppsView;
    const span = showApps ? 6 : 12;

    const colorForApp = apps.reduce((res, val, i) => {
      // res[val.data.id] = colorRange[i];
      return {...res, [val.data.id]: colorRange[i]};
    }, {});

    const lastEpoch = this.props.epochs.length ? this.props.epochs.slice(-1)[0] : [];
    let lastEpoch = this.props.epochs.length ? this.props.epochs.slice(-1)[0] : [];
    const hasValue = p => {
      const accessor = dataAccessorForType(p.dataType);
      if (! accessor(p)) {
        console.log(`No value for src ${p.src} and dest ${p.dest}`);
      }
      return accessor(p);
    };
    lastEpoch = lastEpoch.filter(hasValue);

    const isDataOfType = type => dataPoint => dataPoint.dataType === type;
    const data = lastEpoch.filter(isDataOfType(this.props.dataTypeSelected));
    
    let graph = null;
    const dataTypeForView = view => {
      switch (view) {
      case LATENCY_VIEW:
        return LATENCY_METRICS;
      case THROUGHPUT_VIEW:
        return THROUGHPUT_METRICS;
      default:
        return LATENCY_METRICS;
      }
    };

    if (showApps) {
      graph = (
        <IDCAppsView
    const view1DataType = dataTypeForView(this.state.view1Name);
    const view1Accessor = dataAccessorForType(view1DataType);
    const view1DataPoints = epochsToDataPoints(this.props.epochs)(nbEpochs)(appIds)(view1Accessor)(selectedSource);
    const data1 = lastEpoch.filter(isDataOfType(view1DataType));
    const max1 = d3.max(data1, view1Accessor);
    const min1 = d3.min(data1, view1Accessor);

    const view2DataType = dataTypeForView(this.state.view2Name);
    const view2Accessor = dataAccessorForType(view2DataType);
    const view2DataPoints = epochsToDataPoints(this.props.epochs)(nbEpochs)(appIds)(view2Accessor)(selectedSource);
    const data2 = lastEpoch.filter(isDataOfType(view2DataType));

    const extractPointsOfType = type => epoch => epoch.filter(isDataPointOfType(type));
    const extractMobilityEvents = extractPointsOfType(MOBILITY_EVENT);
    const mobilityEvents = this.props.epochs.flatMap(extractMobilityEvents);

    if (mobilityEvents.length) {
      console.log('Some mobility events ...');
    }
    data2.forEach((d) => {
      const dd = view1Accessor(d);
      if (!dd) {
        console.log(`Null data: ${dd}. `);
      }
    });

    const max2 = d3.max(data2, view2Accessor);
    const min2 = d3.min(data2, view2Accessor);
    
    const width = 700;
    const height = 600;

    let span1 = 6;
    let width1 = 700;
    let span2 = 6;
    let width2 = 700;

    if (this.state.view1Name === VIEW_NAME_NONE) {
      span1 = 0;
      width1 = 0;
      span2 = 12;
      width2 = 1200;
    }

    if (this.state.view2Name === VIEW_NAME_NONE) {
      span1 = 12;
      width1 = 1200;
      span2 = 0;
      width2 = 0;
    }

    const view1 = (
      <ViewForName
        apps={apps}
        colorRange={colorRange}
          width={700}
          height={600}
          data={data}
          dataAccessor={dataAccessor}
          dataType={this.props.dataTypeSelected}
          selectedSource={selectedNodeId}
        width={width1}
        height={height}
        data={data1}
        mobilityEvents={mobilityEvents}
        min={min1}
        max={max1}
        dataPoints={view1DataPoints}
        dataAccessor={view1Accessor}
        dataType={view1DataType}
        selectedSource={selectedSource}
        colorForApp={colorForApp}
          onNodeClicked={(e) => {
            console.log('Node clicked is: ', e.node);
            this.props.changeSourceNodeSelected(e.node);
          }}
        changeSourceNodeSelected={(node) => this.props.changeSourceNodeSelected(node)}
        viewName={this.state.view1Name}
        displayEdgeLabels={this.state.displayEdgeLabels}
      />
    );
    } else {
      graph = (<IDCGraph 
        width={1000}
        height={600}
      />);
    }

    return (
      <Grid>
        <GridCell span={span} style={{marginLeft: -10}}>
          <Elevation z={4}>
            {graph}
          </Elevation>
        </GridCell>

        {showApps ? (<GridCell span={6}  style={{marginRight: -10}}>
          <Elevation z={4}>
            <IDCLineChart
              data={dataPoints}
              width={700} height={600}
              destinations={destinations}
    const view2 = (
      <ViewForName
        apps={apps}
        colorRange={colorRange}
              sourceSelected={this.props.sourceNodeSelected}
              // min={min}
              // max={max}
        width={width2}
        height={height}
        data={data2}
        mobilityEvents={mobilityEvents}
        min={min2}
        max={max2}
        dataPoints={view2DataPoints}
        dataAccessor={view2Accessor}
        dataType={view2DataType}
        selectedSource={selectedSource}
        colorForApp={colorForApp}
        changeSourceNodeSelected={(node) => this.props.changeSourceNodeSelected(node)}
        viewName={this.state.view2Name}
        displayEdgeLabels={this.state.displayEdgeLabels}
      >
      </ViewForName>
    );

    return (
      <>
      <Elevation z={4}
        style={{padding: 10}}
      >
        <DashboardConfiguration
          configurationType={this.state.configurationType}
          displayConfiguration={
            () => {
              this.setState({configurationType: MAIN_CONFIGURATION});
            }}
          hideConfiguration={() => {this.setState({configurationType: ''});}}
          nodeIds={appIds}
          sourceNodeSelected={this.props.sourceNodeSelected}
          changeSourceNodeSelected={(nodeId) => this.props.changeSourceNodeSelected(appMap[nodeId])}
          dashboardViewsList={DASHBOARD_VIEWS_LIST}
          changeView1={(viewName) => this.changeView1(viewName)}
          changeView2={(viewName) => this.changeView2(viewName)}
          displayEdgeLabels={this.state.displayEdgeLabels}
          changeDisplayEdgeLabels={(display) => this.changeDisplayEdgeLabels(display)}
        />
          </Elevation>
        
        </GridCell>) 
          : null}
        <Grid>
          <GridCell span={span1} style={{marginLeft: -10}}>
            {view1}
          </GridCell>

          <GridCell span={span2} style={{marginLeft: -10}}>
            {view2}
          </GridCell>
        </Grid>
      </Elevation>
      
      </>
    );
  }
}
+1 −1
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ import {
  camelCasePrefix,
  firstLetterUpper

} from '../../util/stringManipulation';
} from '../../util/string-manipulation';

import {
  EXEC_EVT_NC_TYPE,
+4 −7
Original line number Diff line number Diff line
@@ -38,8 +38,6 @@ const edgesFromData = (data, dataAccessor, colorForApp, selectedSource) => {

  const apps = Object.keys(m);
 
  console.log('m: ', m);
 
  const edgesFromSource = dataAccessor => src => {
    const rowObject = m[src];
    if (!rowObject) {
@@ -74,7 +72,7 @@ const edgesFromData = (data, dataAccessor, colorForApp, selectedSource) => {
    if (selectedSource) {
      return e.src === selectedSource;
    } else {
      return false;
      return true;
    }
  };
  const edges = _.flatMap(apps.map(edgesFromSource(dataAccessor))).filter(outwardEdgesIfSourceSelected);
@@ -117,7 +115,6 @@ const unitsForDataType = type => {
  }
};


const IDCAppsView = (
  {
    apps,
@@ -129,7 +126,8 @@ const IDCAppsView = (
    width,
    height,
    onNodeClicked,
    colorForApp
    colorForApp,
    displayEdgeLabels
  }
) => {
  
@@ -180,8 +178,7 @@ const IDCAppsView = (
        xlinkHref={`#textPathDef${i}`}
        startOffset={'45%'}
      >
        {`${edgeLabel} ${e.avgData.toFixed(2)} ${edgeUnits}`}
          
        {displayEdgeLabels ? `${edgeLabel} ${e.avgData.toFixed(2)} ${edgeUnits}` : null}
      </textPath>
    </text>
  );
+6 −2
Original line number Diff line number Diff line
@@ -13,10 +13,13 @@ import { Graph } from 'react-d3-graph';
import ReactDOM from 'react-dom';
import * as d3 from 'd3';

import IDCNode from './idc-node.js';

import {
  plusGenerator,
  minusGenerator,
  lineGeneratorNodes,
  lineGeneratorReverse,
  visitNodes,
  blue
} from './graph-utils';
@@ -240,7 +243,7 @@ class IDCGraph extends Component {
            <path
              key={'path' + i}
              id={'textPathDef' + i}
              d={lineGeneratorReverse(d)}
              d={lineGeneratorNodes(d.parent)(d)}
              style={{fill: 'none', 'strokeWidth': 2}}
              stroke={'#aaa'}
              className='line'
@@ -268,7 +271,8 @@ class IDCGraph extends Component {
            xlinkHref={`#textPathDef${i}`}
            startOffset={'20%'}
          >
            {`${Math.ceil(Math.random()*25)}ms`}
            {/* {`${Math.ceil(Math.random()*25)}ms`} */}
            {''}
          </textPath>
        </text>
      );
Loading