Files
Multi-Sensor/POCloud/python-driver/multisensor.py
Patrick McDonagh c7f8d30446 Fix to get real data working with Micro850
Micro800s do not treat instructions as controller scope tags even though they show up in the controller tag list
2018-08-13 17:42:54 -05:00

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