Working in Node-Red. I can use regular global variables across nodes and flows no problem. However, would like to use a global array variable.
Method A - desired functionality
I read in 16 data points at a time (type = double) and want to have them be index 0-15, then the following node would update indexes 16-31; then 32-45 and 46-64 in the last two nodes.
Node Red however, won't let update the array from the second node starting from index #16. I get "TypeError: Cannot read property 'indexOf' of undefined" error.
In lieu of Method A, I could have four different 16-index global arrays. However, accessing them gives erratic results. Trying to access index[n] returns the value from some other index - i.e. global.get("variable"[0]) returns variable[10] and global.get("variable"[1]) returns the value from variable[27].
This describes the problem -
https://www.youtube.com/watch?v=cF1bz8bEozI
Here is my sample flow:
[{"id":"ee1694d.7df4768","type":"i2c in","z":"d556390c.391838","name":"Read Camera","address":"105","command":"128","count":"32","x":240,"y":1480,"wires":[["9e27949c.512c28"]]},{"id":"d9eaa7a4.7f0ed8","type":"inject","z":"d556390c.391838","name":"ON","topic":"1","payload":"1","payloadType":"str","repeat":"","crontab":"","once":false,"x":70,"y":1480,"wires":[["ee1694d.7df4768"]]},{"id":"6dc0727a.4cf53c","type":"i2c in","z":"d556390c.391838","name":"Read Camera","address":"105","command":"160","count":"32","x":240,"y":1520,"wires":[["a7ac4b94.44ce58"]]},{"id":"d6d80973.784148","type":"i2c in","z":"d556390c.391838","name":"Read Camera","address":"105","command":"192","count":"32","x":240,"y":1560,"wires":[["b90d910d.8e743","ebeeb439.54cf18"]]},{"id":"b90d910d.8e743","type":"i2c in","z":"d556390c.391838","name":"Read Camera","address":"105","command":"224","count":"32","x":240,"y":1600,"wires":[["2f7b8dde.7a9902"]]},{"id":"6b1509e2.8bd4d8","type":"debug","z":"d556390c.391838","name":"Row 3,4","active":true,"console":"false","complete":"payload","x":1020,"y":1520,"wires":[]},{"id":"a828b6d2.40da08","type":"delay","z":"d556390c.391838","name":"","pauseType":"delay","timeout":"50","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":98,"y":1656,"wires":[["d6d80973.784148"]]},{"id":"ad0a1424.eaae08","type":"function","z":"d556390c.391838","name":"Save Global variables for Temperature","func":"global.set(\"RangeTemperaturesA\", 0);\n\nfor(i=0; i<16; i++){\n global.set(\"RangeTemperaturesA\"[i], msg.payload[i]); \n}\n\nreturn msg;","outputs":1,"noerr":0,"x":750,"y":1480,"wires":[["6b810d97.0beee4","6dc0727a.4cf53c"]]},{"id":"d386a34f.525d2","type":"function","z":"d556390c.391838","name":"Save Global variables for Temperature","func":"for(i=0; i<16; i++){\n global.set(\"RangeTemperatureB\"[i], msg.payload[i]); \n}\n\nreturn msg;","outputs":1,"noerr":0,"x":750,"y":1520,"wires":[["6b1509e2.8bd4d8"]]},{"id":"11c935c.be330ca","type":"function","z":"d556390c.391838","name":"Save Global variables for Temperature","func":"for(i=0; i<16; i++){\n global.set(\"RangeTemperatureC\"[i], msg.payload[i]); \n}\n\nreturn msg;","outputs":1,"noerr":0,"x":750,"y":1560,"wires":[[]]},{"id":"294185a.d5fe67a","type":"function","z":"d556390c.391838","name":"Save Global variables for Temperature","func":"for(i=0; i<16; i++){\n global.set(\"RangeTemperatureD\"[i], msg.payload[i]); \n}\n\nreturn msg;","outputs":1,"noerr":0,"x":750,"y":1600,"wires":[[]]},{"id":"3ffa9e84.cba002","type":"function","z":"d556390c.391838","name":"Find Max Temperature","func":"//n = Math.max(... global.get(\"RangeTemperature\"));\n\nreturn {payload: global.get(\"RangeTemperature\")};","outputs":1,"noerr":0,"x":900,"y":1660,"wires":[[]]},{"id":"9e27949c.512c28","type":"function","z":"d556390c.391838","name":"Get Temps full row","func":"var gridEye = [];\nvar loop=0;\n\nfor(n=0; n<32; n+=2){\n gridEye[loop] = ((msg.payload[n+1]<<8) | msg.payload[n]) * 0.25;\n //convert to F\n gridEye[loop] = ((5.0/3.0) * gridEye[loop] + 32.0).toFixed(2);\n //add right bitshit to reduce noise\n loop++;\n}\nmsg.payload=gridEye;\n\nreturn msg;","outputs":1,"noerr":0,"x":490,"y":1480,"wires":[["ad0a1424.eaae08"]]},{"id":"a7ac4b94.44ce58","type":"function","z":"d556390c.391838","name":"Get Temps full row","func":"var gridEye = []; //16-byte array with temperature readings\nvar loop=0;\n\nfor(n=0; n<32; n+=2){\n // Get raw values - bitshift left 8 bits then bitwise OR.\n // then take new value and multiply by 0.25 since it reads in 1/4 degree C\n gridEye[loop] = ((msg.payload[n+1]<<8) | msg.payload[n]) * 0.25;\n //convert to F\n gridEye[loop] = ((5.0/3.0) * gridEye[loop] + 32.0).toFixed(2);\n loop++;\n}\n\nmsg.payload=gridEye;\n\nreturn msg;","outputs":1,"noerr":0,"x":490,"y":1520,"wires":[["d386a34f.525d2"]]},{"id":"ebeeb439.54cf18","type":"function","z":"d556390c.391838","name":"Get Temps full row","func":"var gridEye = [];\nvar loop=0;\n/*\nvar pixel = 4;\nvar tmp = ((msg.payload[pixel*2 + 1]<<8) | msg.payload[pixel*2])*0.25; \n//gridEye reads in .25 degree C\ntmp = ((5/3 * tmp) + 32.0); //convert to F\n*/\n\nfor(n=0; n<32; n+=2){\n gridEye[loop] = ((msg.payload[n+1]<<8) | msg.payload[n]) * 0.25;\n //convert to F\n gridEye[loop] = ((5.0/3.0) * gridEye[loop] + 32.0).toFixed(2);\n //add right bitshit to reduce noise\n loop++;\n}\n/*\nfor(n=0; n<8; n++){\n gridEye[n]= ((n/35536 * 60 ) + 20);\n //convert to F\n //gridEye[n] = (((5.0/3.0) * gridEye[n]) + 32).toFixed(2);\n}\n*/\nmsg.payload=gridEye;\n\nreturn msg;","outputs":1,"noerr":0,"x":490,"y":1560,"wires":[["11c935c.be330ca"]]},{"id":"2f7b8dde.7a9902","type":"function","z":"d556390c.391838","name":"Get Temps full row","func":"var gridEye = [];\nvar loop=0;\n/*\nvar pixel = 4;\nvar tmp = ((msg.payload[pixel*2 + 1]<<8) | msg.payload[pixel*2])*0.25; \n//gridEye reads in .25 degree C\ntmp = ((5/3 * tmp) + 32.0); //convert to F\n*/\n\nfor(n=0; n<32; n+=2){\n gridEye[loop] = ((msg.payload[n+1]<<8) | msg.payload[n]) * 0.25;\n //convert to F\n gridEye[loop] = ((5.0/3.0) * gridEye[loop] + 32.0).toFixed(2);\n //add right bitshit to reduce noise\n loop++;\n}\n/*\nfor(n=0; n<8; n++){\n gridEye[n]= ((n/35536 * 60 ) + 20);\n //convert to F\n //gridEye[n] = (((5.0/3.0) * gridEye[n]) + 32).toFixed(2);\n}\n*/\nmsg.payload=gridEye;\n\nreturn msg;","outputs":1,"noerr":0,"x":490,"y":1600,"wires":[["294185a.d5fe67a"]]},{"id":"a0de3101.0c307","type":"function","z":"d556390c.391838","name":"Read from A","func":"var p = global.get(\"RangeTemperaturesA\"[1]);\nmsg.payload = p;\nreturn msg;","outputs":1,"noerr":0,"x":510,"y":1780,"wires":[["dbb0935d.742f7"]]},{"id":"dbb0935d.742f7","type":"debug","z":"d556390c.391838","name":"test A","active":true,"console":"false","complete":"payload","x":670,"y":1860,"wires":[]},{"id":"f6c50374.59f","type":"function","z":"d556390c.391838","name":"Read from B","func":"var n = global.get(\"RangeTemperatureB\"[0]);\nreturn {payload: n};","outputs":1,"noerr":0,"x":770,"y":1780,"wires":[["3e8947bc.be49b8"]]},{"id":"6b810d97.0beee4","type":"debug","z":"d556390c.391838","name":"Row 1,2","active":true,"console":"false","complete":"payload","x":1020,"y":1480,"wires":[]},{"id":"3e8947bc.be49b8","type":"debug","z":"d556390c.391838","name":"test b","active":true,"console":"false","complete":"payload","x":910,"y":1860,"wires":[]},{"id":"23da3f7f.1c4f8","type":"inject","z":"d556390c.391838","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":340,"y":1780,"wires":[["a0de3101.0c307"]]},{"id":"2a146fa7.577fc","type":"inject","z":"d556390c.391838","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":580,"y":1720,"wires":[["f6c50374.59f"]]}]
Edit -
I did a quick test -
For global.set -
global.set("RangeTemperaturesA", i)[i]; gives "TypeError: Cannot read property '0' of undefined"
global.set("RangeTemperaturesA[i]", i); gives "Error: Invalid property expression: unexpected i at position 19"
global.set("RangeTemperaturesA"[i], i); appears to probably work.
Sample code:
for(i=0; i<16; i++){
global.set("RangeTemperaturesA"[i], i);
node.warn("Value: " + i);
}
return msg;
Global.get -
global.get("RangeTemperaturesA"[n]) gives erratic results.
global.get("RangeTemperaturesA[n]") gives "Error: Invalid property expression: unexpected n at position 19"
global.get("RangeTemperaturesA")[n] gives "Value: undefined; Count: 0" gives "Value: undefined; Count: 0" which is perhaps the most promising if the array was never populated correctly.
Sample code:
for(n=0; n<16; n++){
node.warn("Value: " + global.get("RangeTemperaturesA")[n] + "; Count: " + n);
}
return msg;
The problem is due to the way you're trying to address the individual array entries.
With the code: global.get("variable"[0]), you are asking it to use the 0th element of the string "variable" as the argument passed to the get function. In otherwords, it is equivalent to: global.get("v")
Similarly, global.get("variable"[2]) will be equivalent to global.get("r").
You should either move array index inside the quotes:
global.get("variable[0]");
or access the 0th element of the result of the get function:
global.get("variable")[0];
The same holds true for how you are trying to use the set function.
Updates to reflect edits to the quesiton
None of your attempts to use global.set() are correct:
global.set("RangeTemperaturesA", i)[i] - here you are setting the global property RangeTemperaturesA to the value of i. The function set doesn't return anything, so attempting to treat it as an array is just wrong.
global.set("RangeTemperaturesA[i]", i); - this is the closest of the three, however, you are setting the string literal RangeTemperaturesA[i] - JavaScript doesn't know you want the i in the middle of that string to be the value of your local variable i.
global.set("RangeTemperaturesA"[i], i); - no. This is the same error as you had in the original question. "RangeTemperaturesA"[i] will evalute to the ith character of the string RangeTemperaturesA.
To do it properly, you want to use "RangeTemperaturesA["+i+"]" as the key:
global.set("RangeTemperaturesA["+i+"]", i);
When i is 0, that will generate the key RangeTemperaturesA[0].
The same applies for global.get:
var myValue = global.get("RangeTemperaturesA["+i+"]");
All of these examples assume you have already set RangeTemperaturesA to be an array:
global.set("RangeTemperaturesA",[]);
I followed this link
Passing a data frame from-to R and C using .call()
to find the way to access an R data frame inside C.
My requirement is the opposite of that. I have a tabular data in C and need to create an R data frame object in C and return it on as SEXP.
For simple R vectors and lists creation, I followed something like in this link
http://adv-r.had.co.nz/C-interface.html
But I am still wondering how to create a dataframe and return from C to R. Considering a dataframe is a list, I tried creating a list and passing it on, but it expectedly gets me a list in R and not a dataframe.
Any help would be appreciated.
You may make use of the fact that a data.frame object is a list consisting of atomic vectors, each having the same length, with names, class, and row.names attributes properly set:
library(inline)
f <- cxxfunction(signature(), body='
SEXP ret, ans1, ans2, cls, nam, rownam;
PROTECT(ret = Rf_allocVector(VECSXP, 2)); // a list with two elements
PROTECT(ans1 = Rf_allocVector(INTSXP, 3)); // first column
PROTECT(ans2 = Rf_allocVector(INTSXP, 3)); // second column
for (int i=0; i<3; ++i) { // some data
INTEGER(ans1)[i] = i+1;
INTEGER(ans2)[i] = -(i+1);
}
SET_VECTOR_ELT(ret, 0, ans1);
SET_VECTOR_ELT(ret, 1, ans2);
PROTECT(cls = allocVector(STRSXP, 1)); // class attribute
SET_STRING_ELT(cls, 0, mkChar("data.frame"));
classgets(ret, cls);
PROTECT(nam = allocVector(STRSXP, 2)); // names attribute (column names)
SET_STRING_ELT(nam, 0, mkChar("a"));
SET_STRING_ELT(nam, 1, mkChar("b"));
namesgets(ret, nam);
PROTECT(rownam = allocVector(STRSXP, 3)); // row.names attribute
SET_STRING_ELT(rownam, 0, mkChar("1"));
SET_STRING_ELT(rownam, 1, mkChar("2"));
SET_STRING_ELT(rownam, 2, mkChar("3"));
setAttrib(ret, R_RowNamesSymbol, rownam);
UNPROTECT(6);
return ret;
')
Which yields:
print(f())
## a b
## 1 1 -1
## 2 2 -2
## 3 3 -3
Obligatory Rcpp example:
// [[Rcpp::export]]
DataFrame createTwo(){
IntegerVector v = IntegerVector::create(1,2,3);
std::vector<std::string> s(3);
s[0] = "a";
s[1] = "b";
s[2] = "c";
return DataFrame::create(Named("a")=v, Named("b")=s);
}
which will get you a 3x2 data.frame with one char vector and one int vector.
I am currently working on an algorithm, that requires my multidimensional array (currently 3-dimensional) to be sorted by value of sum of first and second item. Specifically:
(condition for item i and j to be swapped)
if ((Array[i][1]+Array[i][2]) > (Array[j][1]+Array[j][2])) return 1;
I have decided, for test purposes, to use select sort. However, my algorithm is required to do all its magic in under 5 seconds. Select sort needs for itself around 10 seconds to sort such array, if it has around 200 000 items.
I have decided to use a better algorithm, since I'm pretty confident in rest of my program. I am aware that unix systems contain a built-in quicksort function, qsort (available through man qsort in terminal). However, I do not know how to implement it.
My idea was to create another array, one dimensional (1D), with same length, containing indexes of items in main array. Through that, I might be able to sort only the secondary 1D array, where first item will have index of item in main array with smallest sum, second will have the second smallest, and so on.
However, how can I do it? Qsort function needs to be provided with comparing function, to decide whether to swap, or not. If I did make my own comparing function (like the one I stated in begin of my question), how can I address with something like (Array[SeconArray[i]][0]), when main Array is only specified in main of the function, and therefore cannot be accessed through another function in same file?
I'll be glad for any tips or tricks how to solve this. I'm also not keen on using qsort. If you think that another sorting algorithm can do better, please, let me know.
Thanks a lot
I've only a limited amount of time to post this, but hopefully the idea is clear. This is one way qsort can be setup to do what you're looking for. This is a 2D array Nx3 that I've populated with random values from [0.0,500). The idea can be extended to a 3D and beyond array.
The trick is to get the row width correct (or in the case of 3D the slab, 4D the cube, etc...)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
int cmp_row(const void* arg1, const void* arg2)
{
const double *ar1 = arg1;
const double *ar2 = arg2;
double diff = (ar1[1] + ar1[2] - ar2[1] - ar2[2]);
return (diff < 0.0) ? -1 : (0.0 < diff);
}
int main(int argc, char *argv[])
{
static const int N = 50;
double (*ar)[3] = NULL;
int i=0;
// see prng
srand((unsigned)time(NULL));
// allocat space for Nx3 2D array.
ar = calloc(N, sizeof(*ar));
// since I've no test data, random fill with values
// from [0..500)
for (i=0;i<N;++i)
{
ar[i][1] = ((double)rand()/(double)RAND_MAX)*500.0;
ar[i][2] = ((double)rand()/(double)RAND_MAX)*500.0;
}
// sort by elements 1+2
qsort(ar, N, sizeof(*ar), cmp_row);
for (i=0;i<N;++i)
{
printf("%3d : %7.3f + %7.3f = %7.3f\n",
i+1, ar[i][1], ar[i][2], ar[i][1]+ar[i][2]);
}
// release allocation
free(ar);
return 0;
}
Note: This gets a little more complex when dealing with what I called the syntax-only 2D+ arrays. Those would me the "arrays" that are actually vectors of pointers. int **ar etc. The premise is nearly the same, however, and only the comparator would have to change. If I've the time I'll add such a beast as an additional sample if input warrants it.
Final Note: This does not protect from potential overflow or underflow of the floating point values. a considerably more complex boolean-logic-only comparator can do that, but unless your data is uber-sensitive to such extremes, its hardly worth it for this example.
Output (obviously your's will vary)
I've included the summation of ar[i][1] + ar[i][2] as evidence of the sorting order doing what I believe you want. I hope this helps.
1 : 47.986 + 1.471 = 49.457
2 : 114.418 + 26.848 = 141.267
3 : 148.183 + 12.145 = 160.328
4 : 46.925 + 161.231 = 208.155
5 : 102.405 + 116.097 = 218.502
6 : 58.676 + 172.490 = 231.167
7 : 144.797 + 99.977 = 244.774
8 : 8.914 + 314.920 = 323.833
9 : 68.885 + 255.924 = 324.809
10 : 107.825 + 220.631 = 328.457
11 : 287.056 + 44.610 = 331.665
12 : 217.505 + 114.799 = 332.304
13 : 240.620 + 104.506 = 345.127
14 : 242.288 + 133.509 = 375.797
15 : 381.538 + 4.073 = 385.611
16 : 4.991 + 383.519 = 388.510
17 : 257.611 + 163.872 = 421.483
18 : 43.278 + 380.951 = 424.230
19 : 300.775 + 129.879 = 430.654
20 : 134.814 + 314.688 = 449.502
21 : 103.281 + 346.874 = 450.155
22 : 197.761 + 263.668 = 461.429
23 : 303.872 + 173.430 = 477.302
24 : 466.265 + 11.400 = 477.665
25 : 108.817 + 391.995 = 500.812
26 : 467.992 + 40.985 = 508.977
27 : 353.493 + 160.398 = 513.891
28 : 406.446 + 130.214 = 536.659
29 : 244.678 + 303.989 = 548.667
30 : 303.282 + 260.434 = 563.716
31 : 254.139 + 317.150 = 571.290
32 : 368.311 + 203.118 = 571.429
33 : 372.654 + 201.597 = 574.251
34 : 143.985 + 454.796 = 598.781
35 : 254.561 + 402.038 = 656.598
36 : 309.922 + 363.872 = 673.795
37 : 196.554 + 478.447 = 675.000
38 : 493.585 + 185.749 = 679.334
39 : 438.196 + 257.858 = 696.054
40 : 347.198 + 360.908 = 708.107
41 : 262.210 + 456.034 = 718.244
42 : 389.174 + 339.315 = 728.489
43 : 300.199 + 446.422 = 746.621
44 : 344.346 + 427.167 = 771.513
45 : 317.604 + 470.313 = 787.917
46 : 312.785 + 475.855 = 788.640
47 : 334.682 + 492.928 = 827.609
48 : 399.056 + 430.449 = 829.505
49 : 460.128 + 373.025 = 833.154
50 : 419.137 + 440.745 = 859.882