Commit 823c3b47 authored by Simon Pastor's avatar Simon Pastor Committed by Kevin Di Lallo
Browse files

frontend for replay event container and event replay pane

parent 5f21b40d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@
    "test:verbose": "jest --verbose true",
    "test:coverage": "jest --verbose true --coverage --colors",
    "build": "webpack",
    "build:dev": "webpack-serve --port 8091 --host 10.3.16.105"
    "build:dev": "webpack-serve --port 8091 --host 10.3.16.150"
  },
  "author": "",
  "license": "ISC",
+123 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019  InterDigital Communications, Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import React, { Component } from 'react';
import { TextField, TextFieldHelperText } from '@rmwc/textfield';
import IDDialog from './id-dialog';
import { MEEP_DLG_SAVE_REPLAY } from '../../meep-constants';

class IDSaveReplayDialog extends Component {
  constructor(props) {
    super(props);
    this.state = {
      err: null,
      replayName: null,
      description: null
    };
  }

  changeReplayName(name) {
    var err = null;

    if (name) {
      if (name.length > 20) {
        err = 'Maximum 20 characters';
      } else if (!name.match(/^(([a-z0-9][-a-z0-9.]*)?[a-z0-9])+$/)) {
        err = 'Lowercase alphanumeric or \'-\'';
      }
    } else {
      err = 'Please enter a replay file name';
    }
    this.setState({
      replayName: name,
      err: err
    });
  }

  changeDescription(desc) {
    var err = null;

    if (desc) {
      if (desc.length > 30) {
        err = 'Maximum 30 characters';
      }
    }
    this.setState({
      description: desc,
      err: err
    });
  }

  saveReplay() {
    this.props.saveReplay(this.state);
  }

  render() {
    return (
      <IDDialog
        title={this.props.title}
        open={this.props.open}
        onClose={this.props.onClose}
        onSubmit={() => this.saveReplay()}
        okDisabled={
          (!this.state.replayName && this.props.replayNameRequired) ||
          this.state.err
        }
        cydata={MEEP_DLG_SAVE_REPLAY}
      >
        <span style={styles.text}>
          {
            'Store the events as a replay file in the MEEP Controller for current deployed scenario (overwrites any existing replay file with the same name)'
          }
        </span>

        <TextField
          outlined
          style={{ width: '100%' }}
          label={'Replay Name'}
          invalid={
            this.state.err ||
            (!this.state.replayName && this.props.replayNameRequired)
          }
          onChange={e => this.changeReplayName(e.target.value)}
          value={this.replayName}
        />
       <TextField
          outlined
          style={{ width: '100%' }}
          label={'Replay Description'}
          invalid={
            this.state.err 
          }
          onChange={e => this.changeDescription(e.target.value)}
          value={this.description}
        />

        <TextFieldHelperText validationMsg={true}>
          <span>{this.state.err}</span>
        </TextFieldHelperText>
      </IDDialog>
    );
  }
}

const styles = {
  text: {
    color: 'gray'
  }
};

export default IDSaveReplayDialog;
+210 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019  InterDigital Communications, Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { connect } from 'react-redux';
import React, { Component } from 'react';
import { Grid, GridCell } from '@rmwc/grid';
import { Elevation } from '@rmwc/elevation';
import { Button } from '@rmwc/button';
import { Checkbox } from '@rmwc/checkbox';

import {
  uiExecChangeEventCreationMode,
  uiExecChangeEventReplayMode
} from '../../state/ui';

import {
  EXEC_BTN_MANUAL_REPLAY,
  EXEC_BTN_AUTO_REPLAY,
  EXEC_BTN_SAVE_REPLAY
} from '../../meep-constants';

const greyColor = 'grey';

const styles = {
  button: {
    marginRight: 0
  },
  slider: {
    container: {
      marginTop: 10,
      marginBottom: 10,
      color: greyColor
    },
    boundaryValues: {
      marginTop: 15
    },
    title: {
      marginBottom: 0
    }
  },
  section1: {
    color: 'white',
    marginRight: 5
  },
  section2: {
    color: 'white',
    marginRight: 5,
    marginLeft: 10
  }
};

