diff --git a/master.js b/master.js index 58e1547f..d5e0f079 100644 --- a/master.js +++ b/master.js @@ -307,6 +307,49 @@ app.post("/api/getStats", function(req,res) { }); } }); + +// Modified version of getStats which feeds the web interface production graphs +// at /nodes. It is like getStats but it does not report items which were not +// produced at the moment of recording. (This is to save space, the 0-items were +// making up about 92% of the response body weight.) +app.post("/api/getTimelineStats", function(req,res) { + console.log(req.body); + if(typeof req.body == "object" && req.body.slaveID) { + // if not specified, get stats for last 24 hours + if(!req.body.fromTime) { + req.body.fromTime = Date.now() - 86400000 // 24 hours in MS + } + if(!req.body.toTime) { + req.body.toTime = Date.now(); + } + console.log("Looking... " + JSON.stringify(req.body)); + db.flows.find({ + slaveID: req.body.slaveID, + }, function(err, docs) { + if(err) { + console.error(err); + return; + } + + //Filter out all the entries outside the time range. + docs = docs.filter(function (el) { + return el.timestamp <= req.body.toTime && el.timestamp >= req.body.fromTime; + }); + + //Filter out all the elements that weren't produced. + docs.forEach(el => { + for(let key in el.data) { + if(el.data[key] === '0') { + delete el.data[key]; + } + } + }); + + res.send(docs); + }); + } +}); + app.get("/api/getFactorioLocale", function(req,res){ getFactorioLocale.asObject(config.factorioDirectory, "en", (err, factorioLocale) => res.send(factorioLocale)); }); diff --git a/static/index.html b/static/index.html index 03a05b22..f5a5559e 100644 --- a/static/index.html +++ b/static/index.html @@ -1,7 +1,7 @@ <%- include("head.html") %> - + @@ -11,6 +11,7 @@

Storage

