From eb208a7e2fd020ab5d07d11cc6d52d1e3dcfcc7c Mon Sep 17 00:00:00 2001 From: Georgios Andreadis Date: Sun, 17 Sep 2017 17:55:04 +0200 Subject: Add simulation mode framework Includes object states in the store (by tick), charting, and progress bars. --- package.json | 3 +- src/actions/experiments.js | 8 +++ src/actions/simulation/load-metric.js | 8 +++ src/actions/simulation/playback.js | 15 ++++ src/actions/simulation/tick.js | 23 +++++++ src/actions/states.js | 10 +++ src/api/routes/experiments.js | 22 +++++- src/components/map/elements/RoomTile.js | 5 +- src/components/map/groups/RackGroup.js | 27 ++++++-- src/components/map/groups/TileGroup.js | 17 +++-- src/components/sidebars/Sidebar.js | 5 +- .../sidebars/elements/LoadBarComponent.js | 19 +++++ .../sidebars/elements/LoadChartComponent.js | 16 +++++ .../sidebars/topology/TopologySidebarComponent.js | 8 +-- .../topology/building/BuildingSidebarComponent.js | 15 ++-- .../building/CancelNewRoomConstructionComponent.js | 9 --- .../building/FinishNewRoomConstructionComponent.js | 9 --- .../building/NewRoomConstructionComponent.js | 24 +++++++ .../building/StartNewRoomConstructionComponent.js | 9 --- .../topology/machine/MachineSidebarComponent.js | 12 +++- .../sidebars/topology/rack/MachineComponent.js | 80 ++++++++++++---------- .../sidebars/topology/rack/RackSidebarComponent.js | 14 +++- .../sidebars/topology/room/RoomSidebarComponent.js | 18 +++-- src/components/simulations/FilterButton.js | 8 +-- src/containers/map/DatacenterContainer.js | 2 +- src/containers/map/RackContainer.js | 26 +++++++ src/containers/map/TileContainer.js | 15 +++- .../sidebars/elements/LoadBarContainer.js | 25 +++++++ .../sidebars/elements/LoadChartContainer.js | 26 +++++++ .../topology/building/BuildingSidebarContainer.js | 2 +- .../building/CancelNewRoomConstructionButton.js | 16 ----- .../building/FinishNewRoomConstructionButton.js | 16 ----- .../building/NewRoomConstructionContainer.js | 28 ++++++++ .../building/StartNewRoomConstructionButton.js | 16 ----- .../topology/machine/MachineSidebarContainer.js | 15 ++++ .../sidebars/topology/rack/MachineContainer.js | 15 +++- .../sidebars/topology/rack/RackSidebarContainer.js | 15 ++++ .../sidebars/topology/room/RoomSidebarContainer.js | 2 + src/reducers/construction-mode.js | 35 ++++++++++ src/reducers/construction.js | 35 ---------- src/reducers/current-ids.js | 23 +++++++ src/reducers/index.js | 18 +++-- src/reducers/simulation-mode.js | 50 ++++++++++++++ src/reducers/simulations.js | 10 --- src/reducers/states.js | 33 +++++++++ src/reducers/topology.js | 12 ---- src/util/simulation-load.js | 26 +++++++ 47 files changed, 625 insertions(+), 220 deletions(-) create mode 100644 src/actions/simulation/load-metric.js create mode 100644 src/actions/simulation/playback.js create mode 100644 src/actions/simulation/tick.js create mode 100644 src/actions/states.js create mode 100644 src/components/sidebars/elements/LoadBarComponent.js create mode 100644 src/components/sidebars/elements/LoadChartComponent.js delete mode 100644 src/components/sidebars/topology/building/CancelNewRoomConstructionComponent.js delete mode 100644 src/components/sidebars/topology/building/FinishNewRoomConstructionComponent.js create mode 100644 src/components/sidebars/topology/building/NewRoomConstructionComponent.js delete mode 100644 src/components/sidebars/topology/building/StartNewRoomConstructionComponent.js create mode 100644 src/containers/map/RackContainer.js create mode 100644 src/containers/sidebars/elements/LoadBarContainer.js create mode 100644 src/containers/sidebars/elements/LoadChartContainer.js delete mode 100644 src/containers/sidebars/topology/building/CancelNewRoomConstructionButton.js delete mode 100644 src/containers/sidebars/topology/building/FinishNewRoomConstructionButton.js create mode 100644 src/containers/sidebars/topology/building/NewRoomConstructionContainer.js delete mode 100644 src/containers/sidebars/topology/building/StartNewRoomConstructionButton.js create mode 100644 src/containers/sidebars/topology/machine/MachineSidebarContainer.js create mode 100644 src/containers/sidebars/topology/rack/RackSidebarContainer.js create mode 100644 src/reducers/construction-mode.js delete mode 100644 src/reducers/construction.js create mode 100644 src/reducers/current-ids.js create mode 100644 src/reducers/simulation-mode.js delete mode 100644 src/reducers/simulations.js create mode 100644 src/reducers/states.js delete mode 100644 src/reducers/topology.js create mode 100644 src/util/simulation-load.js diff --git a/package.json b/package.json index 5e71fcf9..828c4afb 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "redux-logger": "^3.0.6", "redux-saga": "^0.15.6", "redux-thunk": "^2.2.0", - "socket.io-client": "^2.0.3" + "socket.io-client": "^2.0.3", + "victory": "^0.22.2" }, "scripts": { "build-css": "node-sass-chokidar src/ -o src/", diff --git a/src/actions/experiments.js b/src/actions/experiments.js index b067f5d9..c7cc7e24 100644 --- a/src/actions/experiments.js +++ b/src/actions/experiments.js @@ -1,6 +1,7 @@ export const FETCH_EXPERIMENTS_OF_SIMULATION = "FETCH_EXPERIMENTS_OF_SIMULATION"; export const ADD_EXPERIMENT = "ADD_EXPERIMENT"; export const DELETE_EXPERIMENT = "DELETE_EXPERIMENT"; +export const OPEN_EXPERIMENT_SUCCEEDED = "OPEN_EXPERIMENT_SUCCEEDED"; export function fetchExperimentsOfSimulation(simulationId) { return { @@ -22,3 +23,10 @@ export function deleteExperiment(id) { id }; } + +export function openExperimentSucceeded(id) { + return { + type: OPEN_EXPERIMENT_SUCCEEDED, + id + } +} diff --git a/src/actions/simulation/load-metric.js b/src/actions/simulation/load-metric.js new file mode 100644 index 00000000..e426a7d2 --- /dev/null +++ b/src/actions/simulation/load-metric.js @@ -0,0 +1,8 @@ +export const CHANGE_LOAD_METRIC = "CHANGE_LOAD_METRIC"; + +export function changeLoadMetric(metric) { + return { + type: CHANGE_LOAD_METRIC, + metric + }; +} diff --git a/src/actions/simulation/playback.js b/src/actions/simulation/playback.js new file mode 100644 index 00000000..bddb9a0a --- /dev/null +++ b/src/actions/simulation/playback.js @@ -0,0 +1,15 @@ +export const SET_PLAYING = "SET_PLAYING"; + +export function playSimulation() { + return { + type: SET_PLAYING, + playing: true + } +} + +export function pauseSimulation() { + return { + type: SET_PLAYING, + playing: false + } +} diff --git a/src/actions/simulation/tick.js b/src/actions/simulation/tick.js new file mode 100644 index 00000000..68f226d7 --- /dev/null +++ b/src/actions/simulation/tick.js @@ -0,0 +1,23 @@ +export const GO_TO_TICK = "GO_TO_TICK"; +export const SET_LAST_SIMULATED_TICK = "SET_LAST_SIMULATED_TICK"; + +export function incrementTick() { + return (dispatch, getState) => { + const {currentTick} = getState(); + dispatch(goToTick(currentTick + 1)); + } +} + +export function goToTick(tick) { + return { + type: GO_TO_TICK, + tick + } +} + +export function setLastSimulatedTick(tick) { + return { + type: SET_LAST_SIMULATED_TICK, + tick + } +} diff --git a/src/actions/states.js b/src/actions/states.js new file mode 100644 index 00000000..6887a1b3 --- /dev/null +++ b/src/actions/states.js @@ -0,0 +1,10 @@ +export const ADD_TO_STATES = "ADD_TO_STATES"; + +export function addToStates(objectType, tick, object) { + return { + type: ADD_TO_STATES, + objectType, + tick, + object + }; +} diff --git a/src/api/routes/experiments.js b/src/api/routes/experiments.js index 8e9cfd2f..e2306059 100644 --- a/src/api/routes/experiments.js +++ b/src/api/routes/experiments.js @@ -1,5 +1,25 @@ -import {deleteById} from "./util"; +import {deleteById, getById} from "./util"; export function deleteExperiment(experimentId) { return deleteById("/experiments/{experimentId}", {experimentId}); } + +export function getLastSimulatedTick(experimentId) { + return getById("/experiments/{experimentId}/last-simulated-tick", {experimentId}); +} + +export function getAllMachineStates(experimentId) { + return getById("/experiments/{experimentId}/machine-states", {experimentId}); +} + +export function getAllRackStates(experimentId) { + return getById("/experiments/{experimentId}/rack-states", {experimentId}); +} + +export function getAllRoomStates(experimentId) { + return getById("/experiments/{experimentId}/room-states", {experimentId}); +} + +export function getAllTaskStates(experimentId) { + return getById("/experiments/{experimentId}/task-states", {experimentId}); +} diff --git a/src/components/map/elements/RoomTile.js b/src/components/map/elements/RoomTile.js index 4d5e50fc..87dd2f03 100644 --- a/src/components/map/elements/RoomTile.js +++ b/src/components/map/elements/RoomTile.js @@ -1,16 +1,15 @@ import React from "react"; import {Rect} from "react-konva"; -import {ROOM_DEFAULT_COLOR, ROOM_IN_CONSTRUCTION_COLOR} from "../../../colors/index"; import Shapes from "../../../shapes/index"; import {TILE_SIZE_IN_PIXELS} from "../MapConstants"; -const RoomTile = ({tile, newTile}) => ( +const RoomTile = ({tile, color}) => ( ); diff --git a/src/components/map/groups/RackGroup.js b/src/components/map/groups/RackGroup.js index 5dd470de..648c74d7 100644 --- a/src/components/map/groups/RackGroup.js +++ b/src/components/map/groups/RackGroup.js @@ -4,15 +4,28 @@ import {RACK_BACKGROUND_COLOR} from "../../../colors/index"; import RackEnergyFillContainer from "../../../containers/map/RackEnergyFillContainer"; import RackSpaceFillContainer from "../../../containers/map/RackSpaceFillContainer"; import Shapes from "../../../shapes/index"; +import {convertLoadToSimulationColor} from "../../../util/simulation-load"; import TileObject from "../elements/TileObject"; -const RackGroup = ({tile}) => ( - - - - - -); +const RackGroup = ({tile, inSimulation, rackLoad}) => { + let color = RACK_BACKGROUND_COLOR; + if (inSimulation) { + color = convertLoadToSimulationColor(rackLoad); + } + + return ( + + + {inSimulation ? + undefined : + + + + + } + + ); +}; RackGroup.propTypes = { tile: Shapes.Tile, diff --git a/src/components/map/groups/TileGroup.js b/src/components/map/groups/TileGroup.js index 8fbdeb31..3de712d1 100644 --- a/src/components/map/groups/TileGroup.js +++ b/src/components/map/groups/TileGroup.js @@ -1,25 +1,34 @@ import PropTypes from "prop-types"; import React from "react"; import {Group} from "react-konva"; +import {ROOM_DEFAULT_COLOR, ROOM_IN_CONSTRUCTION_COLOR} from "../../../colors/index"; +import RackContainer from "../../../containers/map/RackContainer"; import Shapes from "../../../shapes/index"; +import {convertLoadToSimulationColor} from "../../../util/simulation-load"; import RoomTile from "../elements/RoomTile"; -import RackGroup from "./RackGroup"; -const TileGroup = ({tile, newTile, onClick}) => { +const TileGroup = ({tile, newTile, inSimulation, roomLoad, onClick}) => { let tileObject; switch (tile.objectType) { case "RACK": - tileObject = ; + tileObject = ; break; default: tileObject = null; } + let color = ROOM_DEFAULT_COLOR; + if (newTile) { + color = ROOM_IN_CONSTRUCTION_COLOR; + } else if (inSimulation) { + color = convertLoadToSimulationColor(roomLoad); + } + return ( onClick(tile)} > - + {tileObject} ); diff --git a/src/components/sidebars/Sidebar.js b/src/components/sidebars/Sidebar.js index 2f4d77a7..ce743b17 100644 --- a/src/components/sidebars/Sidebar.js +++ b/src/components/sidebars/Sidebar.js @@ -3,7 +3,10 @@ import React from "react"; import "./Sidebar.css"; const Sidebar = ({isRight, children}) => ( -
+
e.stopPropagation()} + > {children}
); diff --git a/src/components/sidebars/elements/LoadBarComponent.js b/src/components/sidebars/elements/LoadBarComponent.js new file mode 100644 index 00000000..5a39b188 --- /dev/null +++ b/src/components/sidebars/elements/LoadBarComponent.js @@ -0,0 +1,19 @@ +import classNames from "classnames"; +import React from "react"; + +const LoadBarComponent = ({percent, disabled}) => ( +
+
+ {percent}% +
+
+); + +export default LoadBarComponent; diff --git a/src/components/sidebars/elements/LoadChartComponent.js b/src/components/sidebars/elements/LoadChartComponent.js new file mode 100644 index 00000000..54a20116 --- /dev/null +++ b/src/components/sidebars/elements/LoadChartComponent.js @@ -0,0 +1,16 @@ +import React from "react"; +import {VictoryChart, VictoryLine, VictoryScatter} from "victory"; + +const LoadChartComponent = ({data, tick}) => ( + + + + +); + +export default LoadChartComponent; diff --git a/src/components/sidebars/topology/TopologySidebarComponent.js b/src/components/sidebars/topology/TopologySidebarComponent.js index 36f2ecc1..b61c7e3c 100644 --- a/src/components/sidebars/topology/TopologySidebarComponent.js +++ b/src/components/sidebars/topology/TopologySidebarComponent.js @@ -1,9 +1,9 @@ import React from "react"; import BuildingSidebarContainer from "../../../containers/sidebars/topology/building/BuildingSidebarContainer"; +import MachineSidebarContainer from "../../../containers/sidebars/topology/machine/MachineSidebarContainer"; +import RackSidebarContainer from "../../../containers/sidebars/topology/rack/RackSidebarContainer"; import RoomSidebarContainer from "../../../containers/sidebars/topology/room/RoomSidebarContainer"; import Sidebar from "../Sidebar"; -import MachineSidebarComponent from "./machine/MachineSidebarComponent"; -import RackSidebarComponent from "./rack/RackSidebarComponent"; const TopologySidebarComponent = ({interactionLevel}) => { let sidebarContent; @@ -16,10 +16,10 @@ const TopologySidebarComponent = ({interactionLevel}) => { sidebarContent = ; break; case "RACK": - sidebarContent = ; + sidebarContent = ; break; case "MACHINE": - sidebarContent = ; + sidebarContent = ; break; default: sidebarContent = "Missing Content"; diff --git a/src/components/sidebars/topology/building/BuildingSidebarComponent.js b/src/components/sidebars/topology/building/BuildingSidebarComponent.js index 002184ae..835943f2 100644 --- a/src/components/sidebars/topology/building/BuildingSidebarComponent.js +++ b/src/components/sidebars/topology/building/BuildingSidebarComponent.js @@ -1,18 +1,13 @@ import React from "react"; -import CancelNewRoomConstructionButton from "../../../../containers/sidebars/topology/building/CancelNewRoomConstructionButton"; -import FinishNewRoomConstructionButton from "../../../../containers/sidebars/topology/building/FinishNewRoomConstructionButton"; -import StartNewRoomConstructionButton from "../../../../containers/sidebars/topology/building/StartNewRoomConstructionButton"; +import NewRoomConstructionContainer from "../../../../containers/sidebars/topology/building/NewRoomConstructionContainer"; -const BuildingSidebarComponent = ({currentRoomInConstruction}) => { +const BuildingSidebarComponent = ({inSimulation}) => { return (

Building

- {currentRoomInConstruction === -1 ? - : -
- - -
+ {inSimulation ? + undefined : + }
); diff --git a/src/components/sidebars/topology/building/CancelNewRoomConstructionComponent.js b/src/components/sidebars/topology/building/CancelNewRoomConstructionComponent.js deleted file mode 100644 index 15f199a6..00000000 --- a/src/components/sidebars/topology/building/CancelNewRoomConstructionComponent.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; - -const CancelNewRoomConstructionComponent = ({onClick}) => ( -
- Cancel construction -
-); - -export default CancelNewRoomConstructionComponent; diff --git a/src/components/sidebars/topology/building/FinishNewRoomConstructionComponent.js b/src/components/sidebars/topology/building/FinishNewRoomConstructionComponent.js deleted file mode 100644 index d9edbb61..00000000 --- a/src/components/sidebars/topology/building/FinishNewRoomConstructionComponent.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; - -const FinishNewRoomConstructionComponent = ({onClick}) => ( -
- Finalize new room -
-); - -export default FinishNewRoomConstructionComponent; diff --git a/src/components/sidebars/topology/building/NewRoomConstructionComponent.js b/src/components/sidebars/topology/building/NewRoomConstructionComponent.js new file mode 100644 index 00000000..581330ab --- /dev/null +++ b/src/components/sidebars/topology/building/NewRoomConstructionComponent.js @@ -0,0 +1,24 @@ +import React from "react"; + +const NewRoomConstructionComponent = ({onStart, onFinish, onCancel, currentRoomInConstruction}) => { + if (currentRoomInConstruction === -1) { + return ( +
+ Construct a new room +
+ ); + } + return ( +
+
+ Finalize new room +
+
+ Cancel construction +
+
+ ); + +}; + +export default NewRoomConstructionComponent; diff --git a/src/components/sidebars/topology/building/StartNewRoomConstructionComponent.js b/src/components/sidebars/topology/building/StartNewRoomConstructionComponent.js deleted file mode 100644 index 60573532..00000000 --- a/src/components/sidebars/topology/building/StartNewRoomConstructionComponent.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; - -const StartNewRoomConstructionComponent = ({onClick}) => ( -
- Construct a new room -
-); - -export default StartNewRoomConstructionComponent; diff --git a/src/components/sidebars/topology/machine/MachineSidebarComponent.js b/src/components/sidebars/topology/machine/MachineSidebarComponent.js index 218e4f41..0f85f0f9 100644 --- a/src/components/sidebars/topology/machine/MachineSidebarComponent.js +++ b/src/components/sidebars/topology/machine/MachineSidebarComponent.js @@ -1,15 +1,23 @@ import React from "react"; +import LoadBarContainer from "../../../../containers/sidebars/elements/LoadBarContainer"; +import LoadChartContainer from "../../../../containers/sidebars/elements/LoadChartContainer"; import BackToRackContainer from "../../../../containers/sidebars/topology/machine/BackToRackContainer"; import DeleteMachineContainer from "../../../../containers/sidebars/topology/machine/DeleteMachineContainer"; import MachineNameContainer from "../../../../containers/sidebars/topology/machine/MachineNameContainer"; import UnitTabsComponent from "./UnitTabsComponent"; -const MachineSidebarComponent = () => { +const MachineSidebarComponent = ({inSimulation, machineId}) => { return (
- + {inSimulation ? +
+ + +
: + + }
); diff --git a/src/components/sidebars/topology/rack/MachineComponent.js b/src/components/sidebars/topology/rack/MachineComponent.js index 4854456c..b6e9791d 100644 --- a/src/components/sidebars/topology/rack/MachineComponent.js +++ b/src/components/sidebars/topology/rack/MachineComponent.js @@ -1,5 +1,6 @@ import React from "react"; import Shapes from "../../../../shapes"; +import {convertLoadToSimulationColor} from "../../../../util/simulation-load"; const UnitIcon = ({id, type}) => (
@@ -12,41 +13,50 @@ const UnitIcon = ({id, type}) => (
); -const MachineComponent = ({position, machine, onClick}) => ( -
  • - - {position} - -
    - {machine.cpuIds.length > 0 ? - : - undefined - } - {machine.gpuIds.length > 0 ? - : - undefined - } - {machine.memoryIds.length > 0 ? - : - undefined - } - {machine.storageIds.length > 0 ? - : - undefined - } - {machine.cpuIds.length + machine.gpuIds.length + machine.memoryIds.length - + machine.storageIds.length === 0 ? - - Machine with no units - : - undefined - } -
    -
  • -); +const MachineComponent = ({position, machine, inSimulation, machineLoad, onClick}) => { + let color = "white"; + if (inSimulation) { + color = convertLoadToSimulationColor(machineLoad); + } + const hasNoUnits = machine.cpuIds.length + machine.gpuIds.length + machine.memoryIds.length + + machine.storageIds.length === 0; + + return ( +
  • + + {position} + +
    + {machine.cpuIds.length > 0 ? + : + undefined + } + {machine.gpuIds.length > 0 ? + : + undefined + } + {machine.memoryIds.length > 0 ? + : + undefined + } + {machine.storageIds.length > 0 ? + : + undefined + } + {hasNoUnits ? + + Machine with no units + : + undefined + } +
    +
  • + ); +}; MachineComponent.propTypes = { machine: Shapes.Machine diff --git a/src/components/sidebars/topology/rack/RackSidebarComponent.js b/src/components/sidebars/topology/rack/RackSidebarComponent.js index 007add6e..bfcc7e32 100644 --- a/src/components/sidebars/topology/rack/RackSidebarComponent.js +++ b/src/components/sidebars/topology/rack/RackSidebarComponent.js @@ -1,14 +1,24 @@ import React from "react"; +import LoadBarContainer from "../../../../containers/sidebars/elements/LoadBarContainer"; +import LoadChartContainer from "../../../../containers/sidebars/elements/LoadChartContainer"; import DeleteRackContainer from "../../../../containers/sidebars/topology/rack/DeleteRackContainer"; import MachineListContainer from "../../../../containers/sidebars/topology/rack/MachineListContainer"; import RackNameContainer from "../../../../containers/sidebars/topology/rack/RackNameContainer"; import "./RackSidebarComponent.css"; -const RackSidebarComponent = () => { +const RackSidebarComponent = ({inSimulation, rackId}) => { return (
    - + {inSimulation ? +
    + + +
    : +
    + +
    + }
    diff --git a/src/components/sidebars/topology/room/RoomSidebarComponent.js b/src/components/sidebars/topology/room/RoomSidebarComponent.js index a15351f9..59c5fc8f 100644 --- a/src/components/sidebars/topology/room/RoomSidebarComponent.js +++ b/src/components/sidebars/topology/room/RoomSidebarComponent.js @@ -1,12 +1,14 @@ import React from "react"; +import LoadBarContainer from "../../../../containers/sidebars/elements/LoadBarContainer"; +import LoadChartContainer from "../../../../containers/sidebars/elements/LoadChartContainer"; import DeleteRoomContainer from "../../../../containers/sidebars/topology/room/DeleteRoomContainer"; import RackConstructionContainer from "../../../../containers/sidebars/topology/room/RackConstructionContainer"; import RoomNameContainer from "../../../../containers/sidebars/topology/room/RoomNameContainer"; import RoomTypeContainer from "../../../../containers/sidebars/topology/room/RoomTypeContainer"; -const RoomSidebarComponent = ({roomType}) => { +const RoomSidebarComponent = ({roomId, roomType, inSimulation}) => { let allowedObjects; - if (roomType === "SERVER") { + if (!inSimulation && roomType === "SERVER") { allowedObjects = ; } @@ -14,8 +16,16 @@ const RoomSidebarComponent = ({roomType}) => {
    - {allowedObjects} - + {inSimulation ? +
    + + +
    : +
    + {allowedObjects} + +
    + }
    ); }; diff --git a/src/components/simulations/FilterButton.js b/src/components/simulations/FilterButton.js index f95c35fa..b26dd231 100644 --- a/src/components/simulations/FilterButton.js +++ b/src/components/simulations/FilterButton.js @@ -5,10 +5,10 @@ import React from 'react'; const FilterButton = ({active, children, onClick}) => ( ); diff --git a/src/containers/map/DatacenterContainer.js b/src/containers/map/DatacenterContainer.js index 8c80146d..b56adbac 100644 --- a/src/containers/map/DatacenterContainer.js +++ b/src/containers/map/DatacenterContainer.js @@ -8,7 +8,7 @@ const mapStateToProps = state => { return { datacenter: state.objects.datacenter[state.currentDatacenterId], - interactionLevel: state.interactionLevel + interactionLevel: state.interactionLevel, }; }; diff --git a/src/containers/map/RackContainer.js b/src/containers/map/RackContainer.js new file mode 100644 index 00000000..60df9aaf --- /dev/null +++ b/src/containers/map/RackContainer.js @@ -0,0 +1,26 @@ +import {connect} from "react-redux"; +import RackGroup from "../../components/map/groups/RackGroup"; +import {getStateLoad} from "../../util/simulation-load"; + +const mapStateToProps = (state, ownProps) => { + const inSimulation = state.currentExperimentId !== -1; + + let rackLoad = undefined; + if (inSimulation) { + if (state.states.rack[state.currentTick] && state.states.rack[state.currentTick][ownProps.tile.objectId]) { + rackLoad = getStateLoad(state.loadMetric, state.states.rack[state.currentTick][ownProps.tile.objectId]); + } + } + + return { + interactionLevel: state.interactionLevel, + inSimulation, + rackLoad, + }; +}; + +const RackContainer = connect( + mapStateToProps +)(RackGroup); + +export default RackContainer; diff --git a/src/containers/map/TileContainer.js b/src/containers/map/TileContainer.js index 9e98636a..75fac5ad 100644 --- a/src/containers/map/TileContainer.js +++ b/src/containers/map/TileContainer.js @@ -1,11 +1,24 @@ import {connect} from "react-redux"; import {goFromRoomToRack} from "../../actions/interaction-level"; import TileGroup from "../../components/map/groups/TileGroup"; +import {getStateLoad} from "../../util/simulation-load"; const mapStateToProps = (state, ownProps) => { + const tile = state.objects.tile[ownProps.tileId]; + const inSimulation = state.currentExperimentId !== -1; + + let roomLoad = undefined; + if (inSimulation) { + if (state.states.room[state.currentTick] && state.states.room[state.currentTick][tile.roomId]) { + roomLoad = getStateLoad(state.loadMetric, state.states.room[state.currentTick][tile.roomId]); + } + } + return { interactionLevel: state.interactionLevel, - tile: state.objects.tile[ownProps.tileId], + tile, + inSimulation, + roomLoad, }; }; diff --git a/src/containers/sidebars/elements/LoadBarContainer.js b/src/containers/sidebars/elements/LoadBarContainer.js new file mode 100644 index 00000000..08aec93c --- /dev/null +++ b/src/containers/sidebars/elements/LoadBarContainer.js @@ -0,0 +1,25 @@ +import {connect} from "react-redux"; +import LoadBarComponent from "../../../components/sidebars/elements/LoadBarComponent"; +import {getStateLoad} from "../../../util/simulation-load"; + +const mapStateToProps = (state, ownProps) => { + let percent = 0; + let enabled = false; + + const objectStates = state.states[ownProps.objectType]; + if (objectStates[state.currentTick] && objectStates[state.currentTick][ownProps.objectId]) { + percent = Math.floor(100 * getStateLoad(state.loadMetric, objectStates[state.currentTick][ownProps.objectId])); + enabled = true; + } + + return { + percent, + enabled + }; +}; + +const LoadBarContainer = connect( + mapStateToProps +)(LoadBarComponent); + +export default LoadBarContainer; diff --git a/src/containers/sidebars/elements/LoadChartContainer.js b/src/containers/sidebars/elements/LoadChartContainer.js new file mode 100644 index 00000000..7d8b10e5 --- /dev/null +++ b/src/containers/sidebars/elements/LoadChartContainer.js @@ -0,0 +1,26 @@ +import {connect} from "react-redux"; +import LoadChartComponent from "../../../components/sidebars/elements/LoadChartComponent"; +import {getStateLoad} from "../../../util/simulation-load"; + +const mapStateToProps = (state, ownProps) => { + const data = []; + + if (state.lastSimulatedTick !== -1) { + const objectStates = state.states[ownProps.objectType]; + Object.keys(objectStates).forEach(tick => { + if (objectStates[tick][ownProps.objectId]) { + data.push({x: tick, y: getStateLoad(state.loadMetric, objectStates[tick][ownProps.objectId])}); + } + }); + } + + return { + data + }; +}; + +const LoadChartContainer = connect( + mapStateToProps +)(LoadChartComponent); + +export default LoadChartContainer; diff --git a/src/containers/sidebars/topology/building/BuildingSidebarContainer.js b/src/containers/sidebars/topology/building/BuildingSidebarContainer.js index a91ad5ef..a3e09149 100644 --- a/src/containers/sidebars/topology/building/BuildingSidebarContainer.js +++ b/src/containers/sidebars/topology/building/BuildingSidebarContainer.js @@ -3,7 +3,7 @@ import BuildingSidebarComponent from "../../../../components/sidebars/topology/b const mapStateToProps = state => { return { - currentRoomInConstruction: state.construction.currentRoomInConstruction + inSimulation: state.construction.currentExperimentId !== -1 }; }; diff --git a/src/containers/sidebars/topology/building/CancelNewRoomConstructionButton.js b/src/containers/sidebars/topology/building/CancelNewRoomConstructionButton.js deleted file mode 100644 index 399c7a0d..00000000 --- a/src/containers/sidebars/topology/building/CancelNewRoomConstructionButton.js +++ /dev/null @@ -1,16 +0,0 @@ -import {connect} from "react-redux"; -import {cancelNewRoomConstruction} from "../../../../actions/topology/building"; -import CancelNewRoomConstructionComponent from "../../../../components/sidebars/topology/building/CancelNewRoomConstructionComponent"; - -const mapDispatchToProps = dispatch => { - return { - onClick: () => dispatch(cancelNewRoomConstruction()), - }; -}; - -const CancelNewRoomConstructionButton = connect( - null, - mapDispatchToProps -)(CancelNewRoomConstructionComponent); - -export default CancelNewRoomConstructionButton; diff --git a/src/containers/sidebars/topology/building/FinishNewRoomConstructionButton.js b/src/containers/sidebars/topology/building/FinishNewRoomConstructionButton.js deleted file mode 100644 index 8fc192e0..00000000 --- a/src/containers/sidebars/topology/building/FinishNewRoomConstructionButton.js +++ /dev/null @@ -1,16 +0,0 @@ -import {connect} from "react-redux"; -import {finishNewRoomConstruction} from "../../../../actions/topology/building"; -import FinishNewRoomConstructionComponent from "../../../../components/sidebars/topology/building/FinishNewRoomConstructionComponent"; - -const mapDispatchToProps = dispatch => { - return { - onClick: () => dispatch(finishNewRoomConstruction()), - }; -}; - -const FinishNewRoomConstructionButton = connect( - null, - mapDispatchToProps -)(FinishNewRoomConstructionComponent); - -export default FinishNewRoomConstructionButton; diff --git a/src/containers/sidebars/topology/building/NewRoomConstructionContainer.js b/src/containers/sidebars/topology/building/NewRoomConstructionContainer.js new file mode 100644 index 00000000..0236522d --- /dev/null +++ b/src/containers/sidebars/topology/building/NewRoomConstructionContainer.js @@ -0,0 +1,28 @@ +import {connect} from "react-redux"; +import { + cancelNewRoomConstruction, + finishNewRoomConstruction, + startNewRoomConstruction +} from "../../../../actions/topology/building"; +import StartNewRoomConstructionComponent from "../../../../components/sidebars/topology/building/NewRoomConstructionComponent"; + +const mapStateToProps = state => { + return { + currentRoomInConstruction: state.construction.currentRoomInConstruction + }; +}; + +const mapDispatchToProps = dispatch => { + return { + onStart: () => dispatch(startNewRoomConstruction()), + onFinish: () => dispatch(finishNewRoomConstruction()), + onCancel: () => dispatch(cancelNewRoomConstruction()), + }; +}; + +const NewRoomConstructionButton = connect( + mapStateToProps, + mapDispatchToProps +)(StartNewRoomConstructionComponent); + +export default NewRoomConstructionButton; diff --git a/src/containers/sidebars/topology/building/StartNewRoomConstructionButton.js b/src/containers/sidebars/topology/building/StartNewRoomConstructionButton.js deleted file mode 100644 index c2c9808a..00000000 --- a/src/containers/sidebars/topology/building/StartNewRoomConstructionButton.js +++ /dev/null @@ -1,16 +0,0 @@ -import {connect} from "react-redux"; -import {startNewRoomConstruction} from "../../../../actions/topology/building"; -import StartNewRoomConstructionComponent from "../../../../components/sidebars/topology/building/StartNewRoomConstructionComponent"; - -const mapDispatchToProps = dispatch => { - return { - onClick: () => dispatch(startNewRoomConstruction()), - }; -}; - -const StartNewRoomConstructionButton = connect( - null, - mapDispatchToProps -)(StartNewRoomConstructionComponent); - -export default StartNewRoomConstructionButton; diff --git a/src/containers/sidebars/topology/machine/MachineSidebarContainer.js b/src/containers/sidebars/topology/machine/MachineSidebarContainer.js new file mode 100644 index 00000000..b0d5eed9 --- /dev/null +++ b/src/containers/sidebars/topology/machine/MachineSidebarContainer.js @@ -0,0 +1,15 @@ +import {connect} from "react-redux"; +import MachineSidebarComponent from "../../../../components/sidebars/topology/machine/MachineSidebarComponent"; + +const mapStateToProps = state => { + return { + machineId: state.interactionLevel.machineId, + inSimulation: state.currentExperimentId !== -1, + }; +}; + +const MachineSidebarContainer = connect( + mapStateToProps +)(MachineSidebarComponent); + +export default MachineSidebarContainer; diff --git a/src/containers/sidebars/topology/rack/MachineContainer.js b/src/containers/sidebars/topology/rack/MachineContainer.js index 21ab06c4..7406d191 100644 --- a/src/containers/sidebars/topology/rack/MachineContainer.js +++ b/src/containers/sidebars/topology/rack/MachineContainer.js @@ -1,10 +1,23 @@ import {connect} from "react-redux"; import {goFromRackToMachine} from "../../../../actions/interaction-level"; import MachineComponent from "../../../../components/sidebars/topology/rack/MachineComponent"; +import {getStateLoad} from "../../../../util/simulation-load"; const mapStateToProps = (state, ownProps) => { + const machine = state.objects.machine[ownProps.machineId]; + const inSimulation = state.currentExperimentId !== -1; + + let machineLoad = undefined; + if (inSimulation) { + if (state.states.machine[state.currentTick] && state.states.machine[state.currentTick][machine.id]) { + machineLoad = getStateLoad(state.loadMetric, state.states.machine[state.currentTick][machine.id]); + } + } + return { - machine: state.objects.machine[ownProps.machineId], + machine, + inSimulation, + machineLoad }; }; diff --git a/src/containers/sidebars/topology/rack/RackSidebarContainer.js b/src/containers/sidebars/topology/rack/RackSidebarContainer.js new file mode 100644 index 00000000..4652b968 --- /dev/null +++ b/src/containers/sidebars/topology/rack/RackSidebarContainer.js @@ -0,0 +1,15 @@ +import {connect} from "react-redux"; +import RackSidebarComponent from "../../../../components/sidebars/topology/rack/RackSidebarComponent"; + +const mapStateToProps = state => { + return { + rackId: state.objects.tile[state.interactionLevel.tileId].objectId, + inSimulation: state.currentExperimentId !== -1, + }; +}; + +const RackSidebarContainer = connect( + mapStateToProps +)(RackSidebarComponent); + +export default RackSidebarContainer; diff --git a/src/containers/sidebars/topology/room/RoomSidebarContainer.js b/src/containers/sidebars/topology/room/RoomSidebarContainer.js index b7ded316..6dded3e7 100644 --- a/src/containers/sidebars/topology/room/RoomSidebarContainer.js +++ b/src/containers/sidebars/topology/room/RoomSidebarContainer.js @@ -3,7 +3,9 @@ import RoomSidebarComponent from "../../../../components/sidebars/topology/room/ const mapStateToProps = state => { return { + roomId: state.interactionLevel.roomId, roomType: state.objects.room[state.interactionLevel.roomId].roomType, + inSimulation: state.currentExperimentId !== -1, }; }; diff --git a/src/reducers/construction-mode.js b/src/reducers/construction-mode.js new file mode 100644 index 00000000..3e0b7542 --- /dev/null +++ b/src/reducers/construction-mode.js @@ -0,0 +1,35 @@ +import {combineReducers} from "redux"; +import { + CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED, + FINISH_NEW_ROOM_CONSTRUCTION, + START_NEW_ROOM_CONSTRUCTION_SUCCEEDED +} from "../actions/topology/building"; +import {START_RACK_CONSTRUCTION, STOP_RACK_CONSTRUCTION} from "../actions/topology/room"; + +export function currentRoomInConstruction(state = -1, action) { + switch (action.type) { + case START_NEW_ROOM_CONSTRUCTION_SUCCEEDED: + return action.roomId; + case CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED: + case FINISH_NEW_ROOM_CONSTRUCTION: + return -1; + default: + return state; + } +} + +export function inRackConstructionMode(state = false, action) { + switch (action.type) { + case START_RACK_CONSTRUCTION: + return true; + case STOP_RACK_CONSTRUCTION: + return false; + default: + return state; + } +} + +export const construction = combineReducers({ + currentRoomInConstruction, + inRackConstructionMode, +}); diff --git a/src/reducers/construction.js b/src/reducers/construction.js deleted file mode 100644 index 3e0b7542..00000000 --- a/src/reducers/construction.js +++ /dev/null @@ -1,35 +0,0 @@ -import {combineReducers} from "redux"; -import { - CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED, - FINISH_NEW_ROOM_CONSTRUCTION, - START_NEW_ROOM_CONSTRUCTION_SUCCEEDED -} from "../actions/topology/building"; -import {START_RACK_CONSTRUCTION, STOP_RACK_CONSTRUCTION} from "../actions/topology/room"; - -export function currentRoomInConstruction(state = -1, action) { - switch (action.type) { - case START_NEW_ROOM_CONSTRUCTION_SUCCEEDED: - return action.roomId; - case CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED: - case FINISH_NEW_ROOM_CONSTRUCTION: - return -1; - default: - return state; - } -} - -export function inRackConstructionMode(state = false, action) { - switch (action.type) { - case START_RACK_CONSTRUCTION: - return true; - case STOP_RACK_CONSTRUCTION: - return false; - default: - return state; - } -} - -export const construction = combineReducers({ - currentRoomInConstruction, - inRackConstructionMode, -}); diff --git a/src/reducers/current-ids.js b/src/reducers/current-ids.js new file mode 100644 index 00000000..c94d7861 --- /dev/null +++ b/src/reducers/current-ids.js @@ -0,0 +1,23 @@ +import {OPEN_SIMULATION_SUCCEEDED} from "../actions/simulations"; +import {FETCH_LATEST_DATACENTER_SUCCEEDED, RESET_CURRENT_DATACENTER} from "../actions/topology/building"; + +export function currentDatacenterId(state = -1, action) { + switch (action.type) { + case FETCH_LATEST_DATACENTER_SUCCEEDED: + return action.datacenterId; + case RESET_CURRENT_DATACENTER: + return -1; + default: + return state; + } +} + +export function currentSimulationId(state = -1, action) { + switch (action.type) { + case OPEN_SIMULATION_SUCCEEDED: + return action.id; + default: + return state; + } +} + diff --git a/src/reducers/index.js b/src/reducers/index.js index 1f3aa8f2..a9b6bf34 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -1,24 +1,30 @@ import {combineReducers} from "redux"; import {auth} from "./auth"; -import {construction} from "./construction"; +import {construction} from "./construction-mode"; +import {currentDatacenterId, currentSimulationId} from "./current-ids"; import {interactionLevel} from "./interaction-level"; import {map} from "./map"; import {modals} from "./modals"; import {objects} from "./objects"; import {simulationList} from "./simulation-list"; -import {currentSimulationId} from "./simulations"; -import {currentDatacenterId} from "./topology"; +import {currentExperimentId, currentTick, isPlaying, loadMetric} from "./simulation-mode"; +import {states} from "./states"; const rootReducer = combineReducers({ - auth, objects, + states, modals, simulationList, + construction, + map, currentSimulationId, currentDatacenterId, + currentExperimentId, + currentTick, + loadMetric, + isPlaying, interactionLevel, - construction, - map, + auth, }); export default rootReducer; diff --git a/src/reducers/simulation-mode.js b/src/reducers/simulation-mode.js new file mode 100644 index 00000000..da6aa94a --- /dev/null +++ b/src/reducers/simulation-mode.js @@ -0,0 +1,50 @@ +import {OPEN_EXPERIMENT_SUCCEEDED} from "../actions/experiments"; +import {CHANGE_LOAD_METRIC} from "../actions/simulation/load-metric"; +import {SET_PLAYING} from "../actions/simulation/playback"; +import {GO_TO_TICK, SET_LAST_SIMULATED_TICK} from "../actions/simulation/tick"; + +export function currentExperimentId(state = -1, action) { + switch (action.type) { + case OPEN_EXPERIMENT_SUCCEEDED: + return action.id; + default: + return state; + } +} + +export function currentTick(state = 0, action) { + switch (action.type) { + case GO_TO_TICK: + return action.tick; + default: + return state; + } +} + +export function loadMetric(state = "LOAD", action) { + switch (action.type) { + case CHANGE_LOAD_METRIC: + return action.metric; + default: + return state; + } +} + +export function isPlaying(state = false, action) { + switch (action.type) { + case SET_PLAYING: + return action.playing; + default: + return state; + } +} + +export function lastSimulatedTick(state = -1, action) { + switch (action.type) { + case SET_LAST_SIMULATED_TICK: + return action.tick; + default: + return state; + } +} + diff --git a/src/reducers/simulations.js b/src/reducers/simulations.js deleted file mode 100644 index e15c2d21..00000000 --- a/src/reducers/simulations.js +++ /dev/null @@ -1,10 +0,0 @@ -import {OPEN_SIMULATION_SUCCEEDED} from "../actions/simulations"; - -export function currentSimulationId(state = -1, action) { - switch (action.type) { - case OPEN_SIMULATION_SUCCEEDED: - return action.id; - default: - return state; - } -} diff --git a/src/reducers/states.js b/src/reducers/states.js new file mode 100644 index 00000000..a9eb4ce8 --- /dev/null +++ b/src/reducers/states.js @@ -0,0 +1,33 @@ +import {combineReducers} from "redux"; +import {ADD_TO_STATES} from "../actions/states"; + +export const states = combineReducers({ + task: objectStates("task"), + room: objectStates("room"), + rack: objectStates("rack"), + machine: objectStates("machine"), +}); + +function objectStates(type) { + return (state = {}, action) => { + if (action.objectType !== type) { + return state; + } + + if (action.type === ADD_TO_STATES) { + return Object.assign( + {}, + state, + { + [action.tick]: Object.assign( + {}, + state[action.tick], + {[action.object.id]: action.object} + ) + } + ); + } + + return state; + }; +} diff --git a/src/reducers/topology.js b/src/reducers/topology.js deleted file mode 100644 index f98b50e7..00000000 --- a/src/reducers/topology.js +++ /dev/null @@ -1,12 +0,0 @@ -import {FETCH_LATEST_DATACENTER_SUCCEEDED, RESET_CURRENT_DATACENTER} from "../actions/topology/building"; - -export function currentDatacenterId(state = -1, action) { - switch (action.type) { - case FETCH_LATEST_DATACENTER_SUCCEEDED: - return action.datacenterId; - case RESET_CURRENT_DATACENTER: - return -1; - default: - return state; - } -} diff --git a/src/util/simulation-load.js b/src/util/simulation-load.js new file mode 100644 index 00000000..224dc6f4 --- /dev/null +++ b/src/util/simulation-load.js @@ -0,0 +1,26 @@ +import {SIM_HIGH_COLOR, SIM_LOW_COLOR, SIM_MID_HIGH_COLOR, SIM_MID_LOW_COLOR} from "../colors/index"; + +export function convertLoadToSimulationColor(load) { + if (load <= 0.25) { + return SIM_LOW_COLOR; + } else if (load <= 0.5) { + return SIM_MID_LOW_COLOR; + } else if (load <= 0.75) { + return SIM_MID_HIGH_COLOR; + } else { + return SIM_HIGH_COLOR; + } +} + +export function getStateLoad(loadMetric, state) { + switch (loadMetric) { + case "LOAD": + return state.loadFraction; + case "TEMPERATURE": + return state.temperatureC / 100.0; + case "MEMORY": + return state.inUseMemoryMb / 10000.0; + default: + return 0; + } +} -- cgit v1.2.3