Node-Red chart node with stored data - node.js

I have a problem with the chart node. I want to display a line graph, the data source is two MQTT messages. A microcontroller publishes two 2049 element byte arrays in 2 topics (linearccdL and linearccdH). The data are upper and lower bytes of words, and at first I have to join them, it works now.
When the 2049 pcs 16 bit numbers are ready i have to display them on a graph. According to the documentation, the graph node accepts arrays in the form:
[{
"series": ["A", "B", "C"],
"data": [
[{ "x": 1504029632890, "y": 5 },
{ "x": 1504029636001, "y": 4 },
{ "x": 1504029638656, "y": 2 }
],
[{ "x": 1504029633514, "y": 6 },
{ "x": 1504029636622, "y": 7 },
{ "x": 1504029639539, "y": 6 }
],
[{ "x": 1504029634400, "y": 7 },
{ "x": 1504029637959, "y": 7 },
{ "x": 1504029640317, "y": 7 }
]
],
"labels": [""]
}]
I'm using a function to format the data:
var rawDataL = new ArrayBuffer(2049);
var rawDataH = new ArrayBuffer(2049);
var rawData = new Uint16Array(2049);
var outobj = {};
var outarr = [];
var outmsg;
if (msg.topic == "rbmt/linearccdL") {
context.rawDataL = msg.payload;
context.Lset=true;
} else if (msg.topic == "rbmt/linearccdH") {
context.rawDataH = msg.payload;
context.Hset=true;
}
if (context.Lset & context.Hset) {
for (var i=0; i<2049; i++) {
rawData[i]=context.rawDataH[i]*256+context.rawDataL[i];
outobj={"x":i, "y":rawData[i]};
outarr.push(outobj);
}
context.Lset=false;
context.Hset=false;
outmsg = {"series":"[A]", data: outarr};
return outmsg;
}
After putting a debug node i can see the message, seems OK, but on the dashboard i get "No data".
I'm new in JS, can somebody help a bit please?
The debug message: debug msg

Related

NodeJs How to convert parent-child json array to json-object with key-id and value array of all childes

I have a parent-child array that lookes like this:
const myTestArray = [
{
"id": "1",
"parent": "root"
},
{
"id": "2",
"parent": "1"
},
{
"id": "3",
"parent": "1"
},
{
"id": "4",
"parent": "2"
},
{
"id": "5",
"parent": "4"
}
];
From this I have been able to create an nested parent-child json-tree, but now I need another data-structure that looks like this:
const childesArray = {
"1": ["1", "2", "3", "4", "5"],
"2": ["2", "4", "5"],
"3": ["3"],
"4": ["4", "5"],
"5": ["5"]
};
For each element in myTestArray I need an array that contains all childes (deep) for that element.
I guess I also here can use reduce on this array, but I'm stucked right now how I can achive this. Anyone that can point me in the right direction? Or have any solution on this?
Br. Rune
There are several ways to achieve this, but I tried to keep it simple. Take a look at this example.
const nesting = {};
myTestArray.forEach((item) => {
const { parent, id } = item;
if (!nesting[id]) {
nesting[id] = [id];
}
if (!nesting[parent] && parent !== "root") {
nesting[parent] = [parent];
}
if (parent !== "root") {
nesting[parent].push(id);
}
});
console.log(nesting);
//
// {
// "1":["1","2","3"],
// "2":["2","4"],
// "3":["3"],
// "4":["4","5"],
// "5":["5"]
// }
//
You could take the JSON object and iterate through it with a ForEach loop. You can then append an element to the index of its parent, and you would end up with a 2-dimensional array such as the one you specified.
var newArr = {};
myTestArray.forEach(e => {
newArr[e.id] = [e.id];
if(e.parent != "root"){
newArr[e.parent].push(e.id);
}
});
would produce
newArr = { '1': [ '1', '2', '3' ],
'2': [ '2', '4' ],
'3': [ '3' ],
'4': [ '4', '5' ],
'5': [ '5' ] }
Have been using my day on this :)
Think I have a solution now.
Suggestions on improvements are welcome.
function getNestedChildrenArray(inputArray: Array<Record<string,unknown>>, inputId: string) {
let myA: Array<string> = inputArray.reduce((previousValue: Array<string>, currentValue: Record<string,unknown>) => {
if(typeof currentValue.id === 'string' && inputId === currentValue.parent) previousValue.push(currentValue.id);
return previousValue;
}, []);
if(myA.length > 0) {
myA = myA.concat(myA.reduce((previousValue: Array<string>, currentValue: string) => {
previousValue = previousValue.concat(getNestedChildrenArray(inputArray, currentValue));
return previousValue;
},[]));
}
return myA;
}
function getNestedChildrenArrayJsonObject(inputArray: Array<Record<string,unknown>>) {
return inputArray.reduce((previousValue: Record<string,unknown>, currentValue: Record<string,unknown>, index, array) => {
if(typeof currentValue.id === 'string') {
previousValue[currentValue.id] = [currentValue.id].concat(getNestedChildrenArray(array, currentValue.id));
}
return previousValue;
}, {});
}
console.log(getNestedChildrenArrayJsonObject(myTestArray));