const ConfigurationView = props => {
  return (
    <>
      <Grid style={{ marginBottom: 10 }}>
        <GridCell span={6}>
          <Button
            raised
            style={styles.section1}
            onClick={props.onCreateEvent}
            data-cy={EXEC_BTN_MANUAL_REPLAY}
          >
            MANUAL
          </Button>
          <Button
            raised
            style={styles.section1}
            onClick={props.onReplayEvent}
            data-cy={EXEC_BTN_AUTO_REPLAY}
          >
            AUTO-REPLAY
          </Button>
          <Button
            raised
            style={styles.section1}
            onClick={props.onSaveReplay}
            data-cy={EXEC_BTN_SAVE_REPLAY}
          >
            SAVE EVENTS AS ...
          </Button>
        </GridCell>
      </Grid>
    </>
  );
};

const EventConfiguration = props => {
  if (!props.eventCfgMode) {
    return null;
  }

  let configurationView = null;

  configurationView = (
    <ConfigurationView
      onCreateEvent={props.onCreateEvent}
      onReplayEvent={props.onReplayEvent}
      onSaveReplay={props.onSaveReplay}
    />
  );
  return (
    <Elevation
      z={2}
      className="component-style"
      style={{ padding: 10, marginBottom: 10 }}
    >
      <Grid>
        <GridCell span={11}>
          <div style={{ marginBottom: 10 }}>
            <span className="mdc-typography--headline6">
              Event Configuration
            </span>
          </div>
        </GridCell>
        <GridCell span={1}>
          <Button
            outlined
            style={styles.button}
            onClick={() => props.onCloseEventCfg()}
          >
            Close
          </Button>
        </GridCell>
      </Grid>
      {configurationView}
    </Elevation>
  );
};

class EventContainer extends Component {
  constructor(props) {
    super(props);
    this.state = {
      sourceNodeId: ''
    };
  }

  componentDidMount() { }

  componentWillUnmount() {
    clearInterval(this.dataTimer);
  }

  changeReplayLoop(checked) {
    this.props.onReplayLoopChanged(checked);
  }

  // CREATE EVENT PANE
  onCreateEvent() {
    this.props.changeEventCreationMode(true);
    this.props.changeEventReplayMode(false);
  }

  // SHOW REPLAY EVENT PANE
  onReplayEvent() {
    this.props.changeEventReplayMode(true);
    this.props.changeEventCreationMode(false);
  }

  render() {

    return (
      <>
        <EventConfiguration
          eventCfgMode={this.props.eventCfgMode}
          onCloseEventCfg={this.props.onCloseEventCfg}
          onCreateEvent={() => this.onCreateEvent()}
          onReplayEvent={() => this.onReplayEvent()}
          onSaveReplay={this.props.onSaveReplay}
          changeReplayLoop={checked => this.changeReplayLoop(checked)}
          replayLoop={this.props.replayLoop}
        />
      </>
    );
  }
}

const mapStateToProps = state => {
  return {
    eventCreationMode: state.exec.eventCreationMode,
    eventReplayMode: state.exec.eventReplayMode
  };
};

const mapDispatchToProps = dispatch => {
  return {
    changeEventCreationMode: mode => dispatch(uiExecChangeEventCreationMode(mode)),
    changeEventReplayMode: mode => dispatch(uiExecChangeEventReplayMode(mode))
  };
};

const ConnectedEventContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(EventContainer);

