node-red-contrib-prib-functions 0.19.2 → 0.20.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/codeql-analysis.yml +3 -3
- package/.github/workflows/npmpublish.yml +6 -6
- package/.vs/VSWorkspaceState.json +7 -0
- package/.vs/node-red-contrib-prib-functions/v17/.wsuo +0 -0
- package/README.md +6 -2
- package/arima/index.js +18 -0
- package/dataAnalysis/arrayAllRowsSwap.js +15 -0
- package/dataAnalysis/arrayCompareToPrecision.js +34 -0
- package/dataAnalysis/arrayDifference.js +14 -0
- package/dataAnalysis/arrayDifferenceSeasonal.js +15 -0
- package/dataAnalysis/arrayDifferenceSeasonalSecondOrder.js +20 -0
- package/dataAnalysis/arrayDifferenceSecondOrder.js +14 -0
- package/dataAnalysis/arrayForEachRange.js +38 -0
- package/dataAnalysis/arrayOverlay.js +13 -0
- package/dataAnalysis/arrayProduct.js +11 -0
- package/dataAnalysis/arrayRandom.js +14 -0
- package/dataAnalysis/arrayReduceRange.js +11 -0
- package/dataAnalysis/arrayScale.js +11 -0
- package/dataAnalysis/arraySum.js +11 -0
- package/dataAnalysis/arraySumSquared.js +11 -0
- package/dataAnalysis/arraySwap.js +11 -0
- package/dataAnalysis/dataAnalysis.html +31 -14
- package/dataAnalysis/dataAnalysis.js +10 -1
- package/dataAnalysis/generateMatrixFunction.js +89 -0
- package/dataAnalysis/generateVectorFunction.js +25 -0
- package/dataAnalysis/pca.js +472 -325
- package/dataAnalysis/svd.js +239 -0
- package/echart/echart.html +68 -0
- package/echart/echart.js +85 -0
- package/echart/icons/chart-671.png +0 -0
- package/echart/lib/echarts.js +95886 -0
- package/lib/Chart.js +177 -0
- package/lib/Column.js +99 -0
- package/lib/GraphDB.js +14 -0
- package/lib/Table.js +185 -0
- package/lib/objectExtensions.js +361 -0
- package/matrix/matrix.js +2 -48
- package/monitor/BarGauge.js +8 -0
- package/monitor/Dataset.js +29 -0
- package/monitor/DialGauge.js +109 -0
- package/monitor/DialNeedle.js +36 -0
- package/monitor/Format.js +74 -0
- package/monitor/centerElement.js +14 -0
- package/monitor/compareElements.js +95 -0
- package/monitor/defs.js +23 -0
- package/monitor/extensions.js +906 -0
- package/monitor/functions.js +36 -0
- package/monitor/json2xml.js +103 -0
- package/monitor/monitorSystem.html +198 -0
- package/monitor/monitorSystem.js +322 -0
- package/monitor/svgHTML.js +179 -0
- package/monitor/svgObjects.js +64 -0
- package/package.json +18 -6
- package/test/00-objectExtensions.js +94 -0
- package/test/04-tables.js +33 -0
- package/test/data/.config.nodes.json +608 -0
- package/test/data/.config.nodes.json.backup +608 -0
- package/test/data/.config.runtime.json +4 -0
- package/test/data/.config.runtime.json.backup +3 -0
- package/test/data/.config.users.json +21 -0
- package/test/data/.config.users.json.backup +21 -0
- package/test/data/.flow.json.backup +2448 -2207
- package/test/data/float32vector10.npy +0 -0
- package/test/data/flow.json +2412 -2191
- package/test/data/int2matrix2x3.npy +0 -0
- package/test/data/package-lock.json +158 -0
- package/test/data/package.json +11 -0
- package/test/dataAnalysisExtensions.js +472 -0
- package/test/dataAnalysisPCA.js +54 -0
- package/test/dataAnalysisSVD.js +31 -0
- package/test/euclideanDistance.js +2 -2
- package/test/transformNumPy.js +132 -0
- package/transform/NumPy.js +303 -0
- package/transform/transform.html +12 -0
- package/transform/transform.js +34 -2
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const assert=require('assert');
|
|
2
|
+
const should=require("should");
|
|
3
|
+
const svd=require("../dataAnalysis/svd.js");
|
|
4
|
+
require("../dataAnalysis/arrayCompareToPrecision.js");
|
|
5
|
+
|
|
6
|
+
const expected={
|
|
7
|
+
orthonormalizedColumns: [
|
|
8
|
+
[ -0.3913289321676804, 0.3487948014410755, 0.10448989927485411 ],
|
|
9
|
+
[ -0.4639397307294648, -0.34948424610207607, 0.7962721013609022 ],
|
|
10
|
+
[ -0.6197155556759748, -0.5188940242462491, -0.5888137410546312 ],
|
|
11
|
+
[ -0.4975683100363125, 0.6978194177691509, -0.0912742016622088 ]
|
|
12
|
+
],
|
|
13
|
+
singularValues: [ 223.85445199527607, 13.695880509722402, 17.366841363876695 ],
|
|
14
|
+
orthogonalMatrix: [
|
|
15
|
+
[ -0.5061583666044986, -0.7405855572048029, -0.44196916224541927 ],
|
|
16
|
+
[ -0.5596333427471312, -0.10788454127728717, 0.8216881692217926 ],
|
|
17
|
+
[ -0.6562120309792694, 0.6632450212500965, -0.35984970777398323 ]
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('SVD', function(){
|
|
22
|
+
it("getEigenVectors", function(done){
|
|
23
|
+
const data = [[40,50,60],[50,70,60],[80,70,90],[50,60,80]];
|
|
24
|
+
const vectors = svd(data);
|
|
25
|
+
console.log(vectors)
|
|
26
|
+
vectors.orthonormalizedColumns.compareToPrecision(expected.orthonormalizedColumns)
|
|
27
|
+
vectors.singularValues.compareToPrecision(expected.singularValues)
|
|
28
|
+
vectors.orthogonalMatrix.compareToPrecision(expected.orthogonalMatrix)
|
|
29
|
+
done();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
onst assert=require('assert');
|
|
2
2
|
const should=require("should");
|
|
3
3
|
const ed=require("../dataAnalysis/euclideanDistance.js");
|
|
4
4
|
require("../dataAnalysis/forNestedEach");
|
|
5
|
-
|
|
5
|
+
c
|
|
6
6
|
describe('euclideanDistance', function() {
|
|
7
7
|
it("array forNestedEach", function(done) {
|
|
8
8
|
const atest=[1,2,3,4];
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
const assert=require('assert')
|
|
2
|
+
const helper = require("node-red-node-test-helper")
|
|
3
|
+
const NumPy = require("../transform/NumPy.js")
|
|
4
|
+
const transformNode = require("../transform/transform.js");
|
|
5
|
+
const fs = require('fs')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
const filePath = path.join(__dirname, "data/float32vector10.npy")
|
|
8
|
+
const npyFloat32V10 = fs.readFileSync(filePath)
|
|
9
|
+
const npyInt2matrix2x3 = fs.readFileSync(path.join(__dirname, "data/int2matrix2x3.npy"))
|
|
10
|
+
const int2matrix2x3={"dataType":"int64","fortran_order":false,"shape":[1,2,3],"version":1.0,"dataVector":new BigInt64Array([1n,2n,3n,4n,5n,6n])}
|
|
11
|
+
if(!Buffer.prototype.toBufferArray)
|
|
12
|
+
Buffer.prototype.toBufferArray = function() {return this.buffer.slice(this.byteOffset, this.byteOffset + this.byteLength)}
|
|
13
|
+
|
|
14
|
+
const float32V10 = new Float32Array([
|
|
15
|
+
86, 46, 10, 148, 133,
|
|
16
|
+
86, 103, 118, 62, 49
|
|
17
|
+
])
|
|
18
|
+
const NumPyJSON={
|
|
19
|
+
"dataType":"float32",
|
|
20
|
+
"fortran_order":false,
|
|
21
|
+
version:1.0,
|
|
22
|
+
"shape":[10],
|
|
23
|
+
"dataVector":{"0":86,"1":46,"2":10,"3":148,"4":133,"5":86,"6":103,"7":118,"8":62,"9":49}
|
|
24
|
+
}
|
|
25
|
+
helper.init(require.resolve('node-red'));
|
|
26
|
+
|
|
27
|
+
const npy2JSONNode={
|
|
28
|
+
id : "npy2JSON",
|
|
29
|
+
type : "transform",
|
|
30
|
+
name : "npy to JSON",
|
|
31
|
+
actionSource: "npy",
|
|
32
|
+
actionTarget: "JSON",
|
|
33
|
+
sourceProperty:"msg.payload",
|
|
34
|
+
targetProperty:"msg.payload",
|
|
35
|
+
topicProperty:"'test topic'"
|
|
36
|
+
};
|
|
37
|
+
const JSON2npyNode={
|
|
38
|
+
id : "npy2JSON",
|
|
39
|
+
type : "transform",
|
|
40
|
+
name : "JSON to npy",
|
|
41
|
+
actionSource: "JSON",
|
|
42
|
+
actionTarget: "npy",
|
|
43
|
+
sourceProperty:"msg.payload",
|
|
44
|
+
targetProperty:"msg.payload",
|
|
45
|
+
topicProperty:"'test topic'"
|
|
46
|
+
};
|
|
47
|
+
function getAndTestNodeProperties(o) {
|
|
48
|
+
const n = helper.getNode(o.id);
|
|
49
|
+
for(let p in o) n.should.have.property(p, o[p]);
|
|
50
|
+
return n;
|
|
51
|
+
}
|
|
52
|
+
function testFlow(done,node,data,result) {
|
|
53
|
+
const flow = [
|
|
54
|
+
Object.assign(node,{wires : [ [ "outHelper" ],["errorHelper"] ]}),
|
|
55
|
+
{id :"outHelper", type : "helper"},
|
|
56
|
+
{id :"errorHelper", type : "helper"}
|
|
57
|
+
];
|
|
58
|
+
helper.load(transformNode, flow, function() {
|
|
59
|
+
const n=getAndTestNodeProperties(node);
|
|
60
|
+
const outHelper = helper.getNode("outHelper");
|
|
61
|
+
const errorHelper = helper.getNode("errorHelper");
|
|
62
|
+
outHelper.on("input", function(msg) {
|
|
63
|
+
console.log("outHelper "+JSON.stringify(msg.payload));
|
|
64
|
+
if(JSON.stringify(msg.payload)==JSON.stringify(result)) {
|
|
65
|
+
done();
|
|
66
|
+
} else {
|
|
67
|
+
console.log("mismatch expected: "+JSON.stringify(result) +" returned: "+JSON.stringify(msg.payload));
|
|
68
|
+
done("mismatch");
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
errorHelper.on("input", function(msg) {
|
|
72
|
+
console.log("errorHelper "+JSON.stringify(msg));
|
|
73
|
+
done("error check log output");
|
|
74
|
+
});
|
|
75
|
+
n.receive({
|
|
76
|
+
topic:"test",
|
|
77
|
+
payload : data
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
describe('NumPy', function() {
|
|
83
|
+
it("parse npyFloat10",(done)=>{
|
|
84
|
+
const tensor=new NumPy(npyFloat32V10)
|
|
85
|
+
assert.deepStrictEqual(tensor.dataType,'float32')
|
|
86
|
+
assert.deepStrictEqual(tensor.shape,[10])
|
|
87
|
+
assert.deepStrictEqual(tensor.fortran_order,false)
|
|
88
|
+
assert.deepStrictEqual(tensor.dataVector,float32V10)
|
|
89
|
+
done()
|
|
90
|
+
});
|
|
91
|
+
it("parse npyInt2matrix2x3",(done)=>{
|
|
92
|
+
const tensor=new NumPy(npyInt2matrix2x3)
|
|
93
|
+
assert.deepStrictEqual(tensor.toSerializable(),int2matrix2x3)
|
|
94
|
+
done()
|
|
95
|
+
});
|
|
96
|
+
it("parse int2matrix2x3",(done)=>{
|
|
97
|
+
const tensor=new NumPy(int2matrix2x3)
|
|
98
|
+
assert.deepStrictEqual(tensor.toSerializable(),int2matrix2x3)
|
|
99
|
+
console.log({label:"toString",result:tensor.toString()})
|
|
100
|
+
const npy=tensor.toNpy()
|
|
101
|
+
done();
|
|
102
|
+
});
|
|
103
|
+
/*
|
|
104
|
+
it("toNpy float32V10",(done)=>{
|
|
105
|
+
const tensor=new NumPy(NumPyJSON)
|
|
106
|
+
assert.deepStrictEqual(tensor.toNpy(),npyFloat32V10.toBufferArray())
|
|
107
|
+
done();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("toNpy int2matrix2x3",(done)=>{
|
|
111
|
+
const tensor=new NumPy(int2matrix2x3)
|
|
112
|
+
assert.deepStrictEqual(tensor.toNpy(),npyInt2matrix2x3.toBufferArray())
|
|
113
|
+
done();
|
|
114
|
+
});
|
|
115
|
+
*/
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('transform numpy', function() {
|
|
119
|
+
beforeEach(function(done) {
|
|
120
|
+
helper.startServer(done);
|
|
121
|
+
});
|
|
122
|
+
afterEach(function(done) {
|
|
123
|
+
helper.unload();
|
|
124
|
+
helper.stopServer(done);
|
|
125
|
+
});
|
|
126
|
+
it('npy to Array', function(done) {
|
|
127
|
+
testFlow(done,JSON2npyNode,int2matrix2x3,npyInt2matrix2x3);
|
|
128
|
+
});
|
|
129
|
+
it('npy to JSON', function(done) {
|
|
130
|
+
testFlow(done,npy2JSONNode,npyInt2matrix2x3,int2matrix2x3);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
if(!BigInt.prototype.toJSON)
|
|
2
|
+
BigInt.prototype.toJSON = function(){return Number(this.toString())}
|
|
3
|
+
if(!Buffer.prototype.toBufferArray)
|
|
4
|
+
Buffer.prototype.toBufferArray = function() {return this.buffer.slice(this.byteOffset, this.byteOffset + this.byteLength)}
|
|
5
|
+
//const bufferToArrayBuffer = (b) => b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
|
|
6
|
+
const MAGIC_STRING = "\x93NUMPY"
|
|
7
|
+
const defaultVersionStr = "\x01\x00" // version 1.0
|
|
8
|
+
const defaultVersion = 1.0
|
|
9
|
+
const getTotalCells = (shape)=> shape.reduce((a, b) => a * b, 1);
|
|
10
|
+
const dataTypes = {
|
|
11
|
+
"|b1": {
|
|
12
|
+
bytes4DataType: 1,
|
|
13
|
+
name: "bool",
|
|
14
|
+
cellConstructor: Uint8Array,
|
|
15
|
+
setDataView: "setUint8"
|
|
16
|
+
},
|
|
17
|
+
"<u1": {
|
|
18
|
+
name: "uint8",
|
|
19
|
+
bytes4DataType: 8,
|
|
20
|
+
cellConstructor: Uint8Array,
|
|
21
|
+
setDataView: "setUint8"
|
|
22
|
+
},
|
|
23
|
+
"|u1": {
|
|
24
|
+
name: "uint8",
|
|
25
|
+
bytes4DataType: 8,
|
|
26
|
+
cellConstructor: Uint8Array,
|
|
27
|
+
setDataView: "setUint8"
|
|
28
|
+
},
|
|
29
|
+
"|i1": {
|
|
30
|
+
name: "int8",
|
|
31
|
+
bytes4DataType: 8,
|
|
32
|
+
cellConstructor: Int8Array,
|
|
33
|
+
setDataView: "setInt8"
|
|
34
|
+
},
|
|
35
|
+
"<u2": {
|
|
36
|
+
name: "uint16",
|
|
37
|
+
bytes4DataType: 16,
|
|
38
|
+
cellConstructor: Uint16Array,
|
|
39
|
+
setDataView: "setUint16"
|
|
40
|
+
},
|
|
41
|
+
"<i1": {
|
|
42
|
+
name: "int8",
|
|
43
|
+
bytes4DataType: 8,
|
|
44
|
+
cellConstructor: Int8Array,
|
|
45
|
+
setDataView: "setInt16"
|
|
46
|
+
},
|
|
47
|
+
"<i2": {
|
|
48
|
+
name: "int16",
|
|
49
|
+
bytes4DataType: 16,
|
|
50
|
+
cellConstructor: Int16Array,
|
|
51
|
+
setDataView: "setInt16"
|
|
52
|
+
},
|
|
53
|
+
"<u4": {
|
|
54
|
+
name: "uint32",
|
|
55
|
+
bytes4DataType: 32,
|
|
56
|
+
cellConstructor: Int32Array,
|
|
57
|
+
setDataView: "setInt32"
|
|
58
|
+
},
|
|
59
|
+
"<i4": {
|
|
60
|
+
name: "int32",
|
|
61
|
+
bytes4DataType: 32,
|
|
62
|
+
cellConstructor: Int32Array,
|
|
63
|
+
setDataView: "setInt32"
|
|
64
|
+
},
|
|
65
|
+
"<u8": {
|
|
66
|
+
name: "uint64",
|
|
67
|
+
bytes4DataType: 64,
|
|
68
|
+
cellConstructor: BigUint64Array,
|
|
69
|
+
setDataView: "setBigUint64"
|
|
70
|
+
},
|
|
71
|
+
"<i8": {
|
|
72
|
+
name: "int64",
|
|
73
|
+
bytes4DataType: 64,
|
|
74
|
+
cellConstructor: BigInt64Array,
|
|
75
|
+
setDataView: "setBigInt64"
|
|
76
|
+
},
|
|
77
|
+
"<f4": {
|
|
78
|
+
name: "float32",
|
|
79
|
+
bytes4DataType: 32,
|
|
80
|
+
cellConstructor: Float32Array,
|
|
81
|
+
setDataView: "setFloat32"
|
|
82
|
+
},
|
|
83
|
+
"<f8": {
|
|
84
|
+
name: "float64",
|
|
85
|
+
bytes4DataType: 64,
|
|
86
|
+
cellConstructor: Float64Array,
|
|
87
|
+
setDataView: "setFloat64"
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
‘S’ - swap dtype from current to opposite endian
|
|
93
|
+
{‘<’, ‘little’} - little endian
|
|
94
|
+
{‘>’, ‘big’} - big endian
|
|
95
|
+
{‘=’, ‘native’} - native order
|
|
96
|
+
{‘|’, ‘I’} - ignore (no change to byte order)
|
|
97
|
+
|
|
98
|
+
b boolean
|
|
99
|
+
i signed integer
|
|
100
|
+
u unsigned integer f floating-point
|
|
101
|
+
c complex floating-point
|
|
102
|
+
m timedelta
|
|
103
|
+
M datetime
|
|
104
|
+
O object
|
|
105
|
+
S (byte-)string
|
|
106
|
+
U Unicode
|
|
107
|
+
V void
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
* Float data
|
|
111
|
+
typestr == '>f4'
|
|
112
|
+
descr == [('','>f4')]
|
|
113
|
+
|
|
114
|
+
* Complex double
|
|
115
|
+
typestr == '>c8'
|
|
116
|
+
descr == [('real','>f4'), ('imag','>f4')]
|
|
117
|
+
|
|
118
|
+
* RGB Pixel data
|
|
119
|
+
typestr == '|V3'
|
|
120
|
+
descr == [('r','|u1'), ('g','|u1'), ('b','|u1')]
|
|
121
|
+
|
|
122
|
+
* Mixed endian (weird but could happen).
|
|
123
|
+
typestr == '|V8' (or '>u8')
|
|
124
|
+
descr == [('big','>i4'), ('little','<i4')]
|
|
125
|
+
|
|
126
|
+
* Nested structure
|
|
127
|
+
struct {
|
|
128
|
+
int ival;
|
|
129
|
+
struct {
|
|
130
|
+
unsigned short sval;
|
|
131
|
+
unsigned char bval;
|
|
132
|
+
unsigned char cval;
|
|
133
|
+
} sub;
|
|
134
|
+
}
|
|
135
|
+
typestr == '|V8' (or '<u8' if you want)
|
|
136
|
+
descr == [('ival','<i4'), ('sub', [('sval','<u2'), ('bval','|u1'), ('cval','|u1') ]) ]
|
|
137
|
+
|
|
138
|
+
* Nested array
|
|
139
|
+
struct {
|
|
140
|
+
int ival;
|
|
141
|
+
double data[16*4];
|
|
142
|
+
}
|
|
143
|
+
typestr == '|V516'
|
|
144
|
+
descr == [('ival','>i4'), ('data','>f8',(16,4))]
|
|
145
|
+
|
|
146
|
+
* Padded structure
|
|
147
|
+
struct {
|
|
148
|
+
int ival;
|
|
149
|
+
double dval;
|
|
150
|
+
}
|
|
151
|
+
typestr == '|V16'
|
|
152
|
+
descr == [('ival','>i4'),('','|V4'),('dval','>f8')]
|
|
153
|
+
*/
|
|
154
|
+
const dataTypeToNumpyDescr = new Map([
|
|
155
|
+
["float32", "<f4"],
|
|
156
|
+
["float64", "<f8"],
|
|
157
|
+
["int8", "<i1"],
|
|
158
|
+
["int16", "<i2"],
|
|
159
|
+
["int32", "<i4"],
|
|
160
|
+
["int64", "<i8"],
|
|
161
|
+
["uint8", "<u1"],
|
|
162
|
+
["uint16", "<u2"],
|
|
163
|
+
["uint32", "<u4"],
|
|
164
|
+
["uint64", "<u8"],
|
|
165
|
+
["bool", "|b1"],
|
|
166
|
+
]);
|
|
167
|
+
/*
|
|
168
|
+
function array2Npy (tensor) {
|
|
169
|
+
const versionStr = "\x01\x00"; // version 1.0
|
|
170
|
+
const shapeStr = tensor.shape.join(",") + ","
|
|
171
|
+
const descr = dataTypeToNumpyDescr.get(tensor.dtype);
|
|
172
|
+
const header = "{'descr':"+descr+", 'fortran_order': false, 'shape': ("+shapeStr+"), }"
|
|
173
|
+
const unpaddedLength = MAGIC_STRING.length + versionStr.length + 2 + header.length;
|
|
174
|
+
// Spaces to 16-bit align.
|
|
175
|
+
const padding = " ".repeat((16 - (unpaddedLength % 16)) % 16)
|
|
176
|
+
header += padding;
|
|
177
|
+
// Number of bytes is in the Numpy descr
|
|
178
|
+
const bytesPerElement = Number.parseInt(descr[2], 10)
|
|
179
|
+
const dataLen = bytesPerElement * getTotalCells(tensor.shape)
|
|
180
|
+
const totalSize = unpaddedLength + padding.length + dataLen
|
|
181
|
+
const arrayBuffer = new ArrayBuffer(totalSize)
|
|
182
|
+
const view = new DataView(arrayBuffer)
|
|
183
|
+
let pos = writeStrToDataView(view, MAGIC_STRING + versionStr, 0)
|
|
184
|
+
view.setUint16(pos, header.length, true);
|
|
185
|
+
pos += 2 + writeStrToDataView(view, header, pos);
|
|
186
|
+
const setDataView = view.setDataView[dataTypes[descr].setDataView];
|
|
187
|
+
for (let i = 0; i < data.length; i++) {
|
|
188
|
+
view.setDataView(pos,data[i],true)
|
|
189
|
+
pos += bytesPerElement;
|
|
190
|
+
}
|
|
191
|
+
return arrayBuffer;
|
|
192
|
+
}
|
|
193
|
+
*/
|
|
194
|
+
function writeStrToDataView(dataView, str, pos) {
|
|
195
|
+
const sl=str.length
|
|
196
|
+
for (let i = 0; i < sl; i++) {
|
|
197
|
+
dataView.setInt8(pos + i, str.charCodeAt(i));
|
|
198
|
+
}
|
|
199
|
+
return pos + sl;
|
|
200
|
+
}
|
|
201
|
+
function NumPy (tensor){
|
|
202
|
+
this.fortran_order=false
|
|
203
|
+
this.version=defaultVersion
|
|
204
|
+
return this.parse(tensor)
|
|
205
|
+
}
|
|
206
|
+
NumPy.prototype.getBytes4DataType = function(){
|
|
207
|
+
const descr = dataTypeToNumpyDescr.get(this.dtype);
|
|
208
|
+
return Number.parseInt(descr[2], 10)
|
|
209
|
+
}
|
|
210
|
+
NumPy.prototype.getDesc = function() {
|
|
211
|
+
return dataTypeToNumpyDescr.get(this.dtype);
|
|
212
|
+
}
|
|
213
|
+
NumPy.prototype.setDType = function(dataType) {
|
|
214
|
+
this.dataType=dataType
|
|
215
|
+
const descr=dataTypeToNumpyDescr.get(this.dataType);
|
|
216
|
+
if(descr==null) throw Error("data type not found for "+dataType)
|
|
217
|
+
this.setDescr(descr)
|
|
218
|
+
return this
|
|
219
|
+
}
|
|
220
|
+
NumPy.prototype.setDescr = function(descr) {
|
|
221
|
+
this.descr=descr
|
|
222
|
+
const dataTypeDetails = dataTypes[this.descr];
|
|
223
|
+
this.dataType=dataTypeDetails.name
|
|
224
|
+
this.setDataView = dataTypeDetails.setDataView;
|
|
225
|
+
this.cellConstructor = dataTypeDetails.cellConstructor
|
|
226
|
+
this.bytes4DataType = Number.parseInt(this.descr[2], 10)
|
|
227
|
+
return this
|
|
228
|
+
}
|
|
229
|
+
NumPy.prototype.toNpy = function () {
|
|
230
|
+
const versionStr = "\x01\x00"; // version 1.0
|
|
231
|
+
const shapeStr = this.shape.join(",") + ","
|
|
232
|
+
let header = "{'descr':"+this.descr+", 'fortran_order': false, 'shape': ("+shapeStr+"), }"
|
|
233
|
+
const unpaddedLength = MAGIC_STRING.length + versionStr.length + 2 + header.length;
|
|
234
|
+
// 16-bit align with spaces
|
|
235
|
+
header += " ".repeat((16 - (unpaddedLength % 16)) % 16);
|
|
236
|
+
// SIze of Npy
|
|
237
|
+
const totalSize = header.length + this.bytes4DataType * getTotalCells(this.shape)
|
|
238
|
+
// const totalSize = unpaddedLength + padding.length + this.bytes4DataType * getTotalCells(this.shape)
|
|
239
|
+
const arrayBuffer = new ArrayBuffer(totalSize)
|
|
240
|
+
const view = new DataView(arrayBuffer)
|
|
241
|
+
let pos = writeStrToDataView(view, MAGIC_STRING + defaultVersionStr, 0)
|
|
242
|
+
view.setUint16(pos, header.length, true);
|
|
243
|
+
pos += 2 + writeStrToDataView(view, header, pos);
|
|
244
|
+
const data=this.dataVector
|
|
245
|
+
// const setDataView = view[this.setDataView];
|
|
246
|
+
for (let i = 0; i < data.length; i++) {
|
|
247
|
+
// setDataView(pos,data[i],true)
|
|
248
|
+
view[pos]=data[i]
|
|
249
|
+
pos += this.bytes4DataType;
|
|
250
|
+
}
|
|
251
|
+
return arrayBuffer;
|
|
252
|
+
}
|
|
253
|
+
NumPy.prototype.toNpyBuffer = function () {
|
|
254
|
+
return Buffer.from(this.toNpy())
|
|
255
|
+
}
|
|
256
|
+
NumPy.prototype.parse = function(contentIn={dataType:"int8",
|
|
257
|
+
shape:[1],
|
|
258
|
+
dataVector:new int8Array(0)}) {
|
|
259
|
+
if(!contentIn instanceof Object) throw Error("not an object")
|
|
260
|
+
if(contentIn.hasOwnProperty("dataVector")) {
|
|
261
|
+
Object.assign(this,contentIn)
|
|
262
|
+
if(contentIn.dataType) this.setDType(contentIn.dataType)
|
|
263
|
+
else if(contentIn.descr) this.setDescr(contentIn.descr)
|
|
264
|
+
else throw Error("data type not defined")
|
|
265
|
+
if(this.shape==null) this.shape=[1];
|
|
266
|
+
return this
|
|
267
|
+
}
|
|
268
|
+
return this.parseNpy(contentIn)
|
|
269
|
+
}
|
|
270
|
+
NumPy.prototype.parseNpy = function(npyContentIn) {
|
|
271
|
+
// this.npyContent=npyContentIn instanceof ArrayBuffer ? npyContentIn : bufferToArrayBuffer(npyContentIn)
|
|
272
|
+
this.npyContent=npyContentIn instanceof ArrayBuffer ? npyContentIn : npyContentIn.toBufferArray()
|
|
273
|
+
const headerLength = new DataView(this.npyContent.slice(8, 10)).getUint8(0)
|
|
274
|
+
const offsetBytes = 10 + headerLength;
|
|
275
|
+
const headerContents = new TextDecoder("utf-8").decode(
|
|
276
|
+
new Uint8Array(this.npyContent.slice(10, 10 + headerLength))
|
|
277
|
+
);
|
|
278
|
+
const header = JSON.parse(
|
|
279
|
+
headerContents
|
|
280
|
+
.toLowerCase() // True -> true
|
|
281
|
+
.replace(/'/g, '"')
|
|
282
|
+
.replace("(", "[")
|
|
283
|
+
.replace(/,*\),*/g, "]")
|
|
284
|
+
);
|
|
285
|
+
this.setDescr(header.descr);
|
|
286
|
+
this.shape = header.shape
|
|
287
|
+
this.dataVector=new this.cellConstructor(this.npyContent, offsetBytes)
|
|
288
|
+
if(header.fortran_order) this.fortran_order=header.fortran_order
|
|
289
|
+
return this
|
|
290
|
+
}
|
|
291
|
+
NumPy.prototype.toSerializable = function() {
|
|
292
|
+
return {
|
|
293
|
+
dataType:this.dataType,
|
|
294
|
+
fortran_order: this.fortran_order,
|
|
295
|
+
shape:this.shape,
|
|
296
|
+
version:this.version,
|
|
297
|
+
dataVector:this.dataVector
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
NumPy.prototype.toString = function() {
|
|
301
|
+
return JSON.stringify(this.toSerializable())
|
|
302
|
+
}
|
|
303
|
+
module.exports=NumPy;
|
package/transform/transform.html
CHANGED
|
@@ -57,6 +57,8 @@
|
|
|
57
57
|
{ value: "CSV", label: "CSV"},
|
|
58
58
|
{ value: "ISO8385", label: "ISO 8583"},
|
|
59
59
|
{ value: "JSON", label: "JSON"},
|
|
60
|
+
{ value: "npy", label: "npy"},
|
|
61
|
+
{ value: "NumPyObject", label: "NumPy Obect"},
|
|
60
62
|
{ value: "String", label: "String"},
|
|
61
63
|
{ value: "snappy", label: "Snappy"},
|
|
62
64
|
{ value: "path", label: "Path"},
|
|
@@ -137,11 +139,21 @@
|
|
|
137
139
|
options["HTML"]="HTML";
|
|
138
140
|
options["JSON"]="JSON";
|
|
139
141
|
options["Messages"]="Messages";
|
|
142
|
+
options["npy"]="npy";
|
|
143
|
+
options["NumPyObject"]="NumPy Object";
|
|
140
144
|
options["String"]="String";
|
|
141
145
|
options["XLSX"]="XLSX";
|
|
142
146
|
options["XLSXObject"]="XLSX Object";
|
|
143
147
|
options["XML"]="XML";
|
|
144
148
|
break;
|
|
149
|
+
case 'npy':
|
|
150
|
+
options["JSON"]="JSON";
|
|
151
|
+
options["NumPyObject"]="NumPy Object";
|
|
152
|
+
break;
|
|
153
|
+
case 'NumPyObject':
|
|
154
|
+
options["JSON"]="JSON";
|
|
155
|
+
break;
|
|
156
|
+
|
|
145
157
|
case 'path':
|
|
146
158
|
options["Basename"]="basename";
|
|
147
159
|
options["Dirname"]="dirname";
|
package/transform/transform.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const logger = new (require("node-red-contrib-logger"))("transform");
|
|
2
2
|
logger.sendInfo("Copyright 2020 Jaroslav Peter Prib");
|
|
3
3
|
const CompressionTool = require('compressiontool');
|
|
4
|
-
|
|
4
|
+
const NumPy = require("./NumPy.js")
|
|
5
5
|
const regexCSV=/,(?=(?:(?:[^"]*"){2})*[^"]*$)/,
|
|
6
6
|
Buffer=require('buffer').Buffer,
|
|
7
7
|
os=require('os'),
|
|
@@ -332,6 +332,7 @@ const functions={
|
|
|
332
332
|
}
|
|
333
333
|
return data;
|
|
334
334
|
},
|
|
335
|
+
JSONToAVRO: (RED,node,msg,data)=>node.avroTransformer.toBuffer(data), // Encoded buffer.
|
|
335
336
|
JSONToCompressed: (RED,node,msg,data)=>compressor.compress(JSON.stringify(data),
|
|
336
337
|
(compressed)=>{
|
|
337
338
|
node.setData(RED,node,msg,compressed);
|
|
@@ -343,7 +344,6 @@ const functions={
|
|
|
343
344
|
),
|
|
344
345
|
JSONToConfluence:JSONToConfluence,
|
|
345
346
|
JSONToCSV: (RED,node,msg,data)=>Array2csv(node,data),
|
|
346
|
-
JSONToAVRO: (RED,node,msg,data)=>node.avroTransformer.toBuffer(data), // Encoded buffer.
|
|
347
347
|
JSONToHTML: (RED,node,msg,data,level=0)=>{
|
|
348
348
|
if(Array.isArray(data)) {
|
|
349
349
|
return data.length?"<table><tr>"+data.map((r)=>functions.JSONToHTML(RED,node,msg,r,++level)).join("</tr><tr>")+"</tr><table>":"";
|
|
@@ -375,10 +375,15 @@ const functions={
|
|
|
375
375
|
node.send(newMsg);
|
|
376
376
|
}
|
|
377
377
|
},
|
|
378
|
+
JSONTonpy: (RED,node,msg,data)=>new NumPy(data).toNpyBuffer(),
|
|
379
|
+
JSONToNumPyObject: (RED,node,msg,data)=>new NumPy(data),
|
|
378
380
|
JSONToString: (RED,node,msg,data)=>JSON.stringify(data),
|
|
379
381
|
JSONToXLSX:JSONToXLSX,
|
|
380
382
|
JSONToXLSXObject:JSONToXLSXObject,
|
|
381
383
|
JSONToXML: (RED,node,msg,data)=>json2xmlParser.parse(data),
|
|
384
|
+
npyToJSON: (RED,node,msg,data)=>new NumPy(data).toSerializable(),
|
|
385
|
+
npyToNumPyObject: (RED,node,msg,data)=>new NumPy(data),
|
|
386
|
+
NumPyObjectToJSON: (RED,node,msg,data)=> data.toSerializable(),
|
|
382
387
|
StringToCompressed: (RED,node,msg,data)=>compressor.compress(data,
|
|
383
388
|
(compressed)=>{
|
|
384
389
|
node.setData(RED,node,msg,compressed);
|
|
@@ -441,6 +446,23 @@ function evalFunction(id,mapping){
|
|
|
441
446
|
function is(node,value){
|
|
442
447
|
return node.actionSource==value||node.actionTarget==value;
|
|
443
448
|
}
|
|
449
|
+
let jsonata;
|
|
450
|
+
function JSONataTransform(data,ok,error){
|
|
451
|
+
/*
|
|
452
|
+
(async () => {
|
|
453
|
+
return result = await expression.evaluate(data);
|
|
454
|
+
})()
|
|
455
|
+
*/
|
|
456
|
+
|
|
457
|
+
this.transformFuncionCompiled.evalFunction(data,{},(error, result) => {
|
|
458
|
+
if(error) {
|
|
459
|
+
console.error(error);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
console.log("Finished with", result);
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
444
466
|
module.exports = function (RED) {
|
|
445
467
|
function transformNode(n) {
|
|
446
468
|
RED.nodes.createNode(this,n);
|
|
@@ -513,6 +535,16 @@ module.exports = function (RED) {
|
|
|
513
535
|
}
|
|
514
536
|
}
|
|
515
537
|
}
|
|
538
|
+
if(node.transformFuncion && ( node.actionSource=="JSON" || node.actionTarget=="JSON" )) {
|
|
539
|
+
try{
|
|
540
|
+
if(!jsonata) jsonata=require('jsonata')
|
|
541
|
+
node.transformFuncionCompiled = jsonata(node.transformFuncion);
|
|
542
|
+
} catch (ex) {
|
|
543
|
+
error(node,ex,"Transform function error");
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
516
548
|
const typeValidate="invalid"+node.actionSource;
|
|
517
549
|
node.invalidSourceType=typeValidate in functions &! ["XLSX","XLSXObject"].includes(node.actionTarget)?functions[typeValidate]:(()=>false);
|
|
518
550
|
try {
|