summaryrefslogtreecommitdiff
path: root/frontend/src/util
diff options
context:
space:
mode:
authorGeorgios Andreadis <info@gandreadis.com>2020-06-29 15:47:09 +0200
committerFabian Mastenbroek <mail.fabianm@gmail.com>2020-08-24 16:08:41 +0200
commit90fae26aa4bd0e0eb3272ff6e6524060e9004fbb (patch)
treebf6943882f5fa5f3114c01fc571503c79ee1056d /frontend/src/util
parent7032a007d4431f5a0c4c5e2d3f3bd20462d49950 (diff)
Prepare frontend repository for monorepo
This change prepares the frontend Git repository for the monorepo residing at https://github.com/atlarge-research.com/opendc. To accomodate for this, we move all files into a frontend subdirectory.
Diffstat (limited to 'frontend/src/util')
-rw-r--r--frontend/src/util/authorizations.js11
-rw-r--r--frontend/src/util/colors.js29
-rw-r--r--frontend/src/util/date-time.js104
-rw-r--r--frontend/src/util/date-time.test.js35
-rw-r--r--frontend/src/util/jquery.js8
-rw-r--r--frontend/src/util/room-types.js7
-rw-r--r--frontend/src/util/simulation-load.js37
-rw-r--r--frontend/src/util/tile-calculations.js261
-rw-r--r--frontend/src/util/timeline.js19
9 files changed, 511 insertions, 0 deletions
diff --git a/frontend/src/util/authorizations.js b/frontend/src/util/authorizations.js
new file mode 100644
index 00000000..ef649c9c
--- /dev/null
+++ b/frontend/src/util/authorizations.js
@@ -0,0 +1,11 @@
+export const AUTH_ICON_MAP = {
+ OWN: "home",
+ EDIT: "pencil",
+ VIEW: "eye"
+};
+
+export const AUTH_DESCRIPTION_MAP = {
+ OWN: "Own",
+ EDIT: "Can Edit",
+ VIEW: "Can View"
+};
diff --git a/frontend/src/util/colors.js b/frontend/src/util/colors.js
new file mode 100644
index 00000000..1e84e162
--- /dev/null
+++ b/frontend/src/util/colors.js
@@ -0,0 +1,29 @@
+export const GRID_COLOR = "rgba(0, 0, 0, 0.5)";
+export const BACKDROP_COLOR = "rgba(255, 255, 255, 1)";
+export const WALL_COLOR = "rgba(0, 0, 0, 1)";
+
+export const ROOM_DEFAULT_COLOR = "rgba(150, 150, 150, 1)";
+export const ROOM_IN_CONSTRUCTION_COLOR = "rgba(51, 153, 255, 1)";
+export const ROOM_HOVER_VALID_COLOR = "rgba(51, 153, 255, 1)";
+export const ROOM_HOVER_INVALID_COLOR = "rgba(255, 102, 0, 1)";
+export const ROOM_NAME_COLOR = "rgba(245, 245, 245, 1)";
+export const ROOM_TYPE_COLOR = "rgba(245, 245, 245, 1)";
+
+export const TILE_PLUS_COLOR = "rgba(0, 0, 0, 1)";
+
+export const OBJECT_BORDER_COLOR = "rgba(0, 0, 0, 1)";
+
+export const RACK_BACKGROUND_COLOR = "rgba(170, 170, 170, 1)";
+export const RACK_SPACE_BAR_BACKGROUND_COLOR = "rgba(222, 235, 247, 0.6)";
+export const RACK_SPACE_BAR_FILL_COLOR = "rgba(91, 155, 213, 0.7)";
+export const RACK_ENERGY_BAR_BACKGROUND_COLOR = "rgba(255, 242, 204, 0.6)";
+export const RACK_ENERGY_BAR_FILL_COLOR = "rgba(244, 215, 0, 0.7)";
+export const COOLING_ITEM_BACKGROUND_COLOR = "rgba(40, 50, 230, 1)";
+export const PSU_BACKGROUND_COLOR = "rgba(230, 50, 60, 1)";
+
+export const GRAYED_OUT_AREA_COLOR = "rgba(0, 0, 0, 0.6)";
+
+export const SIM_LOW_COLOR = "rgba(197, 224, 180, 1)";
+export const SIM_MID_LOW_COLOR = "rgba(255, 230, 153, 1)";
+export const SIM_MID_HIGH_COLOR = "rgba(248, 203, 173, 1)";
+export const SIM_HIGH_COLOR = "rgba(249, 165, 165, 1)";
diff --git a/frontend/src/util/date-time.js b/frontend/src/util/date-time.js
new file mode 100644
index 00000000..0b752600
--- /dev/null
+++ b/frontend/src/util/date-time.js
@@ -0,0 +1,104 @@
+/**
+ * Parses and formats the given date-time string representation.
+ *
+ * The format assumed is "YYYY-MM-DDTHH:MM:SS".
+ *
+ * @param dateTimeString A string expressing a date and a time, in the above mentioned format.
+ * @returns {string} A human-friendly string version of that date and time.
+ */
+export function parseAndFormatDateTime(dateTimeString) {
+ return formatDateTime(parseDateTime(dateTimeString));
+}
+
+/**
+ * Parses date-time string representations and returns a parsed object.
+ *
+ * The format assumed is "YYYY-MM-DDTHH:MM:SS".
+ *
+ * @param dateTimeString A string expressing a date and a time, in the above mentioned format.
+ * @returns {object} A Date object with the parsed date and time information as content.
+ */
+export function parseDateTime(dateTimeString) {
+ return new Date(dateTimeString + ".000Z");
+}
+
+/**
+ * Serializes the given date and time value to a human-friendly string.
+ *
+ * @param dateTime An object representation of a date and time.
+ * @returns {string} A human-friendly string version of that date and time.
+ */
+export function formatDateTime(dateTime) {
+ let date;
+ const currentDate = new Date();
+
+ date =
+ addPaddingToTwo(dateTime.getDay()) +
+ "/" +
+ addPaddingToTwo(dateTime.getMonth()) +
+ "/" +
+ addPaddingToTwo(dateTime.getFullYear());
+
+ if (
+ dateTime.getFullYear() === currentDate.getFullYear() &&
+ dateTime.getMonth() === currentDate.getMonth()
+ ) {
+ if (dateTime.getDate() === currentDate.getDate()) {
+ date = "Today";
+ } else if (dateTime.getDate() === currentDate.getDate() - 1) {
+ date = "Yesterday";
+ }
+ }
+
+ return (
+ date +
+ ", " +
+ addPaddingToTwo(dateTime.getHours()) +
+ ":" +
+ addPaddingToTwo(dateTime.getMinutes())
+ );
+}
+
+/**
+ * Formats the given number of seconds/ticks to a formatted time representation.
+ *
+ * @param seconds The number of seconds.
+ * @returns {string} A string representation of that amount of second, in the from of HH:MM:SS.
+ */
+export function convertSecondsToFormattedTime(seconds) {
+ if (seconds <= 0) {
+ return "0s";
+ }
+
+ let hour = Math.floor(seconds / 3600);
+ let minute = Math.floor(seconds / 60) % 60;
+ let second = seconds % 60;
+
+ hour = isNaN(hour) ? 0 : hour;
+ minute = isNaN(minute) ? 0 : minute;
+ second = isNaN(second) ? 0 : second;
+
+ if (hour === 0 && minute === 0) {
+ return second + "s";
+ } else if (hour === 0) {
+ return minute + "m" + addPaddingToTwo(second) + "s";
+ } else {
+ return (
+ hour + "h" + addPaddingToTwo(minute) + "m" + addPaddingToTwo(second) + "s"
+ );
+ }
+}
+
+/**
+ * Pads the given integer to have at least two digits.
+ *
+ * @param integer An integer to be padded.
+ * @returns {string} A string containing the padded integer.
+ */
+function addPaddingToTwo(integer) {
+ if (integer < 10) {
+ return "0" + integer.toString();
+ } else {
+ return integer.toString();
+ }
+}
diff --git a/frontend/src/util/date-time.test.js b/frontend/src/util/date-time.test.js
new file mode 100644
index 00000000..6c7a6b16
--- /dev/null
+++ b/frontend/src/util/date-time.test.js
@@ -0,0 +1,35 @@
+import { convertSecondsToFormattedTime, parseDateTime } from "./date-time";
+
+describe("date-time parsing", () => {
+ it("reads components properly", () => {
+ const dateString = "2017-09-27T20:55:01";
+ const parsedDate = parseDateTime(dateString);
+
+ expect(parsedDate.getUTCFullYear()).toEqual(2017);
+ expect(parsedDate.getUTCMonth()).toEqual(8);
+ expect(parsedDate.getUTCDate()).toEqual(27);
+ expect(parsedDate.getUTCHours()).toEqual(20);
+ expect(parsedDate.getUTCMinutes()).toEqual(55);
+ expect(parsedDate.getUTCSeconds()).toEqual(1);
+ });
+});
+
+describe("tick formatting", () => {
+ it("returns '0s' for numbers <= 0", () => {
+ expect(convertSecondsToFormattedTime(-1)).toEqual("0s");
+ expect(convertSecondsToFormattedTime(0)).toEqual("0s");
+ });
+ it("returns only seconds for values under a minute", () => {
+ expect(convertSecondsToFormattedTime(1)).toEqual("1s");
+ expect(convertSecondsToFormattedTime(59)).toEqual("59s");
+ });
+ it("returns seconds and minutes for values under an hour", () => {
+ expect(convertSecondsToFormattedTime(60)).toEqual("1m00s");
+ expect(convertSecondsToFormattedTime(61)).toEqual("1m01s");
+ expect(convertSecondsToFormattedTime(3599)).toEqual("59m59s");
+ });
+ it("returns full time for values over an hour", () => {
+ expect(convertSecondsToFormattedTime(3600)).toEqual("1h00m00s");
+ expect(convertSecondsToFormattedTime(3601)).toEqual("1h00m01s");
+ });
+});
diff --git a/frontend/src/util/jquery.js b/frontend/src/util/jquery.js
new file mode 100644
index 00000000..12a64fc6
--- /dev/null
+++ b/frontend/src/util/jquery.js
@@ -0,0 +1,8 @@
+/**
+ * Binding of the global jQuery variable for use within React.
+ *
+ * This should be used instead of '$', to address ESLint warnings relating to undefined global variables.
+ */
+const jQuery = window["$"];
+
+export default jQuery;
diff --git a/frontend/src/util/room-types.js b/frontend/src/util/room-types.js
new file mode 100644
index 00000000..5cfe3887
--- /dev/null
+++ b/frontend/src/util/room-types.js
@@ -0,0 +1,7 @@
+export const ROOM_TYPE_TO_NAME_MAP = {
+ SERVER: "Server room",
+ HALLWAY: "Hallway",
+ OFFICE: "Office",
+ POWER: "Power room",
+ COOLING: "Cooling room"
+};
diff --git a/frontend/src/util/simulation-load.js b/frontend/src/util/simulation-load.js
new file mode 100644
index 00000000..95e17fed
--- /dev/null
+++ b/frontend/src/util/simulation-load.js
@@ -0,0 +1,37 @@
+import {
+ SIM_HIGH_COLOR,
+ SIM_LOW_COLOR,
+ SIM_MID_HIGH_COLOR,
+ SIM_MID_LOW_COLOR
+} from "./colors";
+
+export const LOAD_NAME_MAP = {
+ LOAD: "computational load",
+ TEMPERATURE: "temperature",
+ MEMORY: "memory use"
+};
+
+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 -1;
+ }
+}
diff --git a/frontend/src/util/tile-calculations.js b/frontend/src/util/tile-calculations.js
new file mode 100644
index 00000000..95886eeb
--- /dev/null
+++ b/frontend/src/util/tile-calculations.js
@@ -0,0 +1,261 @@
+export function deriveWallLocations(tiles) {
+ const { verticalWalls, horizontalWalls } = getWallSegments(tiles);
+ return mergeWallSegments(verticalWalls, horizontalWalls);
+}
+
+function getWallSegments(tiles) {
+ const verticalWalls = {};
+ const horizontalWalls = {};
+
+ tiles.forEach(tile => {
+ const x = tile.positionX,
+ y = tile.positionY;
+
+ for (let dX = -1; dX <= 1; dX++) {
+ for (let dY = -1; dY <= 1; dY++) {
+ if (Math.abs(dX) === Math.abs(dY)) {
+ continue;
+ }
+
+ let doInsert = true;
+ for (let tileIndex in tiles) {
+ if (
+ tiles[tileIndex].positionX === x + dX &&
+ tiles[tileIndex].positionY === y + dY
+ ) {
+ doInsert = false;
+ break;
+ }
+ }
+ if (!doInsert) {
+ continue;
+ }
+
+ if (dX === -1) {
+ if (verticalWalls[x] === undefined) {
+ verticalWalls[x] = [];
+ }
+ if (verticalWalls[x].indexOf(y) === -1) {
+ verticalWalls[x].push(y);
+ }
+ } else if (dX === 1) {
+ if (verticalWalls[x + 1] === undefined) {
+ verticalWalls[x + 1] = [];
+ }
+ if (verticalWalls[x + 1].indexOf(y) === -1) {
+ verticalWalls[x + 1].push(y);
+ }
+ } else if (dY === -1) {
+ if (horizontalWalls[y] === undefined) {
+ horizontalWalls[y] = [];
+ }
+ if (horizontalWalls[y].indexOf(x) === -1) {
+ horizontalWalls[y].push(x);
+ }
+ } else if (dY === 1) {
+ if (horizontalWalls[y + 1] === undefined) {
+ horizontalWalls[y + 1] = [];
+ }
+ if (horizontalWalls[y + 1].indexOf(x) === -1) {
+ horizontalWalls[y + 1].push(x);
+ }
+ }
+ }
+ }
+ });
+
+ return { verticalWalls, horizontalWalls };
+}
+
+function mergeWallSegments(vertical, horizontal) {
+ const result = [];
+ const walls = [vertical, horizontal];
+
+ for (let i = 0; i < 2; i++) {
+ const wallList = walls[i];
+ for (let a in wallList) {
+ a = parseInt(a, 10);
+
+ wallList[a].sort((a, b) => {
+ return a - b;
+ });
+
+ let startPos = wallList[a][0];
+ const isHorizontal = i === 1;
+
+ if (wallList[a].length === 1) {
+ const startPosX = isHorizontal ? startPos : a;
+ const startPosY = isHorizontal ? a : startPos;
+ result.push({
+ startPosX,
+ startPosY,
+ isHorizontal,
+ length: 1
+ });
+ } else {
+ let consecutiveCount = 1;
+ for (let b = 0; b < wallList[a].length - 1; b++) {
+ if (b + 1 === wallList[a].length - 1) {
+ if (wallList[a][b + 1] - wallList[a][b] > 1) {
+ const startPosX = isHorizontal ? startPos : a;
+ const startPosY = isHorizontal ? a : startPos;
+ result.push({
+ startPosX,
+ startPosY,
+ isHorizontal,
+ length: consecutiveCount
+ });
+ consecutiveCount = 0;
+ startPos = wallList[a][b + 1];
+ }
+ const startPosX = isHorizontal ? startPos : a;
+ const startPosY = isHorizontal ? a : startPos;
+ result.push({
+ startPosX,
+ startPosY,
+ isHorizontal,
+ length: consecutiveCount + 1
+ });
+ break;
+ } else if (wallList[a][b + 1] - wallList[a][b] > 1) {
+ const startPosX = isHorizontal ? startPos : a;
+ const startPosY = isHorizontal ? a : startPos;
+ result.push({
+ startPosX,
+ startPosY,
+ isHorizontal,
+ length: consecutiveCount
+ });
+ startPos = wallList[a][b + 1];
+ consecutiveCount = 0;
+ }
+ consecutiveCount++;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+export function deriveValidNextTilePositions(rooms, selectedTiles) {
+ const result = [],
+ newPosition = { x: 0, y: 0 };
+ let isSurroundingTile;
+
+ selectedTiles.forEach(tile => {
+ const x = tile.positionX;
+ const y = tile.positionY;
+ result.push({ x, y });
+
+ for (let dX = -1; dX <= 1; dX++) {
+ for (let dY = -1; dY <= 1; dY++) {
+ if (Math.abs(dX) === Math.abs(dY)) {
+ continue;
+ }
+
+ newPosition.x = x + dX;
+ newPosition.y = y + dY;
+
+ isSurroundingTile = true;
+ for (let index in selectedTiles) {
+ if (
+ selectedTiles[index].positionX === newPosition.x &&
+ selectedTiles[index].positionY === newPosition.y
+ ) {
+ isSurroundingTile = false;
+ break;
+ }
+ }
+
+ if (
+ isSurroundingTile &&
+ findPositionInRooms(rooms, newPosition.x, newPosition.y) === -1
+ ) {
+ result.push({ x: newPosition.x, y: newPosition.y });
+ }
+ }
+ }
+ });
+
+ return result;
+}
+
+export function findPositionInPositions(positions, positionX, positionY) {
+ for (let i = 0; i < positions.length; i++) {
+ const position = positions[i];
+ if (positionX === position.x && positionY === position.y) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+export function findPositionInRooms(rooms, positionX, positionY) {
+ for (let i = 0; i < rooms.length; i++) {
+ const room = rooms[i];
+ if (findPositionInTiles(room.tiles, positionX, positionY) !== -1) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+function findPositionInTiles(tiles, positionX, positionY) {
+ let index = -1;
+
+ for (let i = 0; i < tiles.length; i++) {
+ const tile = tiles[i];
+ if (positionX === tile.positionX && positionY === tile.positionY) {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+}
+
+export function findTileWithPosition(tiles, positionX, positionY) {
+ for (let i = 0; i < tiles.length; i++) {
+ if (tiles[i].positionX === positionX && tiles[i].positionY === positionY) {
+ return tiles[i];
+ }
+ }
+
+ return null;
+}
+
+export function calculateRoomListBounds(rooms) {
+ const min = { x: Number.MAX_VALUE, y: Number.MAX_VALUE };
+ const max = { x: -1, y: -1 };
+
+ rooms.forEach(room => {
+ room.tiles.forEach(tile => {
+ if (tile.positionX < min.x) {
+ min.x = tile.positionX;
+ }
+ if (tile.positionY < min.y) {
+ min.y = tile.positionY;
+ }
+
+ if (tile.positionX > max.x) {
+ max.x = tile.positionX;
+ }
+ if (tile.positionY > max.y) {
+ max.y = tile.positionY;
+ }
+ });
+ });
+
+ max.x++;
+ max.y++;
+
+ const center = {
+ x: min.x + (max.x - min.x) / 2.0,
+ y: min.y + (max.y - min.y) / 2.0
+ };
+
+ return { min, center, max };
+}
diff --git a/frontend/src/util/timeline.js b/frontend/src/util/timeline.js
new file mode 100644
index 00000000..e20d5823
--- /dev/null
+++ b/frontend/src/util/timeline.js
@@ -0,0 +1,19 @@
+export function convertTickToPercentage(tick, maxTick) {
+ if (maxTick === 0) {
+ return "0%";
+ } else if (tick > maxTick) {
+ return maxTick / (maxTick + 1) * 100 + "%";
+ }
+
+ return tick / (maxTick + 1) * 100 + "%";
+}
+
+export function getDatacenterIdOfTick(tick, sections) {
+ for (let i in sections.reverse()) {
+ if (tick >= sections[i].startTick) {
+ return sections[i].datacenterId;
+ }
+ }
+
+ return -1;
+}