Micro800s do not treat instructions as controller scope tags even though they show up in the controller tag list
220 lines
12 KiB
Python
220 lines
12 KiB
Python
"""Driver for multisensor"""
|
|
|
|
import threading
|
|
import json
|
|
import time
|
|
from random import randint
|
|
|
|
from device_base import deviceBase
|
|
from channel import PLCChannel, read_tag, write_tag
|
|
from utilities import get_public_ip_address
|
|
|
|
from file_logger import filelogger as log
|
|
|
|
_ = None
|
|
|
|
# log = file_logger.setup()
|
|
log.info("multisensor startup")
|
|
|
|
# GLOBAL VARIABLES
|
|
WAIT_FOR_CONNECTION_SECONDS = 10
|
|
WATCHDOG_ENABLE = True
|
|
WATCHDOG_SEND_PERIOD = 3600 # Seconds, the longest amount of time before sending the watchdog status
|
|
PLC_IP_ADDRESS = "10.20.4.18"
|
|
|
|
CALIBRATION_TABLES = [[] for x in xrange(8)]
|
|
|
|
TRUE_FALSE = {
|
|
0: "false",
|
|
1: "true"
|
|
}
|
|
|
|
CHANNELS = [
|
|
PLCChannel(PLC_IP_ADDRESS, 'an0val', 'input0.scaledValue', 'REAL', 1.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an1val', 'input1.scaledValue', 'REAL', 1.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an2val', 'input2.scaledValue', 'REAL', 1.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an3val', 'input3.scaledValue', 'REAL', 1.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an4val', 'input4.scaledValue', 'REAL', 1.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an5val', 'input5.scaledValue', 'REAL', 1.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an6val', 'input6.scaledValue', 'REAL', 1.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an7val', 'input7.scaledValue', 'REAL', 1.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond0volume', 'input0.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond1volume', 'input1.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond2volume', 'input2.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond3volume', 'input3.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond4volume', 'input4.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond5volume', 'input5.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond6volume', 'input6.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond7volume', 'input7.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an0active', 'input0.active', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an1active', 'input1.active', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an2active', 'input2.active', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an3active', 'input3.active', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an4active', 'input4.active', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an5active', 'input5.active', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an6active', 'input6.active', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an7active', 'input7.active', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an0ispond', 'input0_cfg.isPondLevel', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an1ispond', 'input1_cfg.isPondLevel', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an2ispond', 'input2_cfg.isPondLevel', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an3ispond', 'input3_cfg.isPondLevel', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an4ispond', 'input4_cfg.isPondLevel', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an5ispond', 'input5_cfg.isPondLevel', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an6ispond', 'input6_cfg.isPondLevel', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'an7ispond', 'input7_cfg.isPondLevel', 'BOOL', 1.0, 600, map_=TRUE_FALSE, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond0volume', 'input0.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond1volume', 'input1.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond2volume', 'input2.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond3volume', 'input3.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond4volume', 'input4.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond5volume', 'input5.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond6volume', 'input6.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
PLCChannel(PLC_IP_ADDRESS, 'pond7volume', 'input7.pondVolume', 'REAL', 1000.0, 600, map_=False, write_enabled=False, plc_type="Micro800"),
|
|
]
|
|
|
|
|
|
class start(threading.Thread, deviceBase):
|
|
"""Start class required by Meshify."""
|
|
|
|
def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None,
|
|
companyId=None, offset=None, mqtt=None, Nodes=None):
|
|
"""Initialize the driver."""
|
|
threading.Thread.__init__(self)
|
|
deviceBase.__init__(self, name=name, number=number, mac=mac, Q=Q,
|
|
mcu=mcu, companyId=companyId, offset=offset,
|
|
mqtt=mqtt, Nodes=Nodes)
|
|
|
|
self.daemon = True
|
|
self.version = "1"
|
|
self.finished = threading.Event()
|
|
self.force_send = False
|
|
self.public_ip_address = ""
|
|
self.public_ip_address_last_checked = 0
|
|
self.watchdog = False
|
|
self.watchdog_last_checked = 0
|
|
self.watchdog_last_sent = 0
|
|
threading.Thread.start(self)
|
|
|
|
# this is a required function for all drivers, its goal is to upload some piece of data
|
|
# about your device so it can be seen on the web
|
|
def register(self):
|
|
"""Register the driver."""
|
|
# self.sendtodb("log", "BOOM! Booted.", 0)
|
|
pass
|
|
|
|
def run(self):
|
|
"""Actually run the driver."""
|
|
for i in range(0, WAIT_FOR_CONNECTION_SECONDS):
|
|
print("multisensor driver will start in {} seconds".format(WAIT_FOR_CONNECTION_SECONDS - i))
|
|
time.sleep(1)
|
|
log.info("BOOM! Starting multisensor driver...")
|
|
|
|
self._check_watchdog()
|
|
self._check_ip_address()
|
|
|
|
self.nodes["multisensor_0199"] = self
|
|
|
|
send_loops = 0
|
|
watchdog_check_after = 60
|
|
ip_check_after = 60
|
|
|
|
while True:
|
|
now = time.time()
|
|
if self.force_send:
|
|
log.warning("FORCE SEND: TRUE")
|
|
|
|
for chan in CHANNELS:
|
|
val = chan.read()
|
|
if chan.check(val, self.force_send):
|
|
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, 'multisensor')
|
|
time.sleep(2)
|
|
|
|
|
|
# print("multisensor driver still alive...")
|
|
if self.force_send:
|
|
if send_loops > 2:
|
|
log.warning("Turning off force_send")
|
|
self.force_send = False
|
|
send_loops = 0
|
|
else:
|
|
send_loops += 1
|
|
|
|
if WATCHDOG_ENABLE:
|
|
if (now - self.watchdog_last_checked) > watchdog_check_after:
|
|
self._check_watchdog()
|
|
|
|
if (now - self.public_ip_address_last_checked) > ip_check_after:
|
|
self._check_ip_address()
|
|
|
|
def read_pond_calibration(self, input_number):
|
|
"""Read all calibration values for a specific pond."""
|
|
last_read_height = -1.0
|
|
cal_values = []
|
|
cal_index = 1
|
|
while last_read_height != 0 and cal_index <= 10:
|
|
cal_tag_height = "input{}.calibrationLevel[{}]".format(input_number, cal_index)
|
|
cal_tag_volume = "input{}.calibrationVolume[{}]".format(input_number, cal_index)
|
|
read_height = read_tag(PLC_IP_ADDRESS, cal_tag_height, plc_type="Micro800")
|
|
time.sleep(2)
|
|
read_volume = read_tag(PLC_IP_ADDRESS, cal_tag_volume, plc_type="Micro800")
|
|
time.sleep(2)
|
|
if read_height and read_volume:
|
|
if read_height[0] > 0.0:
|
|
cal_values.append({"height": read_height[0], "volume": read_volume[0]})
|
|
last_read_height = read_height[0]
|
|
cal_index += 1
|
|
|
|
if cal_values != CALIBRATION_TABLES[input_number] or self.force_send:
|
|
calibration_channel = "pond{}calibration".format(input_number)
|
|
calibration_string = json.dumps(cal_values).replace('"', "'")
|
|
self.sendtodbDev(1, calibration_channel, calibration_string, 0, 'multisensor')
|
|
CALIBRATION_TABLES[input_number] = cal_values
|
|
|
|
def _check_watchdog(self):
|
|
"""Check the watchdog and send to Meshify if changed or stale."""
|
|
test_watchdog = self.multisensor_watchdog()
|
|
now = time.time()
|
|
self.watchdog_last_checked = now
|
|
if test_watchdog != self.watchdog or (now - self.watchdog_last_sent) > WATCHDOG_SEND_PERIOD:
|
|
self.sendtodbDev(1, 'watchdog', test_watchdog, 0, 'multisensor')
|
|
self.watchdog = test_watchdog
|
|
self.watchdog_last_sent = now
|
|
|
|
|
|
def _check_ip_address(self):
|
|
"""Check the public IP address and send to Meshify if changed."""
|
|
self.public_ip_address_last_checked = time.time()
|
|
test_public_ip = get_public_ip_address()
|
|
if not test_public_ip == self.public_ip_address:
|
|
self.sendtodbDev(1, 'public_ip_address', test_public_ip, 0, 'multisensor')
|
|
self.public_ip_address = test_public_ip
|
|
|
|
def multisensor_watchdog(self):
|
|
"""Write a random integer to the PLC and then 1 seconds later check that it has been decremented by 1."""
|
|
randval = randint(0, 32767)
|
|
write_tag(str(PLC_IP_ADDRESS), 'watchdog_INT', randval, plc_type="Micro800")
|
|
time.sleep(1)
|
|
watchdog_val = read_tag(str(PLC_IP_ADDRESS), 'watchdog_INT', plc_type="Micro800")
|
|
try:
|
|
return (randval - 1) == watchdog_val[0]
|
|
except (KeyError, TypeError):
|
|
return False
|
|
|
|
def multisensor_sync(self, name, value):
|
|
"""Sync all data from the driver."""
|
|
self.force_send = True
|
|
print("got sync({}, {})".format(name, value))
|
|
self.sendtodb("log", "synced", 0)
|
|
return True
|
|
|
|
def multisensor_writeplctag(self, name, value):
|
|
"""Write a value to the PLC."""
|
|
new_val = json.loads(str(value).replace("'", '"'))
|
|
tag_n = str(new_val['tag']) # "cmd_Start"
|
|
val_n = new_val['val']
|
|
write_res = write_tag(str(PLC_IP_ADDRESS), tag_n, val_n)
|
|
print("Result of multisensor_writeplctag(self, {}, {}) = {}".format(name, value, write_res))
|
|
if write_res is None:
|
|
write_res = "Error writing to PLC..."
|
|
return write_res
|