This is supposed to create to seperate charts. However when run they are identical and the values alternate between 0 and 1 for index, implying the index is changing (which confuses me idk why this would happen). Can anyone explain what it going on. I get the impression that somewhere i am mixing up the variables i use to reference the two charts, resulting in them being identical.
window.addEventListener(`load`, () => {
Chart.defaults.plugins.tooltip.enabled = false;
let data = {
labels: (() => {
let arr = [];
for(let i = 0;i < 31;i++) {
arr.push((30 - i) * -2);
}
return arr;
})(),
datasets: [
{
label: `first`,
data: (() => {let arr = [];for(let i = 0;i < 31;i++){arr.push(Math.random())}return arr})(),
borderColor: 'rgb(75, 192, 192)',
},
{
label: `second`,
data: (() => {let arr = [];for(let i = 0;i < 31;i++){arr.push(Math.random())}return arr})(),
borderColor: 'rgb(75, 0, 192)',
}
]
}
let graphs = [];
for(let i = 0;i < 2;i++) {
graphs.push(new lineGraph(data, data.labels.length));
}
setInterval(() => {
for(let i = 0;i < graphs.length;i++) {
graphs[i].updateData({first: [i], second: [i]})
}
}, 1000)
})
function lineGraph(data, maxDataSize) {
let div = document.createElement(`div`);
div.classList.add(`module`, `module-line`);
let canvas = document.createElement(`canvas`);
canvas.classList.add(`canvas-line`);
canvas.width = 200;
canvas.height = 200;
div.appendChild(canvas);
this.div = document.body.appendChild(div);
this.canvas = this.div.getElementsByClassName(`canvas-line`).item(0);
this.ctx = this.canvas.getContext(`2d`);
this.chart = new Chart(this.ctx, {
type: `line`,
data: data,
})
this.updateData = (data) => { // data => {datasetlabel: [data], ...}
this.chart.data.datasets.forEach((dataset, index) => {
let newData = data[dataset.label];
if(newData) {
if(dataset.data.length >= maxDataSize) {
for(let i = 0;i < (dataset.data.length - maxDataSize) + newData.length;i++) {
dataset.data.shift();
}
}
dataset.data.push(...newData);
}
});
this.chart.update();
}
return this;
}
Currently, inside setInterval, you alternately update both charts with y-values 0 and 1. Instead of these values, use Match.random(), same as you do when you initialize the chart data.
setInterval(() => {
for (let i = 0; i < graphs.length; i++) {
graphs[i].updateData({
first: [Math.random()],
second: [Math.random()]
})
}
}, 1000)
Please take a look at your amended code below and see how it works.
window.addEventListener(`load`, () => {
Chart.defaults.plugins.tooltip.enabled = false;
let data = {
labels: (() => {
let arr = [];
for (let i = 0; i < 31; i++) {
arr.push((30 - i) * -2);
}
return arr;
})(),
datasets: [{
label: `first`,
data: (() => {
let arr = [];
for (let i = 0; i < 31; i++) {
arr.push(Math.random())
}
return arr
})(),
borderColor: 'rgb(75, 192, 192)',
},
{
label: `second`,
data: (() => {
let arr = [];
for (let i = 0; i < 31; i++) {
arr.push(Math.random())
}
return arr
})(),
borderColor: 'rgb(75, 0, 192)',
}
]
}
let graphs = [];
for (let i = 0; i < 2; i++) {
graphs.push(new lineGraph(data, data.labels.length));
}
setInterval(() => {
for (let i = 0; i < graphs.length; i++) {
graphs[i].updateData({
first: [Math.random()],
second: [Math.random()]
})
}
}, 1000)
})
function lineGraph(data, maxDataSize) {
let div = document.createElement(`div`);
div.classList.add(`module`, `module-line`);
let canvas = document.createElement(`canvas`);
canvas.classList.add(`canvas-line`);
canvas.width = 200;
canvas.height = 40;
div.appendChild(canvas);
this.div = document.body.appendChild(div);
this.canvas = this.div.getElementsByClassName(`canvas-line`).item(0);
this.ctx = this.canvas.getContext(`2d`);
this.chart = new Chart(this.ctx, {
type: `line`,
data: data,
})
this.updateData = (data) => {
this.chart.data.datasets.forEach((dataset, index) => {
let newData = data[dataset.label];
if (newData) {
if (dataset.data.length >= maxDataSize) {
for (let i = 0; i < (dataset.data.length - maxDataSize) + newData.length; i++) {
dataset.data.shift();
}
}
dataset.data.push(...newData);
}
});
this.chart.update();
}
return this;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.min.js"></script>
Related
I use height chart in my project but I have a problem with series I write function for series but in series section cant read my data and dra
const series = useMemo(() => {
const calcvolumeBuy = [];
const calcvolumeSell = [];
if (data) {
for (let i = 1; i < 5 + 1; i += 1) {
const obj = {};
obj.price = data['qd' + i];
obj.volume = data['po' + i];
calcvolumeBuy?.push(obj);
}
for (let i = 1; i < 5 + 1; i += 1) {
const obj = {};
obj.price = data['po' + i];
obj.volume = data['qo' + i];
calcvolumeSell?.push(obj);
}
}
return [
{
name: 'buy',
color: '#03a7a8',
data: calcvolumeBuy,
},
{
name: 'sell',
color: '#fc5857',
data: calcvolumeSell,
},
];
}, [data]);
so this is series how I use :
series: series,
w a chart this is my function I write for series
Sorry I am bad at asking questions, if you don't understand what I am asking please let me know
let node = this.createSvgNode("rect", {
'data-id': section.id || '',
x: section.x,
y: section.y,
width: section.width,
height: section.height,
fill: section.backgroundColor || "#EEEEEE"
});
let thisObject = this;
let paths = thisObject.findPaths(7, 3) // Doesn't work
node.addEventListener("click", function (e) {
let paths = thisObject.findPaths(7, 3) // works
});
I don't understand why let paths = thisObject.findPaths(7, 3) works when it is inside the event listener and doesn't when it is outside of the event listener. isn't it the same?
btw this is the findPaths function:
findPaths: function (startId, endId) {
let paths = [];
let startDoors = doors[startId];
let endDoors = doors[endId];
for (let i = 0; i < startDoors.length; i++) {
let startingPoint = this.findPointByCoordinates(startDoors[i]);
// console.log("Starting Point: " + JSON.stringify(startingPoint));
for (let j = 0; j < doors[endId].length; j++) {
let endingPoint = this.findPointByCoordinates(endDoors[j]);
// console.log("Ending Point: " + JSON.stringify(endingPoint));
let potentialPath = this.recursiveIterationOfPoints([startingPoint], endingPoint);
// console.log(potentialPath);
paths = paths.concat(potentialPath);
}
}
for (let id in paths) {
paths[id] = this.linkRoomsWithPath(paths[id], startId, endId);
}
return paths;
},
I get this error when I put let paths = thisObject.findPaths(7, 3) above the event listener:
Uncaught TypeError: Cannot read property 'length' of undefined
here is the full code
(function (global) {
var IndoorMaps = function (options) {
return new IndoorMaps.init(options);
};
let svgRootNode;
let doors = {};
let hallwayPoints = {};
let currentZIndex = 0;
let validZIndexes = [0];
let showHallwayPoints = false;
//let svgDimensions = {};
let floorPlan = [];
let mapSelection = null;
IndoorMaps.init = function (options) {
this.parseConfiguration(options || {});
this.parseFloorPlan(options.floorSections || {});
this.parseHallwayPoints(options.hallway || {});
//svgDimensions = this.calculateSVGWidthAndHeight();
svgRootNode = this.createSvgNode("svg", {
width: '100%',
height: '100%'
});
this.appendToBody(svgRootNode);
// Render the Map
this.drawMap(floorPlan[currentZIndex] || {});
if (showHallwayPoints) {
this.displayHallwayPoints(options.hallway || {});
}
return svgRootNode;
};
IndoorMaps.init.prototype = {
createSvgNode: function (elementName, attributes = {}, value) {
if (elementName === undefined) {
throw "Element name not found!";
}
let node = document.createElementNS("http://www.w3.org/2000/svg", elementName);
for (let attributeKey in attributes) {
node.setAttribute(attributeKey, attributes[attributeKey]);
}
if (value !== undefined) {
node.innerHTML = value;
}
return node;
},
appendToBody: function (node) {
window.document.body.appendChild(node);
},
draw: function (node) {
let panZoom = svgRootNode.querySelector('.svg-pan-zoom_viewport');
if (!panZoom) {
svgRootNode.append(node);
} else {
panZoom.append(node);
}
},
addDoors: function(roomId, doorPoints) {
doors[roomId] = doorPoints;
for (let i = 0; i < doorPoints.length; i++) {
let coordinates = doorPoints[i];
let attributes = {
x: coordinates.x,
y: coordinates.y,
width: coordinates.width || 0,
height: coordinates.height || 0,
fill: "#FF0000"
};
if (coordinates.width > coordinates.height) {
attributes.x -= coordinates.width / 2;
attributes.y -= coordinates.height;
}
if (coordinates.height > coordinates.width) {
attributes.x -= coordinates.width;
attributes.y -= coordinates.height / 2;
}
let rect = this.createSvgNode("rect", attributes);
this.draw(rect);
}
},
drawMap: function (floorSections) {
for (let i = 0; i < floorSections.length; i++) {
let section = floorSections[i];
if (
section.x === undefined
|| section.y === undefined
|| section.width === undefined
|| section.height === undefined
) {
throw "The x,y coordinates and the width and height are required fields.";
}
let node = this.createSvgNode("rect", {
'data-id': section.id || '',
x: section.x,
y: section.y,
width: section.width,
height: section.height,
fill: section.backgroundColor || "#EEEEEE"
});
let thisObject = this;
node.addEventListener("click", function (e) {
if (mapSelection === null) {
thisObject.removeRouteIndicators();
mapSelection = this;
this.classList.add('selected');
} else {
console.log(mapSelection.dataset.id + " : " + this.dataset.id);
let paths = thisObject.findPaths(mapSelection.dataset.id, this.dataset.id);
let shortestPath = thisObject.findShortestPath(paths);
thisObject.drawPath(shortestPath);
mapSelection = null;
this.classList.add('destination');
}
});
this.draw(node);
this.addDoors(section.id, section.doors);
if (section.label === undefined) {
continue;
}
if (section.label.text === undefined) {
throw "The text must be included for the label to be shown. Set label as null if you do not wish to display a label.";
}
xCoordinate = section.x + (section.label.x || 0);
yCoordinate = section.y + (section.label.y || 0);
let attributes = {
'x': xCoordinate,
'y': yCoordinate,
'fill': section.label.color || '#333333',
'font-family': section.label.fontStyle || 'Verdana',
'font-size': section.label.fontSize || '10'
};
if (section.label.alignment === undefined) {
section.label.alignment = "center|center";
}
if (section.label.alignment !== undefined) {
switch (section.label.alignment) {
case "center|center":
attributes['text-anchor'] = "middle";
attributes['dominant-baseline'] = "middle";
attributes['x'] = (section.width / 2) + section.x;
attributes['y'] = (section.height / 2) + section.y;
break;
case "horizontal_center":
attributes['text-anchor'] = "middle";
attributes['x'] = (section.width / 2) + section.x;
break;
case "vertical_center":
attributes['dominant-baseline'] = "middle";
attributes['y'] = (section.height / 2) + section.y;
break;
default:
throw "Alignment mode requested was not found.";
}
}
let textNode = this.createSvgNode("text", attributes, section.label.text);
this.draw(textNode);
}
},
drawPath: function(path) {
for (let i = 0; i < (path.length - 1); i++) {
let lineNode = this.createSvgNode('line', {
x1: path[i].x,
y1: path[i].y,
x2: path[i + 1].x,
y2: path[i + 1].y,
stroke: "green",
'data-type': "route"
});
this.draw(lineNode);
}
},
removeRouteIndicators: function () {
let nodes = svgRootNode.querySelectorAll('[data-type="route"]');
let panZoom = svgRootNode.querySelector('.svg-pan-zoom_viewport');
for (let index = 0; index < nodes.length; index++) {
if (!panZoom) {
svgRootNode.removeChild(nodes[index]);
} else {
panZoom.removeChild(nodes[index]);
}
}
nodes = svgRootNode.getElementsByClassName('selected');
for (let index = 0; index < nodes.length; index++) {
nodes[index].classList.remove('selected');
}
nodes = svgRootNode.getElementsByClassName('destination');
for (let index = 0; index < nodes.length; index++) {
nodes[index].classList.remove('destination');
}
},
displayHallwayPoints: function (hallwayNodes) {
for (let i = 0; i < hallwayNodes.length; i++) {
this.draw(
this.createSvgNode("circle", {
cx: hallwayNodes[i].x,
cy: hallwayNodes[i].y,
r: 2,
fill: hallwayNodes[i].fill || "black",
'data-id': hallwayNodes[i].id
})
);
}
},
findPaths: function (startId, endId) {
let paths = [];
let startDoors = doors[startId];
let endDoors = doors[endId];
for (let i = 0; i < startDoors.length; i++) {
let startingPoint = this.findPointByCoordinates(startDoors[i]);
console.log("Starting Point: " + JSON.stringify(startingPoint));
for (let j = 0; j < doors[endId].length; j++) {
let endingPoint = this.findPointByCoordinates(endDoors[j]);
console.log("Ending Point: " + JSON.stringify(endingPoint));
let potentialPath = this.recursiveIterationOfPoints([startingPoint], endingPoint);
console.log(potentialPath);
paths = paths.concat(potentialPath);
}
}
for (let id in paths) {
paths[id] = this.linkRoomsWithPath(paths[id], startId, endId);
}
return paths;
},
recursiveIterationOfPoints: function(path, end) {
let results = [];
let current = path[path.length - 1];
console.log(current);
if (current.id === end.id) {
return [path];
}
for (let i = 0; i < current.connected.length; i++) {
let nextPoint = hallwayPoints[current.connected[i]];
let found = false;
path.forEach( function (point, index) {
if (point.id === nextPoint.id) {
found = true;
}
});
if (found)
continue;
var newPath = path.slice();
newPath.push(nextPoint);
results = results.concat(this.recursiveIterationOfPoints(newPath, end));
}
return results;
},
findPointByCoordinates: function (coordinates) {
for (let id in hallwayPoints) {
let point = hallwayPoints[id];
if (point.x === coordinates.x && point.y === coordinates.y) {
return point;
}
}
},
findActualCoordinatesOfDoorByCoordinates: function (coordinates, roomId) {
let validDoors = doors[roomId];
for (let id in validDoors) {
if (validDoors[id].x === coordinates.x && validDoors[id].y === coordinates.y) {
return validDoors[id].actual;
}
}
},
parseHallwayPoints: function (points) {
for (let i = 0; i < points.length; i++) {
hallwayPoints[points[i].id] = points[i];
}
},
findShortestPath: function(paths) {
if (paths.length === 1)
return paths[0];
// Set benchmark range
let shortestDistance = this.calculateDistanceForPath(paths[0]);
let shortestPath = paths[0];
// Skip the first since it's already calculated.
for (let i = 1; i < paths.length; i++) {
let distance = this.calculateDistanceForPath(paths[i]);
if (distance < shortestDistance) {
shortestDistance = distance;
shortestPath = paths[i];
}
}
return shortestPath;
},
calculateDistanceForPath: function(path) {
let totalDistance = 0;
for (let i = 0; i < (path.length - 1); i++) {
// Formula: sqrt[(x0 - x1)^2 + (y0 - y1)^2]
totalDistance += Math.sqrt(Math.pow(path[i].x - path[i + 1].x, 2) + Math.pow(path[i].y - path[i + 1].y, 2));
}
return totalDistance;
},
linkRoomsWithPath: function (path, startId, endId) {
let first = this.findActualCoordinatesOfDoorByCoordinates(path[0], startId);
let last = this.findActualCoordinatesOfDoorByCoordinates(path[path.length - 1], endId);
path.unshift(first);
path.push(last);
return path;
},
parseConfiguration: function (options) {
currentZIndex = options.config.defaultZIndex || 0;
validZIndexes = options.config.validZIndexes || [0];
showHallwayPoints = options.config.showHallwayPoints || false;
},
parseFloorPlan: function (floorLayout) {
for (let i in floorLayout) {
if (floorLayout[i].z === undefined) {
floorLayout[i].z = 0;
}
if (!(floorLayout[i].z in floorPlan)) {
floorPlan[floorLayout[i].z] = [];
}
floorPlan[floorLayout[i].z].push(floorLayout[i]);
}
},
calculateSVGWidthAndHeight: function () {
let dimensions = {
width: 0,
height: 0
};
let floorLayout = floorPlan[currentZIndex];
let first = true;
for (let i in floorLayout) {
let calculatedWidth = floorLayout[i].width + floorLayout[i].x;
let calculatedHeight = floorLayout[i].height + floorLayout[i].y;
if (first) {
dimensions.width = calculatedWidth;
dimensions.height = calculatedHeight;
first = false;
}
if (calculatedWidth > dimensions.width) {
dimensions.width = calculatedWidth;
}
if (calculatedHeight > dimensions.height) {
dimensions.height = calculatedHeight;
}
}
for (let i in hallwayPoints) {
let currentWidth = hallwayPoints[i].x;
let currentHeight = hallwayPoints[i].y;
if (currentWidth > dimensions.width) {
dimensions.width = currentWidth;
}
if (currentHeight > dimensions.height) {
dimensions.height = currentHeight;
}
}
return dimensions;
}
};
window.indoorMaps = IndoorMaps;
}(window));
From the comment above, it seems that at the point you are calling findPaths(), doors exists, but is empty when you call findPaths(), though it is not when the event is triggered. Make sure that doors is filled before try to use it.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
function par(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
function breeding() {
let idOvce = [];
let brOvce = [];
let mesecOvce = [];
let godinaOvce = [];
let istorija1 = [];
let istorija2 = [];
let idOvna = [];
let brOvna = [];
let mesecOvna = [];
let godinaOvna = [];
let y = 0;
let o = 0;
let parovi = [];
let c = 0;
fetch("http://localhost/ovce/ovce.json")
.then(function(resp) {
return resp.json();
})
.then(function(data) {
console.log(data);
for (let i = 0; i < data.ovce.length; i++) {
idOvce[i] = data.ovce[i].id;
brOvce[i] = data.ovce[i].broj;
mesecOvce[i] = data.ovce[i].mesec;
godinaOvce[i] = data.ovce[i].godina;
istorija1[i] = data.ovce[i].istorija1;
istorija2[i] = data.ovce[i].istorija2;
}
});
fetch("http://localhost/ovce/ovnovi.json")
.then(function(resp1) {
return resp1.json();
})
.then(function(data1) {
console.log(data1);
for (let g = 0; g < data1.ovnovi.length; g++) {
idOvna[g] = data1.ovnovi[g].id;
brOvna[g] = data1.ovnovi[g].broj;
mesecOvna[g] = data1.ovnovi[g].mesec;
godinaOvna[g] = data1.ovnovi[g].godina;
}
});
while (o < idOvna.length) {
y = 0;
while (y < idOvce.length) {
if (istorija1[y] != 0) {
if ((istorija2[y] != idOvna[o]) && (istorija2[istorija1[y]] != idOvna[o])) {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
} else {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
y++;
}
o++;
}
console.log(parovi);
return parovi;
}
<html>
<head>
<title>Sheepify</title>
<script src="main.js"></script>
</head>
<body>
<button onclick="breeding()"> Breeding </button>
</body>
</html>
In javascript, i have a loop running and afterwards the pairs array which should be populated is empty.
function pair(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
function problem() {
let y = 0;
let o = 0;
let pairs = [];
let c = 0;
//id, id1, history1, history2 are arrays which are populated from the json files using fetch.
while (o < id.length) {
y = 0;
while (y < id1.length) {
if (history1[y] != 0) {
if ((history2[y] != id[o]) && (history2[history1[y]] != id[o])) {
pairs[c] = new pair(id1[y], id[o]);
c++;
}
} else {
pairs[c] = new pair(id1[y], id[o]);
c++;
}
y++;
}
o++;
}
console.log(pairs);
console.log(pairs.length);
}
When i run a debugger the array is populated and everything is fine, but when i execute the function on button click or through a console, it just returns an empty array. What could be causing this problem?
EDIT: I accidentally pasted the pair function inside the problem function, which isn't the case. I have moved it out now. And changed leght to length as suggested.
EDIT2: Here is the full code, sorry about the variable names, they are in Serbian.
The loop that processes the arrays is outside the fetch callback functions, so it doesn't wait for the arrays to be populated. See Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference.
You can make your function async and use await to wait for them.
Note also that the code that calls this will need to use await or .then() to get the returned parovi array. See How do I return the response from an asynchronous call?
async function breeding() {
let idOvce = [];
let brOvce = [];
let mesecOvce = [];
let godinaOvce = [];
let istorija1 = [];
let istorija2 = [];
let idOvna = [];
let brOvna = [];
let mesecOvna = [];
let godinaOvna = [];
let y = 0;
let o = 0;
let parovi = [];
let c = 0;
await fetch("http://localhost/ovce/ovce.json")
.then(function(resp) {
return resp.json();
})
.then(function(data) {
console.log(data);
for (let i = 0; i < data.ovce.length; i++) {
idOvce[i] = data.ovce[i].id;
brOvce[i] = data.ovce[i].broj;
mesecOvce[i] = data.ovce[i].mesec;
godinaOvce[i] = data.ovce[i].godina;
istorija1[i] = data.ovce[i].istorija1;
istorija2[i] = data.ovce[i].istorija2;
}
});
await fetch("http://localhost/ovce/ovnovi.json")
.then(function(resp1) {
return resp1.json();
})
.then(function(data1) {
console.log(data1);
for (let g = 0; g < data1.ovnovi.length; g++) {
idOvna[g] = data1.ovnovi[g].id;
brOvna[g] = data1.ovnovi[g].broj;
mesecOvna[g] = data1.ovnovi[g].mesec;
godinaOvna[g] = data1.ovnovi[g].godina;
}
});
while (o < idOvna.length) {
y = 0;
while (y < idOvce.length) {
if (istorija1[y] != 0) {
if ((istorija2[y] != idOvna[o]) && (istorija2[istorija1[y]] != idOvna[o])) {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
} else {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
y++;
}
o++;
}
console.log(parovi);
return parovi;
}
function par(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
<html>
<head>
<title>Sheepify</title>
<script src="main.js"></script>
</head>
<body>
<button onclick="breeding()"> Breeding </button>
</body>
</html>
how are you using this problem function within the fetch?
If you are using int outside the fetch its totaly normal, your arrays will be empty.
Edit:
As you have post the fetch method, you need to put all your while loop un the last then, when you are populating the data, because fetch is async =/
Since you have two fetch, and both are populating the array's you need to do the second in the last then of the first fetch. And finaly you can do your while loop in last then of your second fetch.
It may be more clearner using async await method.
function par(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
function breeding() {
let idOvce = [];
let brOvce = [];
let mesecOvce = [];
let godinaOvce = [];
let istorija1 = [];
let istorija2 = [];
let idOvna = [];
let brOvna = [];
let mesecOvna = [];
let godinaOvna = [];
let y = 0;
let o = 0;
let parovi = [];
let c = 0;
fetch("http://localhost/ovce/ovce.json")
.then(function(resp) {
return resp.json();
})
.then(function(data) {
console.log(data);
for (let i = 0; i < data.ovce.length; i++) {
idOvce[i] = data.ovce[i].id;
brOvce[i] = data.ovce[i].broj;
mesecOvce[i] = data.ovce[i].mesec;
godinaOvce[i] = data.ovce[i].godina;
istorija1[i] = data.ovce[i].istorija1;
istorija2[i] = data.ovce[i].istorija2;
}
fetch("http://localhost/ovce/ovnovi.json")
.then(function(resp1) {
return resp1.json();
})
.then(function(data1) {
console.log(data1);
for (let g = 0; g < data1.ovnovi.length; g++) {
idOvna[g] = data1.ovnovi[g].id;
brOvna[g] = data1.ovnovi[g].broj;
mesecOvna[g] = data1.ovnovi[g].mesec;
godinaOvna[g] = data1.ovnovi[g].godina;
while (o < idOvna.length) {
y = 0;
while (y < idOvce.length) {
if (istorija1[y] != 0) {
if ((istorija2[y] != idOvna[o]) && (istorija2[istorija1[y]] != idOvna[o])) {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
} else {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
y++;
}
o++;
}
console.log(parovi);
return parovi;
}
});
});
}
When extending traces with plotly the points are drawn in the past. As you can see in the picture, any
Picture of the problem
The chart now consists of 2 parts one on the right is data from the database, and to the left is data being plotted in "real-time". The data that is being plotted in realtime is to the left of the data from the database is even though the timestamps are after the ones from the databases.
The time stamp on the console logs is correct but plotly is placing them at the wrong time on the x-axis. The should be drawn at the end of the graph.
updateData (sensorData) {
if (!this.initiated || !this.isLiveData) {
return
}
let y = []
let i = 0
let x = []
let traces = []
for (let sensor in this.configuration) {
for (let signal in this.configuration[sensor]) {
let xDate = new Date(sensorData[sensor].timestamp)
if (sensorData.hasOwnProperty(sensor) && sensorData[sensor].hasOwnProperty(signal)) {
y.push([sensorData[sensor][signal]])
x.push([xDate.getTime()])
}
// x time seems to be good here
console.log('Update data', xDate.getTime(), xDate)
traces.push(i)
i++
}
}
console.log('Plotting', y, x, this.widget.getXRange())
if (y.length > 0) {
this.$plotly.extendTraces('plotly-chart', {
y: y,
x: x
}, traces)
}
},
The data from the db is added with following code.
updateFromDb (sensorData) {
let data = []
let x = []
let yData = {}
for (let sensor in this.configuration) {
for (let i = 0, len = sensorData[sensor].length; i < len; i++) {
let timestamp = sensorData[sensor][i].timestamp
x.push(timestamp)
for (let source in this.configuration[sensor]) {
let name = sensor + ' ' + getSignalName(source, this.angleLabel)
if (!yData.hasOwnProperty(name)) {
yData[name] = {
data: [],
color: this.configuration[sensor][source].color
}
}
if (!sensorData[sensor][i].hasOwnProperty(source)) {
yData[name].data.push(0)
} else {
yData[name].data.push(sensorData[sensor][i][source])
}
if (this.configuration[sensor][source].hasOwnProperty('yaxis')) {
yData[name]['yaxis'] = 'y' + this.configuration[sensor][source].yaxis
}
}
}
}
for (let name in yData) {
let sensorData = {
name: name,
x: x,
y: yData[name].data,
type: 'line',
mode: 'lines',
line: {
width: 2,
color: yData[name].color
},
marker: {
width: 2,
color: yData[name].color
}
}
if (yData[name].hasOwnProperty('yaxis')) {
sensorData['yaxis'] = yData[name].yaxis
}
data.push(sensorData)
}
this.$plotly.react(
document.getElementById('plotly-chart'),
data,
this.getLayout(false),
this.chartProperties
)
}
There is also a function that scroll the window over xaxis every 50 ms to make it look smooth.
windowScroller () {
if (!this.initiated || !this.isLiveData) {
return
}
let timeDifference = 0
if (this.timeDifference !== null) {
timeDifference = this.timeDifference
}
/**
* Make sure the line never gets behind the scroller.
*/
if (Object.keys(this.latestPoint).length > 0) {
let latestTime = this.latestPoint[Object.keys(this.latestPoint)[0]].timestamp
let scrollDiff = new Date().getTime() - latestTime
if (scrollDiff !== this.scrollDelay && this.lastDelayTime !== latestTime && scrollDiff < 60000) {
this.lastDelayTime = latestTime
this.scrollDelay = scrollDiff
// console.log('update scroll', scrollDiff, 'from', latestTime)
}
}
let currentTime = new Date().getTime() - timeDifference - this.scrollDelay
let firstTime = new Date().getTime() - this.getMilisecondsFromMinutes(this.widget.getXRange()) - timeDifference - this.scrollDelay
let relayoutPromise = new Promise((resolve, reject) => {
this.$plotly.relayout('plotly-chart', {
xaxis: {
color: '#fff',
type: 'date',
range: [firstTime, currentTime]
}
})
})
relayoutPromise.then(() => {
console.log('relayout')
})
let data = document.getElementById('plotly-chart').data
// We calculate the max points using 4 hertz
let maxPoints = (this.getSecondsFromMinutes(this.widget.getXRange()) + 10) * this.widget.getRefreshRate()
if (this.minMax) {
maxPoints = maxPoints * 2
}
for (let i in data) {
if (data[i].y.length >= maxPoints) {
data[i].y.shift()
data[i].x.shift()
}
}
}
I am currently learning to use Electron for a university project. What I am trying to do is to read data from a .xlsx file, then create a Chart with chart.js and display the data. For that, I am using exceljs and chart.js. For this, I wrote multiple functions (Code below). My problem right now is, that I try to return an Array with some data (It gets created perfectly fine) but it just displays as undefined in the other function.
I know, my code is not good. It's complete spaghetti, honestly. I just need to get this working, it doesn't have to be nice code.
This is the function I use to draw the chart:
function createGraph() {
// Create Canvas if not already created
if (document.getElementById('datacanvas') == null) {
var canvas = document.createElement('canvas');
canvas.setAttribute("id", "datacanvas")
var datadiv = document.getElementById("datadiv");
datadiv.appendChild(canvas);
var ctx = canvas.getContext("2d");
}
else {
var canvas = document.getElementById('datacanvas')
var ctx = canvas.getContext("2d");
}
var labls = ["Januar", "Februar", "März", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember"];
var datasts = createDatasets();
console.log(datasts);
var chart = new Chart(ctx, {
type: 'line',
data: {
labels: labls,
datasets: datasts
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
}
The problem I have is the varible datasts or rather, the data property because the data is undefined. It gets created with these two functions:
function createDatasets() {
console.log("createDatasets");
var dataset = [];
var rdbStrom = document.getElementById('rdbStrom');
var rdbGas = document.getElementById('rdbGas');
var rdbWasser = document.getElementById('rdbWasser');
var rdbGesamt = document.getElementById('rdbGesamt');
if (rdbStrom.checked) {
var set = {
label: 'Stromkosten',
data: getDataArray("strom"),
borderColor: '#FF0000',
borderWidth: 1
};
dataset.push(set);
}
if (rdbGas.checked) {
var set = {
label: 'Gaskosten',
data: getDataArray("gas"),
borderColor: '#00FF00',
borderWidth: 1
};
dataset.push(set);
}
if (rdbWasser.checked) {
var set = {
label: 'Wasserkosten',
data: getDataArray("wasser"),
borderColor: '#0000FF',
borderWidth: 1
};
dataset.push(set);
}
if (rdbGesamt.checked) {
;
var set = {
label: 'Gesamtkosten',
data: getDataArray("gesamt"),
borderColor: '#FFFFFF',
borderWidth: 1
};
dataset.push(set);
}
// Wait
setTimeout(() => {
return dataset;
}, 1000);
}
function getDataArray(type) {
console.log("getDataArray");
var data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var period = [];
var start = new Date(document.getElementById('start').value);
var end = new Date(document.getElementById('end').value);
var tmp = new Date(start);
do {
period.push(tmp.toDateString());
tmp.setDate(tmp.getDate() + 1);
} while (tmp <= end)
if (!fs.existsSync('./Data.xlsx')) {
alert("Error finding File 'Data.xlsx'.");
}
else {
switch (type) {
case "strom": {
workbook.xlsx.readFile('./Data.xlsx')
.then(function () {
var worksheet = workbook.getWorksheet('Data');
for (var i = 2; i <= worksheet.rowCount; i++) {
var r = worksheet.getRow(i);
var d = new Date(r.getCell(1).value);
if (period.includes(d.toDateString())) {
var vbr = r.getCell(3).value;
var prc = r.getCell(4).value;
var gprc = r.getCell(5).value;
var tax = r.getCell(6).value;
var kosten = (vbr * prc) + gprc + tax;
data[d.getMonth()] = data[d.getMonth()] + kosten;
}
}
})
break;
}
case "gas": {
workbook.xlsx.readFile('./Data.xlsx')
.then(function () {
var worksheet = workbook.getWorksheet('Data');
for (var i = 2; i <= worksheet.rowCount; i++) {
var r = worksheet.getRow(i);
var date = new Date(r.getCell(1).value);
if (period.includes(date.toDateString())) {
var vbr = r.getCell(8).value;
var prc = r.getCell(9).value;
var gprc = r.getCell(10).value;
var tax = r.getCell(11).value;
var kosten = (vbr * prc) + gprc + tax;
data[d.getMonth()] = data[d.getMonth()] + kosten;
}
}
})
break;
}
case "wasser": {
workbook.xlsx.readFile('./Data.xlsx')
.then(function () {
var worksheet = workbook.getWorksheet('Data');
for (var i = 2; i <= worksheet.rowCount; i++) {
var r = worksheet.getRow(i);
var date = new Date(r.getCell(1).value);
if (period.includes(date.toDateString())) {
var vbr = r.getCell(13).value;
var prc = r.getCell(14).value;
var gprc = r.getCell(15).value;
var tax = r.getCell(16).value;
var kosten = (vbr * prc) + gprc + tax;
data[d.getMonth()] = data[d.getMonth()] + kosten;
}
}
})
break;
}
default:
break;
}
}
// Wait till process is done reading file
setTimeout(() => {
console.log("Timeout")
for (i = 0; i < data.length; i++) {
console.log("Data[" + i + " ]: " + data[i]);
}
console.log("Return DataArray");
return data;
}, 1000);
}
Again, I know my code is not good, I just need to get this working.
The output in the console is the following:
createDataset
getDataArray
undefined // This is the datasts Variable which I need to wait for
Timeout // This comes from the third function
// Here it displays the data it read from the Excel file from the third function
This is because of asynchronicity issue, you use createDatasets as if it was a synchronous function (such as return 1 + 2) while it relies on asynchronous operations,
ie :
let exampleInt = 0
setTimeout(() => {
// callback
exampleInt = 1;
return dataset;
}, 1000);
// This will be reached before the callback executes, so exampleInt equals 0
You should have a look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises to achieve your goal which is, I guess, wait the end of an operation before executing code based on the result of that operation
Does it brighten your mind ?
Now resolution, first function : createDatasets
function createDatasets() {
console.log("createDatasets");
var dataset = [];
var rdbStrom = document.getElementById('rdbStrom');
var rdbGas = document.getElementById('rdbGas');
var rdbWasser = document.getElementById('rdbWasser');
var rdbGesamt = document.getElementById('rdbGesamt');
// storing each label we need
let dataArraysNeeded = [];
let dataArraysNeededAsPromises = [];
let designParams = {
"strom": {
title: "Stromkosten",
color: "#FF0000"
},
"gas": {
title: "Gaskosten",
color: "#00FF00"
},
"wasser": {
title: "Wasserkosten",
color: "#0000FF"
},
"gesamt": {
title: "Gesamtkosten",
color: "#FFFFFF"
}
};
if (rdbStrom.checked) {
dataArraysNeeded.push('strom');
}
if (rdbGas.checked) {
dataArraysNeeded.push('gas');
}
if (rdbWasser.checked) {
dataArraysNeeded.push('wasser');
}
if (rdbGesamt.checked) {
dataArraysNeeded.push('gesamt');
}
// From here we have an array of labels (ex: ["wasser","gesamt"])
// We now want to get the data array for each of these labels, here is how it's done
for (let i = 0; i < dataArraysNeeded.length; i++) {
dataArraysNeededAsPromises.push(getDataArray(dataArraysNeeded[i]));
}
// This will execute all the promises AND WAIT the end of the slowest promise
return Promise.all(dataArraysNeededAsPromises).then((sets) => {
let currentLabel = "";
// sets[0] equals getDataArray("wasser") for example
for (let j = 0; j < sets.length; j++) {
currentLabel = dataArrayLabel[j]; // "wasser"
dataset.push( {
label: designParams[currentLabel]["title"],
data: sets[j],
borderColor: designParams[currentLabel]["color"],
borderWidth: 1
});
}
return dataset; // Array of objects {label, data, borderColor, borderWidth}
});
}
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all for details on how works Promise.all
Your main function createGraph which calls createDatasets (you can see how the result of a function returning a promise is consumed)
function createGraph() {
// Create Canvas if not already created
if (document.getElementById('datacanvas') == null) {
var canvas = document.createElement('canvas');
canvas.setAttribute("id", "datacanvas")
var datadiv = document.getElementById("datadiv");
datadiv.appendChild(canvas);
var ctx = canvas.getContext("2d");
}
else {
var canvas = document.getElementById('datacanvas')
var ctx = canvas.getContext("2d");
}
var labls = ["Januar", "Februar", "März", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember"];
// Here you instanciate your promise of Dataset, which IS NOT synchronous
var datasetPromise = createDatasets();
// So you need to specifiy a callback, executed on promise completion
return datasetPromise.then((yourDatasetReadyToBeUsed) => {
var chart = new Chart(ctx, {
type: 'line',
data: {
labels: labls,
datasets: yourDatasetReadyToBeUsed
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
return 'completed !';
});
}
I'll let you find the last one as it's very similar to those two functions (getDataArray needs to return a Promise too as it reads a file) !
Is it more clear for you ?