diff --git a/glob/script.js b/glob/script.js index 40d42cc..f383eb4 100644 --- a/glob/script.js +++ b/glob/script.js @@ -44,8 +44,8 @@ function getDataByPath(array, path) { * Shows/Hides the sidebar action-menu */ function toggleActionMenu() { - let state = $('.mainview').attr("state"); - if (state === "retracted") { + let state = $('.mainview').attr("state"); // get the current state of the mainview element(s) + if (state === "retracted") { // set the sate to fullsize or retracted depending on the previous value $('.mainview').attr("state", "fullsize"); } else { $('.mainview').attr("state", "retracted"); @@ -58,11 +58,11 @@ function toggleActionMenu() { * @param title the title of the targtet that is displayed */ function navigate(view, title) { - if (view !== 'index.htm') window.location.hash = view; - else window.location.hash = ""; - $('.content').load(view); - let sideTitle = title || view.replace(/\.htm/, ''); - setTitle(sideTitle); + if (view !== 'index.htm') window.location.hash = view; // set the hash if the url is not the index.htm + else window.location.hash = ""; // reset the hash if the hash url is the index.htm + $('.content').load(view); // load the view into the content class elements + let sideTitle = title || view.replace(/\.htm/, ''); // get the side title as the given title or the filename without extension + setTitle(sideTitle); // set the sides title } /** @@ -81,57 +81,54 @@ function setTitle(title) { * TODO: Seperated Background canvas and graph canvas. Div with span elements for x and y values. */ function drawGraph(element, data, options) { - element.innerHTML = ""; - let cv1 = document.createElement('canvas'); - let cv2 = document.createElement('canvas'); - let spanDiv = document.createElement('div'); - spanDiv.setAttribute('class', 'labels'); - let ctx1 = cv1.getContext('2d'); - let ctx2 = cv2.getContext('2d'); - element.appendChild(cv1); - element.appendChild(cv2); - let canvWidth = cv1.width; - let canvHeight = cv2.height; - ctx1.clearRect(0, 0, canvWidth, canvHeight); - ctx2.clearRect(0, 0, canvWidth, canvHeight); - let xData = []; - let yData = []; + element.innerHTML = ""; // reset the element + let cv1 = document.createElement('canvas'); // create a canvas for the background + let cv2 = document.createElement('canvas'); // create a canvas for the graph + let spanDiv = document.createElement('div'); // create a div for labels + spanDiv.setAttribute('class', 'labels'); // set the class for the div + let ctx1 = cv1.getContext('2d'); // get the 2d-context for canvas 1 + let ctx2 = cv2.getContext('2d'); // get the 2d-context for canvas 2 + element.appendChild(cv1); // append canvas 1 to the element + element.appendChild(spanDiv); // append the labels div to the element + element.appendChild(cv2); // append canvas 2 to the element + let canvWidth = cv2.width = cv1.width = cv1.clientWidth; // set the width of the canvas elements to the clients width + let canvHeight = cv2.height = cv1.height = cv1.clientHeight; // set the height of the canvas elements to the clients height + let xData = []; // set xData to an empty array + let yData = []; // set yData to an empty array if (!options) options = {}; ctx1.beginPath(); - ctx1.strokeStyle = $(element).css('color'); - ctx1.font = "80% Arial"; + ctx1.strokeStyle = $(element).css('color'); // set the stroke style to the parent elements color - for (let dt of data) { + for (let dt of data) { // seperate x values and y values to two arrays xData.push(dt[0]); yData.push(dt[1]); } - let xMax = options.xMax || Math.max.apply(Math, xData); - let xMin = options.xMin || Math.min.apply(Math, xData); - let yMax = options.yMax || Math.max.apply(Math, yData); - let yMin = options.yMin || Math.min.apply(Math, yData); - let xUnit = options.xUnit || ""; - let yUnit = options.yUnit || ""; - - let xStep = canvWidth / (xMax - xMin); - let yStep = canvHeight / (yMax - yMin); - - let gridCount = canvHeight/yStep; - while (gridCount > 10) { - gridCount /= 10; + let xMax = options.xMax || Math.max.apply(Math, xData); // set the max x value + let xMin = options.xMin || Math.min.apply(Math, xData); // set the min x value + let yMax = options.yMax || Math.max.apply(Math, yData); // set the max y value + let yMin = options.yMin || Math.min.apply(Math, yData); // set the min y value + let xUnit = options.xUnit || ""; // set the unit of the x values + let yUnit = options.yUnit || ""; // set the unit of the y values + let xStep = canvWidth / (xMax - xMin); // set the equivalent pixel step size for an x step + let yStep = canvHeight / (yMax - yMin); // set the equivalent pixel step size for an y step + let gridCount = canvHeight/yStep; // define the count of grids on the y axis + + while (gridCount > 7) { + gridCount /= 1.5; } - let gridH = (canvHeight/gridCount); - for (let i = gridH; i < (canvHeight - gridH/10); i+= gridH) { - let span = document.createElement('span'); - span.style = `position: absolute; top: ${(i/canvHeight)*100}%; left: 0`; - span.innerText = Math.round((canvHeight - i)/yStep)+Number(yMin)+" "+yUnit; - spanDiv.appendChild(span); + let gridH = (canvHeight/gridCount); // define the height of the grid + for (let i = gridH; i < (canvHeight - gridH/10); i+= gridH) { // create a vertical grid + let span = document.createElement('span'); // create a span element + span.style = `position: absolute; top: calc(${((i)/canvHeight)*100}% - 1.2em); left: 0`; // set the style of the span element + span.innerText = Math.round((canvHeight - i)/yStep)+Number(yMin)+" "+yUnit; // set the text of the span element + spanDiv.appendChild(span); // append the span element to the div ctx1.moveTo(0, i); - ctx1.lineTo(canvWidth, i); + ctx1.lineTo(canvWidth, i); // draw a grid line ctx1.stroke(); } gridCount = canvWidth/xStep; - while (gridCount > 10) { + while (gridCount > 10) { // do the same as above for the x-axis gridCount /= 2; } let gridW = (canvWidth/gridCount); @@ -147,21 +144,63 @@ function drawGraph(element, data, options) { let x = 0; let y = canvHeight; - if (data.length > 0) { - x = data[0][0]; - y = data[0][1]; + if (data.length > 0) { // get the first coordinate as point a + x = data[0][0] * xStep; + y= canvHeight - ((data[0][1] - yMin) * yStep) } ctx2.beginPath(); - ctx2.strokeStyle = $(element).css('outline-color'); + ctx2.strokeStyle = $(element).css('outline-color'); // set the stroke style to the css value 'outline-color' of the parent for (let dt of data) { - ctx2.moveTo(x, y); - x = dt[0] * xStep; - y = canvHeight - ((dt[1] - yMin) * yStep); - ctx2.lineTo(x, y); + ctx2.moveTo(x, y); // move to point a (point b of last turn) + x = dt[0] * xStep; // get the x value of point b + y = canvHeight - ((dt[1] - yMin) * yStep); // get the y value of point b + ctx2.lineTo(x, y); // draw a line from point a to point b ctx2.stroke(); } - element.appendChild(spanDiv); + + cv2.addEventListener('mousemove', evt => { // add a mousedown listener that displays a tooltip with the x and y values + let mousePos = getMousePos(cv2, evt); + showTooltip(`x: ${Math.round(mousePos.x/xStep*10)/10+' '+xUnit},y: ${Math.round((cv1.clientHeight-mousePos.y)/yStep*10)/10+' '+yUnit}`, {x: evt.clientX, y: evt.clientY}, true); + }); +} + +/** + * returns the position of the mouse over a canvas + * @param canvas + * @param evt + * @returns {{x: number, y: number}} + */ +function getMousePos(canvas, evt) { + let rect = canvas.getBoundingClientRect(); + return { + x: evt.clientX - rect.left, + y: evt.clientY - rect.top + }; +} + +/** + * shows a tooltip with the id #tooltip + * @param text + * @param position + * @param keep if false or not set the tooltip will be deleted after 2 seconds + */ +function showTooltip(text, position, keep) { + let el = document.querySelector('#tooltip') || document.createElement('span'); // set the span to the element with the id or create one if it doesn't exist + el.innerText = text; // set the text of the span to the text + el.setAttribute('id', 'tooltip'); // set the id to 'tooltip' + el.style = `position: absolute; top: ${position.y+10}; left: ${position.x+10}; background: white; color: black;`; // set the style that way that the + // absolute x and y of the mouse represent the spans coordinates + document.body.appendChild(el); // append the span to the documents body + if (!keep) setTimeout(() => el.remove(), 2000); // if keep is false or undefined set a timeout of 2 seconds for deletion of the span +} + +/** + * deletes the tooltip with the id #tooltip + */ +function hideTooltip() { + let tooltip = document.querySelector('#tooltip'); // select the element with the id tooltip + if (tooltip) tooltip.remove(); // delete the element if it does exist } // -- Events -- diff --git a/glob/style/elements.sass b/glob/style/elements.sass index d87b17b..1acfd1a 100644 --- a/glob/style/elements.sass +++ b/glob/style/elements.sass @@ -111,6 +111,8 @@ box-shadow: 0 5px 5px $cOnSurfaceShadow cursor: pointer text-align: justify + .panel.max-width + width: calc(100% - 75px) .panel:hover background: darken($cPrimary, 4%) .panel:active @@ -145,24 +147,26 @@ left: 0 overflow: auto .graph - color: $cPrimaryVariant - box-shadow: inset 0 1px 5px $cOnSurfaceShadow - border-radius: $sPadding - outline-color: $cOnPrimary - background: darken($cPrimary, 4%) position: absolute left: $sPadding top: 20% height: calc(80% - 12.5px) width: calc(100% - 25px) - canvas, .labels - position: absolute - top: 0 - left: 0 - height: 100% - width: 100% - span - padding: 2px - font-weight: bold - color: $cOnPrimary - opacity: 0.75 \ No newline at end of file + +.graph + color: $cPrimaryVariant + box-shadow: inset 0 1px 5px $cOnSurfaceShadow + border-radius: $sPadding + outline-color: $cOnPrimary + background: darken($cPrimary, 4%) + canvas, .labels + position: absolute + top: 0 + left: 0 + height: 100% + width: 100% + span + padding: 2px + font-weight: bold + color: $cOnPrimary + opacity: 0.75 \ No newline at end of file diff --git a/lib/caching.js b/lib/caching.js index c312337..f77fbc5 100644 --- a/lib/caching.js +++ b/lib/caching.js @@ -1,6 +1,7 @@ const fs = require("fs"), path = require("path"), - cache_dir = "./.cache"; + cache_dir = "./.cache", + args = require('args-parser')(process.argv); let cache = {}; let logger = require("winston"); @@ -72,7 +73,7 @@ exports.cache = function (filename, data) { * @return {Boolean} Is it cached or not */ exports.isCached = function (filename) { - return false //TODO: change back + if (args.nocache) return false; // return false if the flag nocache is set let cached_entry = cache[filename]; if (cached_entry) { // check if the cache entry exists logger.debug("Found cache entry for %s", filename); diff --git a/lib/utils.js b/lib/utils.js index e603f14..b128dae 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,8 +1,12 @@ /** * A Series of utility functions */ +const fs = require('fs'), + si = require('systeminformation'); function noOp() {} +let sysdataPath = './res/data/sys.json'; +let sysData = {}; /** * returns the extension of a file for the given filename. @@ -19,7 +23,23 @@ exports.getExtension = function (filename) { console.error(error); return null; } -} +}; + +exports.genSystemData = function () { + si.currentLoad(data => { + if (!sysData["100perc"]) sysData["100perc"] = []; + sysData["100perc"].push([sysData["100perc"].length, data.currentload]); + if (sysData["100perc"].length > 100) { + sysData["100perc"].shift(); + for (let i = 0; i < sysData["100perc"].length; i++) { + sysData["100perc"][i][0] -=1; + } + } + }); + fs.writeFile(sysdataPath, JSON.stringify(sysData), err => { + if (err) console.error(err); + }); +}; /** * lets you define a cleanup for your program exit diff --git a/package.json b/package.json index d481c76..5684a4a 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "jsdom": "^12.2.0", "node-sass": "^4.9.3", "perfy": "^1.1.5", + "systeminformation": "^3.45.9", "winston": "^3.1.0", "winston-daily-rotate-file": "^3.3.3" } diff --git a/res/data/cpu.json b/res/data/cpu.json index 191ee42..f117b57 100644 --- a/res/data/cpu.json +++ b/res/data/cpu.json @@ -29,6 +29,7 @@ [12,31], [13,34], [14,33], - [15,32] + [15,32], + [16,34] ] } diff --git a/res/data/sys.json b/res/data/sys.json new file mode 100644 index 0000000..269b4c6 --- /dev/null +++ b/res/data/sys.json @@ -0,0 +1 @@ +{"100perc":[[0,7.592260658628322],[1,10.5375],[2,10.708504544888557],[3,6.638329791223903],[4,7.6230942264433885],[5,7.015130674002751],[6,8.763849122370223],[7,8.952807869505666],[8,13.56121814791796],[9,10.153423974055134],[10,10.926780591243606],[11,6.225826575171553],[12,11.89020586400499],[13,11.692038931869229],[14,8.594237245852563],[15,10.90455396132252],[16,27.02868350085805],[17,12.44228129289904],[18,13.291770573566083],[19,9.575],[20,11.679560768654854],[21,11.670195541163283],[22,19.142821441959263],[23,15.54559043348281],[24,12.289457267623206],[25,12.060613588374116]]} \ No newline at end of file diff --git a/res/html/about.htm b/res/html/about.htm index 5b3b105..0b045ad 100644 --- a/res/html/about.htm +++ b/res/html/about.htm @@ -1,21 +1,29 @@ -
- -
-

- The Raspberry pi Communication-Network is a network of Raspberry-Pi's. - It's currently still in development.
The purpose of this server is to - display the status of all raspberry-pi's in the network and the data - of several sensors that are connected to these raspberry pi's. The data - is stored in a database running on the main backend-server. The - frontend-server is running either on a differend device or also on the - backend-server-device. If this is the case, then the data is directly - fetched from several json files instead of using webservices. The json- - files are generated by a script running in the background that get's - data from the database and stores it in these json-files every few - seconds (depending on the type of data). -

-
-
\ No newline at end of file + setTitle('About'); + +
+
+

+ The Raspberry pi Communication-Network is a network of Raspberry-Pi's. + It's currently still in development.
The purpose of this server is to + display the status of all raspberry-pi's in the network and the data + of several sensors that are connected to these raspberry pi's. The data + is stored in a database running on the main backend-server. The + frontend-server is running either on a differend device or also on the + backend-server-device. If this is the case, then the data is directly + fetched from several json files instead of using webservices. The json- + files are generated by a script running in the background that get's + data from the database and stores it in these json-files every few + seconds (depending on the type of data). +

+
+
+ + CPU Temperature + +
+ +
+
+
+ \ No newline at end of file diff --git a/res/html/data.htm b/res/html/data.htm index 6c28292..aaed395 100644 --- a/res/html/data.htm +++ b/res/html/data.htm @@ -3,7 +3,7 @@ CPU Temperature -
+
diff --git a/res/scripts/about.js b/res/scripts/about.js new file mode 100644 index 0000000..73f06a6 --- /dev/null +++ b/res/scripts/about.js @@ -0,0 +1,9 @@ +function draw() { + $.getJSON('sys.json', data =>{ + let c = document.querySelector('.graph'); + drawGraph(c, data['100perc'], {yMax: 100, yMin: "0", xUnit: "s", yUnit: "%"}); + }); +} + +draw(); +let refreshInterval = setInterval(draw, 1000); \ No newline at end of file diff --git a/server.js b/server.js index a66c3ff..821a706 100644 --- a/server.js +++ b/server.js @@ -83,6 +83,7 @@ var options = {}; function main() { try { prepro.setLogger(logger); + setInterval(utils.genSystemData, 1000); protocoll.createServer(options, function (req, res) { logger.verbose({'msg': 'Received request', 'method': req.method, 'url': req.url});