From c96e6ffafb62bde1e08987b1fdf3c0786487f6ec Mon Sep 17 00:00:00 2001 From: Georgios Andreadis Date: Tue, 24 Jan 2017 12:06:09 +0100 Subject: Initial commit --- src/scripts/controllers/simulationcontroller.ts | 586 ++++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100644 src/scripts/controllers/simulationcontroller.ts (limited to 'src/scripts/controllers/simulationcontroller.ts') diff --git a/src/scripts/controllers/simulationcontroller.ts b/src/scripts/controllers/simulationcontroller.ts new file mode 100644 index 00000000..8d9553e9 --- /dev/null +++ b/src/scripts/controllers/simulationcontroller.ts @@ -0,0 +1,586 @@ +/// +/// +import * as $ from "jquery"; +import {MapView} from "../views/mapview"; +import {MapController, InteractionLevel, AppMode} from "./mapcontroller"; +import {Util} from "../util"; +import {StateCache} from "./simulation/statecache"; +import {ChartController} from "./simulation/chart"; +import {TaskViewController} from "./simulation/taskview"; +import {TimelineController} from "./simulation/timeline"; + + +export enum ColorRepresentation { + LOAD, + TEMPERATURE, + MEMORY +} + + +export class SimulationController { + public mapView: MapView; + public mapController: MapController; + + public playing: boolean; + public currentTick: number; + public stateCache: StateCache; + public lastSimulatedTick: number; + public simulation: ISimulation; + public experiments: IExperiment[]; + public currentExperiment: IExperiment; + public currentPath: IPath; + public sections: ISection[]; + public currentSection: ISection; + public experimentSelectionMode: boolean; + public traces: ITrace[]; + public schedulers: IScheduler[]; + public sectionIndex: number; + public chartController: ChartController; + public timelineController: TimelineController; + + public colorRepresentation: ColorRepresentation; + public rackToRoomMap: {[key: number]: number;}; + + private taskViewController: TaskViewController; + private tickerId: number; + + + public static showOrHideSimComponents(visibility: boolean): void { + if (visibility) { + $("#statistics-menu").removeClass("hidden"); + $("#experiment-menu").removeClass("hidden"); + $("#tasks-menu").removeClass("hidden"); + $(".timeline-container").removeClass("hidden"); + } else { + $("#statistics-menu").addClass("hidden"); + $("#experiment-menu").addClass("hidden"); + $("#tasks-menu").addClass("hidden"); + $(".timeline-container").addClass("hidden"); + } + } + + constructor(mapController: MapController) { + this.mapController = mapController; + this.mapView = this.mapController.mapView; + this.simulation = this.mapController.mapView.simulation; + this.experiments = this.simulation.experiments; + this.taskViewController = new TaskViewController(this); + this.timelineController = new TimelineController(this); + this.chartController = new ChartController(this); + + this.timelineController.setupListeners(); + this.experimentSelectionMode = true; + this.sectionIndex = 0; + + this.currentTick = 0; + this.playing = false; + this.stateCache = new StateCache(this); + this.colorRepresentation = ColorRepresentation.LOAD; + + this.traces = []; + this.schedulers = []; + + this.mapController.api.getAllTraces().then((data) => { + this.traces = data; + }); + + this.mapController.api.getAllSchedulers().then((data) => { + this.schedulers = data; + }); + } + + public enterMode() { + this.experimentSelectionMode = true; + + if (this.mapController.interactionLevel === InteractionLevel.BUILDING) { + this.mapView.roomLayer.coloringMode = true; + this.mapView.dcObjectLayer.coloringMode = false; + } else if (this.mapController.interactionLevel === InteractionLevel.ROOM || + this.mapController.interactionLevel === InteractionLevel.OBJECT) { + this.mapView.roomLayer.coloringMode = false; + this.mapView.dcObjectLayer.coloringMode = true; + } else if (this.mapController.interactionLevel === InteractionLevel.NODE) { + this.mapController.nodeModeController.goToObjectMode(); + } + + this.mapController.appMode = AppMode.SIMULATION; + this.mapView.dcObjectLayer.detailedMode = false; + this.mapView.gridLayer.setVisibility(false); + this.mapView.updateScene = true; + + this.mapController.setAllMenuModes(); + SimulationController.showOrHideSimComponents(true); + $(".mode-switch").attr("data-selected", "simulation"); + $("#save-version-btn").hide(); + $(".color-indicator").removeClass("hidden"); + + $("#change-experiment-btn").click(() => { + this.playing = false; + this.stateCache.stopCaching(); + this.timelineController.update(); + this.showExperimentsDialog(); + }); + + this.setupColorMenu(); + this.showExperimentsDialog(); + } + + private launchSimulation(): void { + this.onSimulationSectionChange(); + + this.chartController.setup(); + + this.stateCache.startCaching(); + + this.tickerId = setInterval(() => { + this.simulationTick(); + }, 1000); + } + + private onSimulationSectionChange(): void { + this.currentSection = this.currentPath.sections[this.sectionIndex]; + this.mapView.currentDatacenter = this.currentSection.datacenter; + + // Generate a map of all rack IDs in relation to their room IDs for use in room stats + this.rackToRoomMap = {}; + this.currentSection.datacenter.rooms.forEach((room: IRoom) => { + room.tiles.forEach((tile: ITile) => { + if (tile.object !== undefined && tile.objectType === "RACK") { + this.rackToRoomMap[tile.objectId] = room.id; + } + }); + }); + + if (this.mapController.interactionLevel === InteractionLevel.NODE) { + this.mapController.nodeModeController.goToObjectMode(); + } + if (this.mapController.interactionLevel === InteractionLevel.OBJECT) { + this.mapController.objectModeController.goToRoomMode(); + } + if (this.mapController.interactionLevel === InteractionLevel.ROOM) { + this.mapController.roomModeController.goToBuildingMode(); + } + + this.mapView.redrawMap(); + + this.mapView.zoomOutOnDC(); + } + + public exitMode() { + this.closeExperimentsDialog(); + + this.mapController.appMode = AppMode.CONSTRUCTION; + this.mapView.dcObjectLayer.detailedMode = true; + this.mapView.gridLayer.setVisibility(true); + this.mapView.redrawMap(); + + this.stateCache.stopCaching(); + this.playing = false; + + this.mapController.setAllMenuModes(); + SimulationController.showOrHideSimComponents(false); + + this.setColors(); + $(".color-indicator").addClass("hidden")["popover"]("hide").off(); + $(".mode-switch").attr("data-selected", "construction"); + $("#save-version-btn").show(); + + clearInterval(this.tickerId); + } + + public update() { + if (this.stateCache.cacheBlock) { + return; + } + + this.setColors(); + this.updateBuildingStats(); + this.updateRoomStats(); + this.chartController.update(); + this.taskViewController.update(); + } + + public simulationTick(): void { + this.timelineController.update(); + + if (this.currentTick > this.lastSimulatedTick) { + this.currentTick = this.lastSimulatedTick; + this.playing = false; + this.timelineController.setButtonIcon(); + } + + if (this.playing) { + this.checkCurrentSimulationSection(); + this.update(); + + if (!this.stateCache.cacheBlock) { + this.currentTick++; + } + } + } + + public checkCurrentSimulationSection(): void { + for (let i = this.sections.length - 1; i >= 0; i--) { + if (this.currentTick >= this.sections[i].startTick) { + if (this.sectionIndex !== i) { + this.sectionIndex = i; + this.onSimulationSectionChange(); + } + break; + } + } + } + + public transitionFromBuildingToRoom(): void { + this.mapView.roomLayer.coloringMode = false; + this.mapView.dcObjectLayer.coloringMode = true; + + this.setColors(); + this.updateRoomStats(); + this.chartController.update(); + } + + public transitionFromRoomToBuilding(): void { + this.mapView.roomLayer.coloringMode = true; + this.mapView.dcObjectLayer.coloringMode = false; + + this.setColors(); + this.updateBuildingStats(); + this.chartController.update(); + } + + public transitionFromRoomToRack(): void { + this.setColors(); + $("#statistics-menu").addClass("hidden"); + this.chartController.update(); + } + + public transitionFromRackToRoom(): void { + this.setColors(); + $("#statistics-menu").removeClass("hidden"); + } + + public transitionFromRackToNode(): void { + this.chartController.update(); + } + + public transitionFromNodeToRack(): void { + } + + private showExperimentsDialog(): void { + $(".experiment-name-alert").hide(); + + this.populateExperimentsList(); + this.populateDropdowns(); + + $(".experiment-row").click((event: JQueryEventObject) => { + if ($(event.target).hasClass("remove-experiment")) { + return; + } + + let row = $(event.target).closest(".experiment-row"); + this.prepareAndLaunchExperiment(this.experiments[row.index()]); + }); + + $(".experiment-list .list-body").on("click", ".remove-experiment", (event: JQueryEventObject) => { + event.stopPropagation(); + let affectedRow = $(event.target).closest(".experiment-row"); + let index = affectedRow.index(); + let affectedExperiment = this.experiments[index]; + + MapController.showConfirmDeleteDialog("experiment", () => { + this.mapController.api.deleteExperiment(affectedExperiment.simulationId, affectedExperiment.id) + .then(() => { + this.experiments.splice(index, 1); + this.populateExperimentsList(); + }); + }); + }); + + $("#new-experiment-btn").click(() => { + let nameInput = $("#new-experiment-name-input"); + if (nameInput.val() === "") { + $(".experiment-name-alert").show(); + return; + } else { + $(".experiment-name-alert").hide(); + } + + let newExperiment: IExperiment = { + id: -1, + name: nameInput.val(), + pathId: parseInt($("#new-experiment-path-select").val()), + schedulerName: $("#new-experiment-scheduler-select").val(), + traceId: parseInt($("#new-experiment-trace-select").val()), + simulationId: this.simulation.id + }; + + this.mapController.api.addExperimentToSimulation(this.simulation.id, newExperiment) + .then((data: IExperiment) => { + this.simulation.experiments.push(data); + this.prepareAndLaunchExperiment(data); + }); + }); + + $(".window-close").click(() => { + this.exitMode(); + }); + + $(".window-overlay").fadeIn(200); + } + + private prepareAndLaunchExperiment(experiment: IExperiment): void { + this.prepareSimulationData(experiment); + this.launchSimulation(); + this.closeExperimentsDialog(); + } + + private prepareSimulationData(experiment: IExperiment): void { + this.currentExperiment = experiment; + this.currentPath = this.getPathById(this.currentExperiment.pathId); + this.sections = this.currentPath.sections; + this.sectionIndex = 0; + this.currentTick = 0; + this.playing = false; + this.stateCache = new StateCache(this); + this.colorRepresentation = ColorRepresentation.LOAD; + + this.sections.sort((a: ISection, b: ISection) => { + return a.startTick - b.startTick; + }); + + $("#experiment-menu-name").text(experiment.name); + $("#experiment-menu-path").text(SimulationController.getPathName(this.currentPath)); + $("#experiment-menu-scheduler").text(experiment.schedulerName); + $("#experiment-menu-trace").text(experiment.trace.name); + } + + private closeExperimentsDialog(): void { + $(".window-overlay").fadeOut(200); + $(".window-overlay input").val(""); + } + + private populateDropdowns(): void { + let pathDropdown = $("#new-experiment-path-select"); + let traceDropdown = $("#new-experiment-trace-select"); + let schedulerDropdown = $("#new-experiment-scheduler-select"); + + pathDropdown.empty(); + for (let i = 0; i < this.simulation.paths.length; i++) { + pathDropdown.append( + $("