diff options
Diffstat (limited to 'src/scripts/controllers/simulation/timeline.ts')
| -rw-r--r-- | src/scripts/controllers/simulation/timeline.ts | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/src/scripts/controllers/simulation/timeline.ts b/src/scripts/controllers/simulation/timeline.ts new file mode 100644 index 00000000..a558afe1 --- /dev/null +++ b/src/scripts/controllers/simulation/timeline.ts @@ -0,0 +1,161 @@ +import {SimulationController} from "../simulationcontroller"; +import {Util} from "../../util"; +import * as $ from "jquery"; + + +export class TimelineController { + private simulationController: SimulationController; + private startLabel: JQuery; + private endLabel: JQuery; + private playButton: JQuery; + private loadingIcon: JQuery; + private cacheSection: JQuery; + private timeMarker: JQuery; + private timeline: JQuery; + private timeUnitFraction: number; + private timeMarkerWidth: number; + private timelineWidth: number; + + + constructor(simulationController: SimulationController) { + this.simulationController = simulationController; + this.startLabel = $(".timeline-container .labels .start-time-label"); + this.endLabel = $(".timeline-container .labels .end-time-label"); + this.playButton = $(".timeline-container .play-btn"); + this.loadingIcon = this.playButton.find("img"); + this.cacheSection = $(".timeline-container .timeline .cache-section"); + this.timeMarker = $(".timeline-container .timeline .time-marker"); + this.timeline = $(".timeline-container .timeline"); + this.timeMarkerWidth = this.timeMarker.width(); + this.timelineWidth = this.timeline.width(); + } + + public togglePlayback(): void { + if (this.simulationController.stateCache.cacheBlock) { + this.simulationController.playing = false; + return; + } + this.simulationController.playing = !this.simulationController.playing; + this.setButtonIcon(); + } + + public setupListeners(): void { + this.playButton.on("click", () => { + this.togglePlayback(); + }); + + $(".timeline-container .timeline").on("click", (event: JQueryEventObject) => { + let parentOffset = $(event.target).closest(".timeline").offset(); + let clickX = event.pageX - parentOffset.left; + + let newTick = Math.round(clickX / (this.timelineWidth * this.timeUnitFraction)); + + if (newTick > this.simulationController.stateCache.lastCachedTick) { + newTick = this.simulationController.stateCache.lastCachedTick; + } + this.simulationController.currentTick = newTick; + this.simulationController.checkCurrentSimulationSection(); + this.simulationController.update(); + }); + } + + public setButtonIcon(): void { + if (this.simulationController.playing && !this.playButton.hasClass("glyphicon-pause")) { + this.playButton.removeClass("glyphicon-play").addClass("glyphicon-pause"); + } else if (!this.simulationController.playing && !this.playButton.hasClass("glyphicon-play")) { + this.playButton.removeClass("glyphicon-pause").addClass("glyphicon-play"); + } + } + + public update(): void { + this.timeUnitFraction = 1 / (this.simulationController.lastSimulatedTick + 1); + this.timelineWidth = $(".timeline-container .timeline").width(); + + this.updateTimeLabels(); + + this.cacheSection.css("width", this.calculateTickPosition(this.simulationController.stateCache.lastCachedTick)); + this.timeMarker.css("left", this.calculateTickPosition(this.simulationController.currentTick)); + + this.updateTaskIndicators(); + this.updateSectionMarkers(); + + if (this.simulationController.stateCache.cacheBlock) { + this.playButton.removeClass("glyphicon-pause").removeClass("glyphicon-play"); + this.loadingIcon.show(); + } else { + this.loadingIcon.hide(); + this.setButtonIcon(); + } + } + + private updateTimeLabels(): void { + this.startLabel.text(Util.convertSecondsToFormattedTime(this.simulationController.currentTick)); + this.endLabel.text(Util.convertSecondsToFormattedTime(this.simulationController.lastSimulatedTick)); + } + + private updateSectionMarkers(): void { + $(".section-marker").remove(); + + this.simulationController.sections.forEach((simulationSection: ISection) => { + if (simulationSection.startTick === 0) { + return; + } + + this.timeline.append( + $('<div class="section-marker">') + .css("left", this.calculateTickPosition(simulationSection.startTick)) + ); + }); + } + + private updateTaskIndicators(): void { + $(".task-indicator").remove(); + + let tickStateTypes = { + "queueEntryTick": "task-queued", + "startTick": "task-started", + "finishedTick": "task-finished" + }; + + if (this.simulationController.stateCache.lastCachedTick === -1) { + return; + } + + let indicatorCountList = new Array(this.simulationController.stateCache.lastCachedTick); + let indicator; + this.simulationController.currentExperiment.trace.tasks.forEach((task: ITask) => { + for (let tickStateType in tickStateTypes) { + if (!tickStateTypes.hasOwnProperty(tickStateType)) { + continue; + } + + if (task[tickStateType] !== undefined && + task[tickStateType] <= this.simulationController.stateCache.lastCachedTick) { + + let bottomOffset; + if (indicatorCountList[task[tickStateType]] === undefined) { + indicatorCountList[task[tickStateType]] = 1; + bottomOffset = 0; + } else { + bottomOffset = indicatorCountList[task[tickStateType]] * 10; + indicatorCountList[task[tickStateType]]++; + } + indicator = $('<div class="task-indicator ' + tickStateTypes[tickStateType] + '">') + .css("left", this.calculateTickPosition(task[tickStateType])) + .css("bottom", bottomOffset); + this.timeline.append(indicator); + } + } + }); + } + + private calculateTickPosition(tick: number): string { + let correction = 0; + if (this.timeUnitFraction * this.timelineWidth > this.timeMarkerWidth) { + correction = (this.timeUnitFraction * this.timelineWidth - this.timeMarkerWidth) * + (tick / this.simulationController.lastSimulatedTick); + } + + return (100 * (this.timeUnitFraction * tick + correction / this.timelineWidth)) + "%"; + } +}
\ No newline at end of file |
