node-red-contrib-prib-functions 0.14.2 → 0.17.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 +67 -0
- package/.vscode/extensions.json +6 -0
- package/README.md +12 -35
- package/dataAnalysis/arrayLast.js +8 -0
- package/dataAnalysis/arrayPairs.js +15 -0
- package/dataAnalysis/dataAnalysis.html +20 -0
- package/dataAnalysis/dataAnalysis.js +37 -12
- package/dataAnalysis/euclideanDistance.js +54 -0
- package/dataAnalysis/forNestedEach.js +24 -0
- package/package.json +18 -20
- package/test/asn1Test.jsBypass +226 -0
- package/test/dataAnalysis.js +112 -0
- package/test/euclideanDistance.js +99 -0
- package/testing/load-injector.html +2 -2
- package/testing/load-injector.js +9 -4
- package/testing/test.html +16 -0
- package/testing/test.js +6 -4
- package/transform/asn1.js +237 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# For most projects, this workflow file will not need changing; you simply need
|
|
2
|
+
# to commit it to your repository.
|
|
3
|
+
#
|
|
4
|
+
# You may wish to alter this file to override the set of languages analyzed,
|
|
5
|
+
# or to provide custom queries or build logic.
|
|
6
|
+
#
|
|
7
|
+
# ******** NOTE ********
|
|
8
|
+
# We have attempted to detect the languages in your repository. Please check
|
|
9
|
+
# the `language` matrix defined below to confirm you have the correct set of
|
|
10
|
+
# supported CodeQL languages.
|
|
11
|
+
#
|
|
12
|
+
name: "CodeQL"
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
push:
|
|
16
|
+
branches: [ master ]
|
|
17
|
+
pull_request:
|
|
18
|
+
# The branches below must be a subset of the branches above
|
|
19
|
+
branches: [ master ]
|
|
20
|
+
schedule:
|
|
21
|
+
- cron: '35 18 * * 1'
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
analyze:
|
|
25
|
+
name: Analyze
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: false
|
|
30
|
+
matrix:
|
|
31
|
+
language: [ 'javascript' ]
|
|
32
|
+
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
|
33
|
+
# Learn more:
|
|
34
|
+
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
|
35
|
+
|
|
36
|
+
steps:
|
|
37
|
+
- name: Checkout repository
|
|
38
|
+
uses: actions/checkout@v2
|
|
39
|
+
|
|
40
|
+
# Initializes the CodeQL tools for scanning.
|
|
41
|
+
- name: Initialize CodeQL
|
|
42
|
+
uses: github/codeql-action/init@v1
|
|
43
|
+
with:
|
|
44
|
+
languages: ${{ matrix.language }}
|
|
45
|
+
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
46
|
+
# By default, queries listed here will override any specified in a config file.
|
|
47
|
+
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
48
|
+
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
|
49
|
+
|
|
50
|
+
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
51
|
+
# If this step fails, then you should remove it and run the build manually (see below)
|
|
52
|
+
- name: Autobuild
|
|
53
|
+
uses: github/codeql-action/autobuild@v1
|
|
54
|
+
|
|
55
|
+
# ℹ️ Command-line programs to run using the OS shell.
|
|
56
|
+
# 📚 https://git.io/JvXDl
|
|
57
|
+
|
|
58
|
+
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
|
59
|
+
# and modify them (or add more) to build your code if your project
|
|
60
|
+
# uses a compiled language
|
|
61
|
+
|
|
62
|
+
#- run: |
|
|
63
|
+
# make bootstrap
|
|
64
|
+
# make release
|
|
65
|
+
|
|
66
|
+
- name: Perform CodeQL Analysis
|
|
67
|
+
uses: github/codeql-action/analyze@v1
|
package/README.md
CHANGED
|
@@ -50,6 +50,11 @@ Array metrics:
|
|
|
50
50
|
* Normalise
|
|
51
51
|
* Standardization (Z-score Normalization)
|
|
52
52
|
|
|
53
|
+
Array data
|
|
54
|
+
* distances
|
|
55
|
+
* minimum distance(s) between points
|
|
56
|
+
* maximum distance(s) between points
|
|
57
|
+
|
|
53
58
|

|
|
54
59
|
|
|
55
60
|
example:
|
|
@@ -154,7 +159,7 @@ Test example:
|
|
|
154
159
|
|
|
155
160
|
The levenshtein distance between two character strings.
|
|
156
161
|
|
|
157
|
-