JSON Saving Format

How to write information into json in this format?
{
name_data: {
"1": {
name: "A",
},
"2": {
name: "B",
},
"3": {
name: "C",
},
"4": {
name: "D",
}
}
}
Currently this is how I write information into json
client.reqs[1] = {
name: "A",
}
fs.writeFile("./Database/reqs.json", JSON.stringify(client.reqs, null, 4), err => {
if (err) throw err;
})
And this is the resulting format in json
{
"1": {
"name": "A",
},
"2": {
"name": "A",
},
"3": {
"name": "A",
},
"4": {
"name": "A",
}
}
Can somebody give answers, websites, or documentation that can help me answer my question?
Try using JSON.stringify(client.reqs, null, " ")
The other thing you want to is not directly possible, it is more JavaScript than JSON. You cannot remove the quotes from the keys, because it would be invalid JSON.
For having the name_data on top level, do this (indentation change is included in this example):
fs.writeFile("./Database/reqs.json", JSON.stringify({ name_data: client.reqs }, null, 4), err => {
if (err) throw err; // ^ this adds name_data
})
for the custom object key name
all you have to do is just to add ["the name you want"] : value
so I assume there will be something like
for(let i = 0; i <= 5; i++){
client.reqs[i] = {
[i]: "A",
}
}

Count unseen messages from object Lodash

I am trying to get count of unseen messages from object in lodash.
below is my object
[
"conversation_id": "5a88779b2321141f2864e484"
"messages": [
{
"message_id": "5a88779b2321141f2864e483",
"sender_uid": 2,
"receiver_uid": 1,
"created": "2018-02-17T18:42:35.252Z",
"status": 1,
"delivered": false,
"seen": true,
}
]
]
I want to get count of seen: false messages
You can use filter to get all see = false messages and then can check length
var users = {
"conversation_id": "5a88779b2321141f2864e484",
"messages": [
{ "message_id": "5a88779b2321141f2864e483","sender_uid": 2,"receiver_uid": 1,"created": "2018-02-17T18:42:35.252Z","status": 1,"delivered": false,"seen": true,},
{ "message_id": "5a88779b2321141f2864e483","sender_uid": 2,"receiver_uid": 1,"created": "2018-02-17T18:42:35.252Z","status": 1,"delivered": false,"seen": false },
{ "message_id": "5b88779b2321141f2864e483","sender_uid": 2, "receiver_uid": 1, "created": "2018-02-17T18:42:35.252Z", "status": 1,"delivered": false,"seen": false,}
]
}
var unseen_messages = _.filter(users.messages, message => { return !message.seen; }).length;
console.log(unseen_messages);
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
Without lodash you can use reduce
var users = {
"conversation_id": "5a88779b2321141f2864e484",
"messages": [
{ "message_id": "5a88779b2321141f2864e483","sender_uid": 2,"receiver_uid": 1,"created": "2018-02-17T18:42:35.252Z","status": 1,"delivered": false,"seen": true,},
{ "message_id": "5a88779b2321141f2864e483","sender_uid": 2,"receiver_uid": 1,"created": "2018-02-17T18:42:35.252Z","status": 1,"delivered": false,"seen": false },
{ "message_id": "5b88779b2321141f2864e483","sender_uid": 2, "receiver_uid": 1, "created": "2018-02-17T18:42:35.252Z", "status": 1,"delivered": false,"seen": false,}
]
}
items = users.messages;
var totalCount = items.reduce((total, obj) => { return (!obj.seen) ? (total +1) : total }, 0);
console.log(totalCount);
_.sumBy would do the trick
const unseenMessages = _.sumBy(users.messages, message => message.seen === false);

Transition between a circle and a line with svg and d3

