From c28194c00ffef9a24d519e2cc2bd669a708922de Mon Sep 17 00:00:00 2001 From: Nico Melone Date: Thu, 21 Jul 2022 13:58:09 -0500 Subject: [PATCH] added widgets --- .DS_Store | Bin 0 -> 6148 bytes Widgets/.DS_Store | Bin 0 -> 6148 bytes Widgets/topology/topology.js | 157 ++++++ Widgets/vessels/vessels.js | 205 ++++++++ __pycache__/thingsboardAPI.cpython-39.pyc | Bin 0 -> 3311 bytes getImage.py | 24 + get_telemetry.ipynb | 555 ++-------------------- thingsboardAPI.py | 62 +++ timelapse.ipynb | 90 ++++ 9 files changed, 576 insertions(+), 517 deletions(-) create mode 100644 .DS_Store create mode 100644 Widgets/.DS_Store create mode 100644 Widgets/topology/topology.js create mode 100644 Widgets/vessels/vessels.js create mode 100644 __pycache__/thingsboardAPI.cpython-39.pyc create mode 100644 getImage.py create mode 100644 thingsboardAPI.py create mode 100644 timelapse.ipynb diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..df01be594637a1c10b6e53ef13758336122ff77b GIT binary patch literal 6148 zcmeHK!Ait15S{3Xg}p4EJmwen?jf%A3-;i#+E#XjmK0VI!9#yd@WXtQ8Fg!W(2I!7 zK=LNZ%w+PQNr#Bs>Y-i|Er=*XV|1)c2v1Kg9jNgU(4`m^-B3kax}{%) zppKr9`)B?|y{nos#)i{ArYpLmnl?DvJ?)y!a$Uu?+dI)0i#FzY*~AhS+1bnE$@9hQ z`8ZA+>u*?f+tF@&8=Gnj7z4(DF<=b*i~+paEZJPpdSk#CFa|yt;QPU&F$Tq6FkKz! zQV9T@VLAzPt|i#VD+a}05GxQTu0U~hT4FeH$Glg&px6tFJ2@>robLQyytuSG;`?My zE(lt03>X7L1`cJo;P?L$pUh;DKXkE?F<=b*GX~r&U*{_vO5fJe@%XLvXxC^gY}c~_ nfj;;Nz<}?O)3hk-o^{v-#a@u3@O*Ly+K)gY#5!Z(7Z`X0V<1FGfeMa}Fl-T77ahpREV9UPkCJLy(qp+yi-ETr z_K^WS+&i3G1MQsOOb&Ml|jK2`md(QdCT`E=;;sWadVI0Jv00n}`fpff~xzVz7q89?UN@Y!x+} z*qRTvJF|5t9Pf_(Lv$z36n%6CoPkXS4)k&&_5W&j|GydJSI&Sl@UIx)abDz8Jd)Mc w!NWkv$0DTc3<;uB~T*n>=fnPIC43&j5jL>hc>27Z-+Pp1h`ZvX%Q literal 0 HcmV?d00001 diff --git a/Widgets/topology/topology.js b/Widgets/topology/topology.js new file mode 100644 index 0000000..1d884b2 --- /dev/null +++ b/Widgets/topology/topology.js @@ -0,0 +1,157 @@ +self.onInit = function () { + //self.onResize(); + //self.draw(); +}; + +self.onDataUpdated = function () { + //self.draw(); +}; + +//function for scaling a canvas +function fitToContainer(canvas) { + canvas.style.width = "100%"; + canvas.style.height = "100%"; + canvas.width = canvas.offsetWidth; + canvas.height = canvas.offsetHeight; +} + +class Node { + constructor(id, x, y, radius, data) { + this.id = id; + this.x = x; + this.y = y; + this.radius = Math.min(radius, 25); + this.lineLength = 25; + this.data = data; + } + draw() { + this.node = new Path2D(); + this.node.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); + self.ctx.canvas.textAlign = "center"; + self.ctx.canvas.textBaseline = "middle"; + self.ctx.canvas.strokeText(this.data, this.x, this.y); + self.ctx.canvas.fillStyle = "White"; + self.ctx.canvas.stroke(this.node); + self.ctx.canvas.fill(this.node); + } + setX(x) { + this.x = x; + } + setY(y) { + this.y = y; + } + setRadius(radius) { + this.radius = radius; + } +} +class Edge { + constructor(source, target) { + this.source = source; + this.target = target; + //this.draw(); + } + draw() { + self.ctx.canvas.moveTo(self.nodes[this.source].x, self.nodes[this.source].y); + self.ctx.canvas.lineTo(self.nodes[this.target].x, self.nodes[this.target].y); + self.ctx.canvas.stroke(); + } +} + +function getDepth(edges) { + var depth = 1; + console.log(edges); + if (Object.keys(edges).length > 0) { + Object.keys(edges).forEach((target) => { + console.log("Target: " + target); + Object.keys(edges).forEach((nTarget) => { + edges[nTarget].forEach((edge) => { + console.log("Source: " + edge.source); + if (edge.source == target) { + console.log("recurse"); + var slice = Object.fromEntries(Object.entries(edges).slice(0, Object.keys(edges).indexOf(target)) + Object.entries(edges).slice(Object.keys(edges).indexOf(target)) + 1); + depth = depth + getDepth(slice); + } + }); + }); + }); + } + console.log("Depth: " + depth); + return depth; +} + +function placeNodes() { + var numSourceNodes = Object.keys(self.nodes).length - Object.keys(self.edges).length; + var depth = getDepth(self.edges); + console.log(depth); + + if (Object.keys(self.nodes).length == 1) { + self.nodes[Object.keys(self.nodes)[0]].setX(self.canvas.width / 2); + self.nodes[Object.keys(self.nodes)[0]].setY(self.canvas.height / 2); + self.nodes[Object.keys(self.nodes)[0]].setRadius(self.canvas.height * 0.1); + } else { + var counter = 0; + Object.keys(self.edges).forEach((target) => { + self.edges[target].forEach((edge) => { + self.nodes[edge.source].setX(self.canvas.width * (1 / (Object.keys(self.edges).length + 2))); + self.nodes[edge.source].setY(self.canvas.height * ((counter + 1) / (numSourceNodes + 1))); + self.nodes[edge.source].setRadius(self.canvas.height * (1 / (2 * (Object.keys(self.nodes).length + 1)))); + + self.nodes[edge.target].setX(self.canvas.width * (2 / (Object.keys(self.edges).length + 2))); + self.nodes[edge.target].setY(self.canvas.height * ((Object.keys(self.edges).indexOf(target) + 1) / (Object.keys(self.edges).length + 1))); + self.nodes[edge.target].setRadius(self.canvas.height * (1 / (2 * (Object.keys(self.nodes).length + 1)))); + counter++; + }); + }); + } +} + +self.draw = function () { + self.canvas = document.getElementById("topology"); + self.ctx.canvas = self.canvas.getContext("2d"); + fitToContainer(self.canvas); + self.ctx.canvas.globalCompositeOperation = "destination-over"; + var collapse = false; + self.nodes = {}; + let numNodes = 9; + //var centerNode = new Node(100, self.canvas.width * 0.11, self.canvas.height * 0.5, self.canvas.height * (1 / (2 * (numNodes + 1))), 1); + for (let i = 0; i < numNodes; i++) { + self.nodes[i] = new Node(i, 0, 0, 0, i); + //self.nodes[i] = new Node(i, self.canvas.width * 0.11, self.canvas.height * ((i + 1) / (numNodes + 1)), self.canvas.height * (1 / (2 * (numNodes + 1))), (Math.random() * 10).toFixed(2)); + //self.nodes[i].draw(); + } + + self.edges = { 0: [new Edge(3, 0), new Edge(4, 0), new Edge(5, 0), new Edge(6, 0)], 8: [new Edge(0, 8)], 7: [new Edge(8, 7)], 2: [new Edge(1, 2)] }; + + placeNodes(); + Object.keys(self.nodes).forEach((key) => { + self.nodes[key].draw(); + }); + Object.keys(self.edges).forEach((target) => { + self.edges[target].forEach((edge) => { + edge.draw(); + }); + }); + //self.edges.push(new Edge(0,0)); + /*let rNodes = Math.round(numNodes * 0.5); + for (let i = 0; i < rNodes; i++) { + self.nodes[i + numNodes] = new Node(i + numNodes, self.canvas.width * 0.89, self.canvas.height * ((i + 1) / (rNodes + 1)), self.canvas.height * (1 / (2 * (rNodes + 1))), (Math.random() * 20).toFixed(2)); + self.nodes[i + numNodes].draw(); + } + //self.nodes["hidden1"] = new Node("hidden1", self.canvas.width * 0.3, self.canvas.height * 0.5, 0, ""); + //self.nodes["hidden2"] = new Node("hidden2", self.canvas.width * 0.6, self.canvas.height * 0.5, 0, ""); + + for (i = 0; i < numNodes; i++) { + new Edge(i, "hidden1"); + } + new Edge("hidden1", "hidden2"); + for (i = numNodes; i < numNodes + rNodes; i++) { + new Edge("hidden2", i); + } + */ +}; + +self.onResize = function () { + self.draw(); +}; + +self.onDestroy = function () {}; diff --git a/Widgets/vessels/vessels.js b/Widgets/vessels/vessels.js new file mode 100644 index 0000000..fe6190e --- /dev/null +++ b/Widgets/vessels/vessels.js @@ -0,0 +1,205 @@ +self.onInit = function () { + volume = 0; + fluidLevel = 0; + self.ctx.$container.append( + "
" + ); + }; + self.onResize = function () { + self.draw(); + }; + + //function for scaling a canvas + function fitToContainer(canvas) { + canvas.style.width = "100%"; + canvas.style.height = "100%"; + canvas.width = canvas.offsetWidth; + canvas.height = canvas.offsetHeight; + } + + self.onDataUpdated = function () { + self.ctx.detectChanges(); + //setup variables + self.volume = 0; + self.fluidLevel = 0; + for (let i = 0; i < self.ctx.defaultSubscription.data.length; i++) { + if (self.ctx.defaultSubscription.data[i].dataKey.name === self.ctx.settings.vessels[0].volumeKey) { + self.volume = typeof self.ctx.defaultSubscription.data[i].data[0] !== "undefined" ? self.ctx.defaultSubscription.data[i].data[0][1] : 0; + } else if (self.ctx.defaultSubscription.data[i].dataKey.name === self.ctx.settings.vessels[0].fluidLevelKey) { + self.fluidLevel = typeof self.ctx.defaultSubscription.data[i].data[0] !== "undefined" ? self.ctx.defaultSubscription.data[i].data[0][1] : 0; + } + } + self.maxHeight = typeof self.ctx.settings.maxHeight !== "undefined" ? self.ctx.settings.maxHeight : 10; + self.fluidLevelPercent = self.fluidLevel / self.maxHeight; + //draw a new canvas + self.draw(); + }; + + self.drawTank = function (ctx, compartmentWidth, compartmentHeight) { + ctx.moveTo(compartmentWidth * 0.05, compartmentHeight * 0.05); + ctx.lineTo(compartmentWidth * 0.05, compartmentHeight * 0.95); + ctx.ellipse(compartmentWidth / 2, compartmentHeight * 0.95, compartmentWidth / 2 - compartmentWidth * 0.05, compartmentHeight * 0.025, 0, Math.PI, 0, true); + ctx.lineTo(compartmentWidth * 0.95, compartmentHeight * 0.05); + ctx.ellipse(compartmentWidth / 2, compartmentHeight * 0.05, compartmentWidth / 2 - compartmentWidth * 0.05, compartmentHeight * 0.025, 0, 0, 2 * Math.PI); + }; + + self.drawPond = function (ctx, compartmentWidth, compartmentHeight) { + ctx.moveTo(compartmentWidth * 0.05, compartmentHeight * 0.05); + ctx.lineTo(compartmentWidth * 0.15, compartmentHeight * 0.95); + ctx.lineTo(compartmentWidth * 0.85, compartmentHeight * 0.95); + ctx.lineTo(compartmentWidth * 0.95, compartmentHeight * 0.05); + }; + + self.drawVessels = function (ctx, canvas, numVessels, cols, rows) { + if (numVessels <= cols * rows) { + //vessel + ctx.beginPath(); + for (let i = 1; i <= numVessels; i++) { + for (let j = 0; j < self.ctx.defaultSubscription.data.length; j++) { + //console.log(self.ctx.settings.vessels[i - 1].volumeKey); + //console.log(self.ctx.settings.vessels[i - 1].fluidLevelKey); + if (self.ctx.defaultSubscription.data[j].dataKey.name === self.ctx.settings.vessels[i - 1].volumeKey) { + self.volume = typeof self.ctx.defaultSubscription.data[j].data[0] !== "undefined" ? self.ctx.defaultSubscription.data[j].data[0][1] : 0; + } else if (self.ctx.defaultSubscription.data[j].dataKey.name === self.ctx.settings.vessels[i - 1].fluidLevelKey) { + self.fluidLevel = typeof self.ctx.defaultSubscription.data[j].data[0] !== "undefined" ? self.ctx.defaultSubscription.data[j].data[0][1] : 0; + } + //console.log(self.volume); + //console.log(self.fluidLevel); + } + self.maxHeight = typeof self.ctx.settings.vessels[i - 1].maxHeight !== "undefined" ? self.ctx.settings.vessels[i - 1].maxHeight : 10; + self.fluidLevelPercent = self.fluidLevel / self.maxHeight; + var vesselType = typeof self.ctx.settings.vessels[i - 1].vesselType !== "undefined" ? self.ctx.settings.vessels[i - 1].vesselType : "Tank"; + var fluidColor = typeof self.ctx.settings.vessels[i - 1].fluidColor !== "undefined" ? self.ctx.settings.vessels[i - 1].fluidColor : "Water"; + var compartmentWidth = canvas.width / cols; + var compartmentHeight = canvas.height / rows; + //gradient used as water + const grad = ctx.createLinearGradient(0, compartmentHeight * 0.05, 0, compartmentHeight * 0.95); + switch(fluidColor){ + case "Water": grad.addColorStop(0, "rgba(70,220,210,0.75)");grad.addColorStop(0.4, "rgba(0,120,240,0.75)");break; + case "Produced Water": grad.addColorStop(0, "rgba(170, 100, 30,1)");grad.addColorStop(0.4, "rgba(85, 50, 15,1");break; + case "Oil": grad.addColorStop(0,"rgba(100,100,100,0.75)");grad.addColorStop(0.3, "rgba(25,25,25, 0.85");break; + default: grad.addColorStop(0,fluidColor);break; + } + + grad.addColorStop(1, "rgba(0,0,0, 0.8)"); + //draw the vessel + switch (vesselType) { + case "Pond": + self.drawPond(ctx, compartmentWidth, compartmentHeight); + break; + case "Tank": + self.drawTank(ctx, compartmentWidth, compartmentHeight); + break; + } + + //draw + ctx.stroke(); + + ctx.save(); + //setup clip area of container + ctx.clip(); + + ctx.save(); + //set fill style for water to gradient + ctx.fillStyle = grad; + //move grid for animation + if (self.fluidLevelPercent > 1) { + ctx.translate(0, compartmentHeight - (compartmentHeight * 0.95 - compartmentHeight * 0.05) * 1 - compartmentHeight * 0.1); + } else { + ctx.translate(0, compartmentHeight - (compartmentHeight * 0.95 - compartmentHeight * 0.05) * self.fluidLevelPercent - compartmentHeight * 0.1); + } + ctx.fillRect(0, compartmentHeight * 0.05, compartmentWidth, compartmentHeight * 0.95); + ctx.restore(); + ctx.restore(); + + self.drawTicks(ctx, compartmentWidth, compartmentHeight); + self.drawText(ctx, compartmentWidth, compartmentHeight); + + if (i % cols === 0) { + ctx.translate(-(cols - 1) * compartmentWidth, compartmentHeight); + } else { + ctx.translate(compartmentWidth, 0); + } + } + } else { + console.error("Not enough space for Vessels"); + } + }; + + self.drawText = function (ctx, compartmentWidth, compartmentHeight) { + fl = typeof self.fluidLevel === 'number' ? self.fluidLevel.toFixed(2) : "0.00"; + vl = typeof self.volume === 'number' ? self.volume.toFixed(2) : "0.00"; + ctx.textAlign = "left"; + ctx.fillStyle = "rgba(0,0,0,1)"; + var padding = Math.max(Math.sqrt(compartmentWidth), Math.sqrt(compartmentHeight)); + ctx.fillText(`${(self.fluidLevelPercent * 100).toFixed(2)} %`, compartmentWidth / 4 - padding, compartmentHeight / 2); + ctx.fillText(`${fl} Ft`, compartmentWidth / 4 - padding, compartmentHeight / 2 + padding); + ctx.fillText(`${vl} BBLs`, compartmentWidth / 4 - padding, compartmentHeight / 2 + 2 * padding); + }; + + self.drawTicks = function (ctx, compartmentWidth, compartmentHeight, guageAlignment) { + ctx.fillStyle = "rgba(0,0,0,1)"; + ctx.textBaseline = "middle"; + var widthOffset; + switch (guageAlignment) { + case "center": + widthOffset = compartmentWidth * 0.5; + break; + case "left": + widthOffset = compartmentWidth * 0.05; + break; + case "right": + widthOffset = compartmentWidth * 0.95; + break; + default: + widthOffset = compartmentWidth * 0.5; + } + var heightOffset = compartmentHeight * 0.05; + var heightRange = compartmentHeight * 0.9; + var numTicks = Math.min(Math.round(compartmentHeight / 40), 10); + ctx.moveTo(widthOffset, compartmentHeight - heightOffset); + ctx.lineTo(widthOffset, heightOffset); + + for (let i = 0; i < numTicks; i++) { + if (i % 2) { + ctx.moveTo(widthOffset - ctx.lineWidth / 2, heightOffset + (i * heightRange) / numTicks); + ctx.lineTo(widthOffset + 10 + ctx.lineWidth / 2, heightOffset + (i * heightRange) / numTicks); + ctx.textAlign = "left"; + ctx.fillText((self.maxHeight * ((numTicks - i) / numTicks)).toFixed(2), widthOffset + 15, heightOffset + (i * heightRange) / numTicks); + } else { + ctx.moveTo(widthOffset + ctx.lineWidth / 2, heightOffset + (i * heightRange) / numTicks); + ctx.lineTo(widthOffset - (10 + ctx.lineWidth / 2), heightOffset + (i * heightRange) / numTicks); + ctx.textAlign = "right"; + ctx.fillText((self.maxHeight * ((numTicks - i) / numTicks)).toFixed(2), widthOffset - 15, heightOffset + (i * heightRange) / numTicks); + } + } + ctx.stroke(); + }; + + self.draw = function () { + //self.fluidLevel = typeof self.ctx.defaultSubscription.data[0].data[0] !== "undefined" ? self.ctx.defaultSubscription.data[0].data[0][1] : 0; + var numVessels = typeof self.ctx.settings.vessels !== "undefined" ? self.ctx.settings.vessels.length : 1; + var vesselType = typeof self.ctx.settings.vessels[0].vesselType !== "undefined" ? self.ctx.settings.vessels[0].vesselType : "Tank"; + var fluidColor = typeof self.ctx.settings.vessels[0].color !== "undefined" ? self.ctx.settings.vessels[0].color : "Water"; + var numCols = typeof self.ctx.settings.numCols !== "undefined" ? self.ctx.settings.numCols : 1; + var numRows = typeof self.ctx.settings.numRows !== "undefined" ? self.ctx.settings.numRows : 1; + + const canvas = document.getElementById("vessel" + self.ctx.defaultSubscription.id); + if (canvas.getContext) { + const ctx = canvas.getContext("2d"); + fitToContainer(canvas); + //clear frame + ctx.clearRect(0, 0, canvas.width, canvas.height); + + //line style + ctx.lineWidth = Math.max((canvas.width * 0.005) / numCols, (canvas.height * 0.005) / numRows); + ctx.lineJoin = "round"; + ctx.strokeStyle = "black"; + //text style + ctx.font = `${Math.min(Math.sqrt(canvas.width / numCols), Math.sqrt(canvas.height / numRows))}px Times New Roman`; + self.drawVessels(ctx, canvas, numVessels, numCols, numRows); + } + }; + + self.onDestroy = function () {}; + \ No newline at end of file diff --git a/__pycache__/thingsboardAPI.cpython-39.pyc b/__pycache__/thingsboardAPI.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21c3f887cc611de920ef8b14065580e4c1fead0d GIT binary patch literal 3311 zcmchZOLNpl5XUuod%ecyje#T%!4#VSN+^<4xo`-N3gqRYVw1;Kr8cr=uw`~vVx%Qy z)%pZi4mpFX>?4PKp}FP~zJgEr_ei^}7pHQIw$wAy_Ozyd(>>GS)KtyD^~aw(tM}3S ziylTV3lCr7&aOakgR{2LEL+xOcsAQ+$81{6xNC5W+s_PcKQ)^+XUj&zd5tl~QiCa< zhq=h(j)>^Z=ry$e8{P_oY&K2KxcSUzTBSu;JHpi5=wsa{@0N`G8yVSxNqam zu0voRn8{1CZ?ue@?U;S`!sweX;OR?qm*Fiu?pt_vwk0@ny1_=4bQ@+-znye*(akTfY^FlFH={_TdBaqGFB7sGc7*cNFw1^VBrp8+JWsQQ zASj$5Oyi*8DsNqcT*yq>WKTJ~*GV%q8A-umG!EMtEXj6jHOydrP2}JFw9>GZor(5C zNzO?ZLb*KYgmL$kA*b=Uw-7wQ*R!A-M@eueiFzFv%YwUcey_J0j35hgjc>KWcO~6a zeybICvDV6D0R4VeMr3)_VV?hovg*+Ej^f5HsUWVyjvoaLe42CPacVGG5z!9 zn@~+;T<9fgc*>XJaZhA9_@+sg%SnvZCM2baB=6)=2pFD1L;6{obODbzmSTo_bqKmM zqGGVqtj3DtV>rsykp$y4nU5$L1{Vb*jn%>UAv$lcP#AKP0&&}}+_#>aTUOuNv3Du- z?4@El>ScM-!J^(s!!@zESGz#%a$JbTS4_L~%8|!lS6P_Hh@<3V!!F&kX$%EqIZ{mw zt&aJt>M(M9@X5n4k?G80=U8#(ZMTP-5v}~-qA$~}UH%cBKJstYxMTc!cFXBI+?+-B zJu;<_zBP+1oHe%G$NmtE-FGP$bw;{<8+hCbj|V(n->dNWOY#V;D_f_V8S`T3S5O=Z zxOfsrqT(h`K~v6>I1SP8H5mCW`pxM)lyJK>fFjS5+B*=6MNKP;x$-Jwuzxu!|H8BfzkgR=}`f={{0t%Ncv(Jul)f7_7yR7h7WzDFv?LpqC~+- zy=^{ajGj?OqNu?>Jb1@#`0FecwOc6G<{td;-NIbqtx##QFsG{w1suvr;Yf8r>(Yaj zK|Z#6D8r;kWvfL?TU8x1?ASp?SgE&!8iuAVPeSbLPf<-rXJf0SYOPi$;k`EX>#f$~ zUf3@Gk#xxGGDp5gJ=#~~M