|
|
158
163
|
|
|
159
164
|
------------------------------------------------------------
|
|
160
165
|
|
|
@@ -242,6 +247,12 @@ Test/example flow in test/generalTest.json
|
|
|
242
247
|
|
|
243
248
|
# Version
|
|
244
249
|
|
|
250
|
+
0.17.0 Add finished wire to load injector
|
|
251
|
+
|
|
252
|
+
0.16.10 data analysis add eulcidean distance functions. Add array pairs
|
|
253
|
+
|
|
254
|
+
0.16.0 fix data analysis variance and stddev, add sample, add tests
|
|
255
|
+
|
|
245
256
|
0.14.2 fix general test flows. Add icon for data analysis
|
|
246
257
|
|
|
247
258
|
0.14.1 fix capitalization issue with levenshtein Distance
|
|
@@ -256,40 +267,6 @@ Test/example flow in test/generalTest.json
|
|
|
256
267
|
|
|
257
268
|
0.11.0 Transform for AVRO and snappy. Add JSON to CSV
|
|
258
269
|
|
|
259
|
-
0.10.2 Transform validate for array source, bug fixes on transform and add improvements to array to messages. Added node for levenshtein distance.
|
|
260
|
-
|
|
261
|
-
0.10.1 Real time weighted moving average, levenshtein Distance, for test allow testing of "infinity","-infinity" and "NaN" in JSON.
|
|
262
|
-
|
|
263
|
-
0.10.0 Many fixes to transform. Array and csv to various forms work. Added test to validate.
|
|
264
|
-
Improved test to allow for escape to put special characters into a string.
|
|
265
|
-
|
|
266
|
-
0.9.6 Enhance transform with csv ignore lead or trailing lines.
|
|
267
|
-
Add Array and CSV to Messages. Add in topic override
|
|
268
|
-
|
|
269
|
-
0.9.5 Enhance transform with path and setting source and target.
|
|
270
|
-
Outlier allowed to set number of deviations if median and reset or set stats
|
|
271
|
-
Add outlier detection and Pearson R realtime metrics.
|
|
272
|
-
|
|
273
|
-
0.8.1 Add realtime metrics to data analysis.
|
|
274
|
-
|
|
275
|
-
0.7.1
|
|
276
|
-
* fix json to table html and minor code improvements.
|
|
277
|
-
turn off debug mode on spawn process.
|
|
278
|
-
clear down timer on close for host available
|
|
279
|
-
* add Host Available
|
|
280
|
-
|
|
281
|
-
0.6.0
|
|
282
|
-
* add Spawn Process
|
|
283
|
-
* improve experimental transform with json to table html
|
|
284
|
-
|
|
285
|
-
0.5.0
|
|
286
|
-
* test node add select property tested for result
|
|
287
|
-
* dataAnalysis add property analysed
|
|
288
|
-
* add experimental transform
|
|
289
|
-
|
|
290
|
-
0.4.0 Add test, monitor flow, data analysis
|
|
291
|
-
|
|
292
|
-
0.0.1 base
|
|
293
270
|
|
|
294
271
|
# Author
|
|
295
272
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
if(Array.prototype.pairs==null)
|
|
2
|
+
Array.prototype.pairs=function (func) {
|
|
3
|
+
const l=this.length,li=l-1;
|
|
4
|
+
if(func) {
|
|
5
|
+
for (let i=0; i<li ; i++) {
|
|
6
|
+
for (let j =i+1; j<l; j++) {
|
|
7
|
+
func(this[i], this[j],i,j,this);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
} else {
|
|
11
|
+
const r=[];
|
|
12
|
+
this.pairs(func===null? (p1,p2,i1,i2)=>r.push([p1,p2,i1,i2]) : (p1,p2)=>r.push([p1,p2]) )
|
|
13
|
+
return r;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
defaults: {
|
|
7
7
|
name: {value:"",required:false},
|
|
8
8
|
action: {value:"avg",required:true},
|
|
9
|
+
columns:{value:"",required:false},
|
|
9
10
|
outputs: {value:(["realtime","realtimePredict"].includes(this.action)?3:2),required:true},
|
|
10
11
|
outliersBase: {value:"avg",required:false},
|
|
11
12
|
outliersStdDevs: {value:"3",required:false},
|
|
@@ -44,6 +45,11 @@
|
|
|
44
45
|
}
|
|
45
46
|
});
|
|
46
47
|
$("#node-input-action").change(function() {
|
|
48
|
+
if(["distances","distancesMax","distancesMin"].includes( $(this).val() )) {
|
|
49
|
+
$(".form-row-http-in-columns").show();
|
|
50
|
+
} else {
|
|
51
|
+
$(".form-row-http-in-columns").hide();
|
|
52
|
+
}
|
|
47
53
|
if(["movingAvgSimple","movingAvgWeighted","movingAvgExponential","realtime","realtimePredict"].includes( $(this).val() )) {
|
|
48
54
|
$(".form-row-http-in-term").show();
|
|
49
55
|
} else {
|
|
@@ -142,6 +148,9 @@
|
|
|
142
148
|
<option value="corelationship">Corelationship</option>
|
|
143
149
|
<option value="deltas">Deltas</option>
|
|
144
150
|
<option value="deltaNormalised">Deltas Normalised</option>
|
|
151
|
+
<option value="distances">Euclidean Distances</option>
|
|
152
|
+
<option value="distancesMax">Euclidean Distances Max</option>
|
|
153
|
+
<option value="distancesMin">Euclidean Distances Min</option>
|
|
145
154
|
<option value="pearsonR">Linear Correlation Coefficient</option>
|
|
146
155
|
<option value="max">Maximum</option>
|
|
147
156
|
<option value="avg">Mean</option>
|
|
@@ -158,12 +167,19 @@
|
|
|
158
167
|
<option value="realtimePredict">RealTime Metrics + predicts</option>
|
|
159
168
|
<option value="standardize">Standardization (Z-score Normalization)</option>
|
|
160
169
|
<option value="stdDev">Standard Deviation</option>
|
|
170
|
+
<option value="sampleStdDev">Standard Deviation (Sample)</option>
|
|
161
171
|
<option value="skew">Skewness</option>
|
|
162
172
|
<option value="sum">Sum</option>
|
|
163
173
|
<option value="variance">Variance</option>
|
|
174
|
+
<option value="sampleVariance">Variance (Sample)</option>
|
|
164
175
|
</select>
|
|
165
176
|
</div>
|
|
166
177
|
|
|
178
|
+
<div class="form-row form-row-http-in-columns hide">
|
|
179
|
+
<label for="node-input-columns"><i class="icon-bookmark"></i> Columns</label>
|
|
180
|
+
<input type="text" id="node-input-columns" placeholder="columns">
|
|
181
|
+
</div>
|
|
182
|
+
|
|
167
183
|
<div class="form-row form-row-http-in-term hide">
|
|
168
184
|
<label for="node-input-term"><i class="icon-bookmark"></i> Term</label>
|
|
169
185
|
<input type="number" id="node-input-term" placeholder="term" min="2" max="100" step="1">
|
|
@@ -229,4 +245,8 @@ If real-time stats then a message can send directive instruction in topic:
|
|
|
229
245
|
<li>3 standard deviations of the mean (or median), around 99.7%
|
|
230
246
|
</ul>
|
|
231
247
|
</p>
|
|
248
|
+
<p>
|
|
249
|
+
Distance functions take in an array of points in an array and calculates the euclidean distance.
|
|
250
|
+
e.g. max input [ [1,1],[2,2],[3,3],[4,5]]), has output [{distance:5,points:[[1,1],[4,5]],index:[0,3]}]
|
|
251
|
+
</p>
|
|
232
252
|
</script>
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
const logger = new (require("node-red-contrib-logger"))("Data Analysis");
|
|
2
2
|
logger.sendInfo("Copyright 2020 Jaroslav Peter Prib");
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
},
|
|
8
|
-
writable: true,
|
|
9
|
-
configurable: true
|
|
10
|
-
});
|
|
4
|
+
require("./arrayLast");
|
|
5
|
+
const ed=require("./euclideanDistance.js");
|
|
6
|
+
require("./forNestedEach");
|
|
11
7
|
|
|
12
8
|
function crossNormalisedDeltas() {
|
|
13
9
|
const dataPoints=this.dataPoint;
|
|
@@ -120,8 +116,18 @@ function setDataPoint(value,term,node,dp) {
|
|
|
120
116
|
dp.weightedMovingAvg=(dp.weightedMovingAvg*2/count)/(count+1);
|
|
121
117
|
dp.exponentialWeightedMoving.forEach(c=>c.sample(value));
|
|
122
118
|
}
|
|
119
|
+
function getColumns(node) {
|
|
120
|
+
if(node.columns) {
|
|
121
|
+
if(node.columns instanceof Array) return node.columns
|
|
122
|
+
return eval("["+node.columns+"]");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
123
125
|
functions={
|
|
124
126
|
avg:(d)=>functions.sum(d)/d.length,
|
|
127
|
+
arrayMax:(d)=>{ // not tested
|
|
128
|
+
let max=[],indices
|
|
129
|
+
a.forNestedEach((e,f,l)=>{const i=l[l.length-1];if(max[l]<e) {max=e,indices=l}})
|
|
130
|
+
},
|
|
125
131
|
covariance: (d)=>{
|
|
126
132
|
const means=[],covars=[],dl=d.length,dlminus1=dl-1,N=d[0].length;
|
|
127
133
|
d.forEach((e,i)=>{
|
|
@@ -141,11 +147,16 @@ functions={
|
|
|
141
147
|
corelationship:(d)=>{
|
|
142
148
|
const covars=functions.covariance(d);
|
|
143
149
|
const stdDev=d.map(c=>functions.stdDev(c));
|
|
144
|
-
|
|
145
|
-
return 1//covars.map((a)=>a.map((c,i)=>c==null?null:c/(stdDev[i+1]*stdDev[i])));
|
|
150
|
+
return covars.map((a)=>a.map((c,i)=>c==null?null:c/(stdDev[i+1]*stdDev[i])));
|
|
146
151
|
},
|
|
147
152
|
deltas :(d)=>d.map( (c,i)=>c-(d[i-1]||0) ),
|
|
148
153
|
deltaNormalised :(d)=>d.map( (c,i)=>(c-(d[i-1]||0)) / (d[i-1]||0) ),
|
|
154
|
+
distances: (d,term,node)=>{
|
|
155
|
+
if(node.columns) return ed.distances(d,getColumns(node))
|
|
156
|
+
return ed.distances(d);
|
|
157
|
+
},
|
|
158
|
+
distancesMin: (d,term,node)=>ed.minDistances(d,getColumns(node)),
|
|
159
|
+
distancesMax: (d,term,node)=>ed.maxDistances(d,getColumns(node)),
|
|
149
160
|
max: (d)=> Math.max(...d),
|
|
150
161
|
median:(d)=>{
|
|
151
162
|
const i=Math.floor(d.length/2);
|
|
@@ -266,11 +277,23 @@ functions={
|
|
|
266
277
|
}
|
|
267
278
|
return dp;
|
|
268
279
|
},
|
|
280
|
+
sampleVariance:(d)=>{
|
|
281
|
+
const mean=functions.avg(d);
|
|
282
|
+
const sum=functions.sumWithFunction(d,(v)=>v-mean)
|
|
283
|
+
return sum/(d.length-1)
|
|
284
|
+
},
|
|
269
285
|
sum:(d)=>d.reduce((p,c)=>p+c),
|
|
270
286
|
sumWithFunction:(d,f)=>d.reduce((p,c)=>p+f.apply(this,[c]),0),
|
|
271
287
|
variance:(d)=>{ //Var(X) = E (X − E(X))2 = E(X2) − (E(X))2
|
|
272
|
-
|
|
273
|
-
|
|
288
|
+
const n=d.length;
|
|
289
|
+
return functions.sumWithFunction(d,(v)=>Math.pow(v,2))/n - Math.pow(functions.avg(d),2);
|
|
290
|
+
},
|
|
291
|
+
sampleStdDev:(d)=>Math.sqrt(functions.sampleVariance(d)),
|
|
292
|
+
sampleVariance:(d)=>{
|
|
293
|
+
if(d.length<2)return 0
|
|
294
|
+
const mean=functions.avg(d);
|
|
295
|
+
const sum=d.reduce((a,e)=>a+Math.pow(e-mean,2),0);
|
|
296
|
+
return sum/(d.length-1)
|
|
274
297
|
}
|
|
275
298
|
}
|
|
276
299
|
functions.mean=functions.avg;
|
|
@@ -361,7 +384,8 @@ module.exports = function (RED) {
|
|
|
361
384
|
return;
|
|
362
385
|
}
|
|
363
386
|
try{
|
|
364
|
-
|
|
387
|
+
const data=node.getData(msg,node);
|
|
388
|
+
if(data) msg.result=node.actionfunction.apply(node,[data,node.term,node]);
|
|
365
389
|
switch(node.action) {
|
|
366
390
|
case "realtime":
|
|
367
391
|
if(msg.result.outlier) {
|
|
@@ -371,6 +395,7 @@ module.exports = function (RED) {
|
|
|
371
395
|
break;
|
|
372
396
|
}
|
|
373
397
|
} catch(ex) {
|
|
398
|
+
if(logger.active) logger.send({label:"error input",action:node.action,message:ex.message,stack:ex.stack});
|
|
374
399
|
msg.error=ex.message;
|
|
375
400
|
if(node.maxErrorDisplay) {
|
|
376
401
|
--node.maxErrorDisplay;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require("./arrayPairs");
|
|
2
|
+
function distance(point1,point2) {
|
|
3
|
+
try{
|
|
4
|
+
if(point1==null | point2==null | point2.length==0 ) return NaN;
|
|
5
|
+
if(point1.length!==point2.length) throw Error("argument 1 array size not equal argument 2 array size") ;
|
|
6
|
+
const sum=point1.reduce((s,c,i)=>{
|
|
7
|
+
const d=c-point2[i];
|
|
8
|
+
return s+d*d;
|
|
9
|
+
},0);
|
|
10
|
+
return Math.sqrt(sum);
|
|
11
|
+
} catch(ex){
|
|
12
|
+
if(!(point1 instanceof Array)) throw Error("argument 1 not array") ;
|
|
13
|
+
if(!(point2 instanceof Array)) throw Error("argument 2 not array") ;
|
|
14
|
+
throw ex;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function distanceColumns(point1,point2,columns=[]) {
|
|
19
|
+
const sum=columns.reduce((s,c)=>{
|
|
20
|
+
const d=point1[c]-point2[c];
|
|
21
|
+
return s+d*d;
|
|
22
|
+
},0);
|
|
23
|
+
return Math.sqrt(sum)
|
|
24
|
+
}
|
|
25
|
+
function arrayPairs(points,f=distance,args) {
|
|
26
|
+
if(!(points instanceof Array)) throw Error("argument 1 not array") ;
|
|
27
|
+
const r=[];
|
|
28
|
+
points.pairs((p1,p2,i1,i2)=>{
|
|
29
|
+
r.push([ f(p1,p2,args), i1, i2])
|
|
30
|
+
});
|
|
31
|
+
return r;
|
|
32
|
+
}
|
|
33
|
+
function arrayPairsSet(points,f=distance,args,sf=(a,b)=>a>b,maxSetSize) {
|
|
34
|
+
let m,r=[],pi1,pi2;
|
|
35
|
+
points.pairs((p1,p2,i1,i2)=>{
|
|
36
|
+
const d=f(p1,p2,args);
|
|
37
|
+
if(m===d) {
|
|
38
|
+
r.push({points:[p1,p2],index:[i1,i2],distance:d});
|
|
39
|
+
if(maxSetSize && maxSetSize>r.length) r.shift
|
|
40
|
+
} else if(m==null | sf(m,d)==true) {
|
|
41
|
+
m=d;
|
|
42
|
+
r=[{points:[p1,p2],index:[i1,i2],distance:d}];
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return r;
|
|
46
|
+
}
|
|
47
|
+
module.exports={
|
|
48
|
+
distance:distance,
|
|
49
|
+
distanceColumns:distanceColumns,
|
|
50
|
+
distances:arrayPairs,
|
|
51
|
+
distancesColumns:(a,columns)=>arrayPairs(a,distanceColumns,columns),
|
|
52
|
+
minDistances:(a,columns)=>arrayPairsSet(a, columns?distanceColumns:distance, columns, (a,b)=>a>b),
|
|
53
|
+
maxDistances:(a,columns)=>arrayPairsSet(a, columns?distanceColumns:distance, columns, (a,b)=>a<b)
|
|
54
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
if(Array.prototype.forNestedEach==null)
|
|
2
|
+
Array.prototype.forNestedEach=function (func,filter,level=[]) {
|
|
3
|
+
const l=level.length;
|
|
4
|
+
if(filter.hasOwnProperty(l)) {
|
|
5
|
+
const e=filter[l];
|
|
6
|
+
const callFilter=(v)=>!e.includes(v);
|
|
7
|
+
this.forEach((element, index, array) => {
|
|
8
|
+
if(callFilter(index)) return;
|
|
9
|
+
if(element instanceof Array) {
|
|
10
|
+
element.forNestedEach(func,filter,level.push(index))
|
|
11
|
+
} else {
|
|
12
|
+
func(element,index,array,level)
|
|
13
|
+
}
|
|
14
|
+
} )
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
this.forEach((element, index, array) => {
|
|
18
|
+
if(element instanceof Array) {
|
|
19
|
+
element.forNestedEach(func,filter,level.push(index))
|
|
20
|
+
} else {
|
|
21
|
+
func(element,index,array,level)
|
|
22
|
+
}
|
|
23
|
+
} )
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-prib-functions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Node-RED added node functions.",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"xlsx": "
|
|
6
|
+
"avsc": "^5.6.1",
|
|
7
|
+
"fast-xml-parser": "^3.19.0",
|
|
8
|
+
"node-red-contrib-logger": "^0.0.6",
|
|
9
|
+
"xlsx": "^0.17.0"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"eslint-plugin-promise": "^4.2.1",
|
|
20
|
-
"eslint-plugin-standard": "^4.0.1",
|
|
21
|
-
"mocha": "^8.1.3",
|
|
22
|
-
"node-red": "^1.1.3",
|
|
23
|
-
"node-red-node-test-helper": "*"
|
|
12
|
+
"@node-red/runtime": ">=1.2.8",
|
|
13
|
+
"axios": ">=0.21.1",
|
|
14
|
+
"bcrypt": "^5.0.1",
|
|
15
|
+
"bl": "^5.0.0",
|
|
16
|
+
"mocha": "^9.2.2",
|
|
17
|
+
"node-red": "^2.2.2",
|
|
18
|
+
"node-red-node-test-helper": "^0.2.7"
|
|
24
19
|
},
|
|
25
20
|
"scripts": {
|
|
26
|
-
"test": "mocha \"test
|
|
27
|
-
"lint": "eslint --ext .js ./",
|
|
28
|
-
"lint-fix": "eslint --fix --ext .js ./"
|
|
21
|
+
"test": "mocha \"test/**/e*.js\""
|
|
29
22
|
},
|
|
30
23
|
"repository": {
|
|
31
24
|
"type": "git",
|
|
@@ -46,6 +39,8 @@
|
|
|
46
39
|
"data",
|
|
47
40
|
"data analysis",
|
|
48
41
|
"delta",
|
|
42
|
+
"distance",
|
|
43
|
+
"euclidian distance",
|
|
49
44
|
"EMA",
|
|
50
45
|
"EWMA",
|
|
51
46
|
"Exponential Moving Average",
|
|
@@ -61,6 +56,7 @@
|
|
|
61
56
|
"mean",
|
|
62
57
|
"median",
|
|
63
58
|
"minimun",
|
|
59
|
+
"model",
|
|
64
60
|
"monitor",
|
|
65
61
|
"moving",
|
|
66
62
|
"Moving Average",
|
|
@@ -70,6 +66,8 @@
|
|
|
70
66
|
"Process",
|
|
71
67
|
"range",
|
|
72
68
|
"realtime",
|
|
69
|
+
"sample variance",
|
|
70
|
+
"sample stddev",
|
|
73
71
|
"Simple Moving Average",
|
|
74
72
|
"skew",
|
|
75
73
|
"SMA",
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
const assert=require("assert");
|
|
2
|
+
const should=require("should");
|
|
3
|
+
const asn1=require("../transform/asn1.js");
|
|
4
|
+
const request=require('request');
|
|
5
|
+
/*
|
|
6
|
+
function stringFromArrayBuffer (arrayBuffer, onSuccess, onFail) {
|
|
7
|
+
const reader = new FileReader();
|
|
8
|
+
reader.onload = function (event) {
|
|
9
|
+
onSuccess(event.target.result);
|
|
10
|
+
};
|
|
11
|
+
reader.onerror = function (event) {
|
|
12
|
+
onFail(event.target.error);
|
|
13
|
+
};
|
|
14
|
+
reader.readAsBinaryString(new Blob([ arrayBuffer ],
|
|
15
|
+
{ type: 'application/octet-stream' }));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
/*
|
|
20
|
+
* as per https://www.w3.org/Protocols/HTTP-NG/asn1.html
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
const asn1eg = new Uint8Array([
|
|
24
|
+
0x60, // [0110|0000], [APPLICATION 0, Compound] - GetRequest
|
|
25
|
+
0x80, // [1000|0000], Indefinite length
|
|
26
|
+
|
|
27
|
+
0x01, // [0000|0001], [BOOLEAN] GetRequest.headerOnly
|
|
28
|
+
0x01, // [0000|0001], length 1
|
|
29
|
+
0x01, // [0000|0001], value TRUE
|
|
30
|
+
|
|
31
|
+
0x01, // [0000|0001], [BOOLEAN] GetRequest.lock
|
|
32
|
+
0x01, // [0000|0001], length 1
|
|
33
|
+
0x00, // [0000|0000] value FALSE
|
|
34
|
+
|
|
35
|
+
0x61, // [0110|0001], [APPLICATION 1, Compound] - GetRequest.types
|
|
36
|
+
0x80, // indefinite length
|
|
37
|
+
|
|
38
|
+
0xa0, // [1010|0000], [CONTEXT 0, Compound] types.standardTypes
|
|
39
|
+
0x80, // indefinite length
|
|
40
|
+
|
|
41
|
+
0x03, // [0000|0011] [BIT STRING]
|
|
42
|
+
0x02, // length 2
|
|
43
|
+
0x04, // Four bits unused
|
|
44
|
+
0x80, //[1000|0000] {html}
|
|
45
|
+
|
|
46
|
+
0x03, // [0000|0011] [BIT STRING]
|
|
47
|
+
0x02, // length 2
|
|
48
|
+
0x04, // Four bits unused
|
|
49
|
+
0x40, // [0100|0000] {plain-text}
|
|
50
|
+
|
|
51
|
+
0x00,
|
|
52
|
+
0x00, // End of list of standard Types
|
|
53
|
+
0x00,
|
|
54
|
+
0x00, // End of Accept Types
|
|
55
|
+
0x04, // [0000|0100], [OCTET STRING] GetRequest.url
|
|
56
|
+
0x16, // [0001|0110], length 22
|
|
57
|
+
"/ses/magic/moxen.html", // value
|
|
58
|
+
0x00,
|
|
59
|
+
0x00 // End of get request
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
describe('asn1', function() {
|
|
63
|
+
it("decode ans1 eg.",(done)=>{
|
|
64
|
+
const result=asn1.decodeBER(asn1eg);
|
|
65
|
+
console.log({label:"asn1eg",data:asn1eg,result:result.toString()});
|
|
66
|
+
done();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("decode",(done)=>{
|
|
70
|
+
const certB64 = "MIIDLjCCAhagAwIBAgIBATANBgkqhkiG9w0BAQsFADA6MRkwFwYDVQQDExBUZXN0IGNlcnRpZmljYXRlMR0wGwYJKoZIhvcNAQkBFg5zb21lQGVtYWlsLm5ldDAeFw0yMTAzMTYwMDAwMDBaFw0yMTA0MTYwMDAwMDBaMDoxGTAXBgNVBAMTEFRlc3QgY2VydGlmaWNhdGUxHTAbBgkqhkiG9w0BCQEWDnNvbWVAZW1haWwubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3N6J0GUJ8URj2fduC26mjCzWf75jM3QSLQYiXSTAMqJA9apf09GMmT+UC6jq2J1U49mXGezE64uXv2tyys9S07xgRkNAWPJXz0opKYud4XPEpdxKfQkD2XklK+8R3BPhAOOxSpfR+SFkLxTMiDHsOt+Xbb98DZ8F3QkzHLvX42jEfAR0StIRLgFYEtf4vX9q4OsYTeJ4xk61lTJc3d0ep/JTp55fxWRaQdzhg+fkv9XwJxxhW9pJRekZORnRb4Q1Zyw+uecuIffsmhLzang45npfzAKXuPaE6lnRMHauLQ1rGGqYA/Vaq4UU6yZUTVLpsKON7b1xogMQrqIkbqtTuQIDAQABoz8wPTAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIEkDAdBgNVHQ4EFgQUl4hohjz9Xxb4lYhsOiq9wVqKv8YwDQYJKoZIhvcNAQELBQADggEBAIKH86qkFJV3FZyblAMWDSEbEi4MV2Epb5ty7wpSatHvz8NKtmB/hVFGwWFBj5OfS9wfaX6Uw24DyBSBOOqEzonUeqFTDo54zqQ4fQ+UlC/79aq7awGpEuXFnUF3xiLFqHNz5zUeKEYY0W5XKFg/TiW6hAmxlDg5ybAoHDROpwT4u6TuOK6OxMneQRBESmZlO43DYwCG950fXEDJT2gXVLbbSSTln8JBHfTAwOgmsDtaZOCieTS6KYwscWy3u/8xxMyX8NS3A1Zeh0jtk/irKzfsNAdcl8aQwdckGAkPWT/9EqawC33Ep3+2br41+K1jjGT8LeYDlMYSJycWo9tltKc=";
|
|
71
|
+
// const result=asn1.decodeBER(new Uint8Array(Buffer.from(certB64, "base64")).buffer);
|
|
72
|
+
const data=asn1.base64(certB64);
|
|
73
|
+
const result=asn1.decodeBER(data);
|
|
74
|
+
console.log({label:"certB64",data:data,result:result.toString()});
|
|
75
|
+
done();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("encode",(done)=>{
|
|
79
|
+
const value=new Uint8Array([0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]);
|
|
80
|
+
console.log({hex:asn1.hex(value)})
|
|
81
|
+
const berData = new asn1.encode({
|
|
82
|
+
value: (value.buffer),
|
|
83
|
+
identifier: {
|
|
84
|
+
"class": 1,
|
|
85
|
+
tag: 1,
|
|
86
|
+
iconstructed: false
|
|
87
|
+
},
|
|
88
|
+
length: {
|
|
89
|
+
indefiniteForm: false,
|
|
90
|
+
longForm: false,
|
|
91
|
+
length: 10
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
assert.equal(berData.length, 10, "Incorrect value for blockLength");
|
|
95
|
+
done();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const wikiExample=new Uint8Array([0x30,0x13,0x02,0x01,0x05,0x16,0x0e,0x41,0x6e,0x79,0x62,0x6f,0x64,0x79,0x20,0x74,0x68,0x65,0x72,0x65,0x3f]);
|
|
99
|
+
const wikiDecoded=asn1.decode(wikiExample);
|
|
100
|
+
/*
|
|
101
|
+
30 — type tag indicating SEQUENCE
|
|
102
|
+
13 — length in octets of value that follows
|
|
103
|
+
02 — type tag indicating INTEGER
|
|
104
|
+
01 — length in octets of value that follows
|
|
105
|
+
05 — value (5)
|
|
106
|
+
16 — type tag indicating IA5String
|
|
107
|
+
(IA5 means the full 7-bit ISO 646 set, including variants,
|
|
108
|
+
but is generally US-ASCII)
|
|
109
|
+
0e — length in octets of value that follows
|
|
110
|
+
41 6e 79 62 6f 64 79 20 74 68 65 72 65 3f — value ("Anybody there?")
|
|
111
|
+
*/
|
|
112
|
+
console.log(wikiDecoded.toString())
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("ASN.1:2008",()=> {
|
|
116
|
+
function getCase(content,callback){
|
|
117
|
+
const testURL="https://raw.githubusercontent.com/YuryStrozhevsky/ASN1-2008-free-test-suite/master/suite/"+content;
|
|
118
|
+
request(testURL, function (error, response, body) {
|
|
119
|
+
// console.log({url:testURL,error:error,response:response,body:body});
|
|
120
|
+
callback(body)
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
}
|
|
124
|
+
function caseDecodeThrowsError(testCase,error,errorType='TypeError'){
|
|
125
|
+
it(testCase,(done)=>{
|
|
126
|
+
assert.throws(()=>{
|
|
127
|
+
getCase(testCase,(data)=>{
|
|
128
|
+
try{
|
|
129
|
+
const dataArray=new TextEncoder().encode(data);
|
|
130
|
+
const results=asn1.decode(dataArray).toString();
|
|
131
|
+
console.log("caseDecodeThrowsError no error for "+testCase+" results: "+results)
|
|
132
|
+
done()
|
|
133
|
+
} catch(ex){
|
|
134
|
+
console.log("caseDecodeThrowsError error:"+ex.message)
|
|
135
|
+
throw ex;
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
},
|
|
139
|
+
{name: errorType ,message:error}
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function caseDecodeOK(testCase,result){
|
|
144
|
+
it(testCase,(done)=>{
|
|
145
|
+
getCase(testCase,(data)=>{
|
|
146
|
+
console.log()
|
|
147
|
+
const result=asn1.decode(data);
|
|
148
|
+
done();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
caseDecodeThrowsError("tc1.ber","tag > 0x24");
|
|
153
|
+
/* caseDecodeThrowsError("tc2.ber","???");
|
|
154
|
+
caseDecodeThrowsError("tc3.ber","???");
|
|
155
|
+
caseDecodeThrowsError("tc4.ber","???");
|
|
156
|
+
caseDecodeOK("tc5.ber");
|
|
157
|
+
caseDecodeOK("tc6.ber");
|
|
158
|
+
caseDecodeOK("tc7.ber");
|
|
159
|
+
caseDecodeOK("tc8.ber");
|
|
160
|
+
caseDecodeOK("tc9.ber");
|
|
161
|
+
caseDecodeOK("tc10.ber");
|
|
162
|
+
caseDecodeOK("tc11.ber");
|
|
163
|
+
caseDecodeOK("tc12.ber");
|
|
164
|
+
caseDecodeOK("tc13.ber");
|
|
165
|
+
caseDecodeOK("tc14.ber");
|
|
166
|
+
caseDecodeOK("tc15.ber");
|
|
167
|
+
caseDecodeOK("tc16.ber");
|
|
168
|
+
caseDecodeOK("tc17.ber");
|
|
169
|
+
caseDecodeThrowsError("tc18.ber","Value block value must be equal to -4095");
|
|
170
|
+
caseDecodeThrowsError("tc19.ber","End of input reached before message was fully decoded (inconsistent offset and length values)", "Error message inside value block does not match");
|
|
171
|
+
caseDecodeThrowsError("tc20.ber","Value block value must be equal to -4095");
|
|
172
|
+
caseDecodeThrowsError("tc21.ber","Value block value must be equal to -4095");
|
|
173
|
+
caseDecodeThrowsError("tc22.ber","Value block value must be equal to -4095");
|
|
174
|
+
caseDecodeOK("tc23.ber");
|
|
175
|
+
caseDecodeOK("tc24.ber");
|
|
176
|
+
caseDecodeOK("tc25.ber");
|
|
177
|
+
caseDecodeOK("tc26.ber");
|
|
178
|
+
caseDecodeOK("tc27.ber");
|
|
179
|
+
caseDecodeOK("tc28.ber");
|
|
180
|
+
caseDecodeOK("tc29.ber");
|
|
181
|
+
caseDecodeOK("tc30.ber");
|
|
182
|
+
caseDecodeOK("tc31.ber");
|
|
183
|
+
caseDecodeOK("tc32.ber");
|
|
184
|
+
caseDecodeOK("tc33.ber");
|
|
185
|
+
caseDecodeOK("tc34.ber");
|
|
186
|
+
caseDecodeOK("tc35.ber");
|
|
187
|
+
caseDecodeOK("tc36.ber");
|
|
188
|
+
caseDecodeOK("tc37.ber");
|
|
189
|
+
caseDecodeOK("tc38.ber");
|
|
190
|
+
caseDecodeOK("tc39.ber");
|
|
191
|
+
caseDecodeOK("tc40.ber");
|
|
192
|
+
caseDecodeOK("tc41.ber");
|
|
193
|
+
caseDecodeOK("tc42.ber");
|
|
194
|
+
caseDecodeOK("tc43.ber");
|
|
195
|
+
caseDecodeOK("tc44.ber");
|
|
196
|
+
caseDecodeOK("tc45.ber");
|
|
197
|
+
caseDecodeOK("tc46.ber");
|
|
198
|
+
caseDecodeOK("tc47.ber");
|
|
199
|
+
caseDecodeOK("tc48.ber");
|
|
200
|
+
*/
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
/*
|
|
204
|
+
|
|
205
|
+
ECN - Encoding Control Notation (ECN)
|
|
206
|
+
|
|
207
|
+
FooProtocol DEFINITIONS ::= BEGIN
|
|
208
|
+
|
|
209
|
+
FooQuestion ::= SEQUENCE {
|
|
210
|
+
trackingNumber INTEGER(0..199),
|
|
211
|
+
question IA5String
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
FooAnswer ::= SEQUENCE {
|
|
215
|
+
questionNumber INTEGER(10..20),
|
|
216
|
+
answer BOOLEAN
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
FooHistory ::= SEQUENCE {
|
|
220
|
+
questions SEQUENCE(SIZE(0..10)) OF FooQuestion,
|
|
221
|
+
answers SEQUENCE(SIZE(1..10)) OF FooAnswer,
|
|
222
|
+
anArray SEQUENCE(SIZE(100)) OF INTEGER(0..1000),
|
|
223
|
+
...
|
|
224
|
+
}
|
|
225
|
+
END
|
|
226
|
+
*/
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
const assert=require('assert');
|
|
2
|
+
const should=require("should");
|
|
3
|
+
const dataAnalysis=require("../dataAnalysis/dataAnalysis.js");
|
|
4
|
+
const helper=require("node-red-node-test-helper");
|
|
5
|
+
helper.init(require.resolve('node-red'));
|
|
6
|
+
const helperNodeResults={id :"helperNodeResults", type : "helper"}
|
|
7
|
+
const helperNodeDetails={id :"helperNodeDetails", type : "helper"}
|
|
8
|
+
const helperNodeOutliers={id :"helperNodeOutliers", type : "helper"}
|
|
9
|
+
|
|
10
|
+
const base = {
|
|
11
|
+
id : "n1",
|
|
12
|
+
type:"Data Analysis",
|
|
13
|
+
// action: "avg",
|
|
14
|
+
// outputs: {value:(["realtime","realtimePredict"].includes(this.action)?3:2),required:true},
|
|
15
|
+
// outliersBase: "sum",
|
|
16
|
+
// outliersStdDevs: 3,
|
|
17
|
+
// term:10,
|
|
18
|
+
// keyProperty:"msg.topic",
|
|
19
|
+
dataProperty: "msg.payload",
|
|
20
|
+
// dataProperties: ["msg.payload[0]","msg.payload[1]"],
|
|
21
|
+
wires : [[helperNodeResults.id],[helperNodeDetails.id],[helperNodeOutliers.id]]
|
|
22
|
+
} ;
|
|
23
|
+
function getNode(node) {
|
|
24
|
+
const n=helper.getNode(node.id);
|
|
25
|
+
if(n) return n;
|
|
26
|
+
throw Error("node id: "+node.id+" not found, node: "+JSON.stringify(node))
|
|
27
|
+
} ;
|
|
28
|
+
function test(label,action,data,expected) {
|
|
29
|
+
it(label+" "+action, function(done) {
|
|
30
|
+
if(data.length==0) {
|
|
31
|
+
done("no data")
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
let errors=[];
|
|
35
|
+
const newBase=Object.assign({},base,{action:action});
|
|
36
|
+
const flow=[helperNodeResults,helperNodeDetails,helperNodeOutliers,newBase];
|
|
37
|
+
let count=data.length;
|
|
38
|
+
helper.load(dataAnalysis,flow, function() {
|
|
39
|
+
try{
|
|
40
|
+
const resultsNode=getNode(helperNodeResults);
|
|
41
|
+
const detailsNode=getNode(helperNodeDetails);
|
|
42
|
+
const outliersNode=getNode(helperNodeOutliers);
|
|
43
|
+
const n1=getNode(newBase);
|
|
44
|
+
n1.should.have.property("action", action);
|
|
45
|
+
resultsNode.on("input", function(msg) {
|
|
46
|
+
try{
|
|
47
|
+
if(msg.error) throw Error(msg.error);
|
|
48
|
+
assert.strictEqual(msg.result,expected[msg._i]);
|
|
49
|
+
} catch(ex) {
|
|
50
|
+
console.log(JSON.stringify({label:"**** error ",test:label,error:ex.message,count:count,action:action,msg:msg,expected:expected[msg._i]}))
|
|
51
|
+
errors.push(ex.message)
|
|
52
|
+
}
|
|
53
|
+
if(--count) return;
|
|
54
|
+
if(errors.length) done(errors)
|
|
55
|
+
else done();
|
|
56
|
+
});
|
|
57
|
+
detailsNode.on("input", function(msg) {
|
|
58
|
+
console.log(JSON.stringify({label:"***** details ",action:action,msg:msg}))
|
|
59
|
+
});
|
|
60
|
+
outliersNode.on("input", function(msg) {
|
|
61
|
+
console.log(JSON.stringify({label:"***** outliers ",action:action,msg:msg}))
|
|
62
|
+
});
|
|
63
|
+
data.forEach((e,i)=>{
|
|
64
|
+
n1.receive({payload:e,_i :i});
|
|
65
|
+
})
|
|
66
|
+
} catch(ex) {
|
|
67
|
+
console.log(ex.stack)
|
|
68
|
+
done(ex);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}).timeout(4000);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe('Data Anaysis', function() {
|
|
75
|
+
beforeEach(function(done) {
|
|
76
|
+
helper.startServer(done);
|
|
77
|
+
});
|
|
78
|
+
afterEach(function(done) {
|
|
79
|
+
helper.unload();
|
|
80
|
+
helper.stopServer(done);
|
|
81
|
+
});
|
|
82
|
+
const zeros=[undefined,[0],[0,0],[0,0,0]];
|
|
83
|
+
test("test zeros","sum",zeros,[undefined,0,0,0]);
|
|
84
|
+
test("test zeros","avg",zeros,[undefined,0,0,0]);
|
|
85
|
+
test("test zeros","max",zeros,[undefined,0,0,0]);
|
|
86
|
+
test("test zeros","min",zeros,[undefined,0,0,0]);
|
|
87
|
+
test("test zeros","stdDev",zeros,[undefined,0,0,0]);
|
|
88
|
+
const ones=[[1],[1,1],[1,1,1]];
|
|
89
|
+
test("test ones","sum",ones,[1,2,3]);
|
|
90
|
+
test("test ones","avg",ones,[1,1,1]);
|
|
91
|
+
test("test ones","max",ones,[1,1,1]);
|
|
92
|
+
test("test ones","min",ones,[1,1,1]);
|
|
93
|
+
test("test ones","stdDev",ones,[0,0,0]);
|
|
94
|
+
const minus=[[-1],[-1,1],[-1,1,-1]];
|
|
95
|
+
test("test minus","sum",minus,[-1,0,-1]);
|
|
96
|
+
test("test minus","avg",minus,[-1,0,-1/3]);
|
|
97
|
+
test("test minus","max",minus,[-1,1,1]);
|
|
98
|
+
test("test minus","min",minus,[-1,-1,-1]);
|
|
99
|
+
test("test minus","stdDev",minus,[0,1,0.9428090415820634]);
|
|
100
|
+
|
|
101
|
+
const twos=[[2],[2,2],[2,2,2],[2,2,2,2]];
|
|
102
|
+
test("test twos","sum",twos,[2,4,6,8]);
|
|
103
|
+
test("test twos","avg",twos,[2,2,2,2]);
|
|
104
|
+
test("test twos","stdDev",twos,[0,0,0,0]);
|
|
105
|
+
|
|
106
|
+
const c1234=[[1],[1,2],[1,2,3],[1,2,3,4]];
|
|
107
|
+
test("test c1234","sum",c1234,[1,3,6,10]);
|
|
108
|
+
test("test c1234","avg",c1234,[1,1.5,2,10/4]);
|
|
109
|
+
test("test c1234","stdDev",c1234,[0,0.5,0.8164965809277263,1.118033988749895]);
|
|
110
|
+
test("test c1234","sampleStdDev",c1234,[0,0.7071067811865476,1,1.2909944487358056]);
|
|
111
|
+
});
|
|
112
|
+
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const assert=require('assert');
|
|
2
|
+
const should=require("should");
|
|
3
|
+
const ed=require("../dataAnalysis/euclideanDistance.js");
|
|
4
|
+
require("../dataAnalysis/forNestedEach");
|
|
5
|
+
|
|
6
|
+
describe('euclideanDistance', function() {
|
|
7
|
+
it("array forNestedEach", function(done) {
|
|
8
|
+
const atest=[1,2,3,4];
|
|
9
|
+
atest.forNestedEach((e,i,a,l)=>e="a",{0:[2]})
|
|
10
|
+
assert.deepEqual(atest,[ 1, 2, 3, 4 ])
|
|
11
|
+
atest.forNestedEach((e,i,a,l)=>a[i]="a",{0:[2]})
|
|
12
|
+
assert.deepEqual(atest,[ 1, 2, 'a', 4 ]);
|
|
13
|
+
const atest1=[[11,12],[21,22],[31,32],[41,42]];
|
|
14
|
+
atest1.forNestedEach((e,i,a,l)=>a[i]="a",{0:[0,4]})
|
|
15
|
+
assert.deepEqual(atest1 ,[ [ 'a', 'a' ], [ 21, 22 ], [ 31, 32 ], [ 41, 42 ] ])
|
|
16
|
+
done()
|
|
17
|
+
});
|
|
18
|
+
it("array pairs", function(done) {
|
|
19
|
+
assert.strictEqual([].pairs().length,0)
|
|
20
|
+
assert.strictEqual([1].pairs().length,0)
|
|
21
|
+
assert.deepEqual([1,2].pairs(null),[[1,2,0,1]])
|
|
22
|
+
assert.deepEqual([1,2,3].pairs(null),[[1,2,0,1],[1,3,0,2],[2,3,1,2]])
|
|
23
|
+
assert.deepEqual([1,2,3,4].pairs(null),[[1,2,0,1],[1,3,0,2],[1,4,0,3],[2,3,1,2],[2,4,1,3],[3,4,2,3]])
|
|
24
|
+
assert.deepEqual([1,2,3,4].pairs(),[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]])
|
|
25
|
+
assert.deepEqual([[1,2],[3,4]].pairs(),[[[1,2],[3,4]]])
|
|
26
|
+
done()
|
|
27
|
+
});
|
|
28
|
+
it("distance 1d", function(done) {
|
|
29
|
+
assert.strictEqual(ed.distance([],[]), NaN);
|
|
30
|
+
assert.strictEqual(ed.distance([0],[0]), 0);
|
|
31
|
+
assert.strictEqual(ed.distance([0],[1]), 1);
|
|
32
|
+
assert.strictEqual(ed.distance([0],[-1]), 1);
|
|
33
|
+
assert.strictEqual(ed.distance([1],[0]), 1);
|
|
34
|
+
assert.strictEqual(ed.distance([-1],[0]), 1);
|
|
35
|
+
assert.strictEqual(ed.distance([1],[2]), 1);
|
|
36
|
+
assert.strictEqual(ed.distance([2],[1]), 1);
|
|
37
|
+
assert.strictEqual(ed.distance([2],[4]), 2);
|
|
38
|
+
done()
|
|
39
|
+
});
|
|
40
|
+
it("distance 2d", function(done) {
|
|
41
|
+
assert.strictEqual(ed.distance([0,0],[0,0]) ,0);
|
|
42
|
+
assert.strictEqual(ed.distance([0,0],[1,0]) ,1);
|
|
43
|
+
assert.strictEqual(ed.distance([0,0],[0,1]) ,1);
|
|
44
|
+
assert.strictEqual(ed.distance([0,0],[1,1]) ,Math.sqrt(1+1));
|
|
45
|
+
assert.strictEqual(ed.distance([1,0],[0,0]) ,1);
|
|
46
|
+
assert.strictEqual(ed.distance([0,1],[0,0]) ,1);
|
|
47
|
+
assert.strictEqual(ed.distance([1,1],[0,0]) ,Math.sqrt(1+1));
|
|
48
|
+
assert.strictEqual(ed.distance([-1,1],[0,0]) ,Math.sqrt(1+1));
|
|
49
|
+
assert.strictEqual(ed.distance([-1,-1],[0,0]) ,Math.sqrt(1+1));
|
|
50
|
+
assert.strictEqual(ed.distance([1,1],[2,2]), Math.sqrt(1+1));
|
|
51
|
+
assert.strictEqual(ed.distance([0,1],[2,2]), Math.sqrt(2*2+1));
|
|
52
|
+
assert.strictEqual(ed.distance([0,0],[2,3]), Math.sqrt(2*2+3*3));
|
|
53
|
+
done()
|
|
54
|
+
});
|
|
55
|
+
it("distance 3d", function(done) {
|
|
56
|
+
assert.strictEqual(ed.distance([0,0],[0,0]), 0);
|
|
57
|
+
assert.strictEqual(ed.distance([1,1,1],[2,2,2]), Math.sqrt(1+1+1));
|
|
58
|
+
assert.strictEqual(ed.distance([1,1,2],[2,0,4]), Math.sqrt(1+1+2*2));
|
|
59
|
+
assert.strictEqual(ed.distance([0,-1,-2],[0,1,2]), Math.sqrt(0+2*2+4*4));
|
|
60
|
+
done()
|
|
61
|
+
});
|
|
62
|
+
it("distanceColumns 1d", function(done) {
|
|
63
|
+
assert.strictEqual(ed.distanceColumns(), 0);
|
|
64
|
+
assert.strictEqual(ed.distanceColumns([1,2,3],[1,2,3]), 0);
|
|
65
|
+
assert.strictEqual(ed.distanceColumns([1,2,3],[1,2,3],[]), 0);
|
|
66
|
+
assert.strictEqual(ed.distanceColumns([0,0,0],[1,2,3],[0]), 1);
|
|
67
|
+
assert.strictEqual(ed.distanceColumns([0,0,0],[1,2,3],[0,1]), Math.sqrt(1+2*2));
|
|
68
|
+
assert.strictEqual(ed.distanceColumns([0,0,0],[1,2,3],[0,1,2]), Math.sqrt(1+2*2+3*3));
|
|
69
|
+
done()
|
|
70
|
+
});
|
|
71
|
+
it("distances 1d", function(done) {
|
|
72
|
+
assert.deepEqual(ed.distances([[1],[2],[3],[4]]), [[1,0,1],[2,0,2],[3,0,3],[1,1,2],[2,1,3],[1,2,3]] );
|
|
73
|
+
assert.deepEqual(ed.distances([ [1,2,3,4], [11,12,13,14], [111,112,113,114] ]), [[20,0,1],[220,0,2],[200,1,2]]);
|
|
74
|
+
done()
|
|
75
|
+
});
|
|
76
|
+
it("distancesColumns 1d", function(done) {
|
|
77
|
+
assert.deepEqual(ed.distancesColumns([["d",1],["d",2],["d",3],["d",4]],[1]), [[1,0,1],[2,0,2],[3,0,3],[1,1,2],[2,1,3],[1,2,3]] );
|
|
78
|
+
assert.deepEqual(ed.distancesColumns([ ["d",1,2,3,4], ["d",11,12,13,14], ["d",111,112,113,114] ], [1,2,3,4]), [[20,0,1],[220,0,2],[200,1,2]]);
|
|
79
|
+
done()
|
|
80
|
+
});
|
|
81
|
+
it("minDistance 1d", function(done) {
|
|
82
|
+
assert.deepEqual(ed.minDistances([ [1], [2],[3],[4] ]), [{distance:1,points:[[1],[2]],index:[0,1]},{distance:1,points:[[2],[3]],index:[1,2]},{distance:1,points:[[3],[4]],index:[2,3]}]);
|
|
83
|
+
assert.deepEqual(ed.minDistances([ [4], [3],[2],[1] ]), [{distance:1,points:[[4],[3]],index:[0,1]},{distance:1,points:[[3],[2]],index:[1,2]},{distance:1,points:[[2],[1]],index:[2,3]}]);
|
|
84
|
+
done()
|
|
85
|
+
});
|
|
86
|
+
it("maxDistance 1d", function(done) {
|
|
87
|
+
assert.deepEqual(ed.maxDistances([ [1],[2],[3], [4]]), [{distance:3,points:[[1],[4]],index:[0,3]}]);
|
|
88
|
+
done()
|
|
89
|
+
});
|
|
90
|
+
it("maxDistance 2d", function(done) {
|
|
91
|
+
assert.deepEqual(ed.maxDistances([ [1,1],[2,2],[3,3],[4,5]]), [{distance:5,points:[[1,1],[4,5]],index:[0,3]}]);
|
|
92
|
+
done()
|
|
93
|
+
});
|
|
94
|
+
it("maxDistance Columns", function(done) {
|
|
95
|
+
assert.deepEqual(ed.maxDistances([ [1,1,"d"],[2,2,"d"],[3,3,"d"],[4,5,"d"]],[0,1]), [{distance:5,points:[[1,1,"d"],[4,5,"d"]],index:[0,3]}]);
|
|
96
|
+
done()
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
});
|
package/testing/load-injector.js
CHANGED
|
@@ -8,8 +8,9 @@ function thinkTimeTime() {
|
|
|
8
8
|
}
|
|
9
9
|
function nextMessageInjection() {
|
|
10
10
|
if (this.runtimeTimer) {
|
|
11
|
+
this.count++;
|
|
11
12
|
this.receive();
|
|
12
|
-
|
|
13
|
+
const node=this;
|
|
13
14
|
this.nextMessageInjectTimer=setTimeout(function(node){nextMessageInjection.apply(node);},thinkTimeTime.apply(node),node);
|
|
14
15
|
}
|
|
15
16
|
}
|
|
@@ -22,11 +23,15 @@ function runtimeStop() {
|
|
|
22
23
|
clearTimeout(this.nextMessageInjectTimer);
|
|
23
24
|
this.nextMessageInjectTimer=null;
|
|
24
25
|
}
|
|
26
|
+
this.stopped=Date.now();
|
|
27
|
+
this.send([null,{payload:{count:this.count,started:this.started,stopped:this.stopped}}])
|
|
25
28
|
this.status({fill:"red",shape:"ring",text:"Stopped"});
|
|
26
29
|
this.error("Stopped injector");
|
|
27
30
|
}
|
|
28
31
|
function runtimeStart() {
|
|
29
|
-
|
|
32
|
+
const node=this;
|
|
33
|
+
this.started=Date.now();
|
|
34
|
+
this.count=0;
|
|
30
35
|
this.runtimeTimer=true;
|
|
31
36
|
this.runtimeTimer=setTimeout(function(){runtimeStop.apply(node);},this.runtime*1000);
|
|
32
37
|
this.status({fill:"green",shape:"ring",text:"Started"});
|
|
@@ -37,7 +42,7 @@ function runtimeStart() {
|
|
|
37
42
|
module.exports = function (RED) {
|
|
38
43
|
function LoadInjectorNode(n) {
|
|
39
44
|
RED.nodes.createNode(this, n);
|
|
40
|
-
|
|
45
|
+
const node=Object.assign(this,n);
|
|
41
46
|
this.thinktimemin=Number(this.thinktimemin);
|
|
42
47
|
this.thinktimemax=Number(this.thinktimemax);
|
|
43
48
|
if(this.thinktimemax<this.thinktimemin) {
|
|
@@ -96,7 +101,7 @@ module.exports = function (RED) {
|
|
|
96
101
|
|
|
97
102
|
// RED.httpAdmin.post("/loadinjector/:id", RED.auth.needsPermission("inject.write"), function(req,res) {
|
|
98
103
|
RED.httpAdmin.get("/loadinjector/:id", function(req,res) {
|
|
99
|
-
|
|
104
|
+
const node = RED.nodes.getNode(req.params.id);
|
|
100
105
|
if (node && node.type==="Load Injector") {
|
|
101
106
|
try {
|
|
102
107
|
node.warn("Request to "+(node.runtimeTimer?"stop":"start")+" injector");
|
package/testing/test.html
CHANGED
|
@@ -6,9 +6,13 @@
|
|
|
6
6
|
Can select location of property for result on return message.
|
|
7
7
|
For json test positive infinity by property value "Infinity",negative infinity by "-Infinity" and not a number by "NaN".
|
|
8
8
|
</p>
|
|
9
|
+
Error factor allows for numbers to be out by being less than factor abs(expected-result)/expected
|
|
9
10
|
<p>
|
|
10
11
|
A message sent on first port should go thru other nodes and return to test is payload
|
|
11
12
|
is has the expected results. Several test can be arranged.
|
|
13
|
+
</p>
|
|
14
|
+
<p>
|
|
15
|
+
|
|
12
16
|
</p>
|
|
13
17
|
<h3>Inputs</h3>
|
|
14
18
|
If message genrated by this node, then payload checked against expected result
|
|
@@ -37,6 +41,17 @@
|
|
|
37
41
|
</label>
|
|
38
42
|
</div>
|
|
39
43
|
|
|
44
|
+
<div class="form-row form-row-http-in-errorFactor show">
|
|
45
|
+
<label for="node-input-errorFactor" style="white-space: nowrap"><i class="icon-bookmark"></i> Error factor</label>
|
|
46
|
+
<input type="number" id="node-input-errorFactor" list="defaultErrorFactors" >
|
|
47
|
+
<datalist id="defaultErrorFactors">
|
|
48
|
+
<option value="0">
|
|
49
|
+
<option value="0.001">
|
|
50
|
+
<option value="0.000001">
|
|
51
|
+
<option value="0.000000001">
|
|
52
|
+
</datalist>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
40
55
|
<div class="form-row form-row-http-in-resultProperty show">
|
|
41
56
|
<label for="node-input-resultProperty" style="white-space: nowrap"><i class="icon-bookmark"></i> Property </label>
|
|
42
57
|
<input type="text" id="node-input-resultProperty" placeholder="msg.payload">
|
|
@@ -61,6 +76,7 @@
|
|
|
61
76
|
color:"#a6bbcf",
|
|
62
77
|
defaults: {
|
|
63
78
|
name: {value:""},
|
|
79
|
+
errorFactor: {value:0,required:false},
|
|
64
80
|
escapeString: {value:false,required:false},
|
|
65
81
|
infiniteIsNull: {value:false,required:false},
|
|
66
82
|
payload: {value:"", validate: RED.validators.typedInput("payloadType")},
|
package/testing/test.js
CHANGED
|
@@ -29,17 +29,19 @@ function setError(msg,node,err) {
|
|
|
29
29
|
node.send([null,msg]);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function equalObjects(obj1,obj2) {
|
|
32
|
+
function equalObjects(obj1,obj2,errorFactor) {
|
|
33
33
|
if( obj1 === obj2 ) return true;
|
|
34
34
|
if( obj1 === Number.POSITIVE_INFINITY && obj2==="Infinity") return true;
|
|
35
35
|
if( obj1 === Number.NEGATIVE_INFINITY && obj2==="-Infinity") return true;
|
|
36
36
|
if( Number.isNaN(obj1) && obj2==="NaN") return true;
|
|
37
|
-
|
|
37
|
+
const obj1type=typeof obj1;
|
|
38
|
+
if( obj1type != typeof obj2 ) return false;
|
|
39
|
+
if(errorFactor && obj1type=="number") return (Math.abs(obj2-obj1)/obj2)<errorFactor;
|
|
38
40
|
if( !(obj1 instanceof Object) ) return false;
|
|
39
41
|
if( Object.keys(obj1).length !== Object.keys(obj2).length ) return false;
|
|
40
42
|
try{
|
|
41
43
|
for(let key in obj1) {
|
|
42
|
-
if( !equalObjects(obj1[key],obj2[key]) ) return false;
|
|
44
|
+
if( !equalObjects(obj1[key],obj2[key],errorFactor) ) return false;
|
|
43
45
|
}
|
|
44
46
|
} catch(e) {
|
|
45
47
|
return false;
|
|
@@ -69,7 +71,7 @@ module.exports = function(RED) {
|
|
|
69
71
|
try{
|
|
70
72
|
if(msg._test.id!==node.id) {
|
|
71
73
|
setError(msg,node,"Sent by another test "+msg._test.id);
|
|
72
|
-
} else if(!equalObjects(node.getData(msg,node),msg._test.result)) {
|
|
74
|
+
} else if(!equalObjects(node.getData(msg,node),msg._test.result,node.errorFactor)) {
|
|
73
75
|
msg._test.testedValue=node.getData(msg,node);
|
|
74
76
|
setError(msg,node,"Test failed");
|
|
75
77
|
} else {
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
const throwError=(msg,p)=>{throw Object.assign(new Error(msg),p)}
|
|
2
|
+
const Uint8ArrayFromBase64=(b64)=>new Uint8Array(Buffer.from(b64, "base64"))
|
|
3
|
+
const base64FromUint8Array=(a)=>Buffer.toString(a, "base64")
|
|
4
|
+
const hexFromArray=(a)=>Buffer.from(a).toString('hex')
|
|
5
|
+
const UInt8ToBinary=(a)=> {
|
|
6
|
+
const buf = Buffer.allocUnsafe(a.length);
|
|
7
|
+
a.forEach((c,i)=>buf.writeUInt8(c,i))
|
|
8
|
+
return buf
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const test={
|
|
12
|
+
PrintableString:(s)=>{if(s.test(/^[a-z0-9 '()+,-./:=?]*$/i)) return; throw Error("invalid PrintableString")}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Classes() {}
|
|
16
|
+
Classes.prototype.UNIVERSAL=0b00000000;
|
|
17
|
+
Classes.prototype.APPLICATION=0b01000000;
|
|
18
|
+
Classes.prototype.CONTEXTSPECIFIC=0b10000000;
|
|
19
|
+
Classes.prototype.PRIVATE=0b11000000;
|
|
20
|
+
Classes.prototype.values={1:Classes.prototype.UNIVERSAL,2:Classes.prototype.APPLICATION,3:Classes.prototype.CONTEXTSPECIFIC,4:Classes.prototype.PRIVATE};
|
|
21
|
+
Classes.prototype.names={1:"UNIVERSAL",2:"APPLICATION",3:"CONTEXT-SPECIFIC",4:"PRIVATE"};
|
|
22
|
+
Classes.prototype.setOctet=(octet,tag)=>octet|=(Classes.prototype.values[tag]);
|
|
23
|
+
Classes.prototype.setOctetName=(octet,tagName)=>octet|=(Classes.prototype[tagName]);
|
|
24
|
+
Classes.prototype.getOctetValue=(octet)=>octet>>6+1;
|
|
25
|
+
//Classes.prototype.getOctetTag=(octet)=>octet|=Classes.prototype[tag];
|
|
26
|
+
//Classes.prototype.toString=(octet)=>classes.names[Classes.getOctetTag(octet)];
|
|
27
|
+
const classes=new Classes()
|
|
28
|
+
|
|
29
|
+
function PC() {}
|
|
30
|
+
PC.prototype.P=0b0000;
|
|
31
|
+
PC.prototype.Primitive=0b00000000;
|
|
32
|
+
PC.prototype.C=0b00100000;
|
|
33
|
+
PC.prototype.Constructed =0b00100000;
|
|
34
|
+
PC.prototype.isPrimitive =(octet)=>octet&0b00000000;
|
|
35
|
+
PC.prototype.isConstructed =(octet)=>octet&0b00100000;
|
|
36
|
+
PC.prototype.setOctet=(octet)=>octet|=(classes[tag]);
|
|
37
|
+
PC.prototype.toString=(octet)=>pc.isPrimitive(octet)?"Primitive":"Constructed";
|
|
38
|
+
PC.prototype.set=(octet,action)=>octet=(octet^0b00100000)&pc[action];
|
|
39
|
+
const pc=new PC();
|
|
40
|
+
|
|
41
|
+
function Octet1(v) {
|
|
42
|
+
this.value=v
|
|
43
|
+
}
|
|
44
|
+
Octet1.prototype.getClass=()=>Classes.getOctetValue(this.value);
|
|
45
|
+
Octet1.prototype.getTag=()=>this.value&0b00011111;
|
|
46
|
+
Octet1.prototype.isLongForm=()=>(this.value^0b10000000) & (this.value^0b01111111);
|
|
47
|
+
Octet1.prototype.toJSON=()=>{return {"class":this.getClass(this.value), tag:this.getTag(this.value), longForm:this.isLongForm(this.value)};};
|
|
48
|
+
Octet1.prototype.toString=function(){const r=this.toJSON();r.className=classes.names(r.classID) ;return JSON.stringify(r)};
|
|
49
|
+
Octet1.prototype.isConstructed=()=>pc.isConstructed(this.value);
|
|
50
|
+
|
|
51
|
+
function IdentifierOctets(dataArray,i){
|
|
52
|
+
if(dataArray) this.decode(dataArray,i)
|
|
53
|
+
}
|
|
54
|
+
IdentifierOctets.prototype.decode=function (dataArray,i){
|
|
55
|
+
this.tags=[];
|
|
56
|
+
if(!dataArray.length) return;
|
|
57
|
+
const octet1=new Octet1(dataArray[i++]);
|
|
58
|
+
this.tagClass=octet1.getClass();
|
|
59
|
+
this.isConstructed=octet1.isConstructed();
|
|
60
|
+
if(octet1.isLongForm()) //isLongForm
|
|
61
|
+
this.tags.push(octet1.getTag());
|
|
62
|
+
else {
|
|
63
|
+
let more=true
|
|
64
|
+
while (more) {
|
|
65
|
+
const octet=dataArray[i++];
|
|
66
|
+
const tag=octet&0b01111111;
|
|
67
|
+
if(tag>0x24) throw Error("tag = 0x00")
|
|
68
|
+
if(tag>0x24) throw Error("tag > 0x24")
|
|
69
|
+
this.tags.push(tag);
|
|
70
|
+
more=octet&0b1000000;;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
IdentifierOctets.prototype.toJSON=function (){
|
|
75
|
+
return {"class":this.tagClass,constructed:this.isConstructed,tags:this.tags}
|
|
76
|
+
}
|
|
77
|
+
IdentifierOctets.prototype.toJSONExtended=function (){
|
|
78
|
+
const r=this.toJSON();
|
|
79
|
+
r.tags=r.tags.map(c=>({id:c,name:tags[c].name}));
|
|
80
|
+
return r
|
|
81
|
+
};
|
|
82
|
+
IdentifierOctets.prototype.toString==function (){
|
|
83
|
+
return JSON.stringify(this.toJSONExtended())
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
//Basic Encoding Rules (BER)
|
|
87
|
+
function BERencoding(dataArray,i=0) {
|
|
88
|
+
//Identifier octets Type i++
|
|
89
|
+
// if(!(dataArray instanceof Uint8Array) ) throw Error("data not Uint8Array but "+dataArray.constructor.name)
|
|
90
|
+
this.decodeBER(dataArray,i);
|
|
91
|
+
}
|
|
92
|
+
BERencoding.prototype.encode=function(v){
|
|
93
|
+
for(const p in v) {
|
|
94
|
+
switch (p) {
|
|
95
|
+
case "Identifier":
|
|
96
|
+
this.setIdentifier(v[p])
|
|
97
|
+
next;
|
|
98
|
+
default:
|
|
99
|
+
throw Error("unknown base property "+p)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
BERencoding.prototype.setIdentifier=function(v){
|
|
104
|
+
for(const p in v) {
|
|
105
|
+
switch (p) {
|
|
106
|
+
case "class":
|
|
107
|
+
this.tagClass=v[p]
|
|
108
|
+
next;
|
|
109
|
+
case "tag":
|
|
110
|
+
this.tags=[v[p]]
|
|
111
|
+
next;
|
|
112
|
+
case "constructed":
|
|
113
|
+
this.isConstructed=v[p]
|
|
114
|
+
next;
|
|
115
|
+
default:
|
|
116
|
+
throw Error("unknown identifier property "+p)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
BERencoding.prototype.setSchema=function(schema){
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
BERencoding.prototype.decodeBER=function(dataArray,i){
|
|
124
|
+
if(dataArray.constructor.name!=="Uint8Array") throw Error("data not Uint8Array but "+dataArray.constructor.name)
|
|
125
|
+
this.identifier=new IdentifierOctets(dataArray,i);
|
|
126
|
+
if(dataArray[i]===0xFF) { //isIndefinite
|
|
127
|
+
const eocIndex=dataArray.indexOf(0x00,i)
|
|
128
|
+
this.data=dataArray.slice(i,eocIndex-1);
|
|
129
|
+
i=eocIndex+1;
|
|
130
|
+
} else {
|
|
131
|
+
const endPosition=i+LengthOctet.prototype.getLength(dataArray,i);
|
|
132
|
+
this.data=dataArray.slice(i,endPosition);
|
|
133
|
+
i=endPosition+1;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
BERencoding.prototype.decode=BERencoding.prototype.decodeBER;
|
|
137
|
+
BERencoding.prototype.toJSON= function(){
|
|
138
|
+
return {identifier:this.identifier.toJSONExtended(),data:this.data,dataHex:this.data2Hex()};
|
|
139
|
+
}
|
|
140
|
+
BERencoding.prototype.toString= function(){
|
|
141
|
+
return JSON.stringify(this.toJSON());
|
|
142
|
+
}
|
|
143
|
+
BERencoding.prototype.data2Hex= function(){
|
|
144
|
+
const r=[];
|
|
145
|
+
this.data.forEach(c=>r.push("0x"+c.toString(16)))
|
|
146
|
+
return r;
|
|
147
|
+
}
|
|
148
|
+
const constructPrimitive=null;
|
|
149
|
+
const constructConstructed=false
|
|
150
|
+
const constructBoth=true;
|
|
151
|
+
const constructReserved=undefined;
|
|
152
|
+
const PrintableString2Value=(a,i,l)=>{
|
|
153
|
+
const s=i;
|
|
154
|
+
i=i+l;
|
|
155
|
+
return a.slice(s,i)
|
|
156
|
+
};
|
|
157
|
+
const Value2PrintableString=(v)=>{
|
|
158
|
+
test.PrintableString(v);
|
|
159
|
+
return v;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const tags=[
|
|
163
|
+
{name:"End-of-Content (EOC)",permittedConstruction:constructPrimitive},
|
|
164
|
+
{name:"BOOLEAN",permittedConstruction:constructPrimitive,translatedTo:(v)=>v?0x01:0x00,translatedFrom:(a,i)=>a[i++]==0x01},
|
|
165
|
+
{name:"INTEGER",permittedConstruction:constructPrimitive},
|
|
166
|
+
{name:"BIT STRING",permittedConstruction:constructBoth},
|
|
167
|
+
{name:"OCTET STRING",permittedConstruction:constructBoth},
|
|
168
|
+
{name:"NULL",permittedConstruction:constructPrimitive},
|
|
169
|
+
{name:"OBJECT IDENTIFIER",permittedConstruction:constructPrimitive},
|
|
170
|
+
{name:"Object Descriptor",permittedConstruction:constructBoth},
|
|
171
|
+
{name:"EXTERNAL",permittedConstruction:constructConstructed},
|
|
172
|
+
{name:"REAL (float)",permittedConstruction:constructPrimitive},
|
|
173
|
+
{name:"ENUMERATED",permittedConstruction:constructPrimitive},
|
|
174
|
+
{name:"EMBEDDED PDV",permittedConstruction:constructConstructed},
|
|
175
|
+
{name:"UTF8String",permittedConstruction:constructBoth},
|
|
176
|
+
{name:"RELATIVE-OID",permittedConstruction:constructPrimitive},
|
|
177
|
+
{name:"TIME",permittedConstruction:constructPrimitive},
|
|
178
|
+
{name:"Reserved",permittedConstruction:constructReserved},
|
|
179
|
+
{name:"SEQUENCE and SEQUENCE OF",permittedConstruction:constructConstructed},
|
|
180
|
+
{name:"SET and SET OF",permittedConstruction:constructConstructed},
|
|
181
|
+
{name:"NumericString",permittedConstruction:constructBoth},
|
|
182
|
+
{name:"PrintableString",permittedConstruction:constructBoth,translatedTo:Value2PrintableString,translatedFrom:PrintableString2Value},
|
|
183
|
+
{name:"T61String",permittedConstruction:constructBoth},
|
|
184
|
+
{name:"VideotexString",permittedConstruction:constructBoth},
|
|
185
|
+
{name:"IA5String",permittedConstruction:constructBoth},
|
|
186
|
+
{name:"UTCTime",permittedConstruction:constructBoth},
|
|
187
|
+
{name:"GeneralizedTime",permittedConstruction:constructBoth},
|
|
188
|
+
{name:"GraphicString",permittedConstruction:constructBoth},
|
|
189
|
+
{name:"VisibleString",permittedConstruction:constructBoth},
|
|
190
|
+
{name:"GeneralString",permittedConstruction:constructBoth},
|
|
191
|
+
{name:"UniversalString",permittedConstruction:constructBoth},
|
|
192
|
+
{name:"CHARACTER STRING",permittedConstruction:constructConstructed},
|
|
193
|
+
{name:"BMPString",permittedConstruction:constructBoth},
|
|
194
|
+
{name:"DATE",permittedConstruction:constructPrimitive},
|
|
195
|
+
{name:"TIME-OF-DAY",permittedConstruction:constructPrimitive},
|
|
196
|
+
{name:"DATE-TIME",permittedConstruction:constructPrimitive},
|
|
197
|
+
{name:"DURATION",permittedConstruction:constructPrimitive},
|
|
198
|
+
{name:"OID-IRI",permittedConstruction:constructPrimitive},
|
|
199
|
+
{name:"RELATIVE-OID-IRI",permittedConstruction:constructPrimitive},
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
function LengthOctet() {}
|
|
203
|
+
LengthOctet.prototype.definiteShort=0x00;
|
|
204
|
+
LengthOctet.prototype.definiteLong=0xC0;
|
|
205
|
+
LengthOctet.prototype.indefinite= 0xC0;
|
|
206
|
+
LengthOctet.prototype.reserved=0xFF;
|
|
207
|
+
LengthOctet.prototype.isIndefinite= (o)=>o&0xC0;
|
|
208
|
+
LengthOctet.prototype.octets=(o)=>o^0b01111111;
|
|
209
|
+
LengthOctet.prototype.getLength=(octets,i)=>{
|
|
210
|
+
const octet=octets[i++];
|
|
211
|
+
if(octet===0xC0) return undefined;
|
|
212
|
+
if(octet&0xC0) return octet; //isDefiniteShort
|
|
213
|
+
if(octet===0xFF) return NaN; //isReserved
|
|
214
|
+
let l=octet&0b01111111; // length
|
|
215
|
+
let length=octets[i++];
|
|
216
|
+
while (l--) length=length*256+octets[i++]
|
|
217
|
+
if(length < 0) throw new Error('Negative length: ' + length);
|
|
218
|
+
return length;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Canonical Encoding Rules (CER)
|
|
222
|
+
function CERencoding(dataArray,i=0) {
|
|
223
|
+
throw Error("to be done")
|
|
224
|
+
}
|
|
225
|
+
//Distinguished Encoding Rules (DER)
|
|
226
|
+
function DERencoding(dataArray,i=0) {
|
|
227
|
+
throw Error("to be done")
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
module.exports={
|
|
231
|
+
base64:Uint8ArrayFromBase64,
|
|
232
|
+
hex:hexFromArray,
|
|
233
|
+
decode:(dataArray,i)=>new BERencoding(dataArray,i),
|
|
234
|
+
decodeBER:(dataArray,i)=>new BERencoding(dataArray,i),
|
|
235
|
+
decodeBase64:(v)=>BERencoding(arrayFromBase46(v)),
|
|
236
|
+
encode:BERencoding
|
|
237
|
+
}
|