diff --git a/.DS_Store b/.DS_Store index df01be5..9c63b01 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/ThingsBoardClient/__pycache__/dataCollector.cpython-310.pyc b/ThingsBoardClient/__pycache__/dataCollector.cpython-310.pyc new file mode 100644 index 0000000..aee54e4 Binary files /dev/null and b/ThingsBoardClient/__pycache__/dataCollector.cpython-310.pyc differ diff --git a/ThingsBoardClient/__pycache__/utilities.cpython-310.pyc b/ThingsBoardClient/__pycache__/utilities.cpython-310.pyc new file mode 100644 index 0000000..ba8a1cd Binary files /dev/null and b/ThingsBoardClient/__pycache__/utilities.cpython-310.pyc differ diff --git a/ThingsBoardClient/config.json b/ThingsBoardClient/config.json new file mode 100644 index 0000000..67cbc5f --- /dev/null +++ b/ThingsBoardClient/config.json @@ -0,0 +1,7 @@ +{ + "mqttDomain": "hp.henrypump.cloud", + "mqttUserName": "testtesttest", + "mqttPassword": "TESTTESTTEST", + "mqttClientId": "test-device", + "publishInterval": 60 +} \ No newline at end of file diff --git a/ThingsBoardClient/dataCollector.py b/ThingsBoardClient/dataCollector.py new file mode 100644 index 0000000..1ca0259 --- /dev/null +++ b/ThingsBoardClient/dataCollector.py @@ -0,0 +1,50 @@ +from pycomm3 import LogixDriver, CommError +import minimalmodbus as mbs + +class EthernetIP: + def __init__(self, ip="192.168.1.12",type="Micro800" ): + self.driver = LogixDriver(ip) + + def read_tag(self, tag): + try: + with self.driver: + resp = self.driver.read(tag) + return resp + except Exception as e: + print("Error in read_tag") + print(e) + + def read_tags(self, tags): + try: + with self.driver: + resp = self.driver.read(*tags) + return resp + + except CommError as ce: + print(ce) + except Exception as e: + print("Error in read_tags") + print(e) + + def write_tag(self, tag, value): + try: + with self.driver: + resp = self.driver.write(tag, value) + return resp + except Exception as e: + print("Error in write_tag") + print(e) + + def write_tags(self, tags, values): + try: + a = zip(tags, values) + with self.driver: + resp = self.driver.write(*a) + return resp + except Exception as e: + print("Error in write_tag") + print(e) + +class Modbus: + def __init__(self, baud=19200, stopBits=1, station=247, device="tty/S0"): + self.driver = mbs.Instrument(device) \ No newline at end of file diff --git a/ThingsBoardClient/ethernetip_connector.py b/ThingsBoardClient/ethernetip_connector.py new file mode 100644 index 0000000..90a2f55 --- /dev/null +++ b/ThingsBoardClient/ethernetip_connector.py @@ -0,0 +1,140 @@ +from pycomm3 import LogixDriver, CommError +from datetime import datetime as dt +from time import sleep +from random import choice +from string import ascii_lowercase + +from threading import Thread +from thingsboard_gateway.connectors.connector import Connector, log +from thingsboard_gateway.tb_utility.tb_utility import TBUtility +from thingsboard_gateway.tb_utility.tb_loader import TBModuleLoader + + +class EthernetIPConnector(Thread, Connector): + def __init__(self, gateway, config, connector_type): + super().__init__() + self.statistics = {'MessagesReceived': 0, + 'MessagesSent': 0} # Dictionary, will save information about count received and sent messages. + self.__config = config # Save configuration from the configuration file. + self.__gateway = gateway # Save gateway object, we will use some gateway methods for adding devices and saving data from them. + self.setName(self.__config.get("name","%s connector " % self.get_name() + ''.join(choice(ascii_lowercase) for _ in range(5)))) # get from the configuration or create name for logs. + log.info("Starting Custom %s connector", self.get_name()) # Send message to logger + self.daemon = True # Set self thread as daemon + self.stopped = True # Service variable for check state + self.__connected = False # Service variable for check connection to device + self.__devices = {} # Dictionary with devices, will contain devices configurations, converters for devices and serial port objects + self.__load_converters(connector_type) # Call function to load converters and save it into devices dictionary + self.__connect_to_devices() # Call function for connect to devices + self._connector_type = connector_type + self.utilities = TBModuleLoader.import_module(self._connector_type, "Utilities") + log.info('Custom connector %s initialization success.', self.get_name()) # Message to logger + log.info("Devices in configuration file found: %s ", '\n'.join(device for device in self.__devices)) # Message to logger + + def __connect_to_devices(self): + for device in self.__devices: + try: + log.debug(self.__devices[device]["device_config"]["ip"]) + self.__devices[device]["logixdriver"] = LogixDriver(self.__devices[device]["device_config"]["ip"]) + except Exception as e: + log.exception(e) + else: + self.__gateway.add_device(self.__devices[device]["device_config"]["name"], {"connector": self}, self.__devices[device]["device_config"]["type"]) + self.__connected = True + + def get_name(self): + return self.name #self.__config["name"] + + def is_connected(self): + return self.__connected + + def __load_converters(self, connector_type): + devices_config = self.__config.get('devices') + try: + + if devices_config is not None: + for device_config in devices_config: + if device_config.get('converter') is not None: + log.debug(device_config) + converter = TBModuleLoader.import_module(connector_type, device_config['converter']) + self.__devices[device_config['name']] = {'converter': converter(device_config), + 'device_config': device_config} + else: + log.error('Converter configuration for the custom connector %s -- not found, please check your configuration file.', self.get_name()) + else: + log.error('Section "devices" in the configuration not found. A custom connector %s has being stopped.', self.get_name()) + self.close() + except Exception as e: + log.exception(e) + + def run(self): + try: + while True: + if int(dt.timestamp(dt.now())) % self.__config["publishInterval"] == 0: + log.debug("Publish Interval: %s" % dt.now()) + for device in self.__devices: + try: + tags = list(self.__devices[device]["device_config"]["telemetry"].keys()) + with self.__devices[device]["logixdriver"] as driver: + data_from_device = driver.read(*tags) + log.debug(data_from_device) + converted_data = self.__devices[device]['converter'].convert(self.__devices[device]['device_config'], data_from_device) + log.debug(converted_data) + self.__gateway.send_to_storage(self.get_name(), converted_data) + except CommError as ce: + log.error("Error connecting to PLC") + log.error(ce) + except Exception as e: + log.error("Error in for loop of run()") + log.error(e) + sleep(1) + except Exception as e: + log.exception(e) + def open(self): + log.info("Starting EthernetIP Connector") + self.stopped = False + self.start() + + def close(self): + pass + + def on_attributes_update(self, content): + log.info(content) + if self.__devices.get(content["device"]) is not None: + device_config = self.__devices[content["device"]].get("device_config") + if device_config is not None and device_config.get("attributeUpdates") is not None: + requests = device_config["attributeUpdates"] + for request in requests: + attribute = request.get("attributeOnThingsBoard") + log.debug(attribute) + if attribute is not None and attribute in content["data"]: + try: + value = content["data"][attribute] + str_to_send = str(request["stringToDevice"].replace("${" + attribute + "}", str(value))).encode("UTF-8") + self.__devices[content["device"]]["serial"].write(str_to_send) + log.debug("Attribute update request to device %s : %s", content["device"], str_to_send) + time.sleep(.01) + except Exception as e: + log.exception(e) + + def server_side_rpc_handler(self, content): + log.debug(content) + method_split = content["method"].split('_') + method = None + if len(method_split) > 0: + method = "_".join(method_split[1:]) + + commands = { + "ping": self.utilities.ping, + "write_plc": self.utilities.write_plc + } + + if method is not None: + #check utilities for function + log.debug(f"Looking for {method} in Utilities") + log.info(self.__devices) + if method in commands.keys(): + if method == "write_plc": + log.info(commands[method](self.__devices["Big Test Boi"]["logixdriver"],content)) + else: + log.info(commands[method](content)) + diff --git a/ThingsBoardClient/tbclient.ipynb b/ThingsBoardClient/tbclient.ipynb new file mode 100644 index 0000000..05bd1ff --- /dev/null +++ b/ThingsBoardClient/tbclient.ipynb @@ -0,0 +1,189 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from tb_device_mqtt import TBDeviceMqttClient, TBPublishInfo\n", + "from datetime import datetime as dt\n", + "import utilities\n", + "from dataCollector import EthernetIP, Modbus" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def serverRPC(client, request_id, request_body):\n", + " print(client)\n", + " print(request_id)\n", + " print(request_body)\n", + " #use request_body to determine action\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "config = utilities.load_json_file(\"./config.json\")" + ] + }, + { + "cell_type": "code", + "execution_count": 184, + "metadata": {}, + "outputs": [], + "source": [ + "telemetry = {}\n", + "client = TBDeviceMqttClient(config[\"mqttDomain\"])\n", + "client._client.username_pw_set(config[\"mqttUserName\"],config[\"mqttPassword\"])\n", + "client._client._client_id = config[\"mqttClientId\"]\n", + "client.set_server_side_rpc_request_handler(serverRPC)" + ] + }, + { + "cell_type": "code", + "execution_count": 185, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'vendor': 'Rockwell Automation/Allen-Bradley', 'product_type': 'Programmable Logic Controller', 'product_code': 180, 'revision': {'major': 12, 'minor': 11}, 'status': b'4\\x00', 'serial': '60ed86ac', 'product_name': '2080-LC20-20QWB', 'keyswitch': 'UNKNOWN'}\n" + ] + }, + { + "data": { + "text/plain": [ + "{'pond1Height': -32.0,\n", + " 'pond2Height': -17.29999542236328,\n", + " 'pond3Height': 0.0,\n", + " 'pond4Height': 0.0,\n", + " 'pond1Volume': 0.0,\n", + " 'pond2Volume': 0.0,\n", + " 'pond3Volume': 0.0,\n", + " 'pond4Volume': 0.0,\n", + " 'cfgNumberOfPonds': 3}" + ] + }, + "execution_count": 185, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "driver = EthernetIP(\"192.168.2.158\")\n", + "with driver.driver:\n", + " print(driver.driver.get_plc_info())\n", + "read = driver.read_tags(['pond1Height','pond2Height','pond3Height','pond4Height', 'pond1Volume','pond2Volume','pond3Volume','pond4Volume', \"cfgNumberOfPonds\"])\n", + "for ind, k in enumerate(['pond1Height','pond2Height','pond3Height','pond4Height', 'pond1Volume','pond2Volume','pond3Volume','pond4Volume', \"cfgNumberOfPonds\"]):\n", + " telemetry[k] = read[ind][1]\n", + "#driver.write_tags([\"cfgNumberOfPonds\",\"pond2Height\"], [3,2])\n", + "telemetry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "client.connect()\n", + "while True:\n", + " if dt.timestamp(dt.now()) % config[\"publishInterval\"] == 0:\n", + " print(dt.now())\n", + " client.send_telemetry(telemetry)\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 186, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1659040728.331675\n", + "1659040728\n", + "1659040728\n" + ] + }, + { + "ename": "NameError", + "evalue": "name 'truncate' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/Users/nico/Documents/GitHub/ThingsBoard/ThingsBoardClient/tbclient.ipynb Cell 7\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mint\u001b[39m(now))\n\u001b[1;32m 5\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mround\u001b[39m(now))\n\u001b[0;32m----> 6\u001b[0m \u001b[39mprint\u001b[39m(truncate(now))\n", + "\u001b[0;31mNameError\u001b[0m: name 'truncate' is not defined" + ] + } + ], + "source": [ + "\n", + "while True:\n", + " now = dt.timestamp(dt.now())\n", + " print(now)\n", + " print(int(now))\n", + " print(round(now))\n", + " #print(truncate(now))\n", + "\n", + "#% config[\"publishInterval\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "client.send_telemetry(telemetry)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "client.disconnect()" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "32b1684233d9748bd1bb5a29a1b19459c9564d6488d1324e633b9c48826c5d03" + }, + "kernelspec": { + "display_name": "Python 3.10.4 ('thingsboard')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/ThingsBoardClient/utilities.py b/ThingsBoardClient/utilities.py new file mode 100644 index 0000000..8d08d3d --- /dev/null +++ b/ThingsBoardClient/utilities.py @@ -0,0 +1,31 @@ +import json + +def load_json_file(path): + try: + with open(path, "r") as f: + j = json.load(f) + return j + except Exception as e: + print("Error while loading JSON") + print(e) + + +def save_json_file(path,payload): + try: + with open(path, "w") as f: + json.dump(payload,f) + except Exception as e: + print("Error while saving JSON") + print(e) + +def gal_to_bbl(value): + return value / 42.0 + +def bbl_to_gal(value): + return value * 42.0 + +def gpm_to_bpd(value): + return value * ((60 * 24) / 42.0) + +def bpd_to_gpm(value): + return value * (42.0 / (24 * 60)) \ No newline at end of file diff --git a/Widgets/vessels/vessels.js b/Widgets/vessels/vessels.js index fe6190e..008902e 100644 --- a/Widgets/vessels/vessels.js +++ b/Widgets/vessels/vessels.js @@ -1,23 +1,21 @@ self.onInit = function () { - volume = 0; - fluidLevel = 0; - self.ctx.$container.append( - "
" - ); - }; - self.onResize = function () { + volume = 0; + fluidLevel = 0; + self.ctx.$container.append("
"); +}; +self.onResize = function () { self.draw(); - }; - - //function for scaling a canvas - function fitToContainer(canvas) { +}; + +//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.onDataUpdated = function () { self.ctx.detectChanges(); //setup variables self.volume = 0; @@ -33,31 +31,31 @@ self.onInit = function () { self.fluidLevelPercent = self.fluidLevel / self.maxHeight; //draw a new canvas self.draw(); - }; - - self.drawTank = function (ctx, compartmentWidth, compartmentHeight) { +}; + +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); +}; + +self.drawPond = function (ctx, compartmentWidth, compartmentHeight) { + ctx.moveTo(compartmentWidth * 0.05, compartmentHeight * 0.1); 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) { + ctx.lineTo(compartmentWidth * 0.95, compartmentHeight * 0.1); +}; + +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); + //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) { @@ -74,13 +72,24 @@ self.onInit = function () { 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; + 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) { @@ -91,14 +100,14 @@ self.onInit = function () { 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; @@ -111,10 +120,10 @@ self.onInit = function () { 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 { @@ -124,20 +133,20 @@ self.onInit = function () { } 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"; +}; + +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 = "center"; 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.fillText(`${(self.fluidLevelPercent * 100).toFixed(2)} % ${fl} Ft ${vl} BBLs`, compartmentWidth / 2, compartmentHeight * 0.05); + //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; @@ -154,12 +163,12 @@ self.onInit = function () { default: widthOffset = compartmentWidth * 0.5; } - var heightOffset = compartmentHeight * 0.05; - var heightRange = compartmentHeight * 0.9; + var heightOffset = compartmentHeight * 0.1; + var heightRange = compartmentHeight * 0.85; var numTicks = Math.min(Math.round(compartmentHeight / 40), 10); - ctx.moveTo(widthOffset, compartmentHeight - heightOffset); + ctx.moveTo(widthOffset, compartmentHeight - compartmentHeight*0.05); ctx.lineTo(widthOffset, heightOffset); - + for (let i = 0; i < numTicks; i++) { if (i % 2) { ctx.moveTo(widthOffset - ctx.lineWidth / 2, heightOffset + (i * heightRange) / numTicks); @@ -174,23 +183,23 @@ self.onInit = function () { } } ctx.stroke(); - }; - - self.draw = function () { +}; + +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"; @@ -199,7 +208,6 @@ self.onInit = function () { 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 +}; + +self.onDestroy = function () {}; diff --git a/get_telemetry.ipynb b/get_telemetry.ipynb index 46f1cba..2cb3eb9 100644 --- a/get_telemetry.ipynb +++ b/get_telemetry.ipynb @@ -2,26 +2,27 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "import thingsboardAPI\n", - "from datetime import datetime as dt" + "from thingsboardAPI import ThingsBoardAPI\n", + "from datetime import datetime as dt\n", + "import json" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ - "tb = thingsboardAPI(\"nmelone@henry-pump.com\", \"gzU6$26v42mU%3jDzTJf\", \"hp.henrypump.cloud\")" + "tb = ThingsBoardAPI(\"nmelone@henry-pump.com\", \"gzU6$26v42mU%3jDzTJf\", \"hp.henrypump.cloud\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -30,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -43,7 +44,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -52,6 +53,33 @@ " with open('/Users/nico/Documents/tbTimelapse/'+ str(snap[\"ts\"]) + \".jpg\", 'wb') as img:\n", " img.write(base64.b64decode(snap[\"value\"]))" ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'{\\n \"data\": [\\n {\\n \"id\": {\\n \"entityType\": \"DEVICE\",\\n \"id\": \"06488cb0-e0f5-11ec-9bb1-4f9f983f9687\"\\n },\\n \"createdTime\": 1654010351867,\\n \"additionalInfo\": {\\n \"gateway\": false,\\n \"overwriteActivityTime\": false,\\n \"description\": \"\"\\n },\\n \"tenantId\": {\\n \"entityType\": \"TENANT\",\\n \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\\n },\\n \"customerId\": {\\n \"entityType\": \"CUSTOMER\",\\n \"id\": \"7a6ce000-dd3a-11ec-b1ed-e5d3f0ce866e\"\\n },\\n \"name\": \"Camera Trailer 107\",\\n \"type\": \"cameratrailer\",\\n \"label\": \"\",\\n \"deviceProfileId\": {\\n \"entityType\": \"DEVICE_PROFILE\",\\n \"id\": \"9bf921c0-dd3a-11ec-b5bf-ff45c37940c6\"\\n },\\n \"deviceData\": {\\n \"configuration\": {\\n \"type\": \"DEFAULT\"\\n },\\n \"transportConfiguration\": {\\n \"type\": \"DEFAULT\"\\n }\\n },\\n \"firmwareId\": null,\\n \"softwareId\": null,\\n \"ownerId\": {\\n \"entityType\": \"CUSTOMER\",\\n \"id\": \"7a6ce000-dd3a-11ec-b1ed-e5d3f0ce866e\"\\n }\\n },\\n {\\n \"id\": {\\n \"entityType\": \"DEVICE\",\\n \"id\": \"3bbd12c0-e2a9-11ec-bece-e5d3f0ce866e\"\\n },\\n \"createdTime\": 1654197702124,\\n \"additionalInfo\": {\\n \"gateway\": false,\\n \"overwriteActivityTime\": false,\\n \"description\": \"\"\\n },\\n \"tenantId\": {\\n \"entityType\": \"TENANT\",\\n \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\\n },\\n \"customerId\": {\\n \"entityType\": \"CUSTOMER\",\\n \"id\": \"7a6ce000-dd3a-11ec-b1ed-e5d3f0ce866e\"\\n },\\n \"name\": \"Camera Trailer 101\",\\n \"type\": \"cameratrailer\",\\n \"label\": \"\",\\n \"deviceProfileId\": {\\n \"entityType\": \"DEVICE_PROFILE\",\\n \"id\": \"9bf921c0-dd3a-11ec-b5bf-ff45c37940c6\"\\n },\\n \"deviceData\": {\\n \"configuration\": {\\n \"type\": \"DEFAULT\"\\n },\\n \"transportConfiguration\": {\\n \"type\": \"DEFAULT\"\\n }\\n },\\n \"firmwareId\": null,\\n \"softwareId\": null,\\n \"ownerId\": {\\n \"entityType\": \"CUSTOMER\",\\n \"id\": \"7a6ce000-dd3a-11ec-b1ed-e5d3f0ce866e\"\\n }\\n },\\n {\\n \"id\": {\\n \"entityType\": \"DEVICE\",\\n \"id\": \"ac7dd6e0-e0ea-11ec-8c76-ad8278896f52\"\\n },\\n \"createdTime\": 1654005906254,\\n \"additionalInfo\": {\\n \"gateway\": false,\\n \"overwriteActivityTime\": false,\\n \"description\": \"\"\\n },\\n \"tenantId\": {\\n \"entityType\": \"TENANT\",\\n \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\\n },\\n \"customerId\": {\\n \"entityType\": \"CUSTOMER\",\\n \"id\": \"7a6ce000-dd3a-11ec-b1ed-e5d3f0ce866e\"\\n },\\n \"name\": \"Camera Trailer 106\",\\n \"type\": \"cameratrailer\",\\n \"label\": \"\",\\n \"deviceProfileId\": {\\n \"entityType\": \"DEVICE_PROFILE\",\\n \"id\": \"9bf921c0-dd3a-11ec-b5bf-ff45c37940c6\"\\n },\\n \"deviceData\": {\\n \"configuration\": {\\n \"type\": \"DEFAULT\"\\n },\\n \"transportConfiguration\": {\\n \"type\": \"DEFAULT\"\\n }\\n },\\n \"firmwareId\": null,\\n \"softwareId\": null,\\n \"ownerId\": {\\n \"entityType\": \"CUSTOMER\",\\n \"id\": \"7a6ce000-dd3a-11ec-b1ed-e5d3f0ce866e\"\\n }\\n },\\n {\\n \"id\": {\\n \"entityType\": \"DEVICE\",\\n \"id\": \"ebc3d9a0-dd3c-11ec-b1ed-e5d3f0ce866e\"\\n },\\n \"createdTime\": 1653601426490,\\n \"additionalInfo\": {\\n \"gateway\": false,\\n \"overwriteActivityTime\": false,\\n \"description\": \"\"\\n },\\n \"tenantId\": {\\n \"entityType\": \"TENANT\",\\n \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\\n },\\n \"customerId\": {\\n \"entityType\": \"CUSTOMER\",\\n \"id\": \"7a6ce000-dd3a-11ec-b1ed-e5d3f0ce866e\"\\n },\\n \"name\": \"Camera Trailer 123\",\\n \"type\": \"cameratrailer\",\\n \"label\": \"\",\\n \"deviceProfileId\": {\\n \"entityType\": \"DEVICE_PROFILE\",\\n \"id\": \"9bf921c0-dd3a-11ec-b5bf-ff45c37940c6\"\\n },\\n \"deviceData\": {\\n \"configuration\": {\\n \"type\": \"DEFAULT\"\\n },\\n \"transportConfiguration\": {\\n \"type\": \"DEFAULT\"\\n }\\n },\\n \"firmwareId\": null,\\n \"softwareId\": null,\\n \"ownerId\": {\\n \"entityType\": \"CUSTOMER\",\\n \"id\": \"7a6ce000-dd3a-11ec-b1ed-e5d3f0ce866e\"\\n }\\n }\\n ],\\n \"totalPages\": 1,\\n \"totalElements\": 4,\\n \"hasNext\": false\\n}'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "json.dumps(devices, indent=4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/timelapse.ipynb b/timelapse.ipynb index 08c2d51..6213836 100644 --- a/timelapse.ipynb +++ b/timelapse.ipynb @@ -25,7 +25,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -38,11 +38,11 @@ " customer = rest_client.get_customers(page_size=10,page=0,text_search=\"Amerus Safety Solutions\")\n", " #print(customer)\n", " cid = customer.data[0].id.id\n", - " device = rest_client.get_customer_devices(customer_id=cid, page=0, page_size=10,text_search=\"Camera Trailer 104\")\n", + " device = rest_client.get_customer_devices(customer_id=cid, page=0, page_size=10,text_search=\"Camera Trailer 107\")\n", " #print(device)\n", " eType = device.data[0].id.entity_type\n", " eid = device.data[0].id.id\n", - " start = convertDateTimeToMS(\"28 Jun 2022, 00:00:00\")\n", + " start = convertDateTimeToMS(\"28 Jul 2022, 00:00:00\")\n", " end = int(dt.now().timestamp() * 1000)\n", " telemetry = rest_client.get_timeseries(entity_type=eType, entity_id=eid , keys=\"snapshot\", start_ts=start, end_ts=end, limit=1000)\n", " #print(telemetry)\n", @@ -52,7 +52,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -81,7 +81,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.4" + "version": "3.10.5" }, "orig_nbformat": 4 },