JS Fiddle
I am trying to figure out how to "unfurl"/"unravel" a circle by "snipping" it at the top, and then it would animate to a line.
I have made a circle by using 16 points (and a 17th to close it since if I used a closed interpolation, the transition would look weird)
How (would it be via an animation?, just adjusting the x and y points?, another interpolation?) can you transition between the circle and the line one point at a time?
Circle points:
//The data for our line
var circleData = [ { "x": 150 , "y": 20 },
{ "x": 165.30, "y": 23.04},
{ "x": 178.28, "y": 31.71},
{ "x": 186.95, "y": 44.69},
{ "x": 190 , "y": 60 },
{ "x": 186.95, "y": 75.30},
{ "x": 178.28, "y": 88.28},
{ "x": 165.30, "y": 96.95},
{ "x": 150 , "y": 100 },
{ "x": 134.69, "y": 96.95},
{ "x": 121.71, "y": 88.28},
{ "x": 113.04, "y": 75.30},
{ "x": 110 , "y": 60.00},
{ "x": 113.04, "y": 44.69},
{ "x": 121.71, "y": 31.71},
{ "x": 134.69, "y": 23.04},
{ "x": 150 , "y": 20 } ];
Line Points:
var lineData = [ { "x": 10 , "y": 200 },
{ "x": 20 , "y": 200 },
{ "x": 30 , "y": 200 },
{ "x": 40 , "y": 200 },
{ "x": 50 , "y": 200 },
{ "x": 60 , "y": 200 },
{ "x": 70 , "y": 200 },
{ "x": 80 , "y": 200 },
{ "x": 90 , "y": 200 },
{ "x": 100 , "y": 200 },
{ "x": 110 , "y": 200 },
{ "x": 120 , "y": 200 },
{ "x": 130 , "y": 200 },
{ "x": 140 , "y": 200 },
{ "x": 150 , "y": 200 },
{ "x": 160 , "y": 200 },
{ "x": 170 , "y": 200 } ];
The first thing that I noticed when trying Duopixel's demo was that the circle seemed to shrink to fit on the line. Thus I decided to make the line the same length as the circle. Moreover to have a uniform distribution I wrote two functions to create the line and circle data arrays:
var numberOfPoints = 30;
var radius = 60
var margin = {top: 20,left: 20}
var lineLength = 2 * radius * Math.PI
var circleData = $.map(Array(numberOfPoints), function (d, i) {
var imag = margin.left + lineLength / 2 + radius * Math.sin(2 * i * Math.PI / (numberOfPoints - 1))
var real = margin.top + radius - radius * Math.cos(2 * i * Math.PI / (numberOfPoints - 1))
return {x: imag, y: real}
})
var lineData = $.map(Array(numberOfPoints), function (d, i) {
var y = margin.top + 2 * radius;
var x = margin.left + i * lineLength / (numberOfPoints - 1)
return { x: x, y: y}
}).reverse()
So, now, what effect can we apply? I will go with the easiest one: a transition mapping each point of the circle to its point on the line.
var circle = svgContainer.append("g")
.append("path")
.data([circleData])
.attr("d", lineFunction)
.attr("class", "circle")
.on("click", transitionToLine)
function transitionToLine() {
circle.data([lineData])
.transition()
.duration(1000)
.ease("linear")
.attr('d', lineFunction)
circle.on("click", transitionToCircle)
}
function transitionToCircle() {
circle.data([circleData])
.transition()
.duration(1000)
.ease("linear")
.attr('d', lineFunction)
circle.on("click", transitionToLine)
}
Here is the jsFiddle, you just have to click on the node to see the animation.
One important thing to notice is that the transition takes the same time for each point whereas in reality you would like the points in the end to arrive after the points near the middle. The trick you can use is to make the duration of the animation be proportional to the distance from the source point to the destination one but I don't see how to use it with lines as you pass the whole array so you cannot change the duration for a specific point.

NVD3.js coloring specific bars in graph

Is there a way to color specific bars? If a bar is less than the line, color it red.
Code: https://github.com/tvinci/webs/blob/gh-pages/lineplusbar.html
Example: http://tvinci.github.io/webs/lineplusbar.html
I'd like to do something like this but the value for i is not the y value that I send in. It has been modified:
d3.selectAll("rect.nv-bar")
.style("fill", function(d, i){
return i > 50 ? "red":"blue";
});
Data:
var overview_data=[
{
"key" : "Achieved",
"bar": true,
"values" : [ [ "1x" , 30] , [ "2x" , 70] , [ "3x" , 200] ]
},
{
"key" : "Required",
"values" : [ [ "1x" , 50] , [ "2x" , 100] , [ "3x" , 150] ]
}
].map(function(series) {
series.values = series.values.map(function(d) { return {x: d[0], y: d[1] } });
return series;
});
You can do:
d3.selectAll("rect.nv-bar")
.style("fill", function(d, i){
return d.y > 50 ? "red":"blue";
});
The upward answer is right but it won't work when you change the data dynamically in my-case.
You can use the following configuration to get different bar colors based on the value.
var data=[ {
"key": "data",
"values": [
{
"x": 1,
"y": 20
},
{
"x": 2,
"y": 15
},
{
"x": 3,
"y": 85
},
{
"x": 4,
"y": 66
},}];
nv.addGraph(function() {
var chart = nv.models.multiBarChart()
.barColor(getColorArrayForGraph())
.transitionDuration(350)
.reduceXTicks(true)
.rotateLabels(0)
.showControls(true)
.groupSpacing(0.1);
chart.xAxis
.tickFormat(d3.format(',f'));
chart.yAxis
.tickFormat(d3.format(',.1f'));
d3.select('#chart1 svg')
.datum(data)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
}
// GET color array based on the value
function getColorArrayForGraph() {
let colorArray: any = [];
for (let item of data) {
if (item.y > 50) {
colorArray.push('#FF0000');
} else {
colorArray.push('#004c00');
}
}
return colorArray;
};
So here, barcolor(["#FF0000","#00FFCC",....]) function takes arguments as array of colors.
This way you can get the array of colors and use it into barcolor().

Resources