/// /// 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 = 1; 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; } const row = $(event.target).closest(".experiment-row"); this.prepareAndLaunchExperiment(this.experiments[row.index()]); }); $(".experiment-list .list-body").on("click", ".remove-experiment", (event: JQueryEventObject) => { event.stopPropagation(); const affectedRow = $(event.target).closest(".experiment-row"); const index = affectedRow.index(); const affectedExperiment = this.experiments[index]; MapController.showConfirmDeleteDialog("experiment", () => { this.mapController.api.deleteExperiment(affectedExperiment.simulationId, affectedExperiment.id) .then(() => { this.experiments.splice(index, 1); this.populateExperimentsList(); }); }); }); let newExperimentBtn = $("#new-experiment-btn"); // Remove previously added event handlers newExperimentBtn.off(); newExperimentBtn.click(() => { const nameInput = $("#new-experiment-name-input"); if (nameInput.val() === "") { $(".experiment-name-alert").show(); return; } else { $(".experiment-name-alert").hide(); } const 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 = 1; 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 { const pathDropdown = $("#new-experiment-path-select"); const traceDropdown = $("#new-experiment-trace-select"); const schedulerDropdown = $("#new-experiment-scheduler-select"); pathDropdown.empty(); for (let i = 0; i < this.simulation.paths.length; i++) { pathDropdown.append( $("