POCloud templates

This commit is contained in:
Patrick McDonagh
2018-06-19 14:08:49 -05:00
parent 5848e2fd4e
commit 9dbb825103
163 changed files with 1709 additions and 71 deletions

View File

@@ -0,0 +1,296 @@
"""Define Meshify channel class."""
import time
from pycomm.ab_comm.clx import Driver as ClxDriver
from pycomm.cip.cip_base import CommError, DataError
TAG_DATAERROR_SLEEPTIME = 5
def binarray(intval):
"""Split an integer into its bits."""
bin_string = '{0:08b}'.format(intval)
bin_arr = [i for i in bin_string]
bin_arr.reverse()
return bin_arr
def read_tag(addr, tag, plc_type="CLX"):
"""Read a tag from the PLC."""
direct = plc_type == "Micro800"
addr = str(addr)
tag = str(tag)
c = ClxDriver()
try:
if c.open(addr, direct_connection=direct):
try:
v = c.read_tag(tag)
return v
except DataError as e:
c.close()
time.sleep(TAG_DATAERROR_SLEEPTIME)
print("Data Error during readTag({}, {}, plc_type='{}'): {}".format(addr, tag, plc_type, e))
else:
raise DataError("no data")
except CommError:
# err = c.get_status()
c.close()
print("Could not connect during readTag({}, {})".format(addr, tag))
# print err
except AttributeError as e:
c.close()
print("AttributeError during readTag({}, {}): \n{}".format(addr, tag, e))
c.close()
def read_array(addr, tag, start, end, plc_type="CLX"):
"""Read an array from the PLC."""
direct = plc_type == "Micro800"
c = ClxDriver()
if c.open(addr, direct_connection=direct):
arr_vals = []
try:
for i in range(start, end):
tag_w_index = tag + "[{}]".format(i)
v = c.read_tag(tag_w_index)
# print('{} - {}'.format(tag_w_index, v))
arr_vals.append(round(v[0], 4))
# print(v)
if arr_vals:
return arr_vals
print("No length for {}".format(addr))
return False
except Exception:
print("Error during readArray({}, {}, {}, {})".format(addr, tag, start, end))
err = c.get_status()
c.close()
print(err)
return False
c.close()
return False
def write_tag(addr, tag, val, plc_type="CLX"):
"""Write a tag value to the PLC."""
direct = plc_type == "Micro800"
clx = ClxDriver()
if clx.open(addr, direct_connection=direct):
try:
prevval = clx.read_tag(tag)
if direct:
time.sleep(1)
write_result = clx.write_tag(tag, val, prevval[1])
return write_result
except Exception:
print("Error during writeTag({}, {}, {})".format(addr, tag, val))
err = clx.get_status()
clx.close()
print(err)
return False
clx.close()
return False
class Channel(object):
"""Holds the configuration for a Meshify channel."""
def __init__(self, mesh_name, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False):
"""Initialize the channel."""
self.mesh_name = mesh_name
self.data_type = data_type
self.last_value = None
self.value = None
self.last_send_time = 0
self.chg_threshold = chg_threshold
self.guarantee_sec = guarantee_sec
self.map_ = map_
self.write_enabled = write_enabled
def __str__(self):
"""Create a string for the channel."""
return "{}\nvalue: {}, last_send_time: {}".format(self.mesh_name, self.value, self.last_send_time)
def check(self, new_value, force_send=False):
"""Check to see if the new_value needs to be stored."""
send_needed = False
send_reason = ""
if self.data_type == 'BOOL' or self.data_type == 'STRING':
if self.last_send_time == 0:
send_needed = True
send_reason = "no send time"
elif self.value is None:
send_needed = True
send_reason = "no value"
elif not (self.value == new_value):
if self.map_:
if not self.value == self.map_[new_value]:
send_needed = True
send_reason = "value change"
else:
send_needed = True
send_reason = "value change"
elif (time.time() - self.last_send_time) > self.guarantee_sec:
send_needed = True
send_reason = "guarantee sec"
elif force_send:
send_needed = True
send_reason = "forced"
else:
if self.last_send_time == 0:
send_needed = True
send_reason = "no send time"
elif self.value is None:
send_needed = True
send_reason = "no value"
elif abs(self.value - new_value) > self.chg_threshold:
send_needed = True
send_reason = "change threshold"
elif (time.time() - self.last_send_time) > self.guarantee_sec:
send_needed = True
send_reason = "guarantee sec"
elif force_send:
send_needed = True
send_reason = "forced"
if send_needed:
self.last_value = self.value
if self.map_:
try:
self.value = self.map_[new_value]
except KeyError:
print("Cannot find a map value for {} in {} for {}".format(new_value, self.map_, self.mesh_name))
self.value = new_value
else:
self.value = new_value
self.last_send_time = time.time()
print("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason))
return send_needed
def read(self):
"""Read the value."""
pass
def identity(sent):
"""Return exactly what was sent to it."""
return sent
class ModbusChannel(Channel):
"""Modbus channel object."""
def __init__(self, mesh_name, register_number, data_type, chg_threshold, guarantee_sec, channel_size=1, map_=False, write_enabled=False, transformFn=identity):
"""Initialize the channel."""
super(ModbusChannel, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled)
self.mesh_name = mesh_name
self.register_number = register_number
self.channel_size = channel_size
self.data_type = data_type
self.last_value = None
self.value = None
self.last_send_time = 0
self.chg_threshold = chg_threshold
self.guarantee_sec = guarantee_sec
self.map_ = map_
self.write_enabled = write_enabled
self.transformFn = transformFn
def read(self, mbsvalue):
"""Return the transformed read value."""
return self.transformFn(mbsvalue)
class PLCChannel(Channel):
"""PLC Channel Object."""
def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False, plc_type='CLX'):
"""Initialize the channel."""
super(PLCChannel, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled)
self.plc_ip = ip
self.mesh_name = mesh_name
self.plc_tag = plc_tag
self.data_type = data_type
self.last_value = None
self.value = None
self.last_send_time = 0
self.chg_threshold = chg_threshold
self.guarantee_sec = guarantee_sec
self.map_ = map_
self.write_enabled = write_enabled
self.plc_type = plc_type
def read(self):
"""Read the value."""
plc_value = None
if self.plc_tag and self.plc_ip:
read_value = read_tag(self.plc_ip, self.plc_tag, plc_type=self.plc_type)
if read_value:
plc_value = read_value[0]
return plc_value
class BoolArrayChannels(Channel):
"""Hold the configuration for a set of boolean array channels."""
def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False):
"""Initialize the channel."""
self.plc_ip = ip
self.mesh_name = mesh_name
self.plc_tag = plc_tag
self.data_type = data_type
self.last_value = None
self.value = None
self.last_send_time = 0
self.chg_threshold = chg_threshold
self.guarantee_sec = guarantee_sec
self.map_ = map_
self.write_enabled = write_enabled
def compare_values(self, new_val_dict):
"""Compare new values to old values to see if the values need storing."""
send = False
for idx in new_val_dict:
try:
if new_val_dict[idx] != self.last_value[idx]:
send = True
except KeyError:
print("Key Error in self.compare_values for index {}".format(idx))
send = True
return send
def read(self, force_send=False):
"""Read the value and check to see if needs to be stored."""
send_needed = False
send_reason = ""
if self.plc_tag:
v = read_tag(self.plc_ip, self.plc_tag)
if v:
bool_arr = binarray(v[0])
new_val = {}
for idx in self.map_:
try:
new_val[self.map_[idx]] = bool_arr[idx]
except KeyError:
print("Not able to get value for index {}".format(idx))
if self.last_send_time == 0:
send_needed = True
send_reason = "no send time"
elif self.value is None:
send_needed = True
send_reason = "no value"
elif self.compare_values(new_val):
send_needed = True
send_reason = "value change"
elif (time.time() - self.last_send_time) > self.guarantee_sec:
send_needed = True
send_reason = "guarantee sec"
elif force_send:
send_needed = True
send_reason = "forced"
if send_needed:
self.value = new_val
self.last_value = self.value
self.last_send_time = time.time()
print("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason))
return send_needed

Binary file not shown.

View File

@@ -0,0 +1,28 @@
id,name,deviceTypeId,fromMe,io,subTitle,helpExplanation,channelType,dataType,defaultValue,regex,regexErrMsg,units,min,max,change,guaranteedReportPeriod,minReportTime
13723,log,463,FALSE,readwrite,Log,Device Log,device,string,Created,,,,,,,,
,pond1height,463,FALSE,readonly,Pond 1 Height,in Ft,device,float,-99,,,,,,,,
,pond1volume,463,FALSE,readonly,Pond 1 Volume,in BBL,device,float,-99,,,,,,,,
,pond2height,463,FALSE,readonly,Pond 2 Height,in Ft,device,float,-99,,,,,,,,
,pond2volume,463,FALSE,readonly,Pond 2 Volume,in BBL,device,float,-99,,,,,,,,
,pond3height,463,FALSE,readonly,Pond 3 Height,in Ft,device,float,-99,,,,,,,,
,pond3volume,463,FALSE,readonly,Pond 3 Volume,in BBL,device,float,-99,,,,,,,,
,pond4height,463,FALSE,readonly,Pond 4 Height,in Ft,device,float,-99,,,,,,,,
,pond4volume,463,FALSE,readonly,Pond 4 Volume,in BBL,device,float,-99,,,,,,,,
,cfgnumberofponds,463,FALSE,readwrite,Number of Ponds Configured,4-Jan,device,integer,0,,,,,,,,
,public_ip_address,463,FALSE,readonly,Public IP Address,network device address,device,string,,,,,,,,,
,pondvolumetotal,463,FALSE,readonly,Total Pond Volume,in BBL,device,float,-99,,,,,,,,
,pond1calibration,463,FALSE,readonly,Pond 1 Calibration Table,JSON list of objects with height and volume props,device,string,[],,,,,,,,
,pond2calibration,463,FALSE,readonly,Pond 2 Calibration Table,JSON list of objects with height and volume props,device,string,[],,,,,,,,
,pond3calibration,463,FALSE,readonly,Pond 3 Calibration Table,JSON list of objects with height and volume props,device,string,[],,,,,,,,
,pond4calibration,463,FALSE,readonly,Pond 4 Calibration Table,JSON list of objects with height and volume props,device,string,[],,,,,,,,
,addcalibrationpoint,463,FALSE,readwrite,Add Calibration Point Command,JSON Object with pond and height and volume props,device,string,,,,,,,,,
,deletecalibrationpoint,463,FALSE,readwrite,Delete Calibration Point Command,JSON Object with pond and point properties,device,string,,,,,,,,,
,sync,463,FALSE,readwrite,Sync data,Synchronize command,device,string,,,,,,,,,
,pond1highalarmsetpoint,463,FALSE,readwrite,Pond 1 High Alarm Setpoint,in Ft,user input,float,50,,,,,,,,
,pond1lowalarmsetpoint,463,FALSE,readwrite,Pond 1 Low Alarm Setpoint,in Ft,user input,float,0,,,,,,,,
,pond2highalarmsetpoint,463,FALSE,readwrite,Pond 2 High Alarm Setpoint,in Ft,user input,float,50,,,,,,,,
,pond2lowalarmsetpoint,463,FALSE,readwrite,Pond 2 Low Alarm Setpoint,in Ft,user input,float,0,,,,,,,,
,pond3highalarmsetpoint,463,FALSE,readwrite,Pond 3 High Alarm Setpoint,in Ft,user input,float,50,,,,,,,,
,pond3lowalarmsetpoint,463,FALSE,readwrite,Pond 3 Low Alarm Setpoint,in Ft,user input,float,0,,,,,,,,
,pond4highalarmsetpoint,463,FALSE,readwrite,Pond 4 High Alarm Setpoint,in Ft,user input,float,50,,,,,,,,
,pond4lowalarmsetpoint,463,FALSE,readwrite,Pond 4 Low Alarm Setpoint,in Ft,user input,float,0,,,,,,,,
1 id name deviceTypeId fromMe io subTitle helpExplanation channelType dataType defaultValue regex regexErrMsg units min max change guaranteedReportPeriod minReportTime
2 13723 log 463 FALSE readwrite Log Device Log device string Created
3 pond1height 463 FALSE readonly Pond 1 Height in Ft device float -99
4 pond1volume 463 FALSE readonly Pond 1 Volume in BBL device float -99
5 pond2height 463 FALSE readonly Pond 2 Height in Ft device float -99
6 pond2volume 463 FALSE readonly Pond 2 Volume in BBL device float -99
7 pond3height 463 FALSE readonly Pond 3 Height in Ft device float -99
8 pond3volume 463 FALSE readonly Pond 3 Volume in BBL device float -99
9 pond4height 463 FALSE readonly Pond 4 Height in Ft device float -99
10 pond4volume 463 FALSE readonly Pond 4 Volume in BBL device float -99
11 cfgnumberofponds 463 FALSE readwrite Number of Ponds Configured 4-Jan device integer 0
12 public_ip_address 463 FALSE readonly Public IP Address network device address device string
13 pondvolumetotal 463 FALSE readonly Total Pond Volume in BBL device float -99
14 pond1calibration 463 FALSE readonly Pond 1 Calibration Table JSON list of objects with height and volume props device string []
15 pond2calibration 463 FALSE readonly Pond 2 Calibration Table JSON list of objects with height and volume props device string []
16 pond3calibration 463 FALSE readonly Pond 3 Calibration Table JSON list of objects with height and volume props device string []
17 pond4calibration 463 FALSE readonly Pond 4 Calibration Table JSON list of objects with height and volume props device string []
18 addcalibrationpoint 463 FALSE readwrite Add Calibration Point Command JSON Object with pond and height and volume props device string
19 deletecalibrationpoint 463 FALSE readwrite Delete Calibration Point Command JSON Object with pond and point properties device string
20 sync 463 FALSE readwrite Sync data Synchronize command device string
21 pond1highalarmsetpoint 463 FALSE readwrite Pond 1 High Alarm Setpoint in Ft user input float 50
22 pond1lowalarmsetpoint 463 FALSE readwrite Pond 1 Low Alarm Setpoint in Ft user input float 0
23 pond2highalarmsetpoint 463 FALSE readwrite Pond 2 High Alarm Setpoint in Ft user input float 50
24 pond2lowalarmsetpoint 463 FALSE readwrite Pond 2 Low Alarm Setpoint in Ft user input float 0
25 pond3highalarmsetpoint 463 FALSE readwrite Pond 3 High Alarm Setpoint in Ft user input float 50
26 pond3lowalarmsetpoint 463 FALSE readwrite Pond 3 Low Alarm Setpoint in Ft user input float 0
27 pond4highalarmsetpoint 463 FALSE readwrite Pond 4 High Alarm Setpoint in Ft user input float 50
28 pond4lowalarmsetpoint 463 FALSE readwrite Pond 4 Low Alarm Setpoint in Ft user input float 0

View File

@@ -0,0 +1,11 @@
{
"driverFileName": "plcpond.py",
"deviceName": "plcpond",
"driverId": "0220",
"releaseVersion": "1",
"files": {
"file1": "plcpond.py",
"file2": "utilities.py",
"file3": "Channel.py"
}
}

View File

@@ -0,0 +1,2 @@
class deviceBase(object):
pass

View File

@@ -0,0 +1,11 @@
{
"name": "plcpond",
"driverFilename": "plcpond.py",
"driverId": "0000",
"additionalDriverFiles": [
"utilities.py",
"Channel.py"
],
"version": 1,
"s3BucketName": "plcpond"
}

View File

@@ -0,0 +1,247 @@
"""Driver for plcpond"""
import threading
import sys
import json
import time
import logging
from random import randint
from device_base import deviceBase
from Channel import PLCChannel, read_tag, write_tag, TAG_DATAERROR_SLEEPTIME
from utilities import get_public_ip_address
_ = None
# LOGGING SETUP
from logging.handlers import RotatingFileHandler
log_formatter = logging.Formatter('%(asctime)s %(levelname)s %(funcName)s(%(lineno)d) %(message)s')
logFile = './plcpond.log'
my_handler = RotatingFileHandler(logFile, mode='a', maxBytes=500*1024, backupCount=2, encoding=None, delay=0)
my_handler.setFormatter(log_formatter)
my_handler.setLevel(logging.INFO)
logger = logging.getLogger('plcpond')
logger.setLevel(logging.INFO)
logger.addHandler(my_handler)
console_out = logging.StreamHandler(sys.stdout)
console_out.setFormatter(log_formatter)
logger.addHandler(console_out)
logger.info("plcpond startup")
# GLOBAL VARIABLES
WATCHDOG_SEND_PERIOD = 3600 # Seconds, the longest amount of time before sending the watchdog status
PLC_IP_ADDRESS = "192.168.1.12"
CHANNELS = [
PLCChannel(PLC_IP_ADDRESS, "cfgnumberofponds", "cfgNumberOfPonds", "REAL", 0.5, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond1height", "pond1Height", "REAL", 0.5, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond2height", "pond2Height", "REAL", 0.5, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond3height", "pond3Height", "REAL", 0.5, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond4height", "pond4Height", "REAL", 0.5, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond1volume", "pond1Volume", "REAL", 500.0, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond2volume", "pond2Volume", "REAL", 500.0, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond3volume", "pond3Volume", "REAL", 500.0, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond4volume", "pond4Volume", "REAL", 500.0, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pondvolumetotal", "pondVolumeTotal", "REAL", 500.0, 600, map_=False, write_enabled=False, plc_type='Micro800')
]
CALIBRATION_TABLES = [[],[], [], [], []] # position 0 is a dummy table
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
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."""
wait_sec = 15
for i in range(0, wait_sec):
print("plcpond driver will start in {} seconds".format(wait_sec - i))
time.sleep(1)
logger.info("BOOM! Starting plcpond driver...")
public_ip_address = get_public_ip_address()
self.sendtodbDev(1, 'public_ip_address', public_ip_address, 0, 'plcpond')
watchdog = self.plcpond_watchdog()
self.sendtodbDev(1, 'watchdog', watchdog, 0, 'plcpond')
watchdog_send_timestamp = time.time()
self.nodes["plcpond_0199"] = self
send_loops = 0
watchdog_loops = 0
watchdog_check_after = 5000
while True:
if self.force_send:
logger.warning("FORCE SEND: TRUE")
for c in CHANNELS:
v = c.read()
if v is not None: # read returns None if it fails
if c.check(v, self.force_send):
self.sendtodbDev(1, c.mesh_name, c.value, 0, 'plcpond')
time.sleep(TAG_DATAERROR_SLEEPTIME) # sleep to allow Micro800 to handle ENET requests
for pond_index in range(1, 5):
self.read_pond_calibration(pond_index)
# print("plcpond driver still alive...")
if self.force_send:
if send_loops > 2:
logger.warning("Turning off force_send")
self.force_send = False
send_loops = 0
else:
send_loops += 1
watchdog_loops += 1
if watchdog_loops >= watchdog_check_after:
test_watchdog = self.plcpond_watchdog()
if not test_watchdog == watchdog or (time.time() - watchdog_send_timestamp) > WATCHDOG_SEND_PERIOD:
self.sendtodbDev(1, 'watchdog', test_watchdog, 0, 'plcpond')
watchdog = test_watchdog
test_public_ip = get_public_ip_address()
if not test_public_ip == public_ip_address:
self.sendtodbDev(1, 'public_ip_address', test_public_ip, 0, 'plcpond')
public_ip_address = test_public_ip
watchdog_loops = 0
def read_pond_calibration(self, pond_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 = "pond{}CalibrationHeight[{}]".format(pond_number, cal_index)
cal_tag_volume = "pond{}CalibrationVolume[{}]".format(pond_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[pond_number] or self.force_send:
calibration_channel = "pond{}calibration".format(pond_number)
calibration_string = json.dumps(cal_values).replace('"', "'")
self.sendtodbDev(1, calibration_channel, calibration_string, 0, 'plcpond')
CALIBRATION_TABLES[pond_number] = cal_values
def plcpond_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 plcpond_sync(self, name, value):
"""Sync all data from the driver."""
self.force_send = True
# self.sendtodb("log", "synced", 0)
return True
def plcpond_deletecalibrationpoint(self, name, value):
"""Delete a calibration point from a calibration table"""
# {"pond": int, "point": int}
value = value.replace("'", '"')
parsed = json.loads(value)
try:
pond_number = int(parsed['pond'])
point_number = int(parsed['point'])
write_pond = write_tag(PLC_IP_ADDRESS, "inpPondNumber", pond_number, plc_type="Micro800")
time.sleep(2)
write_point = write_tag(PLC_IP_ADDRESS, "inpDeletePointIndex", point_number, plc_type="Micro800")
time.sleep(2)
if write_pond and write_point:
write_cmd = write_tag(PLC_IP_ADDRESS, "cmdDeleteCalibrationPoint", 1, plc_type="Micro800")
time.sleep(2)
if write_cmd:
read_val = read_tag(PLC_IP_ADDRESS, "deleteSuccess", plc_type="Micro800")
if read_val[0] == 1:
self.read_pond_calibration(pond_number)
return True
return "Wrote everything successfully, but delete didn't succeed (Check pond and point values)."
return "Didn't write delete command correctly."
return "Didn't write pond or point correctly."
except KeyError as e:
return "Couldn't parse input value: {} -- {}".format(value, e)
def plcpond_cfgnumberofponds(self, name, value):
"""Write the number of ponds to the plc."""
value = int(value)
return write_tag(PLC_IP_ADDRESS, "cfgNumberOfPonds", value, plc_type="Micro800")
def plcpond_addcalibrationpoint(self, name, value):
"""Add a calibration point to the calibration table"""
# value = {"pond": int, "height": float, "volume": float}
value = value.replace("'", '"')
parsed = json.loads(value)
try:
# parse json values, throw an error if one is missing
pond_number = int(parsed['pond'])
height = float(parsed['height'])
volume = float(parsed['volume'])
# write values to the tags
write_pond = write_tag(PLC_IP_ADDRESS, "inpPondNumber", pond_number, plc_type="Micro800")
time.sleep(2)
write_height = write_tag(PLC_IP_ADDRESS, "inpPondHeight", height, plc_type="Micro800")
time.sleep(2)
write_volume= write_tag(PLC_IP_ADDRESS, "inpPondVolume", volume, plc_type="Micro800")
time.sleep(2)
if write_pond and write_height and write_volume:
write_cmd = write_tag(PLC_IP_ADDRESS, "cmdAddCalibrationPoint", 1, plc_type="Micro800")
time.sleep(2)
if write_cmd:
read_val = read_tag(PLC_IP_ADDRESS, "addSuccess", plc_type="Micro800")
if read_val[0] == 1:
self.read_pond_calibration(pond_number)
return True
return "Wrote everything successfully, but delete didn't succeed (Check pond and point values)."
return "Didn't write delete command correctly."
return "Didn't write pond or point correctly."
except KeyError as e:
return "Couldn't parse input value: {} -- {}".format(value, e)
def plcpond_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']
w = write_tag(str(PLC_IP_ADDRESS), tag_n, val_n)
print("Result of plcpond_writeplctag(self, {}, {}) = {}".format(name, value, w))
if w is None:
w = "Error writing to PLC..."
return w

View File

@@ -0,0 +1,26 @@
import json
import time
from Channel import read_tag, write_tag
PLC_IP_ADDRESS = "192.168.1.12"
def read_pond_calibration(pond_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 = "pond{}CalibrationHeight[{}]".format(pond_number, cal_index)
cal_tag_volume = "pond{}CalibrationVolume[{}]".format(pond_number, cal_index)
print(cal_tag_height, cal_tag_volume)
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)
print(read_height, read_volume)
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
return cal_values

View File

@@ -0,0 +1,51 @@
"""Utility functions for the driver."""
import socket
import struct
def get_public_ip_address():
"""Find the public IP Address of the host device."""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()
return ip
def int_to_float16(int_to_convert):
"""Convert integer into float16 representation."""
bin_rep = ('0' * 16 + '{0:b}'.format(int_to_convert))[-16:]
sign = 1.0
if int(bin_rep[0]) == 1:
sign = -1.0
exponent = float(int(bin_rep[1:6], 2))
fraction = float(int(bin_rep[6:17], 2))
if exponent == float(0b00000):
return sign * 2 ** -14 * fraction / (2.0 ** 10.0)
elif exponent == float(0b11111):
if fraction == 0:
return sign * float("inf")
else:
return float("NaN")
else:
frac_part = 1.0 + fraction / (2.0 ** 10.0)
return sign * (2 ** (exponent - 15)) * frac_part
def ints_to_float(int1, int2):
"""Convert 2 registers into a floating point number."""
mypack = struct.pack('>HH', int1, int2)
f = struct.unpack('>f', mypack)
print("[{}, {}] >> {}".format(int1, int2, f[0]))
return f[0]
def degf_to_degc(temp_f):
"""Convert deg F to deg C."""
return (temp_f - 32.0) * (5.0/9.0)
def degc_to_degf(temp_c):
"""Convert deg C to deg F."""
return temp_c * 1.8 + 32.0