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.
Files changed (82) hide show
  1. package/.github/workflows/codeql-analysis.yml +3 -3
  2. package/.github/workflows/npmpublish.yml +6 -6
  3. package/.vs/VSWorkspaceState.json +7 -0
  4. package/.vs/node-red-contrib-prib-functions/v17/.wsuo +0 -0
  5. package/README.md +84 -70
  6. package/dataAnalysis/arrayAllRowsSwap.js +15 -0
  7. package/dataAnalysis/arrayCompareToPrecision.js +34 -0
  8. package/dataAnalysis/arrayDifference.js +14 -0
  9. package/dataAnalysis/arrayDifferenceSeasonal.js +15 -0
  10. package/dataAnalysis/arrayDifferenceSeasonalSecondOrder.js +20 -0
  11. package/dataAnalysis/arrayDifferenceSecondOrder.js +14 -0
  12. package/dataAnalysis/arrayForEachRange.js +38 -0
  13. package/dataAnalysis/arrayOverlay.js +13 -0
  14. package/dataAnalysis/arrayProduct.js +11 -0
  15. package/dataAnalysis/arrayRandom.js +14 -0
  16. package/dataAnalysis/arrayReduceRange.js +11 -0
  17. package/dataAnalysis/arrayScale.js +11 -0
  18. package/dataAnalysis/arraySum.js +11 -0
  19. package/dataAnalysis/arraySumSquared.js +11 -0
  20. package/dataAnalysis/arraySwap.js +11 -0
  21. package/dataAnalysis/dataAnalysis.html +52 -21
  22. package/dataAnalysis/dataAnalysis.js +73 -44
  23. package/dataAnalysis/generateMatrixFunction.js +89 -0
  24. package/dataAnalysis/generateVectorFunction.js +25 -0
  25. package/dataAnalysis/pca.js +472 -325
  26. package/dataAnalysis/svd.js +239 -0
  27. package/documentation/DataAnalysisRealtime.JPG +0 -0
  28. package/documentation/monitorSystem.JPG +0 -0
  29. package/documentation/monitorSystemTest.JPG +0 -0
  30. package/echart/echart.html +68 -0
  31. package/echart/echart.js +85 -0
  32. package/echart/icons/chart-671.png +0 -0
  33. package/echart/lib/echarts.js +95886 -0
  34. package/lib/Chart.js +177 -0
  35. package/lib/Column.js +99 -0
  36. package/lib/GraphDB.js +14 -0
  37. package/lib/Table.js +185 -0
  38. package/lib/objectExtensions.js +361 -0
  39. package/matrix/matrix.js +95 -56
  40. package/matrix/matrixNode.html +88 -55
  41. package/matrix/matrixNode.js +12 -5
  42. package/monitor/BarGauge.js +8 -0
  43. package/monitor/Dataset.js +29 -0
  44. package/monitor/DialGauge.js +109 -0
  45. package/monitor/DialNeedle.js +36 -0
  46. package/monitor/Format.js +74 -0
  47. package/monitor/centerElement.js +14 -0
  48. package/monitor/compareElements.js +95 -0
  49. package/monitor/defs.js +23 -0
  50. package/monitor/extensions.js +906 -0
  51. package/monitor/functions.js +36 -0
  52. package/monitor/json2xml.js +103 -0
  53. package/monitor/monitorSystem.html +199 -0
  54. package/monitor/monitorSystem.js +322 -0
  55. package/monitor/svgHTML.js +179 -0
  56. package/monitor/svgObjects.js +64 -0
  57. package/package.json +20 -6
  58. package/test/00-objectExtensions.js +94 -0
  59. package/test/04-tables.js +33 -0
  60. package/test/data/.config.nodes.json +608 -0
  61. package/test/data/.config.nodes.json.backup +608 -0
  62. package/test/data/.config.runtime.json +4 -0
  63. package/test/data/.config.runtime.json.backup +3 -0
  64. package/test/data/.config.users.json +21 -0
  65. package/test/data/.config.users.json.backup +21 -0
  66. package/test/data/.flow.json.backup +2820 -2003
  67. package/test/data/float32vector10.npy +0 -0
  68. package/test/data/flow.json +2830 -2033
  69. package/test/data/int2matrix2x3.npy +0 -0
  70. package/test/data/package-lock.json +158 -0
  71. package/test/data/package.json +11 -0
  72. package/test/dataAnalysisExtensions.js +471 -0
  73. package/test/dataAnalysisPCA.js +54 -0
  74. package/test/dataAnalysisSVD.js +31 -0
  75. package/test/euclideanDistance.js +2 -2
  76. package/test/transformConfluence.js +1 -1
  77. package/test/transformNumPy.js +132 -0
  78. package/testing/test.html +1 -1
  79. package/testing/test.js +78 -53
  80. package/transform/NumPy.js +303 -0
  81. package/transform/transform.html +12 -0
  82. package/transform/transform.js +34 -2
