node-red-contrib-prib-functions
Advanced tools
Comparing version 0.10.0 to 0.10.1
@@ -1,10 +0,127 @@ | ||
const nodeLabel="Data Analysis"; | ||
const Logger = require("node-red-contrib-logger"); | ||
const logger = new Logger(nodeLabel); | ||
const logger = new (require("node-red-contrib-logger"))("Data Analysis"); | ||
logger.sendInfo("Copyright 2020 Jaroslav Peter Prib"); | ||
Object.defineProperty(Array.prototype, "last", { | ||
value() { | ||
return this[this.length-1]; | ||
}, | ||
writable: true, | ||
configurable: true | ||
}); | ||
function crossNormalisedDeltas() { | ||
const dataPoints=this.dataPoint; | ||
const keys=Object.keys(dataPoints); | ||
const normalisedAvgDelta=keys.reduce((a,c,i)=>a+=dataPoints[c].normalised,0)/keys.length; | ||
return keys.map(c=>{ | ||
return {key:c,value:dataPoints[c].normalised-normalisedAvgDelta}; | ||
}).sort((a,b)=>a.value-b.value); | ||
} | ||
function predictForKey(node,key) { | ||
const dp=node.dataPoint[key]; | ||
if(dp) return new Predictions(dp); | ||
throw Error("no data points"); | ||
} | ||
function Predictions(dp) { | ||
const predictions=[]; | ||
if(!dp) throw Error("no data points"); | ||
const lastValue=dp.value.last(),lastValue2=lastValue*2; | ||
if(dp.delta) { | ||
const delta=dp.delta, | ||
lastDeltaValue=delta.value.last(), | ||
deltaMovingAvg=delta.movingAvg, | ||
deltaWeightedMovingAvg=delta.weightedMovingAvg; | ||
predictions.push(lastValue+lastDeltaValue); | ||
predictions.push(lastValue+deltaMovingAvg); | ||
predictions.push(lastValue+deltaWeightedMovingAvg); | ||
predictions.push(lastValue2-dp.exponentialWeightedMoving[0].movingAverage); | ||
predictions.push(lastValue+delta.exponentialWeightedMoving[0].movingAverage); | ||
predictions.push(lastValue2-dp.exponentialWeightedMoving[1].movingAverage); | ||
predictions.push(lastValue+delta.exponentialWeightedMoving[1].movingAverage); | ||
predictions.push(lastValue2-dp.exponentialWeightedMoving[2].movingAverage); | ||
predictions.push(lastValue+delta.exponentialWeightedMoving[2].movingAverage); | ||
} | ||
this.prediction=predictions; | ||
return this; | ||
} | ||
Predictions.prototype.validate=function(value) { | ||
this.accuracy=this.predictions.map(c=>Math.abs(c-value)/c); | ||
return this; | ||
}; | ||
function realtimePredict(d,term,node) { | ||
const m=functions.realtime(d,term,node); | ||
m.predict=predict(m); | ||
return m; | ||
} | ||
function EMA(coefficient=0.5) { | ||
if(coefficient<0 || coefficient>1 ) throw Error("coefficient must be between 0 and 1"); | ||
this.factor=(1-coefficient); | ||
this.weightedSum=0; | ||
this.weightedCount=0; | ||
this.movingAverage=0; | ||
return this; | ||
} | ||
EMA.prototype.sample=function(value) { | ||
this.weightedSum=value+this.factor*this.weightedSum; | ||
this.weightedCount=1+this.factor*this.weightedSum; | ||
this.movingAverage=this.weightedSum/this.weightedCount; | ||
return this; | ||
} | ||
function setDataPoint(value,term,node,dp) { | ||
if(logger.active) logger.send({label:"setDataPoint",value:value,term,dp}); | ||
if(!dp.values) { | ||
Object.assign(dp,{ | ||
values:[], | ||
avg:0, | ||
count:0, | ||
movingSum:0, | ||
movingSumSquared:0, | ||
movingSumCubed:0, | ||
outlier:false, | ||
sum:0, | ||
sumSquared:0, | ||
sumCubed:0, | ||
term:term, | ||
weightedMovingSum:0, | ||
exponentialWeightedMoving:[new EMA(0.25),new EMA(0.5),new EMA(0.75)] | ||
}); | ||
} | ||
; | ||
const count=++dp.count,values=dp.values; | ||
values.push(value); | ||
dp.isMaxSize=(values.length>dp.term); | ||
dp.removedValue=(dp.isMaxSize?values.shift():0); | ||
const removedValue=dp.removedValue; | ||
dp.max=Math.max(dp.max||value,value); | ||
dp.min=Math.min(dp.min||value,value); | ||
dp.range=dp.max-dp.min; | ||
dp.sum+=value; | ||
dp.sumSquared+=Math.pow(value,2); | ||
dp.sumCubed+=Math.pow(value,3); | ||
dp.movingSum+=value-removedValue; | ||
dp.movingSumSquared+=Math.pow(value,2)-Math.pow(removedValue,2); | ||
dp.movingSumCubed+=Math.pow(value,3)-Math.pow(removedValue,3); | ||
dp.avg=dp.sum/count; | ||
const avg=dp.avg; | ||
dp.normalised=dp.range ? (value-avg)/dp.range : 0; | ||
dp.movingAvg=dp.movingSum/values.length; | ||
dp.variance=dp.sumSquared/count - Math.pow(avg,2); | ||
dp.stdDev=Math.sqrt(dp.variance); | ||
dp.movingVariance=dp.movingSumSquared/values.length - Math.pow(dp.movingAvg,2); | ||
dp.movingStdDev=Math.sqrt(dp.movingVariance); | ||
dp.median=functions.median(values); | ||
dp.standardized=( (value-avg)/dp.stdDev )||0; | ||
dp.movingStandardized=( (value-dp.movingAvg)/dp.movingStdDev )||0; | ||
dp.skewness=(dp.sumCubed-3*avg*dp.variance-Math.pow(avg,3))/dp.variance*dp.stdDev; | ||
dp.movingSkewness=(dp.movingSumCubed-3*dp.movingAvg*dp.movingVariance-Math.pow(dp.movingAvg,3))/dp.movingVariance*dp.stdDev; | ||
dp.outlier=node.outliersFunction(node,dp,value); | ||
dp.weightedMovingSum+=count*value; | ||
dp.weightedMovingAvg=(dp.weightedMovingAvg*2/count)/(count+1); | ||
dp.exponentialWeightedMoving.forEach(c=>c.sample(value)); | ||
} | ||
functions={ | ||
avg:(d)=>functions.sum(d)/d.length, | ||
deltas :(d)=>d.map( (c,i,a)=>c-(d[i-1]||0) ), | ||
deltaNormalised :(d)=>d.map( (c,i,a)=>(c-(d[i-1]||0)) / (d[i-1]||0) ), | ||
deltas :(d)=>d.map( (c,i)=>c-(d[i-1]||0) ), | ||
deltaNormalised :(d)=>d.map( (c,i)=>(c-(d[i-1]||0)) / (d[i-1]||0) ), | ||
max: (d)=> Math.max(...d), | ||
@@ -22,3 +139,3 @@ median:(d)=>{ | ||
let avg=0; | ||
return d.map( (c,i,a)=>{ avg+=(c-avg)/(i+1); return avg; }); | ||
return d.map( (c,i)=>{ avg+=(c-avg)/(i+1); return avg; }); | ||
}, | ||
@@ -29,3 +146,3 @@ movingAvgExponential:(d,f)=>{ | ||
let s=d[0],fi=1-f; | ||
return d.map( (c,i,a)=>{ | ||
return d.map( (c)=>{ | ||
s=c*f+fi*s; | ||
@@ -46,3 +163,3 @@ return s; | ||
const range=functions.range(d); | ||
if(range==0) return d.map(c=>0); | ||
if(range==0) return d.map(()=>0); | ||
const avg=functions.avg(d), | ||
@@ -103,3 +220,3 @@ offset=avg/range; | ||
stdDev=functions.stdDev(d); | ||
return d.map( (c,i,a)=>(c-avg)/stdDev); | ||
return d.map( (c)=>(c-avg)/stdDev); | ||
}, | ||
@@ -115,3 +232,6 @@ stdDev:(d)=>Math.sqrt(functions.variance(d)), | ||
}, | ||
realtimePredict: realtimePredict, | ||
realtime:(d,term,node)=>{ | ||
if(!d.key) throw Error("key is null, "+JSON.stringify(d)); | ||
if(!d.value) throw Error("value is null "+JSON.stringify(d)); | ||
let dp; | ||
@@ -121,43 +241,11 @@ if(d.key in node.dataPoint) { | ||
} else { | ||
dp={key:d.key, | ||
values:[], | ||
avg:0, | ||
count:0, | ||
movingSum:0, | ||
movingSumSquared:0, | ||
movingSumCubed:0, | ||
outlier:false, | ||
sum:0, | ||
sumSquared:0, | ||
sumCubed:0, | ||
term:term | ||
}; | ||
dp={key:d.key}; | ||
node.dataPoint[d.key]=dp; | ||
} | ||
++dp.count; | ||
dp.values.push(d.value); | ||
dp.isMaxSize=(dp.values.length>dp.term); | ||
dp.removedValue=(dp.isMaxSize?dp.values.shift():0); | ||
dp.max=Math.max(dp.max||d.value,d.value); | ||
dp.min=Math.min(dp.min||d.value,d.value); | ||
dp.range=dp.max-dp.min; | ||
dp.sum+=d.value; | ||
dp.sumSquared+=Math.pow(d.value,2); | ||
dp.sumCubed+=Math.pow(d.value,3); | ||
dp.movingSum+=d.value-dp.removedValue; | ||
dp.movingSumSquared+=Math.pow(d.value,2)-Math.pow(dp.removedValue,2); | ||
dp.movingSumCubed+=Math.pow(d.value,3)-Math.pow(dp.removedValue,3); | ||
dp.normalised=dp.range ? (d.value-dp.avg)/dp.range : 0; | ||
dp.avg=dp.sum/dp.count; | ||
dp.movingAvg=dp.movingSum/dp.values.length; | ||
dp.variance=dp.sumSquared/dp.count - Math.pow(dp.avg,2); | ||
dp.stdDev=Math.sqrt(dp.variance); | ||
dp.movingVariance=dp.movingSumSquared/dp.values.length - Math.pow(dp.movingAvg,2); | ||
dp.movingStdDev=Math.sqrt(dp.movingVariance); | ||
dp.median=functions.median(dp.values); | ||
dp.standardized=( (d.value-dp.avg)/dp.stdDev )||0; | ||
dp.movingStandardized=( (d.value-dp.movingAvg)/dp.movingStdDev )||0; | ||
dp.skewness=(dp.sumCubed-3*dp.avg*dp.variance-Math.pow(dp.avg,3))/dp.variance*dp.stdDev; | ||
dp.movingSkewness=(dp.movingSumCubed-3*dp.movingAvg*dp.movingVariance-Math.pow(dp.movingAvg,3))/dp.movingVariance*dp.stdDev; | ||
dp.outlier=node.outliersFunction(node,dp,d); | ||
setDataPoint(d.value,term,node,dp); | ||
if(dp.delta) { | ||
setDataPoint(d.value-dp.values[dp.values.length-2],term,node,dp.delta); | ||
} else { | ||
dp.delta={}; | ||
} | ||
return dp; | ||
@@ -174,104 +262,113 @@ }, | ||
module.exports = function (RED) { | ||
function dataAnalysisNode(n) { | ||
RED.nodes.createNode(this, n); | ||
let node=Object.assign(this,{outliersStdDevs:3},n,{maxErrorDisplay:10,dataPoint:{}}); | ||
try{ | ||
if(functions.hasOwnProperty(node.action)) { | ||
node.actionfunction=functions[node.action]; | ||
} else { | ||
throw Error("action not found"); | ||
} | ||
switch (node.action) { | ||
case "realtime": | ||
node.outliersStdDevs=Number.parseInt(node.outliersStdDevs,10)||3; | ||
if(![1,2,3].includes(node.outliersStdDevs)) throw Error("outlier std deviation "+node.outliersStdDevs+" not 1,2 or 3"); | ||
const outliersFunction=(node.outliersBase||"avg")=="median"; | ||
node.log("realtime outliersBase set avg "+outliersFunction); | ||
node.outliersFunction=(outliersFunction | ||
?(node,dp,d)=>{ | ||
const standardized=Math.abs(((d.value-dp.median)/dp.stdDev )||0); | ||
// if(logger.active) logger.send({label:"outlier median",standardized:standardized,outliersStdDevs:node.outliersStdDevs,}); | ||
return Math.abs(standardized)>node.outliersStdDevs; | ||
} | ||
:(node,dp,d)=>{ | ||
// if(logger.active) logger.send({label:"outlier avg",standardized:dp.standardized,outliersStdDevs:node.outliersStdDevs}); | ||
return Math.abs(dp.standardized)>node.outliersStdDevs; | ||
}); | ||
node.getDatafunction= "((msg,node)=>{return {key:"+node.keyProperty+",value:"+(node.dataProperty||"msg.payload")+"};})"; | ||
break; | ||
case "pearsonR": | ||
node.getDatafunction= "((msg,node)=>{return ["+node.dataProperties.join(',')+"];})"; | ||
break; | ||
default: | ||
node.getDatafunction= "((msg,node)=>"+(node.dataProperty||"msg.payload")+")"; | ||
} | ||
node.log("get data function: "+node.getDatafunction); | ||
node.getData= eval(node.getDatafunction); | ||
node.status({fill:"green",shape:"ring",text:"Ready"}); | ||
} catch(e) { | ||
logger.send({label:"initialise error",node:n}); | ||
node.error(e); | ||
node.status({fill:"red",shape:"ring",text:"Invalid setup "+e.toString()}); | ||
} | ||
node.on("input", function(msg) { | ||
if(msg.topic && msg.topic.startsWith("@stats")) { | ||
try{ | ||
const topic=msg.topic.trim(' '); | ||
if(topic=="@stats set") { | ||
node.dataPoint=msg.payload; | ||
node.warn(topic); | ||
} else if(topic=="@stats reset") { | ||
node.dataPoint={}; | ||
node.warn(topic); | ||
} else if(topic.startsWith("@stats set ")) { | ||
const dataPoint=topic.substring("@stats set ".length); | ||
node.dataPoint[dataPoint]=msg.payload; | ||
node.warn(topic); | ||
} else if(topic.startsWith("@stats reset ")) { | ||
const dataPoint=topic.substring("@stats reset ".length); | ||
delete node.dataPoint[dataPoint]; | ||
node.warn(topic); | ||
} else { | ||
switch(node.action) { | ||
case "realtime": | ||
msg.result=node.dataPoint; | ||
break | ||
case "pearsonR": | ||
msg.result=functions.pearsonRResults(node); | ||
break | ||
} | ||
node.send([null,msg]); | ||
} | ||
} catch(e) { | ||
node.error(topic+" failed "+e); | ||
} | ||
return; | ||
} | ||
try{ | ||
msg.result=node.actionfunction.apply(node,[node.getData(msg,node),node.term,node]); | ||
switch(node.action) { | ||
case "realtime": | ||
if(msg.result.outlier) { | ||
node.send([msg,null,msg]); | ||
return; | ||
} | ||
break; | ||
} | ||
} catch(e) { | ||
msg.error=e.message; | ||
if(node.maxErrorDisplay) { | ||
--node.maxErrorDisplay; | ||
if(node.action=="realtime") { | ||
node.error(node.action+" error: "+e.toString()); | ||
} else { | ||
node.error(Array.isArray(node.getData(msg,node))? node.action+" error: "+e.toString() : "payload not array"); | ||
} | ||
node.status({fill:"red",shape:"ring",text:"error(s)"}); | ||
} | ||
} | ||
function dataAnalysisNode(n) { | ||
RED.nodes.createNode(this, n); | ||
const node=Object.assign(this, | ||
{outliersStdDevs:3,crossNormalisedDeltas:crossNormalisedDeltas.bind(this)}, | ||
n, | ||
{maxErrorDisplay:10,dataPoint:{}} | ||
); | ||
try{ | ||
if(functions.hasOwnProperty(node.action)) { | ||
node.actionfunction=functions[node.action]; | ||
} else { | ||
throw Error("action not found"); | ||
} | ||
switch (node.action) { | ||
case "realtime": | ||
node.outliersStdDevs=Number.parseInt(node.outliersStdDevs,10)||3; | ||
if(![1,2,3].includes(node.outliersStdDevs)) throw Error("outlier std deviation "+node.outliersStdDevs+" not 1,2 or 3"); | ||
const outliersFunction=(node.outliersBase||"avg")=="median"; | ||
node.log("realtime outliersBase set avg "+outliersFunction); | ||
node.outliersFunction=(outliersFunction | ||
?(node,dp,value)=>{ | ||
const standardized=Math.abs(((value-dp.median)/dp.stdDev )||0); | ||
// if(logger.active) logger.send({label:"outlier median",standardized:standardized,outliersStdDevs:node.outliersStdDevs,}); | ||
return Math.abs(standardized)>node.outliersStdDevs; | ||
} | ||
:(node,dp,value)=>{ | ||
// if(logger.active) logger.send({label:"outlier avg",standardized:dp.standardized,outliersStdDevs:node.outliersStdDevs}); | ||
return Math.abs(dp.standardized)>node.outliersStdDevs; | ||
}); | ||
node.getDatafunction= "((msg,node)=>{return {key:"+node.keyProperty+",value:"+(node.dataProperty||"msg.payload")+"};})"; | ||
break; | ||
case "pearsonR": | ||
node.getDatafunction= "((msg,node)=>{return ["+node.dataProperties.join(',')+"];})"; | ||
break; | ||
default: | ||
node.getDatafunction= "((msg,node)=>"+(node.dataProperty||"msg.payload")+")"; | ||
} | ||
node.log("get data function: "+node.getDatafunction); | ||
node.getData=eval(node.getDatafunction); | ||
node.status({fill:"green",shape:"ring",text:"Ready"}); | ||
} catch(ex) { | ||
logger.send({label:"initialise error",node:n}); | ||
node.error(ex); | ||
node.status({fill:"red",shape:"ring",text:"Invalid setup "+ex.message}); | ||
} | ||
node.on("input", function(msg) { | ||
if(msg.topic && msg.topic.startsWith("@")) { | ||
try{ | ||
const topic=msg.topic.trim(' '); | ||
if(topic=="@stats") { | ||
switch(node.action) { | ||
case "realtime": | ||
msg.result=node.dataPoint; | ||
break; | ||
case "pearsonR": | ||
msg.result=functions.pearsonRResults(node); | ||
break; | ||
} | ||
node.send([null,msg]); | ||
} else if(topic=="@stats set") { | ||
node.dataPoint=msg.payload; | ||
node.warn(topic); | ||
} else if(topic=="@stats reset") { | ||
node.dataPoint={}; | ||
node.warn(topic); | ||
} else if(topic.startsWith("@stats set ")) { | ||
const dataPoint=topic.substring("@stats set ".length); | ||
node.dataPoint[dataPoint]=msg.payload; | ||
node.warn(topic); | ||
} else if(topic.startsWith("@stats reset ")) { | ||
const dataPoint=topic.substring("@stats reset ".length); | ||
delete node.dataPoint[dataPoint]; | ||
node.warn(topic); | ||
} else if(topic=="@deltasCrossNormalised") { | ||
msg.payload=node.crossNormalisedDeltas(); | ||
node.send([null,msg]); | ||
} else { | ||
throw Error("unknown"); | ||
} | ||
} catch(ex) { | ||
node.error(msg.topic+" failed "+ex.message); | ||
} | ||
return; | ||
} | ||
try{ | ||
msg.result=node.actionfunction.apply(node,[node.getData(msg,node),node.term,node]); | ||
switch(node.action) { | ||
case "realtime": | ||
if(msg.result.outlier) { | ||
node.send([msg,null,msg]); | ||
return; | ||
} | ||
break; | ||
} | ||
} catch(ex) { | ||
msg.error=ex.message; | ||
if(node.maxErrorDisplay) { | ||
--node.maxErrorDisplay; | ||
if(node.action=="realtime") { | ||
node.error(node.action+" error: "+ex.message); | ||
} else { | ||
node.error(Array.isArray(node.getData(msg,node))? node.action+" error: "+ex.message : "payload not array"); | ||
} | ||
node.status({fill:"red",shape:"ring",text:"error(s)"}); | ||
} | ||
} | ||
node.send(msg); | ||
}); | ||
} | ||
RED.nodes.registerType(nodeLabel,dataAnalysisNode); | ||
}); | ||
} | ||
RED.nodes.registerType(logger.label,dataAnalysisNode); | ||
}; |
190
package.json
{ | ||
"name": "node-red-contrib-prib-functions", | ||
"version": "0.10.0", | ||
"description": "Node-RED added node functions.", | ||
"dependencies": { | ||
"node-red-contrib-logger": "0.0.2" | ||
}, | ||
"devDependencies": {}, | ||
"scripts": { | ||
"test": "echo \\\"Error: no test specified\\\" && exit 0" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/peterprib/node-red-contrib-prib-functions.git" | ||
}, | ||
"keywords": [ | ||
"node-red", | ||
"append", | ||
"analysis", | ||
"average", | ||
"avg", | ||
"compare", | ||
"CMA", | ||
"cumulative", | ||
"Cumulative Moving Average", | ||
"data", | ||
"data analysis", | ||
"delta", | ||
"EMA", | ||
"EWMA", | ||
"Exponential Moving Average", | ||
"functions", | ||
"injector", | ||
"host available", | ||
"host status", | ||
"Linear Correlation Coefficient", | ||
"load", | ||
"load injector", | ||
"maximum", | ||
"mean", | ||
"median", | ||
"minimun", | ||
"monitor", | ||
"moving", | ||
"Moving Average", | ||
"normalize", | ||
"Pearson Product Moment Correlation", | ||
"PPMC", | ||
"Process", | ||
"range", | ||
"realtime", | ||
"Simple Moving Average", | ||
"skew", | ||
"SMA", | ||
"standard deviation", | ||
"standardize", | ||
"statistics", | ||
"stddev", | ||
"spawn", | ||
"spawn process", | ||
"testing", | ||
"test", | ||
"translate", | ||
"transform", | ||
"variance", | ||
"weigthed", | ||
"Weighted Moving Average", | ||
"WMA", | ||
"Z-score" | ||
], | ||
"node-red": { | ||
"nodes": { | ||
"append": "append/append.js", | ||
"dataAnalysis": "dataAnalysis/dataAnalysis.js", | ||
"hostAvailable": "testing/hostAvailable.js", | ||
"load-injector": "testing/load-injector.js", | ||
"os": "nodejs/os.js", | ||
"monitorflow": "monitor/monitorflow.js", | ||
"spawnProcess": "spawnProcess/spawnProcess.js", | ||
"test": "testing/test.js", | ||
"transform": "transform/transform.js" | ||
} | ||
}, | ||
"author": "Peter Prib", | ||
"license": "GPL-3.0", | ||
"bugs": { | ||
"url": "https://github.com/peterprib/node-red-contrib-prib-functions/issues" | ||
}, | ||
"homepage": "https://github.com/peterprib/node-red-contrib-prib-functions#readme" | ||
"name": "node-red-contrib-prib-functions", | ||
"version": "0.10.1", | ||
"description": "Node-RED added node functions.", | ||
"dependencies": { | ||
"node-red-contrib-logger": "0.0.4" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^6.8.0", | ||
"eslint-config-standard": "^14.1.0", | ||
"eslint-plugin-import": "^2.19.1", | ||
"eslint-plugin-mocha": "^8.0.0", | ||
"eslint-plugin-node": "^11.0.0", | ||
"eslint-plugin-promise": "^4.2.1", | ||
"eslint-plugin-standard": "^4.0.1", | ||
"mocha": "^8.1.3", | ||
"node-red": "^1.1.3", | ||
"node-red-node-test-helper": "*" | ||
}, | ||
"scripts": { | ||
"test": "mocha \"test/**/*.js\"", | ||
"lint": "eslint --ext .js ./", | ||
"lint-fix": "eslint --fix --ext .js ./" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/peterprib/node-red-contrib-prib-functions.git" | ||
}, | ||
"keywords": [ | ||
"node-red", | ||
"append", | ||
"analysis", | ||
"average", | ||
"avg", | ||
"compare", | ||
"CMA", | ||
"cumulative", | ||
"Cumulative Moving Average", | ||
"data", | ||
"data analysis", | ||
"delta", | ||
"EMA", | ||
"EWMA", | ||
"Exponential Moving Average", | ||
"functions", | ||
"injector", | ||
"host available", | ||
"host status", | ||
"Linear Correlation Coefficient", | ||
"levenshtein distance", | ||
"load", | ||
"load injector", | ||
"maximum", | ||
"mean", | ||
"median", | ||
"minimun", | ||
"monitor", | ||
"moving", | ||
"Moving Average", | ||
"normalize", | ||
"Pearson Product Moment Correlation", | ||
"PPMC", | ||
"Process", | ||
"range", | ||
"realtime", | ||
"Simple Moving Average", | ||
"skew", | ||
"SMA", | ||
"standard deviation", | ||
"standardize", | ||
"statistics", | ||
"stddev", | ||
"spawn", | ||
"spawn process", | ||
"testing", | ||
"test", | ||
"translate", | ||
"transform", | ||
"variance", | ||
"weigthed", | ||
"Weighted Moving Average", | ||
"WMA", | ||
"Z-score" | ||
], | ||
"node-red": { | ||
"nodes": { | ||
"append": "append/append.js", | ||
"dataAnalysis": "dataAnalysis/dataAnalysis.js", | ||
"hostAvailable": "testing/hostAvailable.js", | ||
"load-injector": "testing/load-injector.js", | ||
"os": "nodejs/os.js", | ||
"monitorflow": "monitor/monitorflow.js", | ||
"spawnProcess": "spawnProcess/spawnProcess.js", | ||
"test": "testing/test.js", | ||
"transform": "transform/transform.js" | ||
} | ||
}, | ||
"author": "Peter Prib", | ||
"license": "GPL-3.0", | ||
"bugs": { | ||
"url": "https://github.com/peterprib/node-red-contrib-prib-functions/issues" | ||
}, | ||
"homepage": "https://github.com/peterprib/node-red-contrib-prib-functions#readme" | ||
} |
@@ -6,3 +6,3 @@ # [node-red-contrib-prib-functions][2] | ||
* Data Analysis - statistical metrics that has real time option | ||
* Test | ||
* Test | ||
* Load Injector | ||
@@ -14,2 +14,3 @@ * Monitor Flow | ||
* node.js os metrics | ||
* Levenshtein Distance | ||
@@ -180,2 +181,4 @@ ------------------------------------------------------------ | ||
0.10.1 Real time weighted moving average, levenshtein Distance, for test allow testing of "infinity","-infinity" and "NaN" in JSON. | ||
0.10.0 Many fixes to transform. Array and csv to various forms work. Added test to validate. | ||
@@ -182,0 +185,0 @@ Improved test to allow for escape to put special characters into a string. |
@@ -34,2 +34,5 @@ const logger = new (require("node-red-contrib-logger"))("test").sendInfo("Copyright 2020 Jaroslav Peter Prib"); | ||
if( obj1 === obj2 ) return true; | ||
if( obj1 === Number.POSITIVE_INFINITY && obj2==="Infinity") return true; | ||
if( obj1 === Number.NEGATIVE_INFINITY && obj2==="-Infinity") return true; | ||
if( Number.isNaN(obj1) && obj2==="NaN") return true; | ||
if( typeof obj1 != typeof obj2 ) return false; | ||
@@ -36,0 +39,0 @@ if( !(obj1 instanceof Object) ) return false; |
const logger = new (require("node-red-contrib-logger"))("transform"); | ||
logger.sendInfo("Copyright 2020 Jaroslav Peter Prib"); | ||
const regexCSV=/,(?=(?:(?:[^"]*"){2})*[^"]*$)/, | ||
os=require('os'), | ||
path=require('path'), | ||
process=require('process'); | ||
let ISO8583,ISO8583message; | ||
//const regexLines=/(?!\B"[^"]*)\n(?![^"]*"\B)/g; | ||
const regexCSV=/,(?=(?:(?:[^"]*"){2})*[^"]*$)/; | ||
const path = require('path'); | ||
function SendArray(RED,node,msg,array){ | ||
if(logger.active) logger.send({label:"SendArray",size:array.length}); | ||
this.index=0; | ||
this.RED=RED; | ||
this.node=node; | ||
this.msg=msg; | ||
this.array=array; | ||
node.deleteSourceProperty(RED,node,msg); | ||
this.next(); | ||
} | ||
SendArray.prototype.next=function() { | ||
this.usageCPU=process.cpuUsage(this.usageCPU); | ||
this.resourceUsage=process.resourceUsage(); | ||
/* | ||
userCPUTime <integer> maps to ru_utime computed in microseconds. It is the same value as process.cpuUsage().user. | ||
systemCPUTime | ||
*/ | ||
const memoryUsage=process.memoryUsage(), | ||
heapUsedRatio=memoryUsage.heapUsed/memoryUsage.heapTotal, | ||
memoryUsedRatio=os.freemem()/os.totalmem(); | ||
currentTime=Date.now(), | ||
cpuUsedRatio=(this.usageCPU.user+this.usageCPU.system)/((currentTime-this.lastTouchTime)*100000); | ||
if(logger.active) logger.send({label:"SendArray.next",index:this.index,cpuUsedRatio:cpuUsedRatio,memoryUsedRatio:memoryUsedRatio,heapUsedRatio:heapUsedRatio}); | ||
this.lastTouchTime=currentTime; | ||
let i=cpuUsedRatio>0.9 || memoryUsedRatio>0.9 || heapUsedRatio>0.99?1:100; | ||
while(--i) { | ||
if(this.index>=this.array.length) { | ||
delete this; | ||
return; | ||
} | ||
const newMsg=this.RED.util.cloneMessage(this.msg); | ||
newMsg._msgid=newMsg._msgid+":"+this.index; | ||
this.node.setData(this.RED,this.node,newMsg,this.array[this.index++]) | ||
this.node.send(newMsg); | ||
} | ||
const call=this.next.bind(this); | ||
this.timeoutID=setTimeout(call, 100); | ||
}; | ||
function removeQuotes(data){ | ||
@@ -46,9 +86,9 @@ try{ | ||
if(logger.active) logger.send({label:"ArrayToMessages",arraySize:data.length}); | ||
if(data.length>node.maxMessages) throw Error("messages to be created "+data.length +"> max: "+node.maxMessages); | ||
data.map((c,i)=>{ | ||
const newMsg=RED.util.cloneMessage(msg); | ||
newMsg.payload=c; | ||
newMsg._msgid=newMsg._msgid+":"+i; | ||
if(node.hasNewTopic) newMsg.topic=node.topicFunction(RED,node,newMsg); | ||
node.setData(RED,node,msg,c) | ||
node.send(newMsg); | ||
}) | ||
}); | ||
}, | ||
@@ -102,8 +142,2 @@ CSVToArray: (RED,node,msg,data)=>{ | ||
}, | ||
JSONToISO8385: (RED,node,msg,data)=>{ | ||
var d=[]; | ||
Object.getOwnPropertyNames(data).forEach((v)=>d.push([ISO8583BitMapName[v].id,data[v]])); | ||
d.sort((a, b) => a[0] - b[0]); | ||
return ISO8583message.packSync(d); | ||
}, | ||
JSONToArray: (RED,node,msg,data)=>{ | ||
@@ -132,2 +166,20 @@ if(data instanceof Object){ | ||
}, | ||
JSONToISO8385: (RED,node,msg,data)=>{ | ||
var d=[]; | ||
Object.getOwnPropertyNames(data).forEach((v)=>d.push([ISO8583BitMapName[v].id,data[v]])); | ||
d.sort((a, b) => a[0] - b[0]); | ||
return ISO8583message.packSync(d); | ||
}, | ||
JSONToMessages: (RED,node,msg,data)=>{ | ||
if(logger.active) logger.send({label:"JSONToMessages",messages:data.length}); | ||
if(Array.isArray(data)) { | ||
new node.SendArray(RED,node,msg,data); | ||
// functions.ArrayToMessages(RED,node,msg,data); | ||
} else { | ||
const newMsg=RED.util.cloneMessage(msg); | ||
newMsg._msgid=newMsg._msgid+":0"; | ||
node.setData(RED,node,newMsg,data) | ||
node.send(newMsg); | ||
} | ||
}, | ||
JSONToString: (RED,node,msg,data)=>JSON.stringify(data), | ||
@@ -155,6 +207,7 @@ StringToJSON: (RED,node,msg,data)=>JSON.parse(data), | ||
RED.nodes.createNode(this,n); | ||
let node=Object.assign(this,n,{RED:RED}); | ||
let node=Object.assign(this,{maxMessages:1000,SendArray:SendArray},n,{RED:RED}); | ||
node.sendInFunction=["Messages"].includes(node.actionTarget); | ||
node.hasNewTopic=![null,"","msg.topic"].includes(node.topicProperty); | ||
const sourceMap="(RED,node,msg)=>"+(node.sourceProperty||"msg.payload"), | ||
sourceDelete="(RED,node,msg)=>{delete "+(node.sourceProperty||"msg.payload")+";}", | ||
targetMap="(RED,node,msg,data)=>{"+(node.targetProperty||"msg.payload")+"=data;"+ | ||
@@ -168,2 +221,3 @@ (node.sendInFunction && node.hasNewTopic? "" : "msg.topic=node.topicFunction(RED,node,msg);")+ | ||
node.getData=evalFunction("source",sourceMap); | ||
node.deleteSourceProperty=evalFunction("source delete",sourceDelete); | ||
node.setData=evalFunction("target",targetMap); | ||
@@ -170,0 +224,0 @@ node.topicFunction=evalFunction("topic",topicMap); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
1111341
52
1582
1
229
10
+ Addednode-red-contrib-logger@0.0.4(transitive)
- Removednode-red-contrib-logger@0.0.2(transitive)