summaryrefslogtreecommitdiff
path: root/src/scripts/controllers/modes/node.ts
diff options
context:
space:
mode:
authorGeorgios Andreadis <g.andreadis@student.tudelft.nl>2017-01-24 12:06:09 +0100
committerGeorgios Andreadis <g.andreadis@student.tudelft.nl>2017-01-24 12:06:09 +0100
commitc96e6ffafb62bde1e08987b1fdf3c0786487f6ec (patch)
tree37eaf4cf199ca77dc131b4212c526b707adf2e30 /src/scripts/controllers/modes/node.ts
Initial commit
Diffstat (limited to 'src/scripts/controllers/modes/node.ts')
-rw-r--r--src/scripts/controllers/modes/node.ts297
1 files changed, 297 insertions, 0 deletions
diff --git a/src/scripts/controllers/modes/node.ts b/src/scripts/controllers/modes/node.ts
new file mode 100644
index 00000000..3b3f8a32
--- /dev/null
+++ b/src/scripts/controllers/modes/node.ts
@@ -0,0 +1,297 @@
+import {MapController, AppMode, InteractionLevel} from "../mapcontroller";
+import {MapView} from "../../views/mapview";
+import * as $ from "jquery";
+
+
+/**
+ * Class responsible for rendering node mode and handling UI interactions within it.
+ */
+export class NodeModeController {
+ public currentMachine: IMachine;
+
+ private mapController: MapController;
+ private mapView: MapView;
+
+
+ constructor(mapController: MapController) {
+ this.mapController = mapController;
+ this.mapView = this.mapController.mapView;
+
+ this.loadAddDropdowns();
+ }
+
+ /**
+ * Moves the UI model into node mode.
+ *
+ * @param machine The machine that was selected in rack mode
+ */
+ public enterMode(machine: IMachine): void {
+ this.currentMachine = machine;
+ this.populateUnitLists();
+ $("#node-menu").removeClass("hidden");
+
+ if (this.mapController.appMode === AppMode.SIMULATION) {
+ this.mapController.simulationController.transitionFromRackToNode();
+ }
+ }
+
+ /**
+ * Performs cleanup and closing actions before allowing transferal to rack mode.
+ */
+ public goToObjectMode(): void {
+ $("#node-menu").addClass("hidden");
+ $(".node-element-overlay").addClass("hidden");
+ this.currentMachine = undefined;
+ this.mapController.interactionLevel = InteractionLevel.OBJECT;
+
+ if (this.mapController.appMode === AppMode.SIMULATION) {
+ this.mapController.simulationController.transitionFromNodeToRack();
+ }
+ }
+
+ /**
+ * Connects all DOM event listeners to their respective element targets.
+ */
+ public setupEventListeners(): void {
+ let nodeMenu = $("#node-menu");
+
+ nodeMenu.find(".panel-group").on("click", ".remove-unit", (event: JQueryEventObject) => {
+ MapController.showConfirmDeleteDialog("unit", () => {
+ let index = $(event.target).closest(".panel").index();
+
+ if (index === -1) {
+ return;
+ }
+
+ let closestTabPane = $(event.target).closest(".panel-group");
+
+ let objectList, idList;
+ if (closestTabPane.is("#cpu-accordion")) {
+ objectList = this.currentMachine.cpus;
+ idList = this.currentMachine.cpuIds;
+ } else if (closestTabPane.is("#gpu-accordion")) {
+ objectList = this.currentMachine.gpus;
+ idList = this.currentMachine.gpuIds;
+ } else if (closestTabPane.is("#memory-accordion")) {
+ objectList = this.currentMachine.memories;
+ idList = this.currentMachine.memoryIds;
+ } else if (closestTabPane.is("#storage-accordion")) {
+ objectList = this.currentMachine.storages;
+ idList = this.currentMachine.storageIds;
+ }
+
+ idList.splice(idList.indexOf(objectList[index]).id, 1);
+ objectList.splice(index, 1);
+
+ this.mapController.api.updateMachine(this.mapView.simulation.id,
+ this.mapView.currentDatacenter.id, this.mapController.roomModeController.currentRoom.id,
+ this.mapController.objectModeController.currentObjectTile.id, this.currentMachine).then(
+ () => {
+ this.populateUnitLists();
+ this.mapController.objectModeController.updateNodeComponentOverlays();
+ });
+ });
+ });
+
+ nodeMenu.find(".add-unit").on("click", (event: JQueryEventObject) => {
+ let dropdown = $(event.target).closest(".input-group-btn").siblings("select").first();
+
+ let closestTabPane = $(event.target).closest(".input-group").siblings(".panel-group").first();
+ let objectList, idList, typePlural;
+ if (closestTabPane.is("#cpu-accordion")) {
+ objectList = this.currentMachine.cpus;
+ idList = this.currentMachine.cpuIds;
+ typePlural = "cpus";
+ } else if (closestTabPane.is("#gpu-accordion")) {
+ objectList = this.currentMachine.gpus;
+ idList = this.currentMachine.gpuIds;
+ typePlural = "gpus";
+ } else if (closestTabPane.is("#memory-accordion")) {
+ objectList = this.currentMachine.memories;
+ idList = this.currentMachine.memoryIds;
+ typePlural = "memories";
+ } else if (closestTabPane.is("#storage-accordion")) {
+ objectList = this.currentMachine.storages;
+ idList = this.currentMachine.storageIds;
+ typePlural = "storages";
+ }
+
+ if (idList.length + 1 > 4) {
+ this.mapController.showInfoBalloon("Machine has only 4 slots", "warning");
+ return;
+ }
+
+ let id = parseInt(dropdown.val(), 10);
+ idList.push(id);
+ this.mapController.api.getSpecificationOfType(typePlural, id).then((spec: INodeUnit) => {
+ objectList.push(spec);
+
+ this.mapController.api.updateMachine(this.mapView.simulation.id,
+ this.mapView.currentDatacenter.id, this.mapController.roomModeController.currentRoom.id,
+ this.mapController.objectModeController.currentObjectTile.id, this.currentMachine).then(
+ () => {
+ this.populateUnitLists();
+ this.mapController.objectModeController.updateNodeComponentOverlays();
+ });
+ });
+ });
+ }
+
+ /**
+ * Populates the "add" dropdowns with all available unit options.
+ */
+ private loadAddDropdowns(): void {
+ let unitTypes = [
+ "cpus", "gpus", "memories", "storages"
+ ];
+ let dropdowns = [
+ $("#add-cpu-form").find("select"),
+ $("#add-gpu-form").find("select"),
+ $("#add-memory-form").find("select"),
+ $("#add-storage-form").find("select"),
+ ];
+
+ unitTypes.forEach((type: string, index: number) => {
+ this.mapController.api.getAllSpecificationsOfType(type).then((data: any) => {
+ data.forEach((option: INodeUnit) => {
+ dropdowns[index].append($("<option>").val(option.id).text(option.manufacturer + " " + option.family +
+ " " + option.model + " (" + option.generation + ")"));
+ });
+ });
+ });
+ }
+
+ /**
+ * Generates and inserts dynamically HTML code concerning all units of a machine.
+ */
+ private populateUnitLists(): void {
+ // Contains the skeleton of a unit element and inserts the given data into it
+ let generatePanel = (type: string, index: number, list: any, specSection: string): string => {
+ return '<div class="panel panel-default">' +
+ ' <div class="panel-heading">' +
+ ' <h4 class="panel-title">' +
+ ' <a class="glyphicon glyphicon-remove remove-unit" href="javascript:void(0)"></a>' +
+ ' <a class="accordion-toggle collapsed" data-toggle="collapse" data-parent="#' + type + '-accordion"' +
+ ' href="#' + type + '-' + index + '">' +
+ list[index].manufacturer + ' ' + list[index].family + ' ' + list[index].model +
+ ' </a>' +
+ ' </h4>' +
+ ' </div>' +
+ ' <div id="' + type + '-' + index + '" class="panel-collapse collapse">' +
+ ' <table class="spec-table">' +
+ ' <tbody>' +
+ specSection +
+ ' </tbody>' +
+ ' </table>' +
+ ' </div>' +
+ '</div>';
+ };
+
+ // Generates the structure of the specification list of a processing unit
+ let generateProcessingUnitHtml = (element: IProcessingUnit) => {
+ return ' <tr>' +
+ ' <td class="glyphicon glyphicon-tasks"></td>' +
+ ' <td>Number of Cores</td>' +
+ ' <td>' + element.numberOfCores + '</td>' +
+ ' </tr>' +
+ ' <tr>' +
+ ' <td class="glyphicon glyphicon-dashboard"></td>' +
+ ' <td>Clockspeed (MHz)</td>' +
+ ' <td>' + element.clockRateMhz + '</td>' +
+ ' </tr>' +
+ ' <tr>' +
+ ' <td class="glyphicon glyphicon-flash"></td>' +
+ ' <td>Energy Consumption (W)</td>' +
+ ' <td>' + element.energyConsumptionW + '</td>' +
+ ' </tr>' +
+ ' <tr>' +
+ ' <td class="glyphicon glyphicon-alert"></td>' +
+ ' <td>Failure Rate (%)</td>' +
+ ' <td>' + element.failureModel.rate + '</td>' +
+ ' </tr>';
+ };
+
+ // Generates the structure of the spec list of a storage unit
+ let generateStorageUnitHtml = (element: IStorageUnit) => {
+ return ' <tr>' +
+ ' <td class="glyphicon glyphicon-floppy-disk"></td>' +
+ ' <td>Size (Mb)</td>' +
+ ' <td>' + element.sizeMb + '</td>' +
+ ' </tr>' +
+ ' <tr>' +
+ ' <td class="glyphicon glyphicon-dashboard"></td>' +
+ ' <td>Speed (Mb/s)</td>' +
+ ' <td>' + element.speedMbPerS + '</td>' +
+ ' </tr>' +
+ ' <tr>' +
+ ' <td class="glyphicon glyphicon-flash"></td>' +
+ ' <td>Energy Consumption (W)</td>' +
+ ' <td>' + element.energyConsumptionW + '</td>' +
+ ' </tr>' +
+ ' <tr>' +
+ ' <td class="glyphicon glyphicon-alert"></td>' +
+ ' <td>Failure Rate (%)</td>' +
+ ' <td>' + element.failureModel.rate + '</td>' +
+ ' </tr>';
+ };
+
+ // Inserts a "No units" message into the container of the given unit type
+ let addNoUnitsMessage = (type: string) => {
+ $("#" + type + "-accordion").append("<p>There are currently no units present here. " +
+ "<em>Add some with the dropdown below!</em></p>");
+ };
+
+ let container = $("#cpu-accordion");
+ container.children().remove(".panel");
+ container.children().remove("p");
+
+ if (this.currentMachine.cpus.length === 0) {
+ addNoUnitsMessage("cpu");
+ } else {
+ this.currentMachine.cpus.forEach((element: ICPU, i: number) => {
+ let specSection = generateProcessingUnitHtml(element);
+ let content = generatePanel("cpu", i, this.currentMachine.cpus, specSection);
+ container.append(content);
+ });
+ }
+
+ container = $("#gpu-accordion");
+ container.children().remove(".panel");
+ container.children().remove("p");
+ if (this.currentMachine.gpus.length === 0) {
+ addNoUnitsMessage("gpu");
+ } else {
+ this.currentMachine.gpus.forEach((element: IGPU, i: number) => {
+ let specSection = generateProcessingUnitHtml(element);
+ let content = generatePanel("gpu", i, this.currentMachine.gpus, specSection);
+ container.append(content);
+ });
+ }
+
+ container = $("#memory-accordion");
+ container.children().remove(".panel");
+ container.children().remove("p");
+ if (this.currentMachine.memories.length === 0) {
+ addNoUnitsMessage("memory");
+ } else {
+ this.currentMachine.memories.forEach((element: IMemory, i: number) => {
+ let specSection = generateStorageUnitHtml(element);
+ let content = generatePanel("memory", i, this.currentMachine.memories, specSection);
+ container.append(content);
+ });
+ }
+
+ container = $("#storage-accordion");
+ container.children().remove(".panel");
+ container.children().remove("p");
+ if (this.currentMachine.storages.length === 0) {
+ addNoUnitsMessage("storage");
+ } else {
+ this.currentMachine.storages.forEach((element: IMemory, i: number) => {
+ let specSection = generateStorageUnitHtml(element);
+ let content = generatePanel("storage", i, this.currentMachine.storages, specSection);
+ container.append(content);
+ });
+ }
+ }
+} \ No newline at end of file