@@ -0,0 +1,36 @@
1
+ module.exports={
2
+ coalesce:()=>{
3
+ const args = coalesce.arguments;
4
+ for (let i=0; i<args.length; ++i)
5
+ if (args[i] != null) return args[i];
6
+ return null;
7
+ },
8
+ deepClone:(deepObject)=>{
9
+ if(deepObject==null) return null;
10
+ if(deepObject instanceof Array)
11
+ let newObj=[];
12
+ else if(deepObject instanceof String)
13
+ return new String(deepObject);
14
+ else if(deepObject instanceof Number)
15
+ return new Number(deepObject);
16
+ else if(deepObject instanceof Date)
17
+ return new Date(deepObject);
18
+ else if(typeof deepObject == "object")
19
+ let newObj={};
20
+ else return deepObject;
21
+ for (i in deepObject)
22
+ newObj[i]=deepClone(deepObject[i]);
23
+ return newObj;
24
+ },
25
+ getWord:(value,wordPosition)=>value.split(/\s+/g,wordPosition+1)[wordPosition-1],
26
+ rangeLimit (value,min,max) {
27
+ if(min!=null)
28
+ if(value<min)
29
+ return min;
30
+ if(max!=null)
31
+ if(value>max)
32
+ return max;
33
+ return value;
34
+ },
35
+ nullif:(a,b)=>(a==b?null:a)
36
+ }
@@ -0,0 +1,103 @@
1
+ const Format=require('./Format')
2
+ function json2xml(tag, obj, emptyIsNull, hexObjectWhenTag) {
3
+ try{
4
+ if(hexObjectWhenTag!=null) {
5
+ const l=hexObjectWhenTag.length
6
+ for(let i=0;i<l;i++)
7
+ if(hexObjectWhenTag[i]==tag)
8
+ return '<' + tag + '><jsonObject>'
9
+ + Format.StringToHex(JSON.stringify(obj))
10
+ + '</jsonObject></' + tag + '>'
11
+ }
12
+ if (typeof obj === 'undefined' || obj === null)
13
+ return '<' + tag + '/>';
14
+ if (typeof obj !== 'object')
15
+ return '<' + tag + '>'
16
+ + Format.xmlEncodeString(obj)
17
+ + '</' + tag + '>';
18
+
19
+ let elementValue ='';
20
+ if (obj.constructor === Array) {
21
+ for (let i = 0; i < obj.length; i++) {
22
+ if (typeof obj[i] !== 'object'
23
+ || obj[i].constructor == Object) {
24
+ elementValue += json2xml('jsonArrayElement', obj[i],emptyIsNull, hexObjectWhenTag);
25
+ continue;
26
+ }
27
+ throw new Error((typeof obj[i]) + ' is not supported.');
28
+ }
29
+ return '<'+tag+'>' + elementValue + '</'+tag+'>';
30
+ }
31
+ if (obj.constructor !== Object)
32
+ return '<' + tag + '/>';
33
+ let attributes ='';
34
+ if (typeof obj['#text'] !== 'undefined') {
35
+ if (typeof obj['#text'] == 'object')
36
+ throw new Error((typeof obj['#text']) + ' which is #text, not supported.');
37
+ elementValue += Format.xmlEncodeString(obj['#text']);
38
+ }
39
+ for (let name in obj) {
40
+ if(name==null) continue;
41
+ let objElement=obj[name];
42
+ if(objElement==null) continue
43
+ if (name.charAt(0) == '$') {
44
+ elementValue += '<jsonDollarParameter name="' + name + '">'+Format.xmlEncodeString(objElement)+'</jsonDollarParameter>';
45
+ continue;
46
+ }
47
+ if (name.charAt(0) == '@') {
48
+ if (typeof obj[b] == 'object')
49
+ throw new Error((typeof objElement) + ' attribute not supported.');
50
+ attributes += ' ' + name.substring(1) + '="' + Format.xmlEncodeString(objElement) + '"';
51
+ continue;
52
+ }
53
+ switch (obj[name].constructor) {
54
+ case Array :
55
+ if(hexObjectWhenTag!=null) {
56
+ for(let i=0;i<hexObjectWhenTag.length;i++)
57
+ if(hexObjectWhenTag[i]==name) {
58
+ elementValue += '<' + name + '><jsonObject>'
59
+ + Format.StringToHex(JSON.stringify(objElement))
60
+ + '</jsonObject></' + name + '>';
61
+ break;
62
+ }
63
+ if( i<hexObjectWhenTag.length) continue;
64
+ }
65
+ elementValue+='<'+name+'>';
66
+ for (let i = 0; i < objElement.length; i++) {
67
+ if (typeof objElement[i] !== 'object'
68
+ || objElement[i].constructor == Object) {
69
+ elementValue += json2xml('jsonArrayElement', objElement[i],emptyIsNull, hexObjectWhenTag);
70
+ continue;
71
+ }
72
+ throw new Error((typeof objElement[i]) + ' is not supported.');
73
+ }
74
+ elementValue+='</'+name+'>';
75
+ continue;
76
+ case Object :
77
+ elementValue += json2xml(name, objElement,emptyIsNull, hexObjectWhenTag);
78
+ continue;
79
+ case String :
80
+ if(objElement==null) continue;
81
+ if(emptyIsNull&&objElement=="") continue;
82
+ if(objElement.length<36) {
83
+ attributes += ' '+name+'="'+Format.xmlEncodeString(objElement)+'"';
84
+ continue;
85
+ }
86
+ elementValue += '<' + name + '>' + Format.xmlEncodeString(objElement) + '</' + name + '>';
87
+ continue;
88
+ case Number :
89
+ attributes += ' '+name+'="'+objElement+'"';
90
+ continue;
91
+ case Boolean :
92
+ attributes += ' '+name+'="'+(objElement?'jsonTrue':'jsonFalse')+'"';
93
+ continue;
94
+ default:
95
+ throw new Error((typeof objElement) + ' is not supported.');
96
+ }
97
+ }
98
+ return '<' + tag + attributes + ( elementValue =='' ? '/>' : '>'+ elementValue + '</' + tag + '>' );
99
+ } catch(e) {
100
+ throw new Error('jso2xml error: '+e);
101
+ }
102
+ }
103
+ module.exports=json2xml
@@ -0,0 +1,199 @@
1
+ <script type="text/javascript">
2
+ const nodeNameURL="MonitorSystem";
3
+ const geval=eval
4
+ try{
5
+ const url=nodeNameURL+"/svgHTML"
6
+ console.log("fetching "+url)
7
+ const xhr = new XMLHttpRequest()
8
+ xhr.open("GET", url)
9
+ xhr.onload = () => {
10
+ console.log("fetched "+url)
11
+ if (xhr.readyState == 4 && xhr.status == 200) {
12
+ try{
13
+ geval?.(xhr.response)
14
+ } catch(ex){
15
+ console.log("fetch: "+xhr.status);
16
+ }
17
+ } else {
18
+ console.error("fetch: "+xhr.status);
19
+ }
20
+ }
21
+ xhr.send()
22
+ } catch(ex) {
23
+ console.error("fetch: " +ex.message)
24
+ }
25
+ const nodeSvg={}
26
+ const nodeDetails={}
27
+ let svgUpdate=false
28
+ function drawAll() {
29
+ Object.keys(nodeDetails).forEach(id=>{
30
+ try{drawBase(id)} catch(ex) {console.error("draw "+id+" "+ex.stack)}
31
+ })
32
+ }
33
+ function drawBase(id) {
34
+ const svgBase=nodeSvg[id]
35
+ if(svgBase.element==null) {
36
+ svgBase.element=document.getElementById(id)
37
+ if(svgBase.element==null) return
38
+ }
39
+ if(svgBase.g) svgBase.g.remove()
40
+ try{
41
+ drawSVGElement(svgBase.element,
42
+ {action:"g",id:"_systemMonitor","font-size":10,style:"z-index:99999;",children:nodeDetails[id]},
43
+ id
44
+ )
45
+ } catch(ex) {
46
+ svgUpdate=false
47
+ if(ex.message.startsWith("element id not found")) return
48
+ console.error(ex.message + " for "+JSON.stringify(nodeDetails[id]))
49
+ }
50
+ }
51
+ function refreshState(){
52
+ const action=svgUpdate==true?"getDataUpdateSVG":"getDataSVG"
53
+ $.get( "/"+nodeNameURL+"/"+action+"/" )
54
+ .done(function(json) {
55
+ Object.assign(nodeDetails,json)
56
+ try{
57
+ drawAll()
58
+ svgUpdate=true
59
+ } catch (ex) {
60
+ svgUpdate=false
61
+ }
62
+ }).fail(function( jqXHR, textStatus, error ) {
63
+ console.error("Monitor System refresh state failed: "+jqXHR.status+" "+textStatus+ " error "+error)
64
+ });
65
+ }
66
+ if(!systemMonitor)
67
+ var systemMonitor=setInterval(refreshState, 1000)
68
+ /*globals RED */
69
+ RED.nodes.registerType('Monitor System', {
70
+ category: 'Monitor',
71
+ color: '#fdeea2',
72
+ defaults: {
73
+ name: {value: "",required:false},
74
+ metrics: {value: null,required:true},
75
+ graphs: {value: null,required:true}
76
+ },
77
+ inputs:0,
78
+ inputLabels: "in",
79
+ outputs:1,
80
+ outputLabels: ["out"],
81
+ icon: "icons8-heart-monitor-40.png",
82
+ label: function() {
83
+ console.log("*****+++++")
84
+ if(!nodeSvg.hasOwnProperty(this.id)) {
85
+ nodeSvg[this.id]={element:document.getElementById(this.id),actions:[{action:"text",x:0 ,y:60 ,children:["Waiting Refresh"]}]}
86
+ }
87
+ drawBase(this.id)
88
+ return this.name||this._("Monitor System");
89
+ },
90
+ labelStyle: function() {
91
+ return "node_label_italic";
92
+ },
93
+ oneditprepare: function() {
94
+ $("#node-input-metrics").typedInput({type:"metrics", types:[{
95
+ value: "metrics",
96
+ multiple: true,
97
+ options: [
98
+ { value: "freeMemory", label: "Free Memory"},
99
+ { value: "totalMemory", label: "Total Memory"},
100
+ { value: "gaugeMemory", label: "Gauge Memory"},
101
+ { value: "chartMemoryUsage", label: "Chart Memory"},
102
+ { value: "heapStatistics", label: "Heap Statistics"},
103
+ { value: "heapCodeStatistics", label: "Heap Code Statistics"},
104
+ { value: "heapSpaceStatistics", label: "Heap Space Statistics"},
105
+ { value: "loadAvg", label: "Load Avg"},
106
+ { value: "memoryUsage", label: "Memory Usage"},
107
+ { value: "resourceUsage", label: "Resource Usage"},
108
+ { value: "chartLineResource", label: "Resource Chart"},
109
+ ]
110
+ }]})
111
+ },
112
+ oneditsave: function() {
113
+ },
114
+ oneditresize: function() {
115
+ },
116
+ resizeRule : function(file,newWidth) {
117
+ },
118
+ button: {
119
+ enabled: function() {
120
+ return !this.changed;
121
+ },
122
+ onclick: function() {
123
+ if (this.changed) {
124
+ return RED.notify(RED._("workflow undeployed changes"),"warning");
125
+ }
126
+ let label=this._def.label.call(this);
127
+ if (label.length > 30) {
128
+ label = label.substring(0,50)+"...";
129
+ }
130
+ label=label.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
131
+ const node=this;
132
+ function sendCommand(element,action) {
133
+ $(element).dialog("close");
134
+ $.get( "/"+nodeNameURL+"/"+node.id+"/"+action )
135
+ .done(function(json) {
136
+ RED.notify(node._(nodeNameURL+" signal success",{label:label}),{type:"success",id:"Load Injector"});
137
+ $('<div></div>').appendTo('body').html(json2html(json))
138
+ .dialog({
139
+ modal: true,
140
+ title: (node.name||nodeNameURL)+" "+action,
141
+ zIndex: 10000,
142
+ autoOpen: true,
143
+ width: 'auto',
144
+ resizable: false,
145
+ buttons: {
146
+ close: function (event, ui) {
147
+ $(this).remove();
148
+ }
149
+ }
150
+ });
151
+ }).fail(function( jqXHR, textStatus, error ) {
152
+ if (jqXHR.status === 404) {
153
+ RED.notify(node._(nodeNameURL+" signal not deployed"),"error");
154
+ } else if (jqXHR.status === 500) {
155
+ RED.notify(node._(nodeNameURL+" signal inject failed with error "+(jqXHR.responseText||textStatus||error||"")),"error");
156
+ } else if (jqXHR.status === 0) {
157
+ RED.notify(node._(nodeNameURL+" signal no response"),"error");
158
+ } else {
159
+ RED.notify(node._(nodeNameURL+" signal unexpected status:"+jqXHR.status+" message:"+jqXHR.responseText||textStatus+" "+error),"error");
160
+ }
161
+ });
162
+ }
163
+ $('<div></div>').appendTo('body').html('<div>Choose Action</div>')
164
+ .dialog({
165
+ modal: true, title: (node.name||'Monitor System'), zIndex: 10000, autoOpen: true,
166
+ width: 'auto', resizable: false,
167
+ buttons: {
168
+ "Start": function () {
169
+ sendCommand(this,"Start");
170
+ },
171
+ "Stop": function () {
172
+ sendCommand(this,"Stop");
173
+ }
174
+ },
175
+ close: function (event, ui) {
176
+ $(this).remove();
177
+ }
178
+ });
179
+ }
180
+ }
181
+ });
182
+ </script>
183
+
184
+ <script type="text/x-red" data-template-name="Monitor System">
185
+ <div class="form-row">
186
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
187
+ <input type="text" id="node-input-name" placeholder="Name">
188
+ </div>
189
+ <div class="form-row">
190
+ <label for="node-input-metrics"><i class="fa fa-tag"></i> Metrics</label>
191
+ <input type="text" id="node-input-metrics">
192
+ </div>
193
+ </script>
194
+
195
+ <script type="text/x-red" data-help-name="Monitor System">
196
+ <p>Displays counts of flows in diagram
197
+ </p>
198
+ </script>
199
+
@@ -0,0 +1,322 @@
1
+ const logger = new (require("node-red-contrib-logger"))("Monitor System")
2
+ logger.sendInfo("Copyright 2020 Jaroslav Peter Prib");
3
+ const os = require('node:os');
4
+ const v8 = require('node:v8');
5
+ const { memoryUsage, resourceUsage } = require('node:process')
6
+ const fs = require('fs')
7
+ const path = require("path");
8
+ const svgObject = require("./svgObjects.js");
9
+ const resourceLabels={
10
+ userCPUTime: "User Time (ms)",
11
+ systemCPUTime: "System Time (ms)",
12
+ maxRSS: "Max RSS (kb)",
13
+ // sharedMemorySize": "Shared Memeory", //<integer> maps to ru_ixrss but is not supported by any platform."
14
+ // unsharedDataSize <integer> maps to ru_idrss but is not supported by any platform.
15
+ // unsharedStackSize <integer> maps to ru_isrss but is not supported by any platform.
16
+ minorPageFault: "Minor Page Faults",
17
+ majorPageFault: "Major Page Faults",
18
+ // swappedOut: // <integer> maps to ru_nswap but is not supported by any platform.
19
+ fsRead: "Reads",
20
+ fsWrite: "Writes",
21
+ //ipcSent <integer> maps to ru_msgsnd but is not supported by any platform.
22
+ //ipcReceived <integer> maps to ru_msgrcv but is not supported by any platform.
23
+ //signalsCount <integer> maps to ru_nsignals but is not supported by any platform.
24
+ // voluntaryContextSwitches: "Voluntary Context Switches" // This field is not supported on Windows.
25
+ // involuntaryContextSwitches: "Involuntary Context Switches" // This field is not supported on Window
26
+ }
27
+
28
+ const chartLine=new svgObject.Chart({type:"line",
29
+ labels:["RSS: Resident Set Size","Heap Total","Heap Used","External","Array Buffer","Free Memory","Total Memory"]})
30
+ const getPoints=(samples,metrics,metric,...properties)=>{
31
+ const points=[],size=Math.min(samples,metrics.length)
32
+ for(let i=0; i<size ; i++){
33
+ const m=metrics[i][metric]
34
+ points.push(properties.map(p=>m[p]))
35
+ }
36
+ return points
37
+ }
38
+ const getDiffPoints=(samples,metrics,metric,...properties)=>{
39
+ const points=getPoints(samples+1,metrics,metric,...properties)
40
+ return Array.from({ length: points.length-1}, (_, i) =>{
41
+ const next=points[i+1]
42
+ return points[i].map((c,j)=>c-next[j])
43
+ })
44
+ }
45
+
46
+ const properties=Object.keys(resourceLabels)
47
+ const labels=properties.map(c=>resourceLabels[c])
48
+ const chartLineResource=new svgObject.Chart({type:"line",labels:labels,normaliseDimension:"column",
49
+ getData:()=>getDiffPoints(11,metrics,"resourceUsage",...properties)
50
+ })
51
+
52
+ let svgHTMLCache
53
+ const {
54
+ performance,
55
+ PerformanceObserver,
56
+ } = require("node:perf_hooks");
57
+ const { action, children } = require("./defs.js");
58
+ const obs = new PerformanceObserver(list => {
59
+ const entry = list.getEntries()[0]
60
+ })
61
+ /*
62
+ https://nodejs.org/api/perf_hooks.html
63
+
64
+ PerformanceEntry {
65
+ name: 'gc',
66
+ entryType: 'gc',
67
+ startTime: 2820.567669,
68
+ duration: 1.315709,
69
+ kind: 1
70
+ }
71
+ */
72
+
73
+ process.resourceUsage()
74
+ const systemMetrics=()=>{
75
+ return {
76
+ freeMemory: os.freemem(),
77
+ heapCodeStatistics:v8.getHeapCodeStatistics(),
78
+ heapSpaceStatistics:v8.getHeapSpaceStatistics(),
79
+ heapStatistics:v8.getHeapStatistics(),
80
+ memoryUsage:memoryUsage(),
81
+ loadAvg: os.loadavg(),
82
+ timestamp: Date.now(),
83
+ resourceUsage: resourceUsage(),
84
+ totalMemory: os.totalmem(),
85
+ uptime: os.uptime()
86
+ };
87
+ }
88
+ const metricLabel={
89
+ freeMemory: "Free Memory",
90
+ heapCodeStatistics:"Heap Code",
91
+ heapSpaceStatistics:"Heap Space",
92
+ heapStatistics:"Heap",
93
+ memoryUsage:"Memory Usage",
94
+ loadAvg: "Load avg",
95
+ resourceUsage: "Resource Usage",
96
+ totalMemory: "Total Memory"
97
+ }
98
+ const svcTextLine=(metric,value)=>{
99
+ return {action:"text",id:metric,height:10 ,children:[metricLabel[metric]+": "+(typeof value== "object"?JSON.stringify(value):value)]}
100
+ }
101
+ const svcTextLineUpdate=(metric,value)=>{
102
+ return {action:"update",id:metric,children:[metricLabel[metric]+": "+(typeof value== "object"?JSON.stringify(value):value)]}
103
+ }
104
+
105
+ const mappingMemory=["freeMemory","memoryUsage.","totalMemory",]
106
+
107
+ const getPointsMemory=()=>{
108
+ const points=[],size=Math.min(11,metrics.length)
109
+ for(let i=0; i<size ; i++){
110
+ const mi=metrics[i]
111
+ const m=mi.memoryUsage
112
+ points.push([m.rss,m.heapTotal,m.heapUsed,m.external,m.arrayBuffers,mi.freeMemory,mi.totalMemory])
113
+ }
114
+ return points
115
+ }
116
+ //const gapAngle=100
117
+ //const dialAngleArea=360-gapAngle
118
+ const memoryDial=new svgObject.DialGauge({title:"% used memory"})
119
+ const metricFunction={
120
+ chartMemoryUsage: ()=>({action:"g",id:"chartMemoryUsage",height:100,children:[chartLine.setData(getPointsMemory()).get()]}),
121
+ freeMemory: svcTextLine,
122
+ gaugeMemory: ()=>{
123
+ const currentState=metrics[0]
124
+ memoryDial.setMax(currentState.totalMemory)
125
+ return memoryDial.get(currentState.totalMemory-currentState.memoryUsage)
126
+ },
127
+ heapCodeStatistics:svcTextLine,
128
+ heapSpaceStatistics:svcTextLine,
129
+ heapStatistics:svcTextLine,
130
+ memoryUsage:svcTextLine,
131
+ loadAvg:svcTextLine,
132
+ resourceUsage:svcTextLine,
133
+ totalMemory:svcTextLine,
134
+ chartLineResource:()=>({action:"g",id:"chartLineResource",height:100,children:[chartLineResource.get()]})
135
+ }
136
+
137
+ const metricFunctionUpdate={
138
+ chartMemoryUsage: ()=>chartLine.setData(getPointsMemory()).getUpdate(),
139
+ freeMemory: svcTextLineUpdate,
140
+ gaugeMemory: ()=>{
141
+ const currentState=metrics[0]
142
+ return memoryDial.setMax(currentState.totalMemory).getUpdate(currentState.totalMemory-currentState.freeMemory)
143
+ },
144
+ heapCodeStatistics:svcTextLineUpdate,
145
+ heapSpaceStatistics:svcTextLineUpdate,
146
+ heapStatistics:svcTextLineUpdate,
147
+ memoryUsage:svcTextLineUpdate,
148
+ loadAvg:svcTextLineUpdate,
149
+ resourceUsage:svcTextLineUpdate,
150
+ totalMemory:svcTextLineUpdate,
151
+ chartLineResource:()=>chartLineResource.getUpdate()
152
+ }
153
+
154
+ let metrics=[]
155
+ const nodes=[]
156
+ function getJson(){
157
+ return nodes.reduce((p,node)=>{
158
+ p[node.id]=metrics.length?node.getSendData(metrics[0]):null
159
+ return p;
160
+ },{})
161
+ }
162
+ function updateJson(){
163
+ return nodes.reduce((p,node)=>{
164
+ try{
165
+ if(metrics[0]) p[node.id]=node.getSendDataUpdate(metrics[0])
166
+ } catch (ex) {
167
+ logger.active&&logger.sendErrorAndStackDump("failed for "+node.id,ex)
168
+ }
169
+ return p;
170
+ },{})
171
+ }
172
+
173
+ let runtimeTimer
174
+ function runtimeStop() {
175
+ if(runtimeTimer) {
176
+ clearTimeout(runtimeTimer)
177
+ runtimeTimer=null
178
+ }
179
+ nodes.forEach(node=>{
180
+ node.status({fill:"red",shape:"ring",text:"Stopped"})
181
+ })
182
+ }
183
+ const reset=()=>{
184
+ metrics=[];
185
+ nodes.forEach(node=>node.status({fill:"yellow",shape:"ring",text:"Metrics Reset"}))
186
+ }
187
+ const runtimeStart=(node)=>{
188
+ logger.active&&logger.send({ label: 'start system monitoring'})
189
+ if(runtimeTimer) clearTimeout(runtimeTimer)
190
+ runtimeTimer=setInterval(function(){checkLoop();},1000);
191
+ nodes.forEach(node=>{
192
+ node.status({fill:"green",shape:"ring",text:"Collecting"})
193
+ })
194
+ }
195
+ function checkLoop() {
196
+ logger.active&&logger.send({ label: 'checkloop collect state'})
197
+ const currentState=systemMetrics();
198
+ metrics.unshift(currentState);
199
+ }
200
+
201
+ module.exports = function (RED) {
202
+ function nodeFunction(n) {
203
+ RED.nodes.createNode(this, n);
204
+ let node=Object.assign(this,n);
205
+ if(runtimeTimer) node.status({fill:"green",shape:"ring",text:"Collecting"})
206
+ else node.status({fill:"yellow",shape:"ring",text:"Initial state"});
207
+ const selectedMetrics=node.metrics.split(',')
208
+ const sendData=selectedMetrics.reduce((p,c,i)=>{
209
+ p.push("metricFunction."+c+"('"+c+"',metricSample."+c+")")
210
+ return p
211
+ },[]).join(",")
212
+ const sendDataUpdate=selectedMetrics.reduce((p,c,i)=>{
213
+ p.push("metricFunctionUpdate."+c+"('"+c+"',metricSample."+c+")")
214
+ return p
215
+ },[]).join(",")
216
+ try{
217
+ logger.active&&logger.info({label:"sendData",function:sendData})
218
+ node.getSendData=eval("(metricSample)=>svgObject.calculatePositionVertically(["+sendData+"],60)")
219
+ const evalText="(metricSample)=>["+sendDataUpdate+"]"
220
+ logger.active&&logger.debug({ label: 'eval', node:{id: node.id, name: node.name},eval:evalText})
221
+ node.getSendDataUpdate=eval(evalText)
222
+ } catch(ex) {
223
+ node.getSendData=()=>{return [{action:"text",x:0 ,y:60 ,children:["failed: "+ex.message]}]}
224
+ logger.active&&logger.error({ label: 'eval', node:{id: node.id, name: node.name},error:ex.message,display:display,sendData:sendData,stack:ex.stack })
225
+ node.status({fill:"red",shape:"ring",text:"error: "+ex.message});
226
+ return
227
+ }
228
+ node.on('close', function (removed, done) {
229
+ nodes=[]
230
+ done()
231
+ })
232
+ nodes.push(node)
233
+ if(!runtimeTimer) {
234
+ runtimeStart(node)
235
+ }
236
+ }
237
+ const url='/' + logger.label.replace(" ","") + '/'
238
+ RED.httpAdmin.get(url + ':id/:action/', RED.auth.needsPermission(logger.label + '.write'), function (req, res) {
239
+ const node = RED.nodes.getNode(req.params.id);
240
+ if (node && node.type===logger.label) {
241
+ try {
242
+ switch (req.params.action) {
243
+ case "getBase":
244
+ res.status(200).json(node.getBaseSVG())
245
+ break
246
+ case "Start":
247
+ runtimeStart()
248
+ node.warn("Request to start monitor system");
249
+ break
250
+ case "Stop":
251
+ runtimeStop()
252
+ node.warn("Request to stop monitor system");
253
+ break
254
+ case "observeGC":
255
+ obs.observe({ entryTypes: ['gc'] });
256
+ node.warn("Request to observe gc");
257
+ break
258
+ case "observeDisconnect":
259
+ obs.disconnect();
260
+ node.warn("Request to observe disconnect")
261
+ break
262
+ case "trace gc on":
263
+ v8.setFlagsFromString('--trace-gc')
264
+ node.warn("Request to --trace-gc");
265
+ break
266
+ case "trace gc off":
267
+ v8.setFlagsFromString('--notrace-gc')
268
+ node.warn("Request to --notrace-gc");
269
+ break
270
+ default:
271
+ throw Error("invalid action "+req.params.action)
272
+ }
273
+ res.sendStatus(200);
274
+ } catch(ex) {
275
+ var reason1='Internal Server Error, monitor system failed '+ex.toString();
276
+ logger.error(reason1);
277
+ res.status(500).send(reason1);
278
+ logger.active&&logger.error(ex.stack);
279
+ }
280
+ } else {
281
+ var reason2="request to reset monitor flow failed for id:" +req.params.id;
282
+ node.error(reason1);
283
+ res.status(404).send(reason2);
284
+ }
285
+ });
286
+ RED.httpAdmin.get(url + ':action/', RED.auth.needsPermission( logger.label + '.read'), function (req, res) {
287
+ try {
288
+ logger.active&&logger.info({ label: ' httpAdmin '+req.params.action})
289
+ switch (req.params.action) {
290
+ case "svgHTML":
291
+ if(svgHTMLCache) {
292
+ res.type('json').status(200).send(svgHTMLCache)
293
+ } else {
294
+ fs.readFile(path.join(__dirname,"svgHTML.js"), "utf8", (err, data) => {
295
+ if(err) {
296
+ logger.error("svgHTML get error: "+err)
297
+ res.status(500).send("NOT FOUND")
298
+ } else {
299
+ svgHTMLCache=data
300
+ res.type('json').status(200).send(data);
301
+ }
302
+ })
303
+ }
304
+ break
305
+ case "getDataSVG":
306
+ res.status(200).json(getJson())
307
+ break;
308
+ case "getDataUpdateSVG":
309
+ res.status(200).json(updateJson())
310
+ break;
311
+ default:
312
+ throw Error("invalid action "+req.params.action)
313
+ }
314
+ } catch(ex) {
315
+ var reason1='Internal Server Error, monitor system failed '+ex.toString();
316
+ logger.error(reason1);
317
+ res.status(500).send(reason1);
318
+ logger.active&&logger.error(ex.stack);
319
+ }
320
+ });
321
+ RED.nodes.registerType(logger.label,nodeFunction);
322
+ };