export default ConnectedEventContainer;
+1 −1
Original line number Diff line number Diff line
@@ -114,7 +114,7 @@ class EventCreationPane extends Component {
  }

  render() {
    if (this.props.page !== PAGE_EXECUTE) {
    if (this.props.page !== PAGE_EXECUTE || this.props.hide) {
      return null;
    }

+243 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019  InterDigital Communications, Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { connect } from 'react-redux';
import React, { Component } from 'react';
import { Button } from '@rmwc/button';
import { Checkbox } from '@rmwc/checkbox';
import { Select } from '@rmwc/select';
import { Grid, GridInner, GridCell } from '@rmwc/grid';
import { Typography } from '@rmwc/typography';
import { updateObject } from '../../util/object-util';

import { uiExecChangeReplayFileSelected } from '../../state/ui';

import {
  EXEC_EVT_REPLAY_FILES,
  EXEC_BTN_REPLAY_START,
  EXEC_BTN_REPLAY_STOP
} from '../../meep-constants';

import {
  execChangeReplayFilesList
} from '../../state/exec';

import { EXEC_EVT_TYPE, PAGE_EXECUTE } from '../../meep-constants';

const ReplayFileSelect = props => {
  return (
    <Grid style={styles.field}>
      <GridCell span={12}>
        <Select
          style={styles.select}
          label="Replay file"
          fullwidth="true"
          outlined
          options={props.replayFiles}
          onChange={props.onChange}
          onClick={props.onClick}
          value={props.replayFileSelected}
          data-cy={EXEC_EVT_REPLAY_FILES}
        />
      </GridCell>
    </Grid>
  );
};

class EventReplayPane extends Component {
  constructor(props) {
    super(props);
    this.state = {
      description: null
    };
  }

  triggerReplay(play) {

    if (play) {
      if (this.props.replayLoop) {
        this.props.api.loopReplay(this.props.replayFileSelected, (error, data, response) => {
            if (error) {
            console.log(error)
          }
        });
      } else {
        this.props.api.playReplayFile(this.props.replayFileSelected, (error, data, response) => {
          if (error) {
            console.log(error)
          }
        });
      }
    } else { //stop
     this.props.api.stopReplayFile(this.props.replayFileSelected, (error, data, response) => {
          if (error) {
          console.log(error)
        }
      });
    }
  }

  changeLoop(checked) {
    this.props.onReplayLoopChanged(checked);
  }

  /**
   * Callback function to receive the result of the getReplayList operation.
   * @callback module:api/EventReplayApi~getReplayFileListCallback
   * @param {String} error Error message, if any.
   * @param {module:model/ReplayFileList} data The data returned by the service call.
   */
  getReplayFileListCb(error, data) {
    if (error !== null) {
      // TODO: consider showing an alert/toast
      return;
    }
    this.props.changeReplayFilesList(data.replayFiles);

  }

  /**
   * Callback function to receive the result of the getReplayFile operation.
   * @callback module:api/EventReplayApi~getReplayFileCallback
   * @param {String} error Error message, if any.
   * @param {module:model/Replay} data The data returned by the service call.
   */
  getReplayFileCb(error, data) {
    if (error !== null) {
      // TODO: consider showing an alert/toast
      return;
    }
    this.state.description = data.description;

  }

  updateReplayFileList() {
    this.props.api.getReplayFileList((error, data, response) => {
      this.getReplayFileListCb(error, data, response);
    });
  }

  getDescription(name) {
    this.props.api.getReplayFile(name, (error, data, response) => {
      this.getReplayFileCb(error, data, response);
    });
  }

  render() {
    if (this.props.page !== PAGE_EXECUTE || this.props.hide) {
      return null;
    }

    return (
      <div style={{ padding: 10 }}>
        <div style={styles.block}>
          <Typography use="headline6">Replay Events</Typography>
        </div>
        <ReplayFileSelect
          replayFiles={this.props.replayFiles}
          replayFileSelected={this.props.replayFileSelected}
          onClick={() => this.updateReplayFileList()}
          onChange={event => {
            this.props.changeReplayFileSelected(event.target.value);
            this.getDescription(event.target.value);
          }}
        />
        <div style={styles.block}>
          <Typography use="subtitle2">{this.state.description}</Typography>
        </div>
        <Grid style={{ marginBottom: 10 }}>
          <GridCell span={2}>
            <Checkbox
              checked={this.props.replayLoop}
              onChange={e => this.changeLoop(e.target.checked)}
            >
              Loop
            </Checkbox>
          </GridCell>
        </Grid>
        <Grid style={{ marginTop: 10 }}>
          <GridInner align={'right'}>
            <GridCell span={12}>
              <Button
                raised
                style={styles.section1}
                onClick={() => this.triggerReplay(true)}
                data-cy={EXEC_BTN_REPLAY_START}
              >
                START
              </Button>
              <Button
                raised
                style={styles.section1}
                onClick={() => this.triggerReplay(false)}
                data-cy={EXEC_BTN_REPLAY_STOP}
              >
                STOP
              </Button>
              <Button
                outlined
                style={styles.button}
                onClick={this.props.onClose}
              >
                Close
              </Button>
            </GridCell>
          </GridInner>
        </Grid>
      </div>

    );
  }
}

const styles = {
  button: {
    marginRight: 0
  },
  block: {
    marginBottom: 20
  },
  field: {
    marginBottom: 10
  },
  section1: {
    color: 'white',
    marginRight: 5
  },
  select: {
    width: '100%'
  }
};

const mapStateToProps = state => {
  return {
    page: state.ui.page
  };
};

const mapDispatchToProps = dispatch => {
  return {
    changeReplayFileSelected: name => dispatch(uiExecChangeReplayFileSelected(name)),
    changeReplayFilesList: list => dispatch(execChangeReplayFilesList(list))
  };
};

const ConnectedEventReplayPane = connect(
  mapStateToProps,
  mapDispatchToProps
)(EventReplayPane);

export default ConnectedEventReplayPane;
Loading