node-red-contrib-prib-functions 0.19.2 → 0.21.0
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 +84 -70
- 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 +52 -21
- package/dataAnalysis/dataAnalysis.js +73 -44
- 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/documentation/DataAnalysisRealtime.JPG +0 -0
- package/documentation/monitorSystem.JPG +0 -0
- package/documentation/monitorSystemTest.JPG +0 -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 +95 -56
- package/matrix/matrixNode.html +88 -55
- package/matrix/matrixNode.js +12 -5
- 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 +199 -0
- package/monitor/monitorSystem.js +322 -0
- package/monitor/svgHTML.js +179 -0
- package/monitor/svgObjects.js +64 -0
- package/package.json +20 -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 +2820 -2003
- package/test/data/float32vector10.npy +0 -0
- package/test/data/flow.json +2830 -2033
- 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 +471 -0
- package/test/dataAnalysisPCA.js +54 -0
- package/test/dataAnalysisSVD.js +31 -0
- package/test/euclideanDistance.js +2 -2
- package/test/transformConfluence.js +1 -1
- package/test/transformNumPy.js +132 -0
- package/testing/test.html +1 -1
- package/testing/test.js +78 -53
- 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
|
+
});
|
package/testing/test.html
CHANGED
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
this.payloadType=setType(this.payloadType);
|
|
154
154
|
this.resultType=setType(this.resultType);
|
|
155
155
|
function setInput(t) {
|
|
156
|
-
let types=['flow','global','str','num','bool','json','bin','date','env'];
|
|
156
|
+
let types=['flow','global','str','num','bool','json','jsonata','bin','date','env'];
|
|
157
157
|
if(t=="result") types.push('re');
|
|
158
158
|
$("#node-input-"+t+"Type").val(this[t+"Type"]);
|
|
159
159
|
$("#node-input-"+t).typedInput({
|
package/testing/test.js
CHANGED
|
@@ -29,25 +29,35 @@ function setError(msg,node,err) {
|
|
|
29
29
|
node.send([null,msg]);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function equalObjects(obj1,obj2,errorFactor) {
|
|
33
|
-
if( obj1 === obj2 ) return
|
|
32
|
+
function equalObjects(obj1,obj2,errorFactor,callEquals=()=>true,callNotEquals=()=>false) {
|
|
33
|
+
if( obj1 === obj2 ) return callEquals();
|
|
34
34
|
if(obj1 instanceof Buffer ) return Buffer.compare(obj1, obj2) === 0
|
|
35
|
-
if( obj1 === Number.POSITIVE_INFINITY && obj2==="Infinity") return
|
|
36
|
-
if( obj1 === Number.NEGATIVE_INFINITY && obj2==="-Infinity") return
|
|
37
|
-
if( Number.isNaN(obj1) && obj2==="NaN") return
|
|
35
|
+
if( obj1 === Number.POSITIVE_INFINITY && obj2==="Infinity") return callEquals();
|
|
36
|
+
if( obj1 === Number.NEGATIVE_INFINITY && obj2==="-Infinity") return callEquals();
|
|
37
|
+
if( Number.isNaN(obj1) && obj2==="NaN") return callEquals();
|
|
38
38
|
const obj1type=typeof obj1;
|
|
39
|
-
if( obj1type != typeof obj2 ) return
|
|
39
|
+
if( obj1type != typeof obj2 ) return callNotEquals();
|
|
40
40
|
if(errorFactor && obj1type=="number") return (Math.abs(obj2-obj1)/obj2)<errorFactor;
|
|
41
|
-
if( !(obj1 instanceof Object) ) return
|
|
42
|
-
if( Object.keys(obj1).length !== Object.keys(obj2).length ) return
|
|
41
|
+
if( !(obj1 instanceof Object) ) return callNotEquals();
|
|
42
|
+
if( Object.keys(obj1).length !== Object.keys(obj2).length ) return callNotEquals();
|
|
43
43
|
try{
|
|
44
44
|
for(let key in obj1) {
|
|
45
|
-
if( !equalObjects(obj1[key],obj2[key],errorFactor) ) return
|
|
45
|
+
if( !equalObjects(obj1[key],obj2[key],errorFactor) ) return callNotEquals();
|
|
46
46
|
}
|
|
47
47
|
} catch(e) {
|
|
48
|
-
return
|
|
48
|
+
return callNotEquals();
|
|
49
49
|
}
|
|
50
|
-
return
|
|
50
|
+
return callEquals();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const testedOK=(node,msg)=>{
|
|
54
|
+
node.status({fill:"green",shape:"ring",text:"Success"});
|
|
55
|
+
delete msg._test;
|
|
56
|
+
node.send([null,null,msg]);
|
|
57
|
+
}
|
|
58
|
+
const testedFailed=(node,msg)=>{
|
|
59
|
+
msg._test.testedValue=node.getData(msg,node);
|
|
60
|
+
setError(msg,node,"Test failed");
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
module.exports = function(RED) {
|
|
@@ -56,9 +66,17 @@ module.exports = function(RED) {
|
|
|
56
66
|
RED.nodes.createNode(this,n);
|
|
57
67
|
let node=Object.assign(this,n);
|
|
58
68
|
try{
|
|
59
|
-
node.
|
|
69
|
+
node.isJSONata=node.resultType=="jsonata"
|
|
60
70
|
if(node.escapeString && node.resultType=="str") {
|
|
61
71
|
node.getData=eval("((msg,node)=>escapeSpecialChars("+(node.resultProperty||"msg.payload")+"))");
|
|
72
|
+
} else{
|
|
73
|
+
if(node.isJSONata) {
|
|
74
|
+
if(!node.result.startsWith("$boolean"))
|
|
75
|
+
throw Error("JSONata must have $boolean outcome, found: "+node.resultProperty.substr(0,8))
|
|
76
|
+
node.resultExpression=RED.util.prepareJSONataExpression(node.result, node)
|
|
77
|
+
node.resultExpression.assign('node', node);
|
|
78
|
+
}
|
|
79
|
+
node.getData=eval("((msg,node)=>"+(node.resultProperty||"msg.payload")+")");
|
|
62
80
|
}
|
|
63
81
|
node.status({fill:"green",shape:"ring",text:"Ready"});
|
|
64
82
|
} catch(e) {
|
|
@@ -66,59 +84,66 @@ module.exports = function(RED) {
|
|
|
66
84
|
node.status({fill:"red",shape:"ring",text:"Invalid setup "+e.toString()});
|
|
67
85
|
}
|
|
68
86
|
node.payloadEscape=(node.payloadType=="str"&&node.escapeString);
|
|
69
|
-
node.equalObjects=node.resultType=="re"?
|
|
87
|
+
node.equalObjects=node.resultType=="re"?
|
|
88
|
+
(value,regex,callEquals,callNotEquals)=>(RegExp(regex).test(value)?callEquals():callNotEquals()):
|
|
89
|
+
(obj1,obj2,errorFactor,callEquals,callNotEquals)=>equalObjects(obj1,obj2,errorFactor,callEquals,callNotEquals);
|
|
70
90
|
node.on("input",function(msg) {
|
|
71
91
|
if(msg._test) {
|
|
72
92
|
try{
|
|
73
|
-
if(msg._test.id!==node.id)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
93
|
+
if(msg._test.id!==node.id) return setError(msg,node,"Sent by another test "+msg._test.id);
|
|
94
|
+
|
|
95
|
+
if(node.isJSONata)
|
|
96
|
+
return RED.util.evaluateJSONataExpression(node.resultExpression,msg,(err,data)=>{
|
|
97
|
+
if(err) testedFailed(node,msg)
|
|
98
|
+
else return data?testedOK(node,msg):testedFailed(node,msg)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
node.equalObjects(node.getData(msg,node),msg._test.result,node.errorFactor,
|
|
102
|
+
()=>testedOK(node,msg),
|
|
103
|
+
()=>testedFailed(node,msg)
|
|
104
|
+
);
|
|
105
|
+
|
|
83
106
|
} catch(ex){
|
|
84
107
|
setError(msg,node,"Test failed on get data "+ex.message);
|
|
85
108
|
}
|
|
86
109
|
return;
|
|
87
110
|
}
|
|
88
111
|
node.status({fill:"yellow",shape:"ring",text:"waiting on response"});
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
let result=RED.util.evaluateNodeProperty(node.result,node.resultType,node,msg,(err,data)=>{
|
|
113
|
+
if(err) node.error(err,msg)
|
|
114
|
+
msg._test={
|
|
115
|
+
id:node.id,
|
|
116
|
+
result:node.escapeString&&node.resultType=="str"?escapeSpecialChars(data):data
|
|
117
|
+
};
|
|
118
|
+
msg.topic=node.topic;
|
|
119
|
+
if(["flow","global"].includes(node.payloadType)) {
|
|
120
|
+
RED.util.evaluateNodeProperty(node.payload,node.payloadType,node,msg, (err,res)=>{
|
|
121
|
+
if (err) {
|
|
122
|
+
node.error(err,msg);
|
|
123
|
+
} else {
|
|
124
|
+
msg.payload=res;
|
|
125
|
+
node.send(msg);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
try {
|
|
130
|
+
if ( (node.payloadType == null && node.payload === "") || node.payloadType === "date") {
|
|
131
|
+
msg.payload = Date.now();
|
|
132
|
+
} else if (node.payloadType == null) {
|
|
133
|
+
msg.payload = node.payload;
|
|
134
|
+
} else if (node.payloadType === 'none') {
|
|
135
|
+
msg.payload = "";
|
|
136
|
+
} else {
|
|
137
|
+
msg.payload=RED.util.evaluateNodeProperty(this.payload,node.payloadType,node,msg);
|
|
138
|
+
if(node.payloadEscape) msg.payload=msg.payload.escapeSpecialChars();
|
|
139
|
+
}
|
|
101
140
|
node.send(msg);
|
|
141
|
+
msg = null;
|
|
142
|
+
} catch(err) {
|
|
143
|
+
node.error(err,msg);
|
|
102
144
|
}
|
|
103
|
-
});
|
|
104
|
-
} else {
|
|
105
|
-
try {
|
|
106
|
-
if ( (node.payloadType == null && node.payload === "") || node.payloadType === "date") {
|
|
107
|
-
msg.payload = Date.now();
|
|
108
|
-
} else if (node.payloadType == null) {
|
|
109
|
-
msg.payload = node.payload;
|
|
110
|
-
} else if (node.payloadType === 'none') {
|
|
111
|
-
msg.payload = "";
|
|
112
|
-
} else {
|
|
113
|
-
msg.payload=RED.util.evaluateNodeProperty(this.payload,node.payloadType,node,msg);
|
|
114
|
-
if(node.payloadEscape) msg.payload=msg.payload.escapeSpecialChars();
|
|
115
|
-
}
|
|
116
|
-
node.send(msg);
|
|
117
|
-
msg = null;
|
|
118
|
-
} catch(err) {
|
|
119
|
-
node.error(err,msg);
|
|
120
145
|
}
|
|
121
|
-
}
|
|
146
|
+
});
|
|
122
147
|
});
|
|
123
148
|
}
|
|
124
149
|
|
|
@@ -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;
|