finalized tb gateway extension
This commit is contained in:
BIN
ThingsBoardClient/__pycache__/dataCollector.cpython-310.pyc
Normal file
BIN
ThingsBoardClient/__pycache__/dataCollector.cpython-310.pyc
Normal file
Binary file not shown.
BIN
ThingsBoardClient/__pycache__/utilities.cpython-310.pyc
Normal file
BIN
ThingsBoardClient/__pycache__/utilities.cpython-310.pyc
Normal file
Binary file not shown.
7
ThingsBoardClient/config.json
Normal file
7
ThingsBoardClient/config.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"mqttDomain": "hp.henrypump.cloud",
|
||||||
|
"mqttUserName": "testtesttest",
|
||||||
|
"mqttPassword": "TESTTESTTEST",
|
||||||
|
"mqttClientId": "test-device",
|
||||||
|
"publishInterval": 60
|
||||||
|
}
|
||||||
50
ThingsBoardClient/dataCollector.py
Normal file
50
ThingsBoardClient/dataCollector.py
Normal file
@@ -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)
|
||||||
140
ThingsBoardClient/ethernetip_connector.py
Normal file
140
ThingsBoardClient/ethernetip_connector.py
Normal file
@@ -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))
|
||||||
|
|
||||||
189
ThingsBoardClient/tbclient.ipynb
Normal file
189
ThingsBoardClient/tbclient.ipynb
Normal file
@@ -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<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[1;32m <a href='vscode-notebook-cell:/Users/nico/Documents/GitHub/ThingsBoard/ThingsBoardClient/tbclient.ipynb#ch0000011?line=3'>4</a>\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mint\u001b[39m(now))\n\u001b[1;32m <a href='vscode-notebook-cell:/Users/nico/Documents/GitHub/ThingsBoard/ThingsBoardClient/tbclient.ipynb#ch0000011?line=4'>5</a>\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mround\u001b[39m(now))\n\u001b[0;32m----> <a href='vscode-notebook-cell:/Users/nico/Documents/GitHub/ThingsBoard/ThingsBoardClient/tbclient.ipynb#ch0000011?line=5'>6</a>\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
|
||||||
|
}
|
||||||
31
ThingsBoardClient/utilities.py
Normal file
31
ThingsBoardClient/utilities.py
Normal file
@@ -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))
|
||||||
@@ -1,23 +1,21 @@
|
|||||||
self.onInit = function () {
|
self.onInit = function () {
|
||||||
volume = 0;
|
volume = 0;
|
||||||
fluidLevel = 0;
|
fluidLevel = 0;
|
||||||
self.ctx.$container.append(
|
self.ctx.$container.append("<div><canvas id='vessel" + self.ctx.defaultSubscription.id + "'></canvas></div>");
|
||||||
"<div><canvas id='vessel" + self.ctx.defaultSubscription.id + "'></canvas></div>"
|
};
|
||||||
);
|
self.onResize = function () {
|
||||||
};
|
|
||||||
self.onResize = function () {
|
|
||||||
self.draw();
|
self.draw();
|
||||||
};
|
};
|
||||||
|
|
||||||
//function for scaling a canvas
|
//function for scaling a canvas
|
||||||
function fitToContainer(canvas) {
|
function fitToContainer(canvas) {
|
||||||
canvas.style.width = "100%";
|
canvas.style.width = "100%";
|
||||||
canvas.style.height = "100%";
|
canvas.style.height = "100%";
|
||||||
canvas.width = canvas.offsetWidth;
|
canvas.width = canvas.offsetWidth;
|
||||||
canvas.height = canvas.offsetHeight;
|
canvas.height = canvas.offsetHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onDataUpdated = function () {
|
self.onDataUpdated = function () {
|
||||||
self.ctx.detectChanges();
|
self.ctx.detectChanges();
|
||||||
//setup variables
|
//setup variables
|
||||||
self.volume = 0;
|
self.volume = 0;
|
||||||
@@ -33,31 +31,31 @@ self.onInit = function () {
|
|||||||
self.fluidLevelPercent = self.fluidLevel / self.maxHeight;
|
self.fluidLevelPercent = self.fluidLevel / self.maxHeight;
|
||||||
//draw a new canvas
|
//draw a new canvas
|
||||||
self.draw();
|
self.draw();
|
||||||
};
|
};
|
||||||
|
|
||||||
self.drawTank = function (ctx, compartmentWidth, compartmentHeight) {
|
self.drawTank = function (ctx, compartmentWidth, compartmentHeight) {
|
||||||
ctx.moveTo(compartmentWidth * 0.05, compartmentHeight * 0.05);
|
ctx.moveTo(compartmentWidth * 0.05, compartmentHeight * 0.05);
|
||||||
ctx.lineTo(compartmentWidth * 0.05, compartmentHeight * 0.95);
|
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.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.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);
|
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) {
|
self.drawPond = function (ctx, compartmentWidth, compartmentHeight) {
|
||||||
ctx.moveTo(compartmentWidth * 0.05, compartmentHeight * 0.05);
|
ctx.moveTo(compartmentWidth * 0.05, compartmentHeight * 0.1);
|
||||||
ctx.lineTo(compartmentWidth * 0.15, compartmentHeight * 0.95);
|
ctx.lineTo(compartmentWidth * 0.15, compartmentHeight * 0.95);
|
||||||
ctx.lineTo(compartmentWidth * 0.85, compartmentHeight * 0.95);
|
ctx.lineTo(compartmentWidth * 0.85, compartmentHeight * 0.95);
|
||||||
ctx.lineTo(compartmentWidth * 0.95, compartmentHeight * 0.05);
|
ctx.lineTo(compartmentWidth * 0.95, compartmentHeight * 0.1);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.drawVessels = function (ctx, canvas, numVessels, cols, rows) {
|
self.drawVessels = function (ctx, canvas, numVessels, cols, rows) {
|
||||||
if (numVessels <= cols * rows) {
|
if (numVessels <= cols * rows) {
|
||||||
//vessel
|
//vessel
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
for (let i = 1; i <= numVessels; i++) {
|
for (let i = 1; i <= numVessels; i++) {
|
||||||
for (let j = 0; j < self.ctx.defaultSubscription.data.length; j++) {
|
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].volumeKey);
|
||||||
//console.log(self.ctx.settings.vessels[i - 1].fluidLevelKey);
|
//console.log(self.ctx.settings.vessels[i - 1].fluidLevelKey);
|
||||||
if (self.ctx.defaultSubscription.data[j].dataKey.name === self.ctx.settings.vessels[i - 1].volumeKey) {
|
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;
|
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) {
|
} 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;
|
var compartmentHeight = canvas.height / rows;
|
||||||
//gradient used as water
|
//gradient used as water
|
||||||
const grad = ctx.createLinearGradient(0, compartmentHeight * 0.05, 0, compartmentHeight * 0.95);
|
const grad = ctx.createLinearGradient(0, compartmentHeight * 0.05, 0, compartmentHeight * 0.95);
|
||||||
switch(fluidColor){
|
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 "Water":
|
||||||
case "Produced Water": grad.addColorStop(0, "rgba(170, 100, 30,1)");grad.addColorStop(0.4, "rgba(85, 50, 15,1");break;
|
grad.addColorStop(0, "rgba(70,220,210,0.75)");
|
||||||
case "Oil": grad.addColorStop(0,"rgba(100,100,100,0.75)");grad.addColorStop(0.3, "rgba(25,25,25, 0.85");break;
|
grad.addColorStop(0.4, "rgba(0,120,240,0.75)");
|
||||||
default: grad.addColorStop(0,fluidColor);break;
|
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)");
|
grad.addColorStop(1, "rgba(0,0,0, 0.8)");
|
||||||
//draw the vessel
|
//draw the vessel
|
||||||
switch (vesselType) {
|
switch (vesselType) {
|
||||||
@@ -91,14 +100,14 @@ self.onInit = function () {
|
|||||||
self.drawTank(ctx, compartmentWidth, compartmentHeight);
|
self.drawTank(ctx, compartmentWidth, compartmentHeight);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//draw
|
//draw
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
//setup clip area of container
|
//setup clip area of container
|
||||||
ctx.clip();
|
ctx.clip();
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
//set fill style for water to gradient
|
//set fill style for water to gradient
|
||||||
ctx.fillStyle = grad;
|
ctx.fillStyle = grad;
|
||||||
@@ -111,10 +120,10 @@ self.onInit = function () {
|
|||||||
ctx.fillRect(0, compartmentHeight * 0.05, compartmentWidth, compartmentHeight * 0.95);
|
ctx.fillRect(0, compartmentHeight * 0.05, compartmentWidth, compartmentHeight * 0.95);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
self.drawTicks(ctx, compartmentWidth, compartmentHeight);
|
self.drawTicks(ctx, compartmentWidth, compartmentHeight);
|
||||||
self.drawText(ctx, compartmentWidth, compartmentHeight);
|
self.drawText(ctx, compartmentWidth, compartmentHeight);
|
||||||
|
|
||||||
if (i % cols === 0) {
|
if (i % cols === 0) {
|
||||||
ctx.translate(-(cols - 1) * compartmentWidth, compartmentHeight);
|
ctx.translate(-(cols - 1) * compartmentWidth, compartmentHeight);
|
||||||
} else {
|
} else {
|
||||||
@@ -124,20 +133,20 @@ self.onInit = function () {
|
|||||||
} else {
|
} else {
|
||||||
console.error("Not enough space for Vessels");
|
console.error("Not enough space for Vessels");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.drawText = function (ctx, compartmentWidth, compartmentHeight) {
|
self.drawText = function (ctx, compartmentWidth, compartmentHeight) {
|
||||||
fl = typeof self.fluidLevel === 'number' ? self.fluidLevel.toFixed(2) : "0.00";
|
fl = typeof self.fluidLevel === "number" ? self.fluidLevel.toFixed(2) : "0.00";
|
||||||
vl = typeof self.volume === 'number' ? self.volume.toFixed(2) : "0.00";
|
vl = typeof self.volume === "number" ? self.volume.toFixed(2) : "0.00";
|
||||||
ctx.textAlign = "left";
|
ctx.textAlign = "center";
|
||||||
ctx.fillStyle = "rgba(0,0,0,1)";
|
ctx.fillStyle = "rgba(0,0,0,1)";
|
||||||
var padding = Math.max(Math.sqrt(compartmentWidth), Math.sqrt(compartmentHeight));
|
var padding = Math.max(Math.sqrt(compartmentWidth), Math.sqrt(compartmentHeight));
|
||||||
ctx.fillText(`${(self.fluidLevelPercent * 100).toFixed(2)} %`, compartmentWidth / 4 - padding, compartmentHeight / 2);
|
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(`${fl} Ft`, compartmentWidth / 4 - padding, compartmentHeight / 2 + padding);
|
||||||
ctx.fillText(`${vl} BBLs`, compartmentWidth / 4 - padding, compartmentHeight / 2 + 2 * padding);
|
//ctx.fillText(`${vl} BBLs`, compartmentWidth / 4 - padding, compartmentHeight / 2 + 2 * padding);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.drawTicks = function (ctx, compartmentWidth, compartmentHeight, guageAlignment) {
|
self.drawTicks = function (ctx, compartmentWidth, compartmentHeight, guageAlignment) {
|
||||||
ctx.fillStyle = "rgba(0,0,0,1)";
|
ctx.fillStyle = "rgba(0,0,0,1)";
|
||||||
ctx.textBaseline = "middle";
|
ctx.textBaseline = "middle";
|
||||||
var widthOffset;
|
var widthOffset;
|
||||||
@@ -154,12 +163,12 @@ self.onInit = function () {
|
|||||||
default:
|
default:
|
||||||
widthOffset = compartmentWidth * 0.5;
|
widthOffset = compartmentWidth * 0.5;
|
||||||
}
|
}
|
||||||
var heightOffset = compartmentHeight * 0.05;
|
var heightOffset = compartmentHeight * 0.1;
|
||||||
var heightRange = compartmentHeight * 0.9;
|
var heightRange = compartmentHeight * 0.85;
|
||||||
var numTicks = Math.min(Math.round(compartmentHeight / 40), 10);
|
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);
|
ctx.lineTo(widthOffset, heightOffset);
|
||||||
|
|
||||||
for (let i = 0; i < numTicks; i++) {
|
for (let i = 0; i < numTicks; i++) {
|
||||||
if (i % 2) {
|
if (i % 2) {
|
||||||
ctx.moveTo(widthOffset - ctx.lineWidth / 2, heightOffset + (i * heightRange) / numTicks);
|
ctx.moveTo(widthOffset - ctx.lineWidth / 2, heightOffset + (i * heightRange) / numTicks);
|
||||||
@@ -174,23 +183,23 @@ self.onInit = function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.stroke();
|
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;
|
//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 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 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 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 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;
|
var numRows = typeof self.ctx.settings.numRows !== "undefined" ? self.ctx.settings.numRows : 1;
|
||||||
|
|
||||||
const canvas = document.getElementById("vessel" + self.ctx.defaultSubscription.id);
|
const canvas = document.getElementById("vessel" + self.ctx.defaultSubscription.id);
|
||||||
if (canvas.getContext) {
|
if (canvas.getContext) {
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.getContext("2d");
|
||||||
fitToContainer(canvas);
|
fitToContainer(canvas);
|
||||||
//clear frame
|
//clear frame
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
//line style
|
//line style
|
||||||
ctx.lineWidth = Math.max((canvas.width * 0.005) / numCols, (canvas.height * 0.005) / numRows);
|
ctx.lineWidth = Math.max((canvas.width * 0.005) / numCols, (canvas.height * 0.005) / numRows);
|
||||||
ctx.lineJoin = "round";
|
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`;
|
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.drawVessels(ctx, canvas, numVessels, numCols, numRows);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.onDestroy = function () {};
|
self.onDestroy = function () {};
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -25,7 +25,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 8,
|
"execution_count": 3,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@@ -38,11 +38,11 @@
|
|||||||
" customer = rest_client.get_customers(page_size=10,page=0,text_search=\"Amerus Safety Solutions\")\n",
|
" customer = rest_client.get_customers(page_size=10,page=0,text_search=\"Amerus Safety Solutions\")\n",
|
||||||
" #print(customer)\n",
|
" #print(customer)\n",
|
||||||
" cid = customer.data[0].id.id\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",
|
" #print(device)\n",
|
||||||
" eType = device.data[0].id.entity_type\n",
|
" eType = device.data[0].id.entity_type\n",
|
||||||
" eid = device.data[0].id.id\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",
|
" 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",
|
" 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",
|
" #print(telemetry)\n",
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 9,
|
"execution_count": 4,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.10.4"
|
"version": "3.10.5"
|
||||||
},
|
},
|
||||||
"orig_nbformat": 4
|
"orig_nbformat": 4
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user