+
diff --git a/static/main.js b/static/main.js index 6806f13d..7cb7fdad 100644 --- a/static/main.js +++ b/static/main.js @@ -78,7 +78,7 @@ function getImageFromName(name){ return "https://wiki.factorio.com/images/" + capitalizeFirstLetter(replaceAll(factorioLocale["fluid-name"][name], ' ', '_').toLowerCase()) + ".png"; } else if(factorioLocale["equipment-name"] && factorioLocale["equipment-name"][name]){ return "https://wiki.factorio.com/images/" + capitalizeFirstLetter(replaceAll(factorioLocale["equipment-name"][name], ' ', '_').toLowerCase()) + ".png"; - } else return ""; + } else return "/pictures/unknown-item.png"; } window.factorioLocale = {}; $.getJSON("/api/getFactorioLocale", (locale) => { diff --git a/static/pictures/unknown-item.png b/static/pictures/unknown-item.png new file mode 100644 index 00000000..59a23725 Binary files /dev/null and b/static/pictures/unknown-item.png differ diff --git a/static/slaveDisplay.js b/static/slaveDisplay.js index 2add9d04..c1dc2292 100644 --- a/static/slaveDisplay.js +++ b/static/slaveDisplay.js @@ -44,32 +44,34 @@ let chartIgnoreList = [ // ID of slave, ID of canvasjs div without # function makeGraph(slaveID, selector) { - post("api/getStats", {slaveID: slaveID}, function(data){ + post("api/getTimelineStats", {slaveID: slaveID}, function(pointsInTime) { //console.log("Building chart " + slaveID + " with this data:") - //console.log(data) - if(data.length > 0) { - // find keys - let itemNames = []; - for(let key in data[data.length - 1].data) { - itemNames[itemNames.length] = key + + //Find all keys at all points in time. + let itemNames = {}; + pointsInTime.forEach(function(point) { + for(let key in point.data) { + itemNames[key] = true; } - let chartData = []; - for(let o = 0; o < itemNames.length; o++) { - if(!chartIgnoreList.includes(itemNames[o])){ - chartData[chartData.length] = generateLineChartArray(data, itemNames[o]); - } - } - //console.log(chartData) - drawChart(selector, chartData) - } - // callback(data); + }); + itemNames = Object.keys(itemNames); + + //Generate a chart list for each item. + const displayNames = itemNames.filter(function(name) { + return !chartIgnoreList.includes(name); + }); + const chartData = displayNames.map(function(name) { + return generateLineChartArray(pointsInTime, name) + }); + + chartData.length && drawChart(selector, chartData) }) } // production info // string, object, function(object) function post(url, data, callback) { - console.log("POST " + url + JSON.stringify(data)) + console.log("POST " + url, data); var xhr = new XMLHttpRequest(); xhr.open("POST", url, true); xhr.setRequestHeader("Content-type", "application/json"); @@ -83,27 +85,26 @@ function post(url, data, callback) { } function generateLineChartArray(data, nameKey) { + const legendKeys = ["copper-cable", "iron-plate", "copper-plate", "electronic-circuit", "steel-plate", "advanced-circuit", "crude-oil", "petroleum-gas"]; let chartData = []; for(let i = 0; i < data.length; i++) { - // only show recent data - if(data[i].timestamp > Date.now() - (24*60*60*1000)){ - let y = data[i].data[nameKey] - if(!data[i].data[nameKey]) { - y = 0; - } else if(y < 0) { - y = 0; - } - chartData[chartData.length] = { - x: new Date(data[i].timestamp), - y: Number(y) - } + let y = data[i].data[nameKey] + if(!data[i].data[nameKey]) { + y = 0; + } else if(y < 0) { + y = 0; + } + chartData[chartData.length] = { + x: new Date(data[i].timestamp), + y: Number(y) } } chartData = sortByKey(chartData, "x"); let xyz = {}; xyz.name = nameKey; xyz.type = "line"; - if(nameKey == "copper-wire"||nameKey == "iron-plate"||nameKey == "copper-plate"||nameKey == "electronic-circuit"||nameKey == "steel-plate"||nameKey == "advanced-circuit"||nameKey == "crude-oil"||nameKey == "petroleum-gas"){ + + if(legendKeys.indexOf(nameKey) >= 0){ xyz.showInLegend = true; } xyz.dataPoints = chartData; @@ -113,7 +114,7 @@ function generateLineChartArray(data, nameKey) { var chartsByID = {}; function drawChart(selector, chartData) { // selector is ID of element, ex "chartContainer" or "-123199123" - console.log(chartData) + // console.log(chartData) chartsByID[selector] = new CanvasJS.Chart(selector, { title:{ text: "Production graph" diff --git a/static/storage.js b/static/storage.js index c68cd982..ee15e77d 100644 --- a/static/storage.js +++ b/static/storage.js @@ -1,16 +1,49 @@ // function to draw data we recieve from ajax requests +_lastData = []; function drawcontents(data) { - data = sortByKey(data, "count"); - let result = ""; - for(i = 0;i < data.length; i++) { - var img = getImageFromName(data[i].name); - var searchField = document.querySelector("#search"); - if(!searchField.value || data[i].name.includes(searchField.value)) { - result = result + ""; + data = data || _lastData; //Cache data so we can drawcontents without waiting for the server, for the search box. + _lastData = data; + + const search = new RegExp(document.querySelector("#search").value, 'i'); + data = data.filter(function(item) { + return search.test(item.name); + }) + + sortByKey(data, "count"); + + const table = document.querySelector("#contents tbody"); //tables have tbody inserted automatically + const rows = table.children; + + //update existing rows or create new ones + data.forEach(function(item, i) { + let row = rows[i]; + if(!row) { + row = document.createElement('tr'); + row.innerHTML = ""; + table.appendChild(row); } + + const img = row.querySelector('img'); + const imgName = getImageFromName(item.name); + if(img.getAttribute('src') !== imgName) { + img.setAttribute('src',imgName); + } + + const name = row.querySelector('.name'); + if(name.textContent !== item.name) { + name.textContent = item.name; + } + + const count = row.querySelector('.count'); + if(count.textContent !== ''+item.count) { + count.textContent = item.count; + } + }); + + //remove excess rows, for example, after filtering + while (data.length < rows.length) { + table.removeChild(rows[data.length]); } - result = result + "
" + data[i].name + "" + data[i].count + "
" - document.getElementById("contents").innerHTML = result; } // get cluster inventory from master @@ -33,8 +66,11 @@ if(JSON.parse(localStorage.settings)["Periodically update storage screen"]) { // function to sort arrays of objects after a keys value function sortByKey(array, key) { - return array.sort(function(a, b) { - var x = Number(a[key]); var y = Number(b[key]); - return ((x < y) ? 1 : ((x > y) ? -1 : 0)); + array.sort(function(a, b) { + return b[key] - a[key]; }); -} \ No newline at end of file +} + +document.querySelector("#search").addEventListener('input', function() { + drawcontents(); +}) \ No newline at end of file