Updated totalizers

This commit is contained in:
2020-09-11 14:08:38 -05:00
parent f4fd410c7e
commit 1bddf77591
21 changed files with 2270 additions and 179 deletions

View File

@@ -10,40 +10,15 @@ tags = [
PLCChannel(PLC_IP_ADDRESS, "fm6_flowrate", "Val_FM6_FR", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_out_flowrate", "Val_FRTotalOut", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_in_flowrate", "Val_FRTotalIn", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm1_todays", "Flowmeter_Totals.Todays_Totalflow", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm2_todays", "Val_FM2_Todays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm3_todays", "Val_FM3_Todays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm4_todays", "Val_FM4_Todays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm5_todays", "Val_FM5_Todays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm6_todays", "Val_FM6_Todays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_out_todays", "Val_TodaysTotalOut", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_in_todays", "Val_TodaysTotalIn", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm1_yesterdays", "Flowmeter_Totals.Yesterdays_Totalflow", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm2_yesterdays", "Val_FM2_Yesterdays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm3_yesterdays", "Val_FM3_Yesterdays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm4_yesterdays", "Val_FM4_Yesterdays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm5_yesterdays", "Val_FM5_Yesterdays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm6_yesterdays", "Val_FM6_Yesterdays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_out_yesterdays", "Val_YesterdaysTotalOut", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_in_yesterdays", "Val_YesterdaysTotalIn", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm1_month", "Flowmeter_Totals.CurrentMonth_Totalflow", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm2_month", "Val_FM2_Months", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm3_month", "Val_FM3_Months", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm4_month", "Val_FM4_Months", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm5_month", "Val_FM5_Months", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm6_month", "Val_FM6_Months", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_out_months", "Val_MonthsTotalOut", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_in_months", "Val_MonthsTotalIn", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm1_lastmonths", "Flowmeter_Totals.LastMonth_Totalflow", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm2_lastmonths", "Val_FM2_LastMonths", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm3_lastmonths", "Val_FM3_LastMonths", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm4_lastmonths", "Val_FM4_LastMonths", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm5_lastmonths", "Val_FM5_LastMonths", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm6_lastmonths", "Val_FM6_LastMonths", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_out_lastmonths", "Val_LastMonthsTotalOut", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_in_lastmonths", "Val_LastMonthsTotalIn", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "pond_level","val_FluidLevel","REAL", 200, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "pond_volume","val_PondVolume","REAL", 1000000, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "fm1_lifetime", "Flowmeter_Totals.Flow_Totalizer", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm2_lifetime", "Val_FM2_T1", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm3_lifetime", "Val_FM3_T1", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm4_lifetime", "Val_FM4_T1", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm5_lifetime", "Val_FM5_T1", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm6_lifetime", "Val_FM6_T1", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_in_lifetime", "Total_In.Flow_Totalizer", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "pond_level","val_FluidLevel","REAL", 200, 3600, plc_type="CLX"),
#PLCChannel(PLC_IP_ADDRESS, "pond_volume","val_PondVolume","REAL", 1000000, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "intake_pressure","val_IntakePressure","REAL", 100000, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "intake_temperature","val_IntakeTemperature","REAL", 10000, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "discharge_pressure","val_TubingPressure","REAL", 10000, 3600, plc_type="CLX"),
@@ -86,4 +61,38 @@ tags = [
PLCChannel(PLC_IP_ADDRESS, "total_out_net","in_HART_Flowmeter_Net","REAL", 1000000, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "total_out_forward","in_HART_Flowmeter_Fwd","REAL", 1000000, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "total_out_reverse","in_HART_Flowmeter_Rev","REAL", 1000000, 3600, plc_type="CLX")
]
]
""" PLCChannel(PLC_IP_ADDRESS, "fm1_todays", "Flowmeter_Totals.Today_Totalflow", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm2_todays", "Val_FM2_Todays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm3_todays", "Val_FM3_Todays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm4_todays", "Val_FM4_Todays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm5_todays", "Val_FM5_Todays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm6_todays", "Val_FM6_Todays", "REAL", 1000000, 3600, plc_type='CLX'),
#PLCChannel(PLC_IP_ADDRESS, "total_out_todays", "Val_TodaysTotalOut", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_in_todays", "Val_TodaysTotalIn", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm1_yesterdays", "Flowmeter_Totals.Yesterdays_Totalflow", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm2_yesterdays", "Val_FM2_Yesterdays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm3_yesterdays", "Val_FM3_Yesterdays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm4_yesterdays", "Val_FM4_Yesterdays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm5_yesterdays", "Val_FM5_Yesterdays", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm6_yesterdays", "Val_FM6_Yesterdays", "REAL", 1000000, 3600, plc_type='CLX'),
#PLCChannel(PLC_IP_ADDRESS, "total_out_yesterdays", "Val_YesterdaysTotalOut", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_in_yesterdays", "Val_YesterdaysTotalIn", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm1_month", "Flowmeter_Totals.Monthlys_Totalflow", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm2_month", "Val_FM2_Months", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm3_month", "Val_FM3_Months", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm4_month", "Val_FM4_Months", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm5_month", "Val_FM5_Months", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm6_month", "Val_FM6_Months", "REAL", 1000000, 3600, plc_type='CLX'),
#PLCChannel(PLC_IP_ADDRESS, "total_out_months", "Val_MonthsTotalOut", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_in_months", "Val_MonthsTotalIn", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm1_lastmonths", "Flowmeter_Totals.PrevMonthlys_Totalflow", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm2_lastmonths", "Val_FM2_LastMonths", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm3_lastmonths", "Val_FM3_LastMonths", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm4_lastmonths", "Val_FM4_LastMonths", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm5_lastmonths", "Val_FM5_LastMonths", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "fm6_lastmonths", "Val_FM6_LastMonths", "REAL", 1000000, 3600, plc_type='CLX'),
#PLCChannel(PLC_IP_ADDRESS, "total_out_lastmonths", "Val_LastMonthsTotalOut", "REAL", 1000000, 3600, plc_type='CLX'),
PLCChannel(PLC_IP_ADDRESS, "total_in_lastmonths", "Val_LastMonthsTotalIn", "REAL", 1000000, 3600, plc_type='CLX'), """

View File

@@ -28,6 +28,7 @@ from Channel import PLCChannel, ModbusChannel,read_tag, write_tag, TAG_DATAERROR
from utilities import get_public_ip_address, get_private_ip_address, get_additional_tags, convert_int
from file_logger import filelogger as log
from Tags import tags
from datetime import datetime as dt
path = "/root/python_firmware/drivers/additional_tags.py"
@@ -53,6 +54,23 @@ IP_CHECK_PERIOD = 60
CHANNELS = tags + additional_tags
TOTALIZERS = persistence.load("totalizers.json")
if not TOTALIZERS:
TOTALIZERS = {}
for x in ['total_in','fm1', 'fm2', 'fm3', 'fm4', 'fm5', 'fm6']:
TOTALIZERS[x] = {
'Todays': 0,
'Yesterdays': 0,
'Current Months': 0,
'Previous Months': 0,
'Monthly Holding': 0,
'Daily Holding': 0,
'Lifetime': 0,
'Day': 0,
'Month': 0,
'Last Report': 0
}
persistence.store(TOTALIZERS, "totalizers.json")
class start(threading.Thread, deviceBase):
"""Start class required by Meshify."""
@@ -66,7 +84,7 @@ class start(threading.Thread, deviceBase):
mqtt=mqtt, Nodes=Nodes)
self.daemon = True
self.version = "1"
self.version = "2"
self.finished = threading.Event()
self.force_send = False
self.public_ip_address = ""
@@ -123,6 +141,8 @@ class start(threading.Thread, deviceBase):
if chan.plc_tag in convert_list:
converted_value = convert_int(chan.plc_tag, val)
self.sendtodbDev(1, chan.mesh_name, converted_value, 0, 'advvfdipppond')
elif "lifetime" in chan.mesh_name:
self.totalize(val,chan.mesh_name[:-9])
else:
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, 'advvfdipppond')
#time.sleep(TAG_DATAERROR_SLEEPTIME) # sleep to allow Micro800 to handle ENET requests
@@ -211,4 +231,87 @@ class start(threading.Thread, deviceBase):
new_val = json.loads(str(value).replace("'", '"'))
PERSIST['flowmeter_units'] = new_val
persistence.store(PERSIST, "extra_data.json")
self.sendtodbDev(1, 'flowunits', PERSIST['flowmeter_units'], 0, 'advvfdipppond')
self.sendtodbDev(1, 'flowunits', PERSIST['flowmeter_units'], 0, 'advvfdipppond')
def totalize(self, val, totalizer):
right_now = dt.today()
month = right_now.month
day = right_now.day
#Totalize Today, Yesterday, Month, Last Month
#if the stored day is 0 then it's a fresh run of this should initalize values now
if TOTALIZERS[totalizer]['Day'] == 0:
TOTALIZERS[totalizer]['Day'] = day
TOTALIZERS[totalizer]['Month'] = month
TOTALIZERS[totalizer]['Daily Holding'] = val
TOTALIZERS[totalizer]['Monthly Holding'] = val
persistence.store(TOTALIZERS, 'totalizers.json')
#Communication error during initialization check if lifetime has reported properly and update holdings
if TOTALIZERS[totalizer]['Daily Holding'] == None and not(val == None):
TOTALIZERS[totalizer]['Daily Holding'] = val
TOTALIZERS[totalizer]['Monthly Holding'] = val
try:
if val - TOTALIZERS[totalizer]['Daily Holding'] - TOTALIZERS[totalizer]['Todays'] > 500 or time.time() - TOTALIZERS[totalizer]['Last Report'] > 3600 or self.force_send:
TOTALIZERS[totalizer]['Todays'] = val - TOTALIZERS[totalizer]['Daily Holding']
TOTALIZERS[totalizer]['Current Months'] = val - TOTALIZERS[totalizer]['Monthly Holding']
TOTALIZERS[totalizer]['Lifetime'] = val
self.sendtodbDev(1, '{}_todays'.format(totalizer), TOTALIZERS[totalizer]['Todays'], 0, 'advvfdipppond')
if totalizer == "total_in":
self.sendtodbDev(1, '{}_months'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'advvfdipppond')
else:
self.sendtodbDev(1, '{}_month'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'advvfdipppond')
self.sendtodbDev(1, '{}_yesterdays'.format(totalizer), TOTALIZERS[totalizer]['Yesterdays'], 0, 'advvfdipppond')
self.sendtodbDev(1, '{}_lifetime'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'advvfdipppond')
if self.force_send:
self.sendtodbDev(1, '{}_lastmonths'.format(totalizer), TOTALIZERS[totalizer]['Previous Months'], 0, 'advvfdipppond')
TOTALIZERS[totalizer]['Last Report'] = time.time()
except:
if time.time() - TOTALIZERS[totalizer]['Last Report'] > 3600 or self.force_send:
self.sendtodbDev(1, '{}_todays'.format(totalizer), TOTALIZERS[totalizer]['Todays'], 0, 'advvfdipppond')
if totalizer == "total_in":
self.sendtodbDev(1, '{}_months'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'advvfdipppond')
else:
self.sendtodbDev(1, '{}_month'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'advvfdipppond')
self.sendtodbDev(1, '{}_yesterdays'.format(totalizer), TOTALIZERS[totalizer]['Yesterdays'], 0, 'advvfdipppond')
self.sendtodbDev(1, '{}_lifetime'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'advvfdipppond')
if self.force_send:
self.sendtodbDev(1, '{}_lastmonths'.format(totalizer), TOTALIZERS[totalizer]['Previous Months'], 0, 'advvfdipppond')
TOTALIZERS[totalizer]['Last Report'] = time.time()
#If the current day doesn't equal the stored day roll the dailies over
if not(day == TOTALIZERS[totalizer]['Day']):
#if a comms error use the stored values else use the latested values
if val == None:
TOTALIZERS[totalizer]['Yesterdays'] = TOTALIZERS[totalizer]['Todays']
TOTALIZERS[totalizer]['Todays'] = 0
TOTALIZERS[totalizer]['Daily Holding'] = TOTALIZERS[totalizer]['Lifetime']
else:
TOTALIZERS[totalizer]['Yesterdays'] = val - TOTALIZERS[totalizer]['Daily Holding']
TOTALIZERS[totalizer]['Todays'] = 0
TOTALIZERS[totalizer]['Daily Holding'] = val
TOTALIZERS[totalizer]['Lifetime'] = val
TOTALIZERS[totalizer]['Day'] = day
self.sendtodbDev(1, '{}_todays'.format(totalizer), TOTALIZERS[totalizer]['Todays'], 0, 'advvfdipppond')
self.sendtodbDev(1, '{}_yesterdays'.format(totalizer), TOTALIZERS[totalizer]['Yesterdays'], 0, 'advvfdipppond')
self.sendtodbDev(1, '{}_lifetime'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'advvfdipppond')
TOTALIZERS[totalizer]['Last Report'] = time.time()
#the day has rolled over if the month also rolls over
if not(month == TOTALIZERS[totalizer]['Month']):
#if a comms error use the stored values else use the latested values
if val == None:
TOTALIZERS[totalizer]['Previous Months'] = TOTALIZERS[totalizer]['Current Months']
TOTALIZERS[totalizer]['Current Months'] = 0
TOTALIZERS[totalizer]['Monthly Holding'] = TOTALIZERS[totalizer]['Lifetime']
else:
TOTALIZERS[totalizer]['Previous Months'] = val - TOTALIZERS[totalizer]['Monthly Holding']
TOTALIZERS[totalizer]['Current Months'] = 0
TOTALIZERS[totalizer]['Monthly Holding'] = val
TOTALIZERS[totalizer]['Month'] = month
if totalizer == "total_in":
self.sendtodbDev(1, '{}_months'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'advvfdipppond')
else:
self.sendtodbDev(1, '{}_month'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'advvfdipppond')
self.sendtodbDev(1, '{}_lastmonths'.format(totalizer), TOTALIZERS[totalizer]['Previous Months'], 0, 'advvfdipppond')
self.sendtodbDev(1, '{}_lifetime'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'advvfdipppond')
TOTALIZERS[totalizer]['Last Report'] = time.time()
persistence.store(TOTALIZERS, 'totalizers.json')

View File

@@ -8,7 +8,7 @@
"file4": "Tags.py"
},
"deviceName": "advvfdipppond",
"releaseVersion": "1",
"releaseVersion": "2",
"driverFileName": "advvfdipppond.py",
"driverId": "0100"
}

View File

@@ -0,0 +1,349 @@
"""Define Meshify channel class."""
import time
from pycomm.ab_comm.clx import Driver as ClxDriver
from pycomm.cip.cip_base import CommError, DataError
import struct
#from file_logger import filelogger as log
from flowmonitor import INSTRUMENT, log, lock
instrument = INSTRUMENT
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"
clx = ClxDriver()
try:
if clx.open(addr, direct_connection=direct):
try:
val = clx.read_tag(tag)
clx.close()
return val
except DataError as err:
clx.close()
time.sleep(TAG_DATAERROR_SLEEPTIME)
log.error("Data Error during readTag({}, {}): {}".format(addr, tag, err))
except CommError:
# err = c.get_status()
log.error("Could not connect during readTag({}, {})".format(addr, tag))
except AttributeError as err:
clx.close()
log.error("AttributeError during readTag({}, {}): \n{}".format(addr, tag, err))
clx.close()
return False
def read_array(addr, tag, start, end, plc_type="CLX"):
"""Read an array from the PLC."""
direct = plc_type == "Micro800"
clx = ClxDriver()
if clx.open(addr, direct_connection=direct):
arr_vals = []
try:
for i in range(start, end):
tag_w_index = tag + "[{}]".format(i)
val = clx.read_tag(tag_w_index)
arr_vals.append(round(val[0], 4))
if arr_vals:
clx.close()
return arr_vals
else:
log.error("No length for {}".format(addr))
clx.close()
return False
except Exception:
log.error("Error during readArray({}, {}, {}, {})".format(addr, tag, start, end))
err = clx.get_status()
clx.close()
log.error(err)
clx.close()
def write_tag(addr, tag, val, plc_type="CLX"):
"""Write a tag value to the PLC."""
direct = plc_type == "Micro800"
clx = ClxDriver()
try:
if clx.open(addr, direct_connection=direct):
try:
initial_val = clx.read_tag(tag)
write_status = clx.write_tag(tag, val, initial_val[1])
clx.close()
return write_status
except DataError as err:
clx_err = clx.get_status()
clx.close()
log.error("--\nDataError during writeTag({}, {}, {}, plc_type={}) -- {}\n{}\n".format(addr, tag, val, plc_type, err, clx_err))
except CommError as err:
clx_err = clx.get_status()
log.error("--\nCommError during write_tag({}, {}, {}, plc_type={})\n{}\n--".format(addr, tag, val, plc_type, err))
clx.close()
return False
def byteSwap32(array):
#array is a list of 2 dec numbers
newVal = ""
for i in array:
i = hex(i).replace('0x', '')
while len(i) < 4:
i = "0" + i
print i
newVal = i + newVal
print newVal
return struct.unpack('!f', newVal.decode('hex'))[0]
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 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:
log.error("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()
log.info("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, transform_fn=identity, unit_number=1, scaling=0):
"""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.transform_fn = transform_fn
self.unit_number = unit_number
self.scaling= scaling
def read(self):
"""Return the transformed read value."""
with lock:
print("ATTEMPTING TO READ ON {}".format(self.mesh_name))
if self.data_type == "FLOAT":
try:
read_value = instrument.read_float(self.register_number,4,self.channel_size)
except Exception as e:
log.info(e)
return None
elif self.data_type == "FLOATBS":
try:
read_value = byteSwap32(instrument.read_registers(self.register_number,2, 4))
except Exception as e:
log.info(e)
return None
elif self.data_type == "INTEGER" or self.data_type == "STRING":
try:
read_value = instrument.read_register(self.register_number, self.scaling, 4)
except Exception as e:
log.info(e)
return None
read_value = self.transform_fn(read_value)
return read_value
def write(self, value):
"""Write a value to a register"""
if self.data_type == "FLOAT":
value = float(value)
elif self.data_type == "INTEGER":
value = int(value)
else:
value = str(value)
try:
instrument.write_register(self.register_number,value, self.scaling, 16 if self.channel_size > 1 else 6 )
return True
except Exception as e:
log.info("Failed to write value: {}".format(e))
return False
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."""
super(BoolArrayChannels, 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
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:
log.error("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:
val = read_tag(self.plc_ip, self.plc_tag)
if val:
bool_arr = binarray(val[0])
new_val = {}
for idx in self.map_:
try:
new_val[self.map_[idx]] = bool_arr[idx]
except KeyError:
log.error("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()
log.info("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason))
return send_needed

View File

@@ -0,0 +1,12 @@
from Channel import PLCChannel, ModbusChannel
from flowmonitor import PLC_IP_ADDRESS
tags = [
ModbusChannel('gpm_flow', 3873, 'FLOATBS', 10, 3600,channel_size=2, unit_number=2),
ModbusChannel('psi_pressure', 0, 'FLOAT', 100, 3600,channel_size=2, unit_number=2),
ModbusChannel('run_status', 0, 'STRING', 1, 3600,channel_size=2, unit_number=2),
ModbusChannel('gal_total', 2609, 'FLOATBS', 100, 3600,channel_size=2, unit_number=2),
#ModbusChannel('gal_total_thismonth', 2609, 'FLOAT', 100, 3600,channel_size=2, unit_number=flowmeter_unit_number),
#ModbusChannel('gal_total_yesterday', 2609, 'FLOAT', 100, 3600,channel_size=2, unit_number=flowmeter_unit_number),
#ModbusChannel('gal_total_lastmonth', 2609, 'FLOAT', 100, 3600,channel_size=2, unit_number=flowmeter_unit_number)
]

View File

@@ -0,0 +1,14 @@
{
"files": {
"file3": "file_logger.py",
"file2": "Channel.py",
"file1": "flowmonitor.py",
"file6": "persistence.py",
"file5": "utilities.py",
"file4": "Tags.py"
},
"deviceName": "flowmonitor",
"releaseVersion": "21",
"driverFileName": "flowmonitor.py",
"driverId": "0140"
}

View File

@@ -0,0 +1,18 @@
"""Logging setup for flow-monitor"""
import logging
from logging.handlers import RotatingFileHandler
import sys
log_formatter = logging.Formatter('%(asctime)s %(levelname)s %(funcName)s(%(lineno)d) %(message)s')
log_file = './flowmonitor.log'
my_handler = RotatingFileHandler(log_file, mode='a', maxBytes=500*1024,
backupCount=2, encoding=None, delay=0)
my_handler.setFormatter(log_formatter)
my_handler.setLevel(logging.INFO)
filelogger = logging.getLogger('flowmonitor')
filelogger.setLevel(logging.INFO)
filelogger.addHandler(my_handler)
console_out = logging.StreamHandler(sys.stdout)
console_out.setFormatter(log_formatter)
filelogger.addHandler(console_out)

View File

@@ -0,0 +1,522 @@
"""Driver for flow-monitor"""
import threading
import json
import time
import os
from device_base import deviceBase
import persistence
from utilities import get_public_ip_address, get_private_ip_address
from file_logger import filelogger as log
from datetime import datetime as dt
import minimalmodbusM1
try:
os.system("/usr/sbin/ntpdate pool.ntp.org")
except:
dtz = dt.fromtimestamp(last_measured_timestamp)
os.system('date -s "{}-{}-{} {}:{}:{}"'.format(dtz.year,dtz.month,dtz.day,dtz.hour,dtz.minute,dtz.second))
PLC_IP_ADDRESS = "192.168.1.12"
#from Tags import tags
_ = None
log.info("flow-monitor startup")
# GLOBAL VARIABLES
WAIT_FOR_CONNECTION_SECONDS = 20
IP_CHECK_PERIOD = 60
#CHANNELS = tags
# PERSISTENCE FILE
PERSIST = persistence.load()
if not PERSIST:
PERSIST = {
"pressure_raw_min": 0.0,
"pressure_raw_max": 10.0,
"flow_raw_min": 3.89,
"pressure_psi_min": 0.0,
"gpm_or_bpd": "gpm",
"gpm_ignore_limit": 1.0,
"flow_gpm_max": 100.0,
"pressure_psi_max": 600.0,
"flow_raw_max": 19.54,
"flow_gpm_min": 0.0,
"lowflow": 10.0,
"modbus": False
}
persistence.store(PERSIST, 'persist.json')
try:
PERSIST['modbus']
except:
PERSIST['modbus'] = False
persistence.store(PERSIST, 'persist.json')
TOTALIZER = persistence.load('totalizers.json')
if not TOTALIZER:
TOTALIZER = {
'Todays': 0,
'Yesterdays': 0,
'Current Months': 0,
'Previous Months': 0,
'Monthly Holding': 0,
'Daily Holding': 0,
'Lifetime': 0,
'Day': 0,
'Month': 0,
'Last Report': 0
}
persistence.store(TOTALIZER, 'totalizers.json')
def scale(raw_val, raw_min, raw_max, eu_min, eu_max):
"""Scale a raw value."""
if raw_val < raw_min:
raw_val = raw_min
if raw_val > raw_max:
raw_val = raw_max
slope = (eu_max - eu_min) / (raw_max - raw_min)
intercept = eu_max - (slope * raw_max)
return slope * raw_val + intercept
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 = "21"
self.lock = threading.Lock()
self.force_send = False
self.public_ip_address = ""
self.private_ip_address = ""
self.public_ip_address_last_checked = 0
self.ping_counter = 0
self.finished = threading.Event()
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."""
self.instrument = self.startRS485()
global CHANNELS, INSTRUMENT, lock
INSTRUMENT = self.instrument
lock = self.lock
#from Channel import PLCChannel, ModbusChannel,read_tag, write_tag, TAG_DATAERROR_SLEEPTIME
from Tags import tags
CHANNELS = tags
for i in range(0, WAIT_FOR_CONNECTION_SECONDS):
print("flow-monitor driver will start in {} seconds".format(WAIT_FOR_CONNECTION_SECONDS - i))
time.sleep(1)
log.info("BOOM! Starting flow-monitor driver...")
self._check_ip_address()
self.nodes["flowmonitor_0140"] = self
send_loops = 0
self.sendtodb("setrawmin", PERSIST["flow_raw_min"], 0)
self.sendtodb("setrawmax", PERSIST["flow_raw_max"], 0)
self.sendtodb("setgpmmin", PERSIST["flow_gpm_min"], 0)
self.sendtodb("setgpmmax", PERSIST["flow_gpm_max"], 0)
self.sendtodb("setpressurerawmin", PERSIST["pressure_raw_min"], 0)
self.sendtodb("setpressurerawmax", PERSIST["pressure_raw_max"], 0)
self.sendtodb("setpressurepsimin", PERSIST["pressure_psi_min"], 0)
self.sendtodb("setpressurepsimax", PERSIST["pressure_psi_max"], 0)
gal_totalizer_value = TOTALIZER['Lifetime']
last_measured_timestamp = time.time()
while True:
try:
# Gets a dictionary of the IO states
# {
# 'bat': u'23.10',
# 'ver': u'Mar 16 2016 21:29:31',
# 'dout3': 'Off',
# 'temp': u'40.37',
# 'vin': u'24.6',
# 'pulse': u'0',
# 'dout4': 'Off',
# 'dout1': 'Off',
# 'din2': 'Off',
# 'din1': 'Off',
# 'dout2': 'On',
# 'cloop': u'0.0',
# 'analog4': u'0.0',
# 'analog3': u'0.0',
# 'analog2': u'0.0',
# 'analog1': u'0.0',
# 'relay1': 'Off'
# }
mcu_status = self.mcu.getDict()
except Exception as e:
log.error("Error getting MCU State: {}".format(e))
cloop_val = float(mcu_status['cloop'])
analog1_val = float(mcu_status['analog1'])
din1_val = 1 if mcu_status['din1'] == 'On' else 0 # Check DIGITAL INPUT 1 for run status
scaled_cloop = scale(cloop_val, PERSIST["flow_raw_min"], PERSIST["flow_raw_max"], PERSIST["flow_gpm_min"], PERSIST["flow_gpm_max"])
psi_val = scale(analog1_val, PERSIST["pressure_raw_min"], PERSIST["pressure_raw_max"], PERSIST["pressure_psi_min"], PERSIST["pressure_psi_max"])
if PERSIST["gpm_or_bpd"] == "gpm":
gpm_val = scaled_cloop
bpd_val = (gpm_val / 42.0) * 60.0 * 24.0 # Computes BPD from GPM
else:
bpd_val = scaled_cloop
gpm_val = (((bpd_val * 42.0) / 24.0) / 60.0) # Computes GPM from BPD
if gpm_val < PERSIST["gpm_ignore_limit"]:
gpm_val = 0
bpd_val = 0
#Determine run status
runstatus = "undefined"
if din1_val == 0 and gpm_val == 0:
runstatus = "Stopped" #Stopped
elif din1_val == 0 and gpm_val > 10:
runstatus = "Running" #Assumed running might not have run indication
elif din1_val == 0 and gpm_val > 0:
runstatus = "Running: Low Flow"
elif din1_val == 1 and gpm_val == 0:
runstatus = "Running: No Flow" #no flow warning
elif din1_val == 1 and gpm_val < PERSIST["lowflow"]:
runstatus = "Running: Low Flow" #low flow warning
elif din1_val == 1 and gpm_val >= PERSIST["lowflow"]:
runstatus = "Running" #running normally
now = time.time()
time_diff = now - last_measured_timestamp
if 0 < time_diff < 180:
# Volume flowed since last measuring
gal_flow_delta = (time_diff / 60.0) * gpm_val
# Increment totalizers
gal_totalizer_value += gal_flow_delta
last_measured_timestamp = now
elif time_diff < 0:
#negative time difference means clock got reset or somehow went the wrong way
try:
os.system("/usr/sbin/ntpdate pool.ntp.org")
except:
dtz = dt.fromtimestamp(last_measured_timestamp)
os.system('date -s "{}-{}-{} {}:{}:{}"'.format(dtz.year,dtz.month,dtz.day,dtz.hour,dtz.minute,dtz.second))
now = time.time()
elif time_diff > 180:
last_measured_timestamp = now
if self.force_send:
log.warning("FORCE SEND: TRUE")
for chan in CHANNELS:
if chan.mesh_name == "psi_pressure":
val = psi_val
elif chan.mesh_name == "run_status":
val = runstatus
elif PERSIST["modbus"]:
val = chan.read()
if chan.mesh_name == "gpm_flow":
if val == 0:
runstatus = "Stopped"
elif val > PERSIST["lowflow"]:
runstatus = "Running"
else:
runstatus = "Running: Low Flow"
elif chan.mesh_name == "gpm_flow":
val = gpm_val
else:
val = gal_totalizer_value
if chan.mesh_name == 'gal_total':
self.totalize(val)
elif chan.check(val, self.force_send):
self.sendtodb(chan.mesh_name, chan.value, 0)
if chan.mesh_name == 'gpm_flow':
if val == None:
self.sendtodb('bpd_flow', chan.value,0)
else:
self.sendtodb('bpd_flow', (chan.value / 42.0) * 60 * 24, 0)
#time.sleep(TAG_DATAERROR_SLEEPTIME) # sleep to allow Micro800 to handle ENET requests
if PERSIST["modbus"]:
time.sleep(5)
# print("flow-monitor 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 (now - self.public_ip_address_last_checked) > IP_CHECK_PERIOD:
self._check_ip_address()
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()
test_public_ip = test_public_ip
test_private_ip = get_private_ip_address()
if not test_public_ip == self.public_ip_address and not test_public_ip == "0.0.0.0":
self.sendtodb('public_ip_address', test_public_ip, 0)
self.public_ip_address = test_public_ip
if not test_private_ip == self.private_ip_address:
self.sendtodb('private_ip_address', test_private_ip, 0)
self.private_ip_address = test_private_ip
def flowmonitor_sync(self, name, value):
"""Sync all data from the driver."""
self.force_send = True
# self.sendtodb("log", "synced", 0)
return True
def flowmonitor_startcmd(self, name, value):
"""Start the well."""
self.mcu.relay1(str(1))
return True
def flowmonitor_stopcmd(self, name, value):
"""Stop the well."""
self.mcu.relay1(str(0))
return True
def flowmonitor_modbus(self, name, value):
if PERSIST["modbus"] == False:
PERSIST["modbus"] = True
else:
PERSIST["modbus"] = False
persistence.store(PERSIST, "persist.json")
return True
def flowmonitor_setrawmin(self, name, value):
"""Set the raw min scaling value."""
try:
PERSIST['flow_raw_min'] = float(value)
self.sendtodb("setrawmin", PERSIST['flow_raw_min'], 0)
persistence.store(PERSIST)
except Exception as e:
log.error("Could not set self.flow_raw_min: {}".format(e))
return(True)
def flowmonitor_setrawmax(self, name, value):
"""Set the raw max scaling value."""
try:
PERSIST['flow_raw_max'] = float(value)
self.sendtodb("setrawmax", PERSIST['flow_raw_max'], 0)
persistence.store(PERSIST)
except Exception as e:
log.error("Could not set self.flow_raw_max: {}".format(e))
return(True)
def flowmonitor_setgpmmin(self, name, value):
"""Set the gpm min scaling value."""
try:
PERSIST['flow_gpm_min'] = float(value)
self.sendtodb("setgpmmin", PERSIST['flow_gpm_min'], 0)
persistence.store(PERSIST)
except Exception as e:
log.error("Could not set self.flow_gpm_min: {}".format(e))
return(True)
def flowmonitor_setgpmmax(self, name, value):
"""Set the gpm max scaling value."""
try:
PERSIST['flow_gpm_max'] = float(value)
self.sendtodb("setgpmmax", PERSIST['flow_gpm_max'], 0)
persistence.store(PERSIST)
except Exception as e:
log.error("Could not set self.flow_gpm_max: {}".format(e))
return(True)
def flowmonitor_setpressurerawmin(self, name, value):
"""Set the pressure raw min scaling value."""
try:
PERSIST['pressure_raw_min'] = float(value)
self.sendtodb("setpressurerawmin", PERSIST['pressure_raw_min'], 0)
persistence.store(PERSIST)
except Exception as e:
log.error("Could not set self.pressure_raw_min: {}".format(e))
return(True)
def flowmonitor_setpressurerawmax(self, name, value):
"""Set the pressure raw max scaling value."""
try:
PERSIST['pressure_raw_max'] = float(value)
self.sendtodb("setpressurerawmax", PERSIST['pressure_raw_max'], 0)
persistence.store(PERSIST)
except Exception as e:
log.error("Could not set self.pressure_raw_max: {}".format(e))
return(True)
def flowmonitor_setpressurepsimin(self, name, value):
"""Set the pressure psi min scaling value."""
try:
PERSIST['pressure_psi_min'] = float(value)
self.sendtodb("setpressurepsimin", PERSIST['pressure_psi_min'], 0)
persistence.store(PERSIST)
except Exception as e:
log.error("Could not set self.pressure_psi_min: {}".format(e))
return(True)
def flowmonitor_setpressurepsimax(self, name, value):
"""Set the pressure psi max scaling value."""
try:
PERSIST['pressure_psi_max'] = float(value)
self.sendtodb("setpressurepsimax", PERSIST['pressure_psi_max'], 0)
persistence.store(PERSIST)
except Exception as e:
log.error("Could not set self.pressure_psi_max: {}".format(e))
return(True)
def flowmonitor_setgpmignorelimit(self, name, value):
"""Set the GPM Ignore Limit."""
try:
PERSIST['gpm_ignore_limit'] = float(value)
self.sendtodb("setgpmignorelimit", PERSIST['gpm_ignore_limit'], 0)
persistence.store(PERSIST)
return True
except Exception as e:
log.error("Error during flowmonitor_setgpmignorelimit: {}".format(e))
return False
def flowmonitor_gpmorbpd(self, name, value):
"""Set the read in value to GPM or BPD"""
try:
PERSIST["gpm_or_bpd"] = str(value)
self.sendtodb("gpmorbpd", PERSIST["gpm_or_bpd"], 0)
return True
except Exception as e:
log.error("Error during flowmonitor_setgpmorbpd: {}".format(e))
return False
def flowmonitor_setlowflow(self, name, value):
"""Set the low flow limit"""
try:
PERSIST["lowflow"] = float(value)
self.sendtodb("setlowflow", PERSIST["lowflow"],0)
persistence.store(PERSIST)
except Exception as e:
log.error("Error during flomonitor_setlowflow: {}".format(e))
return False
return True
def totalize(self, val):
right_now = dt.today()
month = right_now.month
day = right_now.day
#Totalize Today, Yesterday, Month, Last Month
#if the stored day is 0 then it's a fresh run of this should initalize values now
if TOTALIZER['Day'] == 0:
TOTALIZER['Day'] = day
TOTALIZER['Month'] = month
TOTALIZER['Daily Holding'] = val
TOTALIZER['Monthly Holding'] = val
persistence.store(TOTALIZER, 'totalizers.json')
#Communication error during initialization check if lifetime has reported properly and update holdings
if TOTALIZER['Daily Holding'] == None and not(val == None):
TOTALIZER['Daily Holding'] = val
TOTALIZER['Monthly Holding'] = val
try:
if val - TOTALIZER['Daily Holding'] - TOTALIZER['Todays'] > 500 or time.time() - TOTALIZER['Last Report'] > 3600 or self.force_send:
TOTALIZER['Todays'] = val - TOTALIZER['Daily Holding']
TOTALIZER['Current Months'] = val - TOTALIZER['Monthly Holding']
TOTALIZER['Lifetime'] = val
self.sendtodb('gal_total', TOTALIZER['Todays'], 0)
self.sendtodb('bbl_total', TOTALIZER['Todays']/42, 0)
self.sendtodb('gal_total_thismonth', TOTALIZER['Current Months'], 0)
self.sendtodb('bbl_total_thismonth', TOTALIZER['Current Months']/42, 0)
self.sendtodb('gal_total_yesterday', TOTALIZER['Yesterdays'], 0)
self.sendtodb('bbl_total_yesterday', TOTALIZER['Yesterdays']/42, 0)
if self.force_send:
self.sendtodb('gal_total_lastmonth', TOTALIZER['Previous Months'], 0)
self.sendtodb('bbl_total_lastmonth', TOTALIZER['Previous Months']/42, 0)
TOTALIZER['Last Report'] = time.time()
except:
if time.time() - TOTALIZER['Last Report'] > 3600 or self.force_send:
self.sendtodb('gal_total', TOTALIZER['Todays'], 0)
self.sendtodb('bbl_total', TOTALIZER['Todays']/42, 0)
self.sendtodb('gal_total_thismonth', TOTALIZER['Current Months'], 0)
self.sendtodb('bbl_total_thismonth', TOTALIZER['Current Months']/42, 0)
self.sendtodb('gal_total_yesterday', TOTALIZER['Yesterdays'], 0)
self.sendtodb('bbl_total_yesterday', TOTALIZER['Yesterdays']/42, 0)
if self.force_send:
self.sendtodb('gal_total_lastmonth', TOTALIZER['Previous Months'], 0)
self.sendtodb('bbl_total_lastmonth', TOTALIZER['Previous Months']/42, 0)
TOTALIZER['Last Report'] = time.time()
#If the current day doesn't equal the stored day roll the dailies over
if not(day == TOTALIZER['Day']):
#if a comms error use the stored values else use the latested values
if val == None:
TOTALIZER['Yesterdays'] = TOTALIZER['Todays']
TOTALIZER['Todays'] = 0
TOTALIZER['Daily Holding'] = TOTALIZER['Lifetime']
else:
TOTALIZER['Yesterdays'] = val - TOTALIZER['Daily Holding']
TOTALIZER['Todays'] = 0
TOTALIZER['Daily Holding'] = val
TOTALIZER['Lifetime'] = val
TOTALIZER['Day'] = day
self.sendtodb('gal_total', TOTALIZER['Todays'], 0)
self.sendtodb('bbl_total', TOTALIZER['Todays']/42, 0)
self.sendtodb('total_fm_yesterday_gal', TOTALIZER['Yesterdays'], 0)
self.sendtodb('total_fm_yesterday_bbls', TOTALIZER['Yesterdays']/42, 0)
self.sendtodb('lifetime_flow_meter_gal', TOTALIZER['Lifetime'], 0)
self.sendtodb('lifetime_flow_meter_bbls', TOTALIZER['Lifetime']/42, 0)
TOTALIZER['Last Report'] = time.time()
#the day has rolled over if the month also rolls over
if not(month == TOTALIZER['Month']):
#if a comms error use the stored values else use the latested values
if val == None:
TOTALIZER['Previous Months'] = TOTALIZER['Current Months']
TOTALIZER['Current Months'] = 0
TOTALIZER['Monthly Holding'] = TOTALIZER['Lifetime']
else:
TOTALIZER['Previous Months'] = val - TOTALIZER['Monthly Holding']
TOTALIZER['Current Months'] = 0
TOTALIZER['Monthly Holding'] = val
TOTALIZER['Month'] = month
self.sendtodb('gal_total_thismonth', TOTALIZER['Current Months'], 0)
self.sendtodb('bbl_total_thismonth', TOTALIZER['Current Months']/42, 0)
self.sendtodb('gal_total_lastmonth', TOTALIZER['Previous Months'], 0)
self.sendtodb('bbl_total_lastmonth', TOTALIZER['Previous Months']/42, 0)
TOTALIZER['Last Report'] = time.time()
persistence.store(TOTALIZER, 'totalizers.json')
def startRS485(self):
instrument = ""
with self.lock:
#minimalmodbus.BAUDRATE = 9600
#minimalmodbus.STOPBITS = 1
connected = False
while connected == False:
log.info("Attempting to setup RS485")
connected = self.mcu.set485Baud(9600)#switch to configurable
time.sleep(1)
log.info("RS485 SETUP SUCCESSFUL!!!!!")
serial = self.mcu.rs485
instrument = minimalmodbusM1.Instrument(1,serial)
instrument.address = 2 #switch to configurable
return instrument

View File

@@ -0,0 +1,21 @@
"""Data persistance functions."""
# if more advanced persistence is needed, use a sqlite database
import json
def load(filename="persist.json"):
"""Load persisted settings from the specified file."""
try:
with open(filename, 'r') as persist_file:
return json.load(persist_file)
except Exception:
return False
def store(persist_obj, filename="persist.json"):
"""Store the persisting settings into the specified file."""
try:
with open(filename, 'w') as persist_file:
return json.dump(persist_obj, persist_file, indent=4)
except Exception:
return False

View File

@@ -0,0 +1,64 @@
"""Utility functions for the driver."""
import socket
import struct
import urllib
import contextlib
def get_private_ip_address():
"""Find the private IP Address of the host device."""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
sock.connect(("8.8.8.8", 80))
except Exception as e:
return e
ip_address = sock.getsockname()[0]
sock.close()
return ip_address
def get_public_ip_address():
ip_address = "0.0.0.0"
try:
with contextlib.closing(urllib.urlopen("http://checkip.amazonaws.com")) as url:
ip_address = url.read()
except Exception as e:
print("could not resolve check IP: {}".format(e))
return ip_address
return ip_address[:-1]
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")
return float("NaN")
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_unpacked = struct.unpack('>f', mypack)
print("[{}, {}] >> {}".format(int1, int2, f_unpacked[0]))
return f_unpacked[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

View File

@@ -8,16 +8,16 @@ tags = [
PLCChannel(PLC_IP_ADDRESS, "val_fm1_t1","val_fm1_t1","REAL", 50, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm1_t2","val_fm1_t2","REAL", 50, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm1_t3","val_fm1_t3","REAL", 50, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm1_yesterday","val_fm1_yesterday","REAL", 100, 86400, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm1_today","val_fm1_today","REAL", 25, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm1_lastmonth","val_fm1_lastmonth","REAL", 100, 86400, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm1_month","val_fm1_month","REAL", 100, 3600, plc_type="Micro800"),
#PLCChannel(PLC_IP_ADDRESS, "val_fm1_yesterday","val_fm1_yesterday","REAL", 100, 86400, plc_type="Micro800"),
#PLCChannel(PLC_IP_ADDRESS, "val_fm1_today","val_fm1_today","REAL", 25, 3600, plc_type="Micro800"),
#PLCChannel(PLC_IP_ADDRESS, "val_fm1_lastmonth","val_fm1_lastmonth","REAL", 100, 86400, plc_type="Micro800"),
#PLCChannel(PLC_IP_ADDRESS, "val_fm1_month","val_fm1_month","REAL", 100, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm2_fr","val_fm2_fr","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm2_t1","val_fm2_t1","REAL", 50, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm2_t2","val_fm2_t2","REAL", 50, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm2_t3","val_fm3_t3","REAL", 50, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm2_yesterday","val_fm2_yesterday","REAL", 100, 86400, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm2_today","val_fm2_today","REAL", 25, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm2_lastmonth","val_fm2_lastmonth","REAL", 100, 86400, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "val_fm2_month","val_fm2_month","REAL", 100, 3600, plc_type="Micro800")
#PLCChannel(PLC_IP_ADDRESS, "val_fm2_yesterday","val_fm2_yesterday","REAL", 100, 86400, plc_type="Micro800"),
#PLCChannel(PLC_IP_ADDRESS, "val_fm2_today","val_fm2_today","REAL", 25, 3600, plc_type="Micro800"),
#PLCChannel(PLC_IP_ADDRESS, "val_fm2_lastmonth","val_fm2_lastmonth","REAL", 100, 86400, plc_type="Micro800"),
#PLCChannel(PLC_IP_ADDRESS, "val_fm2_month","val_fm2_month","REAL", 100, 3600, plc_type="Micro800")
]

View File

@@ -8,7 +8,7 @@
"file4": "Tags.py"
},
"deviceName": "flowmeterskid",
"releaseVersion": "2",
"releaseVersion": "3",
"driverFileName": "flowmeterskid.py",
"driverId": "0100"
}

View File

@@ -12,13 +12,13 @@ from utilities import get_public_ip_address
from file_logger import filelogger as log
PLC_IP_ADDRESS = "192.168.1.12"
from Tags import tags
from datetime import datetime as dt
_ = None
log.info("flowmeterskid startup")
# GLOBAL VARIABLES
WAIT_FOR_CONNECTION_SECONDS = 60
WAIT_FOR_CONNECTION_SECONDS = 20
IP_CHECK_PERIOD = 60
@@ -32,6 +32,24 @@ if not PERSIST:
}
persistence.store(PERSIST)
TOTALIZERS = persistence.load("totalizers.json")
if not TOTALIZERS:
TOTALIZERS = {}
for x in ['fm1', 'fm2']:
TOTALIZERS[x] = {
'Todays': 0,
'Yesterdays': 0,
'Current Months': 0,
'Previous Months': 0,
'Monthly Holding': 0,
'Daily Holding': 0,
'Lifetime': 0,
'Day': 0,
'Month': 0,
'Last Report': 0
}
persistence.store(TOTALIZERS, "totalizers.json")
class start(threading.Thread, deviceBase):
"""Start class required by Meshify."""
@@ -44,7 +62,7 @@ class start(threading.Thread, deviceBase):
mqtt=mqtt, Nodes=Nodes)
self.daemon = True
self.version = "2"
self.version = "3"
self.finished = threading.Event()
self.force_send = False
self.public_ip_address = ""
@@ -80,7 +98,12 @@ class start(threading.Thread, deviceBase):
val = chan.read()
if chan.check(val, self.force_send):
if chan.mesh_name in PERSIST["ignore_list"]:
self.sendtodbDev(1, chan.mesh_name, None, 0, 'flowmeterskid')
if "_t1" in chan.mesh_name:
self.totalizer_null(chan.mesh_name.split("_")[1])
else:
self.sendtodbDev(1, chan.mesh_name, None, 0, 'flowmeterskid')
elif "_t1" in chan.mesh_name:
self.totalize(val,chan.mesh_name.split("_")[1])
else:
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, 'flowmeterskid')
#time.sleep(TAG_DATAERROR_SLEEPTIME) # sleep to allow Micro800 to handle ENET requests
@@ -150,3 +173,87 @@ class start(threading.Thread, deviceBase):
PERSIST["ignore_list"].remove(value)
persistence.store(PERSIST)
return True
def totalize(self, val, totalizer):
right_now = dt.today()
month = right_now.month
day = right_now.day
#Totalize Today, Yesterday, Month, Last Month
#if the stored day is 0 then it's a fresh run of this should initalize values now
if TOTALIZERS[totalizer]['Day'] == 0:
TOTALIZERS[totalizer]['Day'] = day
TOTALIZERS[totalizer]['Month'] = month
TOTALIZERS[totalizer]['Daily Holding'] = val
TOTALIZERS[totalizer]['Monthly Holding'] = val
persistence.store(TOTALIZERS, 'totalizers.json')
#Communication error during initialization check if lifetime has reported properly and update holdings
if TOTALIZERS[totalizer]['Daily Holding'] == None and not(val == None):
TOTALIZERS[totalizer]['Daily Holding'] = val
TOTALIZERS[totalizer]['Monthly Holding'] = val
try:
if val - TOTALIZERS[totalizer]['Daily Holding'] - TOTALIZERS[totalizer]['Todays'] > 500 or time.time() - TOTALIZERS[totalizer]['Last Report'] > 3600 or self.force_send:
TOTALIZERS[totalizer]['Todays'] = val - TOTALIZERS[totalizer]['Daily Holding']
TOTALIZERS[totalizer]['Current Months'] = val - TOTALIZERS[totalizer]['Monthly Holding']
TOTALIZERS[totalizer]['Lifetime'] = val
self.sendtodbDev(1, 'val_{}_today'.format(totalizer), TOTALIZERS[totalizer]['Todays'], 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_month'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_yesterday'.format(totalizer), TOTALIZERS[totalizer]['Yesterdays'], 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_t1'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'flowmeterskid')
if self.force_send:
self.sendtodbDev(1, 'val_{}_lastmonth'.format(totalizer), TOTALIZERS[totalizer]['Previous Months'], 0, 'flowmeterskid')
TOTALIZERS[totalizer]['Last Report'] = time.time()
except:
if time.time() - TOTALIZERS[totalizer]['Last Report'] > 3600 or self.force_send:
self.sendtodbDev(1, 'val_{}_today'.format(totalizer), TOTALIZERS[totalizer]['Todays'], 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_month'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_yesterday'.format(totalizer), TOTALIZERS[totalizer]['Yesterdays'], 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_t1'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'flowmeterskid')
if self.force_send:
self.sendtodbDev(1, 'val_{}_lastmonth'.format(totalizer), TOTALIZERS[totalizer]['Previous Months'], 0, 'flowmeterskid')
TOTALIZERS[totalizer]['Last Report'] = time.time()
#If the current day doesn't equal the stored day roll the dailies over
if not(day == TOTALIZERS[totalizer]['Day']):
#if a comms error use the stored values else use the latested values
if val == None:
TOTALIZERS[totalizer]['Yesterdays'] = TOTALIZERS[totalizer]['Todays']
TOTALIZERS[totalizer]['Todays'] = 0
TOTALIZERS[totalizer]['Daily Holding'] = TOTALIZERS[totalizer]['Lifetime']
else:
TOTALIZERS[totalizer]['Yesterdays'] = val - TOTALIZERS[totalizer]['Daily Holding']
TOTALIZERS[totalizer]['Todays'] = 0
TOTALIZERS[totalizer]['Daily Holding'] = val
TOTALIZERS[totalizer]['Lifetime'] = val
TOTALIZERS[totalizer]['Day'] = day
self.sendtodbDev(1, 'val_{}_today'.format(totalizer), TOTALIZERS[totalizer]['Todays'], 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_yesterday'.format(totalizer), TOTALIZERS[totalizer]['Yesterdays'], 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_t1'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'flowmeterskid')
TOTALIZERS[totalizer]['Last Report'] = time.time()
#the day has rolled over if the month also rolls over
if not(month == TOTALIZERS[totalizer]['Month']):
#if a comms error use the stored values else use the latested values
if val == None:
TOTALIZERS[totalizer]['Previous Months'] = TOTALIZERS[totalizer]['Current Months']
TOTALIZERS[totalizer]['Current Months'] = 0
TOTALIZERS[totalizer]['Monthly Holding'] = TOTALIZERS[totalizer]['Lifetime']
else:
TOTALIZERS[totalizer]['Previous Months'] = val - TOTALIZERS[totalizer]['Monthly Holding']
TOTALIZERS[totalizer]['Current Months'] = 0
TOTALIZERS[totalizer]['Monthly Holding'] = val
TOTALIZERS[totalizer]['Month'] = month
self.sendtodbDev(1, 'val_{}_month'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_lastmonth'.format(totalizer), TOTALIZERS[totalizer]['Previous Months'], 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_t1'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'flowmeterskid')
TOTALIZERS[totalizer]['Last Report'] = time.time()
persistence.store(TOTALIZERS, 'totalizers.json')
def totalizer_null(self, totalizer):
if time.time() - TOTALIZERS[totalizer]['Last Report'] > 3600 or self.force_send:
self.sendtodbDev(1, 'val_{}_today'.format(totalizer), None, 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_month'.format(totalizer), None, 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_yesterday'.format(totalizer), None, 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_lastmonth'.format(totalizer), None, 0, 'flowmeterskid')
self.sendtodbDev(1, 'val_{}_t1'.format(totalizer), None, 0, 'flowmeterskid')
TOTALIZERS[totalizer]['Last Report'] = time.time()
persistence.store(TOTALIZERS, 'totalizers.json')

View File

@@ -8,7 +8,7 @@
"file4": "Tags.py"
},
"deviceName": "plcfreshwater",
"releaseVersion": "12",
"releaseVersion": "13",
"driverFileName": "plcfreshwater.py",
"driverId": "0100"
}

View File

@@ -53,7 +53,7 @@ class start(threading.Thread, deviceBase):
mqtt=mqtt, Nodes=Nodes)
self.daemon = True
self.version = "12"
self.version = "13"
self.finished = threading.Event()
self.force_send = False
self.public_ip_address = ""
@@ -238,6 +238,9 @@ class start(threading.Thread, deviceBase):
self.sendtodbDev(1, 'total_fm_yesterday_bbls', PERSIST['Yesterdays']/42, 0, 'plcfreshwater')
self.sendtodbDev(1, 'lifetime_flow_meter_gal', PERSIST['Lifetime'], 0, 'plcfreshwater')
self.sendtodbDev(1, 'lifetime_flow_meter_bbls', PERSIST['Lifetime']/42, 0, 'plcfreshwater')
if self.force_send:
self.sendtodbDev(1, 'total_fm_last_month_gal', PERSIST['Previous Months'], 0, 'plcfreshwater')
self.sendtodbDev(1, 'total_fm_last_month_bbls', PERSIST['Previous Months']/42, 0, 'plcfreshwater')
PERSIST['Last Report'] = time.time()
except:
if time.time() - PERSIST['Last Report'] > 3600 or self.force_send:
@@ -249,6 +252,9 @@ class start(threading.Thread, deviceBase):
self.sendtodbDev(1, 'total_fm_yesterday_bbls', PERSIST['Yesterdays']/42, 0, 'plcfreshwater')
self.sendtodbDev(1, 'lifetime_flow_meter_gal', PERSIST['Lifetime'], 0, 'plcfreshwater')
self.sendtodbDev(1, 'lifetime_flow_meter_bbls', PERSIST['Lifetime']/42, 0, 'plcfreshwater')
if self.force_send:
self.sendtodbDev(1, 'total_fm_last_month_gal', PERSIST['Previous Months'], 0, 'plcfreshwater')
self.sendtodbDev(1, 'total_fm_last_month_bbls', PERSIST['Previous Months']/42, 0, 'plcfreshwater')
PERSIST['Last Report'] = time.time()
#If the current day doesn't equal the stored day roll the dailies over

View File

@@ -1,10 +1,20 @@
"""Define Meshify channel class."""
import time
from pycomm.ab_comm.clx import Driver as ClxDriver
from pycomm.cip.cip_base import CommError, DataError
import time
import minimalmodbusM1
from file_logger import filelogger as log
import struct
import minimalmodbusM1
from PiFlow import mcug
#minimalmodbus.BAUDRATE = 9600
#minimalmodbus.STOPBITS = 1
connected = False
while connected == False:
connected = mcug.set485Baud(9600)
time.sleep(1)
serial = mcug.rs485
instrument = minimalmodbusM1.Instrument(1,serial)
instrument.address = 2
TAG_DATAERROR_SLEEPTIME = 5
def binarray(intval):
"""Split an integer into its bits."""
@@ -14,69 +24,78 @@ def binarray(intval):
return bin_arr
def read_tag(addr, tag):
def read_tag(addr, tag, plc_type="CLX"):
"""Read a tag from the PLC."""
c = ClxDriver()
direct = plc_type == "Micro800"
clx = ClxDriver()
try:
if c.open(addr):
if clx.open(addr, direct_connection=direct):
try:
v = c.read_tag(tag)
return v
except DataError:
c.close()
print("Data Error during readTag({}, {})".format(addr, tag))
val = clx.read_tag(tag)
clx.close()
return val
except DataError as err:
clx.close()
time.sleep(TAG_DATAERROR_SLEEPTIME)
log.error("Data Error during readTag({}, {}): {}".format(addr, tag, err))
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()
clx.close()
log.error("Could not connect during readTag({}, {})".format(addr, tag))
except AttributeError as err:
clx.close()
log.error("AttributeError during readTag({}, {}): \n{}".format(addr, tag, err))
clx.close()
return False
def read_array(addr, tag, start, end):
def read_array(addr, tag, start, end, plc_type="CLX"):
"""Read an array from the PLC."""
c = ClxDriver()
if c.open(addr):
direct = plc_type == "Micro800"
clx = ClxDriver()
if clx.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 len(arr_vals) > 0:
val = clx.read_tag(tag_w_index)
arr_vals.append(round(val[0], 4))
if arr_vals:
clx.close()
return arr_vals
else:
print("No length for {}".format(addr))
log.error("No length for {}".format(addr))
clx.close()
return False
except Exception:
print("Error during readArray({}, {}, {}, {})".format(addr, tag, start, end))
err = c.get_status()
c.close()
print err
pass
c.close()
log.error("Error during readArray({}, {}, {}, {})".format(addr, tag, start, end))
err = clx.get_status()
clx.close()
log.error(err)
clx.close()
def write_tag(addr, tag, val):
def write_tag(addr, tag, val, plc_type="CLX"):
"""Write a tag value to the PLC."""
c = ClxDriver()
if c.open(addr):
try:
cv = c.read_tag(tag)
wt = c.write_tag(tag, val, cv[1])
return wt
except Exception:
print("Error during writeTag({}, {}, {})".format(addr, tag, val))
err = c.get_status()
c.close()
print err
c.close()
direct = plc_type == "Micro800"
clx = ClxDriver()
try:
if clx.open(addr, direct_connection=direct):
try:
initial_val = clx.read_tag(tag)
write_status = clx.write_tag(tag, val, initial_val[1])
clx.close()
return write_status
except DataError as err:
clx_err = clx.get_status()
clx.close()
log.error("--\nDataError during writeTag({}, {}, {}, plc_type={}) -- {}\n{}\n".format(addr, tag, val, plc_type, err, clx_err))
except CommError as err:
clx_err = clx.get_status()
log.error("--\nCommError during write_tag({}, {}, {}, plc_type={})\n{}\n--".format(addr, tag, val, plc_type, err))
clx.close()
return False
class Channel(object):
@@ -102,14 +121,14 @@ class Channel(object):
"""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.data_type == 'BOOL' or self.data_type == 'STRING' or type(new_value) == str:
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):
elif self.value != new_value:
if self.map_:
if not self.value == self.map_[new_value]:
send_needed = True
@@ -145,12 +164,12 @@ class Channel(object):
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))
log.error("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))
log.info("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason))
return send_needed
def read(self):
@@ -162,7 +181,6 @@ def identity(sent):
"""Return exactly what was sent to it."""
return sent
def volume_units(vunit):
units = {
0: "cm cubed/s",
@@ -302,12 +320,156 @@ def totalizer_units(tunit):
}
return units[tunit]
def int_to_bits(n,x):
return pad_to_x([int(digit) for digit in bin(n)[2:]],x) # [2:] to chop off the "0b" part
def pad_to_x(n,x):
while len(n) < x:
n = [0] + n
return n
def status_codes(n):
status_array = int_to_bits(n,16)
status_low = {
0: "Stopped;",
1: "Operating in Forward;",
2: "Operating in Reverse;",
3: "DC operating;"
}
status_mid = {
0: "",
1: "Speed searching;",
2: "Accelerating;",
3: "At constant speed;",
4: "Decelerating;",
5: "Decelerating to stop;",
6: "H/W OCS;",
7: "S/W OCS;",
8: "Dwell operating;"
}
status_high = {
0: "Normal state",
4: "Warning occurred",
8: "Fault occurred"
}
values = {
0: 8,
1: 4,
2: 2,
3: 1
}
stats_low = status_array[12:]
stats_mid = status_array[8:12]
stats_high = status_array[:4]
low = 0
mid = 0
high = 0
for x in range(4):
if stats_low[x] == 1:
low = low + values[x]
if stats_mid[x] == 1:
mid = mid + values[x]
if stats_high[x] == 1:
high = high + values[x]
return status_low[low] + " " + status_mid[mid] + ' ' + status_high[high]
def fault_code_a(n):
fault_code_array = int_to_bits(n,16)
""" fault = {
0: "OCT",
1: "OVT",
2: "EXT-A",
3: "EST",
4: "COL",
5: "GFT",
6: "OHT",
7: "ETH",
8: "OLT",
9: "Reserved",
10: "EXT-B",
11: "EEP",
12: "FAN",
13: "POT",
14: "IOLT",
15: "LVT"
} """
fault = {
0: "Overload Trip",
1: "Underload Trip",
2: "Inverter Overload Trip",
3: "E-Thermal Trip",
4: "Ground Fault Trip",
5: "Output Image Trip",
6: "Inmput Imaging Trip",
7: "Reserved",
8: "Reserved",
9: "NTC Trip",
10: "Overcurrent Trip",
11: "Overvoltage Trip",
12: "External Trip",
13: "Arm Short",
14: "Over Heat Trip",
15: "Fuse Open Trip"
}
faults = []
counter = 15
for x in range(16):
if fault_code_array[x] == 1:
faults = [fault[counter]] + faults
counter = counter - 1
return ' '.join(faults)
def fault_code_b(n):
fault_code_array = int_to_bits(n,8)
""" fault = {
0: "COM",
1: "Reserved",
2: "NTC",
3: "REEP",
4: "OC2",
5: "NBR",
6: "SAFA",
7: "SAFB"
} """
fault = {
0: "Reserved",
1: "Reserved",
2: "Reserved",
3: "FAN Trip",
4: "Reserved",
5: "Reserved",
6: "Pre PID Fail",
7: "Bad contact at basic I/O board",
8: "External Brake Trip",
9: "No Motor Trip",
10: "Bad Option Card",
11: "Reserved",
12: "Reserved",
13: "Reserved",
14: "Pre Over Heat Trip",
15: "Reserved"
}
faults = []
counter = 7
for x in range(8):
if fault_code_array[x] == 1:
faults = [fault[counter]] + faults
counter = counter - 1
return ' '.join(faults)
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, transform_fn=identity, unit_number=1, scaling=0, mcu=None):
def __init__(self, mesh_name, register_number, data_type, chg_threshold, guarantee_sec, channel_size=1, map_=False, write_enabled=False, transform_fn=identity, unit_number=1, scaling=0):
"""Initialize the channel."""
super(ModbusChannel, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled)
self.mesh_name = mesh_name
@@ -324,63 +486,45 @@ class ModbusChannel(Channel):
self.transform_fn = transform_fn
self.unit_number = unit_number
self.scaling= scaling
self.mcu = mcu
baud = 9600
connected = False
while connected == False:
try:
#print("CONNECTING!!!!!!!!!!!!!!!!!!!!!!!!!")
connected = self.mcu.set485Baud(baud)
time.sleep(1)
except Exception as e:
print("Error on connect: ", e)
serial4 = self.mcu.rs485
self.instrument = minimalmodbusM1.Instrument(1, serial4)
self.instrument.address = 1
def read(self):
"""Return the transformed read value."""
print("ATTEMPTING TO READ ON {}".format(self.mesh_name))
if self.data_type == "FLOAT":
try:
read_value = self.instrument.read_float(self.register_number,4,self.channel_size)
read_value = instrument.read_float(self.register_number,4,self.channel_size)
except IOError as e:
log.error(e)
log.info(e)
return None
elif self.data_type == "INTEGER" or self.data_type == "STRING":
try:
read_value = self.instrument.read_register(self.register_number, self.scaling, 4)
read_value = instrument.read_register(self.register_number, self.scaling, 4)
except IOError as e:
log.error(e)
return None
elif self.data_type == "FLOATBS":
try:
t = self.instrument.read_registers(self.register_number, 2, 3)
read_value = round(self.byteSwap32(t), 2)
except IOError as e:
log.error(e)
log.info(e)
return None
read_value = self.transform_fn(read_value)
return read_value
def byteSwap32(self, array):
newVal = ""
for i in array:
i = hex(i).replace('0x', '')
while len(i) < 4:
i = "0" + i
print i
newVal = i + newVal
print newVal
return struct.unpack('!f', newVal.decode('hex'))[0]
def write(self, value):
"""Write a value to a register"""
if self.data_type == "FLOAT":
value = float(value)
elif self.data_type == "INTEGER":
value = int(value)
else:
value = str(value)
try:
instrument.write_register(self.register_number,value, self.scaling, 16 if self.channel_size > 1 else 6 )
return True
except Exception as e:
log.info("Failed to write value: {}".format(e))
return False
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):
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
@@ -394,12 +538,13 @@ class PLCChannel(Channel):
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)
read_value = read_tag(self.plc_ip, self.plc_tag, plc_type=self.plc_type)
if read_value:
plc_value = read_value[0]
@@ -411,6 +556,7 @@ class BoolArrayChannels(Channel):
def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False):
"""Initialize the channel."""
super(BoolArrayChannels, 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
@@ -431,7 +577,7 @@ class BoolArrayChannels(Channel):
if new_val_dict[idx] != self.last_value[idx]:
send = True
except KeyError:
print("Key Error in self.compare_values for index {}".format(idx))
log.error("Key Error in self.compare_values for index {}".format(idx))
send = True
return send
@@ -440,15 +586,15 @@ class BoolArrayChannels(Channel):
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])
val = read_tag(self.plc_ip, self.plc_tag)
if val:
bool_arr = binarray(val[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))
log.error("Not able to get value for index {}".format(idx))
if self.last_send_time == 0:
send_needed = True
@@ -470,5 +616,5 @@ class BoolArrayChannels(Channel):
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
log.info("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason))
return send_needed

519
promagmbs/rework/PiFlow.py Normal file
View File

@@ -0,0 +1,519 @@
"""Driver for PiFlow"""
import os
import threading
import json
import time
from random import randint
from datetime import datetime as dt
from device_base import deviceBase
import persistence
from utilities import get_public_ip_address, get_private_ip_address
from file_logger import filelogger as log
"""import RPi.GPIO as GPIO
Relay_Ch1 = 26
Relay_Ch2 = 20
Relay_Ch3 = 21
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(Relay_Ch1,GPIO.OUT)
GPIO.output(Relay_Ch1, GPIO.HIGH)
GPIO.setup(Relay_Ch2,GPIO.OUT)
GPIO.output(Relay_Ch2, GPIO.HIGH)
GPIO.setup(Relay_Ch3,GPIO.OUT)
GPIO.output(Relay_Ch3, GPIO.HIGH)
"""
_ = None
os.system('sudo timedatectl set-timezone America/Chicago')
log.info("PiFlow startup")
# GLOBAL VARIABLES
WAIT_FOR_CONNECTION_SECONDS = 5
IP_CHECK_PERIOD = 60
# PERSISTENCE FILE
PERSIST = persistence.load('persist.json')
if not PERSIST:
PERSIST = {
'flowmeter': 247,
'drive': 1,
'isVFD': False,
'drive_enabled': True,
'state': False,
'state_timer': 0,
'plc_ip': '192.168.1.12',
'yesterday_totalizer_1': dt.today().day,
'yesterday_totalizer_2': dt.today().day,
'yesterday_totalizer_3': dt.today().day,
'yesterday_total_totalizer_1': 0,
'yesterday_total_midnight_totalizer_1': 0,
'yesterday_total_totalizer_2': 0,
'yesterday_total_midnight_totalizer_2': 0,
'yesterday_total_totalizer_3': 0,
'yesterday_total_midnight_totalizer_3': 0
}
persistence.store(PERSIST, 'persist.json')
try:
if time.time() - PERSIST['state_timer'] >= 60:
GPIO.output(Relay_Ch1, GPIO.HIGH)
PERSIST['state'] = False
persistence.store(PERSIST, "persist.json")
elif PERSIST['state']:
GPIO.output(Relay_Ch1, GPIO.LOW)
else:
GPIO.output(Relay_Ch1, GPIO.HIGH)
except:
PERSIST['state'] = False
PERSIST['state_time'] = time.time()
persistence.store(PERSIST, "persist.json")
drive_enabled = PERSIST['drive_enabled']
try:
isVFD = PERSIST['isVFD']
except:
PERSIST['isVFD'] = False
isVFD = PERSIST['isVFD']
persistence.store(PERSIST)
try:
plc_ip = PERSIST['plc_ip']
except:
PERSIST['plc_ip'] = '192.168.1.12'
plc_ip = PERSIST['plc_ip']
persistence.store(PERSIST)
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 = "25"
self.finished = threading.Event()
self.force_send = False
self.public_ip_address = ""
self.private_ip_address = ""
self.public_ip_address_last_checked = 0
self.status = ""
self.alarm = ""
global mcug, CHANNELS
mcug = mcu
from Tags import tags
CHANNELS = tags
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("PiFlow driver will start in {} seconds".format(WAIT_FOR_CONNECTION_SECONDS - i))
time.sleep(1)
log.info("BOOM! Starting PiFlow driver...")
#self._check_watchdog()
self._check_ip_address()
self.nodes["PiFlow_0199"] = self
send_loops = 0
while True:
now = time.time()
if self.force_send:
log.warning("FORCE SEND: TRUE")
if isVFD:
status = {}
for chan in CHANNELS[:24]: #build status/alarm strings
try:
val = chan.read()
chan.check(val, self.force_send)
status[chan.mesh_name] = chan.value
except Exception as e:
log.warning("An error occured in status check: {}".format(e))
try:
self.sendStatus(status)
except Exception as e:
log.warning("An error occured in send status: {}".format(e))
for chan in CHANNELS[24:]:
try:
val = chan.read()
if chan.mesh_name in ['totalizer_1','totalizer_2','totalizer_3']:
right_now = dt.today()
today_total, yesterday_total = self.totalize(val, PERSIST['yesterday_'+chan.mesh_name], right_now.day, right_now.hour, right_now.minute, PERSIST['yesterday_total_midnight_'+chan.mesh_name], PERSIST['yesterday_total_'+chan.mesh_name], chan.mesh_name)
if chan.check(val, self.force_send):
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, 'PiFlow')
self.sendtodbDev(1,"today_"+chan.mesh_name, today_total,0,'PiFlow')
self.sendtodbDev(1,"yesterday_"+chan.mesh_name, yesterday_total,0,'PiFlow')
self.sendtodbDev(1, chan.mesh_name + "_units", "BBL",0,'PiFlow')
else:
if chan.check(val, self.force_send):
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, 'PiFlow')
except Exception as e:
log.warning("An error occured in data collection: {}".format(e))
else:
for chan in CHANNELS:
try:
if chan.mesh_name == "remote_start":
val = PERSIST["state"]
else:
val = None
for _ in range(3):
temp = chan.read()
if not temp == None:
val = temp
if val == None:
log.info("No modbus data sending previous value")
val = chan.value
if chan.mesh_name in ['totalizer_1','totalizer_2','totalizer_3']:
right_now = dt.today()
today_total, yesterday_total = self.totalize(val, PERSIST['yesterday_'+chan.mesh_name], right_now.day, right_now.hour, right_now.minute, PERSIST['yesterday_total_midnight_'+chan.mesh_name], PERSIST['yesterday_total_'+chan.mesh_name], chan.mesh_name)
if chan.check(val, self.force_send):
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, 'PiFlow')
self.sendtodbDev(1,"today_"+chan.mesh_name, today_total,0,'PiFlow')
self.sendtodbDev(1,"yesterday_"+chan.mesh_name, yesterday_total,0,'PiFlow')
elif chan.mesh_name == "volume_flow" and not PERSIST['drive_enabled']:
if chan.check(val, self.force_send):
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, 'PiFlow')
if chan.value > 0:
self.sendtodbDev(1, "run_status", "Running", 0, 'PiFlow')
else:
self.sendtodbDev(1,"run_status", "Stopped", 0, 'PiFlow')
elif chan.mesh_name == "remote_start":
if chan.check(val, self.force_send):
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, 'PiFlow')
PERSIST["state_timer"] = time.time()
persistence.store(PERSIST, "persist.json")
else:
if chan.check(val, self.force_send):
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, 'PiFlow')
except Exception as e:
log.warning("An error occured: {}".format(e))
time.sleep(3)
# print("PiFlow 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 (now - self.public_ip_address_last_checked) > IP_CHECK_PERIOD:
self._check_ip_address()
time.sleep(10)
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()
test_public_ip = test_public_ip[:-1]
test_private_ip = get_private_ip_address()
if not test_public_ip == self.public_ip_address and not test_public_ip == "0.0.0.0":
self.sendtodbDev(1, 'public_ip_address', test_public_ip, 0, 'PiFlow')
self.public_ip_address = test_public_ip
if not test_private_ip == self.private_ip_address:
self.sendtodbDev(1, 'private_ip_address', test_private_ip, 0, 'PiFlow')
self.private_ip_address = test_private_ip
def PiFlow_sync(self, name, value):
"""Sync all data from the driver."""
self.force_send = True
# self.sendtodb("log", "synced", 0)
return True
def PiFlow_flowmeternumber(self, name, unit_number):
"""Change the unit number for the PiFlow flow meter"""
unit_number = int(unit_number)
if drive_enabled:
for chan in CHANNELS[0:8]:
chan.unit_number = unit_number
PERSIST['flowmeter'] = unit_number
persistence.store(PERSIST, 'persist.json')
return True
else:
for chan in CHANNELS:
chan.unit_number = unit_number
PERSIST['flowmeter'] = unit_number
persistence.store(PERSIST, 'persist.json')
self.sendtodbDev(1, 'flowmeternumber', unit_number, 0,'PiFlow')
return True
return False
def PiFlow_drivenumber(self, name, unit_number):
"""Change the unit number for the PiFlow drive"""
unit_number = int(unit_number)
for chan in CHANNELS[8:]:
chan.unit_number = unit_number
PERSIST['drive'] = unit_number
persistence.store(PERSIST, 'persist.json')
self.sendtodbDev(1, 'drivenumber', unit_number, 0,'PiFlow')
return True
def PiFlow_reboot(self, name, value):
os.system('reboot')
return True
def PiFlow_drive_enabled(self, name, value):
value = int(value)
if value == 1:
PERSIST['drive_enabled'] = True
else:
PERSIST['drive_enabled'] = False
persistence.store(PERSIST, 'persist.json')
self.sendtodbDev(1, 'drive_enabled', value, 0,'PiFlow')
return True
def PiFlow_write(self, name, value):
"""Write a value to the device via modbus"""
new_val = json.loads(str(value).replace("'", '"'))
addr_n = int(new_val['addr'])
reg_n = int(new_val['reg'])
val_n = new_val['val']
for chan in CHANNELS:
if chan.unit_number == addr_n and chan.register_number == reg_n:
write_res = chan.write(val_n)
log.info("Result of PiFlow_write(self, {}, {}) = {}".format(name, value, write_res))
return write_res
"""def PiFlow_start(self, name, value):
if isVFD:
#do something with the plc
log.info("Sending START signal to PLC")
else:
log.info("Sending START signal to Drive via relay {}".format(Relay_Ch1))
GPIO.output(Relay_Ch1,GPIO.LOW)
PERSIST["state"] = True
PERSIST["state_timer"] = time.time()
persistence.store(PERSIST,"persist.json")
return True"""
"""def PiFlow_stop(self, name, value):
if isVFD:
log.info("Sending STOP signal to PLC")
#do something with the plc
else:
log.info("Sending STOP signal to Drive")
GPIO.output(Relay_Ch1,GPIO.HIGH)
PERSIST["state"] = False
PERSIST["state_timer"] = time.time()
persistence.store(PERSIST, "persist.json")
return True"""
def totalize(self,val, yesterday, day, hour, minute, yesterday_total_midnight, yesterday_total,channel):
if (yesterday_total == 0 and yesterday_total_midnight == 0) or (yesterday_total == None or yesterday_total_midnight == None):
yesterday_total_midnight = val
PERSIST['yesterday_total_midnight_'+channel] = yesterday_total_midnight
persistence.store(PERSIST, 'persist.json')
today_total = val - yesterday_total_midnight
if hour == 0 and minute == 0 and not(day == yesterday):
yesterday_total = today_total
yesterday_total_midnight = val
today_total = val - yesterday_total_midnight
yesterday = day
PERSIST['yesterday_'+channel] = yesterday
PERSIST['yesterday_total_'+channel] = yesterday_total
PERSIST['yesterday_total_midnight_'+channel] = yesterday_total_midnight
persistence.store(PERSIST,'persist.json')
return today_total,yesterday_total
def sendStatus(self,status):
status_string = ""
fault_codes = {
0: "",
2: "Auxiliary Input",
3: "Power Loss",
4: "UnderVoltage",
5: "OverVoltage",
7: "Motor Overload",
8: "Heatsink OvrTemp",
9: "Thermister OvrTemp",
10: "DynBrake OverTemp",
12: "HW OverCurrent",
13: "Ground Fault",
14: "Ground Warning",
15: "Load Loss",
17: "Input Phase Loss",
18: "Motor PTC Trip",
19: "Task Overrun",
20: "TorqPrv Spd Band",
21: "Output PhaseLoss",
24: "Decel Inhibit",
25: "OverSpeed Limit",
26: "Brake Slipped",
27: "Torq Prove Cflct",
28: "TP Encls Config",
29: "Analog In Loss",
33: "AuRsts Exhausted",
35: "IPM OverCurrent",
36: "SW OverCurrent",
38: "Phase U to Grnd",
39: "Phase V to Grnd",
40: "Phase W to Grnd",
41: "Phase UV Short",
42: "Phase VW Short",
43: "Phase WU Short",
44: "Phase UNegToGrnd",
45: "Phase VNegToGrnd",
46: "Phase WNegToGrnd",
48: "System Defaulted",
49: "Drive Powerup",
51: "Clr Fault Queue",
55: "Ctrl Bd Overtemp",
59: "Invalid Code",
61: "Shear Pin 1",
62: "Shear Pin 2",
64: "Drive Overload",
67: "Pump Off",
71: "Port 1 Adapter",
72: "Port 2 Adapter",
73: "Port 3 Adapter",
74: "Port 4 Adapter",
75: "Port 5 Adapter",
76: "Port 6 Adapter",
77: "IR Volts Range",
78: "FluxAmpsRef Rang",
79: "Excessive Load",
80: "AutoTune Aborted",
81: "Port 1 DPI Loss",
82: "Port 2 DPI Loss",
83: "Port 3 DPI Loss",
84: "Port 4 DPI Loss",
85: "Port 5 DPI Loss",
86: "Port 6 DPI Loss",
87: "Ixo VoltageRange",
91: "Pri VelFdbk Loss",
93: "Hw Enable Check",
94: "Alt VelFdbk Loss",
95: "Aux VelFdbk Loss",
96: "PositionFdbkLoss",
97: "Auto Tach Switch",
100: "Parameter Chksum",
101: "PwrDn NVS Blank",
102: "NVS Not Blank",
103: "PwrDn Nvs Incomp",
104: "Pwr Brd Checksum",
106: "Incompat MCB-PB",
107: "Replaced MCB-PB",
108: "Anlg Cal Chksum",
110: "Ivld Pwr Bd Data",
111: "PwrBd Invalid ID",
112: "PwrBd App MinVer",
113: "Tracking DataErr",
115: "PwrDn Table Full",
116: "PwrDnEntry2Large",
117: "PwrDn Data Chksm",
118: "PwrBd PwrDn Chks",
124: "App ID Changed",
125: "Using Backup App",
134: "Start on PowerUp",
137: "Ext Prechrg Err",
138: "Precharge Open",
141: "Autn Enc Angle",
142: "Autn Spd Rstrct",
143: "AutoTune CurReg",
144: "AutoTune Inertia",
145: "AutoTune Travel",
13037: "Net IO Timeout"
}
if status['vfd_active'] == "Stopped":
status_string = status_string + status['vfd_active'] + "; " + status['vfd_ready']
else:
status_string = status_string + status['vfd_active']
if status['vfd_rev']:
status_string = status_string + '; ' + status['vfd_rev']
if status['vfd_fwd']:
status_string = status_string + '; ' + status['vfd_fwd']
if status['vfd_atreference']:
status_string = status_string + '; ' + status['vfd_atreference']
alarm_string = ""
if status['vfd_faulted'] == "Drive Faulted":
status_string = status_string + '; ' + status['vfd_faulted']
if status['vfd_commloss']:
alarm_string = alarm_string + '; ' + status['vfd_commloss']
if status['vfd_fbkalarm']:
alarm_string = alarm_string + '; ' + status['vfd_fbkalarm']
if status['vfd_faultcode']:
alarm_string = alarm_string + '; ' + "Fault: {} Fault code: {}".format(fault_codes[status['vfd_faultcode']],str(status['vfd_faultcode']))
if status['minspeedalarm']:
alarm_string = alarm_string + '; ' + status['minspeedalarm']
if status['pumpedoff']:
alarm_string = alarm_string + '; ' + status['pumpedoff']
if status['lockedout']:
alarm_string = alarm_string + '; ' + status['lockedout']
if status['tubingpressurehi']:
alarm_string = alarm_string + '; ' + status['tubingpressurehi']
if status['tubingpressurehihi']:
alarm_string = alarm_string + '; ' + status['tubingpressurehihi']
if status['tubingpressurelo']:
alarm_string = alarm_string + '; ' + status['tubingpressurelo']
if status['tubingpressurelolo']:
alarm_string = alarm_string + '; ' + status['tubingpressurelolo']
if status['flowmeterhihi']:
alarm_string = alarm_string + '; ' + status['flowmeterhihi']
if status['flowmeterhi']:
alarm_string = alarm_string + '; ' + status['flowmeterhi']
if status['flowmeterlolo']:
alarm_string = alarm_string + '; ' + status['flowmeterlolo']
if status['flowmeterlo']:
alarm_string = alarm_string + '; ' + status['flowmeterlo']
if status['fluidlevellolo']:
alarm_string = alarm_string + '; ' + status['fluidlevellolo']
if status['fluidlevello']:
alarm_string = alarm_string + '; ' + status['fluidlevello']
if status['fluidlevelhi']:
alarm_string = alarm_string + '; ' + status['fluidlevelhi']
if status['fluidlevelhihi']:
alarm_string = alarm_string + '; ' + status['fluidlevelhihi']
try:
if status_string and status_string[0] == '; ':
status_string = status_string[1:]
if status_string and status_string[-1] == '; ':
status_string = status_string[:-1]
if alarm_string and alarm_string[0] == '; ':
alarm_string = alarm_string[1:]
if alarm_string and alarm_string[-1] == '; ':
alarm_string = alarm_string[:-1]
except Exception as e:
log.warning("Error in send status semicolon: {}".format(e))
if self.status != status_string:
self.status = status_string
log.info("Sending {} for {}".format(status_string, 'run_status'))
self.sendtodbDev(1, 'run_status', status_string, 0, 'PiFlow')
if self.alarm != alarm_string:
self.alarm = alarm_string
log.info("Sending {} for {}".format(alarm_string, 'fault_a'))
self.sendtodbDev(1, 'fault_a', alarm_string, 0 , 'PiFlow')

92
promagmbs/rework/Tags.py Normal file
View File

@@ -0,0 +1,92 @@
from Channel import PLCChannel,Channel, ModbusChannel, status_codes, fault_code_a, fault_code_b, volume_units, totalizer_units
import persistence
PERSIST = persistence.load('persist.json')
flowmeter_unit_number = PERSIST['flowmeter']
drive_enabled = PERSIST['drive_enabled']
isVFD = PERSIST['isVFD']
if drive_enabled:
drive_unit_number = PERSIST['drive']
try:
plc_ip = PERSIST['plc_ip']
except:
PERSIST['plc_ip'] = '192.168.1.12'
persistence.store(PERSIST)
if isVFD:
tags = [
PLCChannel(plc_ip,'vfd_atreference','sts_VFD_AtReference','BOOL',0,3600,map_={0: "", 1: "At speed"},plc_type='Micro800'),
PLCChannel(plc_ip,'vfd_rev','sts_VFD_REV','BOOL',0,3600,map_={0: "", 1: "Operating in Reverse"},plc_type='Micro800'),
PLCChannel(plc_ip,'vfd_fwd','sts_VFD_FWD','BOOL',0,3600,map_={0: "", 1: "Operating in Forward"},plc_type='Micro800'),
PLCChannel(plc_ip,'vfd_active','sts_VFD_Active','BOOL',0,3600,map_={0: "Stopped", 1: "Running"},plc_type='Micro800'),
PLCChannel(plc_ip,'vfd_ready','sts_VFD_Ready','BOOL',0,3600,map_={0: "Drive Not Ready", 1: "Drive Ready"},plc_type='Micro800'),
PLCChannel(plc_ip,'vfd_faultcode','sts_VFD_FaultCode','REAL',0,3600, plc_type='Micro800'),
PLCChannel(plc_ip,'vfd_faulted','AL0_VFD','BOOL',0,3600,map_={0: "", 1: "Drive Faulted"},plc_type='Micro800'),
PLCChannel(plc_ip,'vfd_commloss','AL0_VFDComLoss','BOOL',0,3600,map_={0: "", 1: "Drive Comms Loss"},plc_type='Micro800'),
PLCChannel(plc_ip,'vfd_fbkalarm','AL0_VFD_FBAlarm','BOOL',0,3600,map_={0: "", 1: "Drive Lost Feedback"},plc_type='Micro800'),
PLCChannel(plc_ip,'tubingpressurehi','AL0_TubingPressureHi','BOOL',0,3600,map_={0: "", 1: "High Tubing Pressure"},plc_type='Micro800'),
PLCChannel(plc_ip,'tubingpressurehihi','AL0_TubingPressureHiHi','BOOL',0,3600,map_={0: "", 1: "High High Tubing Pressure"},plc_type='Micro800'),
PLCChannel(plc_ip,'tubingpressurelo','AL0_TubingPressureLo','BOOL',0,3600,map_={0: "", 1: "Low Tubing Pressure"},plc_type='Micro800'),
PLCChannel(plc_ip,'tubingpressurelolo','AL0_TubingPressureLoLo','BOOL',0,3600,map_={0: "", 1: "Low Low Tubing Pressure"},plc_type='Micro800'),
PLCChannel(plc_ip,'flowmeterhihi','AL0_FlowMeterHiHi','BOOL',0,3600,map_={0: "", 1: "High High FM Flow Rate"},plc_type='Micro800'),
PLCChannel(plc_ip,'flowmeterhi','AL0_FlowMeterHi','BOOL',0,3600,map_={0: "", 1: "High FM Flow Rate"},plc_type='Micro800'),
PLCChannel(plc_ip,'flowmeterlolo','AL0_FlowMeterLoLo','BOOL',0,3600,map_={0: "", 1: "Low Low FM Flow Rate"},plc_type='Micro800'),
PLCChannel(plc_ip,'flowmeterlo','AL0_FlowMeterLo','BOOL',0,3600,map_={0: "", 1: "Low FM Flow Rate"},plc_type='Micro800'),
PLCChannel(plc_ip,'minspeedalarm','AL0_MinSpeedAlarm','BOOL',0,3600,map_={0: "", 1: "Drive not able to maintain min speed"},plc_type='Micro800'),
PLCChannel(plc_ip,'pumpedoff','AL0_PumpedOff','BOOL',0,3600,map_={0: "", 1: "Pumped Off"},plc_type='Micro800'),
PLCChannel(plc_ip,'fluidlevellolo','AL0_FluidLevelLoLo','BOOL',0,3600,map_={0: "", 1: "Low Low Fluid Level"},plc_type='Micro800'),
PLCChannel(plc_ip,'fluidlevello','AL0_FluidLevelLo','BOOL',0,3600,map_={0: "", 1: "Low Fluid Level"},plc_type='Micro800'),
PLCChannel(plc_ip,'fluidlevelhi','AL0_FluidLevelHi','BOOL',0,3600,map_={0: "", 1: "High Fluid Level"},plc_type='Micro800'),
PLCChannel(plc_ip,'fluidlevelhihi','AL0_FluidLevelHiHi','BOOL',0,3600,map_={0: "", 1: "High High Fluid Level"},plc_type='Micro800'),
PLCChannel(plc_ip,'lockedout','AlarmLockOut','BOOL',0,3600,map_={0: "", 1: "Locked Out Repeated Alarms"},plc_type='Micro800'),
PLCChannel(plc_ip,'volume_flow','Val_FlowmeterFR','REAL',5,3600,plc_type='Micro800'),
PLCChannel(plc_ip,'current','val_VFD_OutputCurrent','REAL',5,3600,plc_type='Micro800'),
PLCChannel(plc_ip,'frequency','val_VFD_ActualSpeed','REAL',5,3600,plc_type='Micro800'),
PLCChannel(plc_ip,'pid_feedback','val_FluidLevel','REAL',5,3600,plc_type='Micro800'),
PLCChannel(plc_ip,'totalizer_1','Val_FlowMeterT1','REAL',5,3600,plc_type='Micro800'),
PLCChannel(plc_ip,'totalizer_2','Val_FlowMeterT2','REAL',5,3600,plc_type='Micro800'),
PLCChannel(plc_ip,'totalizer_3','Val_FlowMeterT3','REAL',5,3600,plc_type='Micro800'),
PLCChannel(plc_ip,'volume_flow_units','CMD_FlowMeterUnit','BOOL',1,3600,map_={0: "GPM", 1: "BPD"},plc_type='Micro800')
]
else:
if drive_enabled:
tags = [
ModbusChannel('volume_flow', 3873, 'FLOAT', 10, 3600,channel_size=2, unit_number=flowmeter_unit_number),
ModbusChannel('totalizer_1', 2609, 'FLOAT', 100, 3600,channel_size=2, unit_number=flowmeter_unit_number),
ModbusChannel('totalizer_2', 2809, 'FLOAT', 100, 3600,channel_size=2, unit_number=flowmeter_unit_number),
ModbusChannel('totalizer_3', 3009, 'FLOAT', 100, 3600,channel_size=2, unit_number=flowmeter_unit_number),
ModbusChannel('volume_flow_units', 2102, 'INTEGER', 1,86400,channel_size=1, unit_number=flowmeter_unit_number, transform_fn=volume_units),
ModbusChannel('totalizer_1_units', 4603, 'INTEGER', 1,86400,channel_size=1, unit_number=flowmeter_unit_number, transform_fn=totalizer_units),
ModbusChannel('totalizer_2_units', 4604, 'INTEGER', 1,86400,channel_size=1, unit_number=flowmeter_unit_number, transform_fn=totalizer_units),
ModbusChannel('totalizer_3_units', 4605, 'INTEGER', 1,86400,channel_size=1, unit_number=flowmeter_unit_number, transform_fn=totalizer_units),
ModbusChannel('remote_start', 0000, 'INTEGER', 1, 86400, channel_size=1, unit_number=flowmeter_unit_number),
ModbusChannel('run_status', 772, 'STRING', 0, 3600, channel_size=1, unit_number=drive_unit_number, transform_fn=status_codes),
ModbusChannel('frequency', 784, 'INTEGER', 2, 3600, channel_size=2, unit_number=drive_unit_number,scaling=2 ),
ModbusChannel('current', 783, 'INTEGER', 2, 3600, channel_size=2, unit_number=drive_unit_number,scaling=1 ),
ModbusChannel('fault_a', 815, 'STRING', 1, 3600, channel_size=1, unit_number=drive_unit_number,transform_fn=fault_code_a),
ModbusChannel('fault_b', 816, 'STRING', 1, 3600, channel_size=1, unit_number=drive_unit_number,transform_fn=fault_code_b),
ModbusChannel('pid_ref', 791, 'INTEGER', 5, 3600, channel_size=1, unit_number=drive_unit_number,scaling=1),
ModbusChannel('pid_feedback', 792, 'INTEGER', 5, 3600, channel_size=1, unit_number=drive_unit_number,scaling=1),
ModbusChannel('motor_rated_current', 4896, 'INTEGER', 300, 86400, channel_size=1, unit_number=drive_unit_number,scaling=1),
ModbusChannel('sleep_delay', 4924, 'INTEGER', 5, 86400, channel_size=1, unit_number=drive_unit_number, scaling=1)
]
else:
tags = [
ModbusChannel('volume_flow', 3873, 'FLOAT', 10, 3600,channel_size=2, unit_number=flowmeter_unit_number),
ModbusChannel('totalizer_1', 2609, 'FLOAT', 100, 3600,channel_size=2, unit_number=flowmeter_unit_number),
ModbusChannel('totalizer_2', 2809, 'FLOAT', 100, 3600,channel_size=2, unit_number=flowmeter_unit_number),
ModbusChannel('totalizer_3', 3009, 'FLOAT', 100, 3600,channel_size=2, unit_number=flowmeter_unit_number),
ModbusChannel('volume_flow_units', 2102, 'INTEGER', 1, 86400,channel_size=1, unit_number=flowmeter_unit_number, transform_fn=volume_units),
ModbusChannel('totalizer_1_units', 4603, 'INTEGER', 1, 86400,channel_size=1, unit_number=flowmeter_unit_number, transform_fn=totalizer_units),
ModbusChannel('totalizer_2_units', 4604, 'INTEGER', 1, 86400,channel_size=1, unit_number=flowmeter_unit_number, transform_fn=totalizer_units),
ModbusChannel('totalizer_3_units', 4605, 'INTEGER', 1, 86400,channel_size=1, unit_number=flowmeter_unit_number, transform_fn=totalizer_units),
ModbusChannel('remote_start', 0000, 'BOOL', 1, 86400, channel_size=1, unit_number=flowmeter_unit_number)
]

View File

@@ -29,7 +29,28 @@ tags = [
PLCChannel(PLC_IP_ADDRESS, "reverse_out_lifetime","Val_Out_T3","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "total_in_lifetime", "Val_T1TotalIn", "REAL",1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "total_out_lifetime", "Val_T1TotalOut", "REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "fm1_month","Val_Flowmeter1MonthTotal","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_raw_min","pond1scaling.rawmin","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_raw_max","pond1scaling.rawmax","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_eu_min","pond1scaling.eumin","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_eu_max","pond1scaling.eumax","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_offset","pond1offset","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_raw_min","pond2scaling.rawmin","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_raw_max","pond2scaling.rawmax","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_eu_min","pond2scaling.eumin","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_eu_max","pond2scaling.eumax","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_offset","pond2offset","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_height","pond1Height","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_height","pond2height","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_volume","pond1volume","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_volume","pond2volume","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_sum_volume","pondVolumeTotal","REAL", 3000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_high_spt","SPT_Pond_1_High_Level","REAL", 1, 86400, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_high_spt","SPT_Pond_2_High_Level","REAL", 1, 86400, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_alarm","Pond1HiAlarm","BOOL", 0, 3600, plc_type="Micro800", map_={0: "Good", 1: "High", None: "Error"}),
PLCChannel(PLC_IP_ADDRESS, "pond_2_alarm","Pond2HiAlarm","BOOL", 0, 3600, plc_type="Micro800", map_={0: "Good", 1: "High", None: "Error"})
]
""" PLCChannel(PLC_IP_ADDRESS, "fm1_month","Val_Flowmeter1MonthTotal","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "fm2_month","Val_Flowmeter2MonthTotal","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "fm3_month","Val_Flowmeter3MonthTotal","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "fm4_month","Val_Flowmeter4MonthTotal","REAL", 1000000000, 3600, plc_type="Micro800"),
@@ -76,24 +97,4 @@ tags = [
PLCChannel(PLC_IP_ADDRESS, "fm9_todays","Val_Flowmeter9TodaysTotal","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "fm10_todays","Val_Flowmeter10TodaysTotal","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "total_in_today", "Val_TodaysTotalIn", "REAL",1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "total_out_today", "Val_TodaysTotalOut", "REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_raw_min","pond1scaling.rawmin","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_raw_max","pond1scaling.rawmax","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_eu_min","pond1scaling.eumin","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_eu_max","pond1scaling.eumax","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_offset","pond1offset","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_raw_min","pond2scaling.rawmin","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_raw_max","pond2scaling.rawmax","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_eu_min","pond2scaling.eumin","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_eu_max","pond2scaling.eumax","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_offset","pond2offset","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_height","pond1Height","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_height","pond2height","REAL", 5, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_volume","pond1volume","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_volume","pond2volume","REAL", 1000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_sum_volume","pondVolumeTotal","REAL", 3000000000, 3600, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_high_spt","SPT_Pond_1_High_Level","REAL", 1, 86400, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_2_high_spt","SPT_Pond_2_High_Level","REAL", 1, 86400, plc_type="Micro800"),
PLCChannel(PLC_IP_ADDRESS, "pond_1_alarm","Pond1HiAlarm","BOOL", 0, 3600, plc_type="Micro800", map_={0: "Good", 1: "High", None: "Error"}),
PLCChannel(PLC_IP_ADDRESS, "pond_2_alarm","Pond2HiAlarm","BOOL", 0, 3600, plc_type="Micro800", map_={0: "Good", 1: "High", None: "Error"})
]
PLCChannel(PLC_IP_ADDRESS, "total_out_today", "Val_TodaysTotalOut", "REAL", 1000000000, 3600, plc_type="Micro800"), """

View File

@@ -8,7 +8,7 @@
"file4": "Tags.py"
},
"deviceName": "tenflowmeterskid",
"releaseVersion": "1",
"releaseVersion": "4",
"driverFileName": "tenflowmeterskid.py",
"driverId": "0100"
}

View File

@@ -12,7 +12,7 @@ from utilities import get_public_ip_address, get_private_ip_address
from file_logger import filelogger as log
PLC_IP_ADDRESS = "192.168.1.12"
from Tags import tags
from datetime import datetime as dt
_ = None
log.info("tenflowmeterskid startup")
@@ -32,6 +32,24 @@ if not PERSIST:
}
persistence.store(PERSIST)
TOTALIZERS = persistence.load("totalizers.json")
if not TOTALIZERS:
TOTALIZERS = {}
for x in ['total_in', 'total_out', 'fm1', 'fm2', 'fm3', 'fm4', 'fm5', 'fm6', 'fm7', 'fm8', 'fm9', 'fm10']:
TOTALIZERS[x] = {
'Todays': 0,
'Yesterdays': 0,
'Current Months': 0,
'Previous Months': 0,
'Monthly Holding': 0,
'Daily Holding': 0,
'Lifetime': 0,
'Day': 0,
'Month': 0,
'Last Report': 0
}
persistence.store(TOTALIZERS, "totalizers.json")
CALIBRATION_TABLES = [[],[], [], ]
class start(threading.Thread, deviceBase):
@@ -46,12 +64,13 @@ class start(threading.Thread, deviceBase):
mqtt=mqtt, Nodes=Nodes)
self.daemon = True
self.version = "1"
self.version = "4"
self.finished = threading.Event()
self.force_send = False
self.public_ip_address = ""
self.private_ip_address = ""
self.public_ip_address_last_checked = 0
self.ping_counter = 0
threading.Thread.start(self)
# this is a required function for all drivers, its goal is to upload some piece of data
@@ -83,7 +102,12 @@ class start(threading.Thread, deviceBase):
val = chan.read()
if chan.check(val, self.force_send):
if chan.mesh_name in PERSIST["ignore_list"]:
self.sendtodbDev(1, chan.mesh_name, None, 0, 'tenflowmeterskid')
if "lifetime" in chan.mesh_name and chan.mesh_name not in ['forward_out_lifetime', 'reverse_out_lifetime', 'net_out_lifetime']:
self.totalizer_null(chan.mesh_name[:-9])
else:
self.sendtodbDev(1, chan.mesh_name, None, 0, 'tenflowmeterskid')
elif "lifetime" in chan.mesh_name and chan.mesh_name not in ['forward_out_lifetime', 'reverse_out_lifetime', 'net_out_lifetime']:
self.totalize(val, chan.mesh_name[:-9])
else:
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, 'tenflowmeterskid')
#time.sleep(TAG_DATAERROR_SLEEPTIME) # sleep to allow Micro800 to handle ENET requests
@@ -327,4 +351,88 @@ class start(threading.Thread, deviceBase):
if value in PERSIST["ignore_list"]:
PERSIST["ignore_list"].remove(value)
persistence.store(PERSIST)
return True
return True
def totalize(self, val, totalizer):
right_now = dt.today()
month = right_now.month
day = right_now.day
#Totalize Today, Yesterday, Month, Last Month
#if the stored day is 0 then it's a fresh run of this should initalize values now
if TOTALIZERS[totalizer]['Day'] == 0:
TOTALIZERS[totalizer]['Day'] = day
TOTALIZERS[totalizer]['Month'] = month
TOTALIZERS[totalizer]['Daily Holding'] = val
TOTALIZERS[totalizer]['Monthly Holding'] = val
persistence.store(TOTALIZERS, 'totalizers.json')
#Communication error during initialization check if lifetime has reported properly and update holdings
if TOTALIZERS[totalizer]['Daily Holding'] == None and not(val == None):
TOTALIZERS[totalizer]['Daily Holding'] = val
TOTALIZERS[totalizer]['Monthly Holding'] = val
try:
if val - TOTALIZERS[totalizer]['Daily Holding'] - TOTALIZERS[totalizer]['Todays'] > 500 or time.time() - TOTALIZERS[totalizer]['Last Report'] > 3600 or self.force_send:
TOTALIZERS[totalizer]['Todays'] = val - TOTALIZERS[totalizer]['Daily Holding']
TOTALIZERS[totalizer]['Current Months'] = val - TOTALIZERS[totalizer]['Monthly Holding']
TOTALIZERS[totalizer]['Lifetime'] = val
self.sendtodbDev(1, '{}_todays'.format(totalizer), TOTALIZERS[totalizer]['Todays'], 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_month'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_yesterdays'.format(totalizer), TOTALIZERS[totalizer]['Yesterdays'], 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_lifetime'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'tenflowmeterskid')
if self.force_send:
self.sendtodbDev(1, '{}_lastmonth'.format(totalizer), TOTALIZERS[totalizer]['Previous Months'], 0, 'tenflowmeterskid')
TOTALIZERS[totalizer]['Last Report'] = time.time()
except:
if time.time() - TOTALIZERS[totalizer]['Last Report'] > 3600 or self.force_send:
self.sendtodbDev(1, '{}_todays'.format(totalizer), TOTALIZERS[totalizer]['Todays'], 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_month'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_yesterdays'.format(totalizer), TOTALIZERS[totalizer]['Yesterdays'], 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_lifetime'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'tenflowmeterskid')
if self.force_send:
self.sendtodbDev(1, '{}_lastmonth'.format(totalizer), TOTALIZERS[totalizer]['Previous Months'], 0, 'tenflowmeterskid')
TOTALIZERS[totalizer]['Last Report'] = time.time()
#If the current day doesn't equal the stored day roll the dailies over
if not(day == TOTALIZERS[totalizer]['Day']):
#if a comms error use the stored values else use the latested values
if val == None:
TOTALIZERS[totalizer]['Yesterdays'] = TOTALIZERS[totalizer]['Todays']
TOTALIZERS[totalizer]['Todays'] = 0
TOTALIZERS[totalizer]['Daily Holding'] = TOTALIZERS[totalizer]['Lifetime']
else:
TOTALIZERS[totalizer]['Yesterdays'] = val - TOTALIZERS[totalizer]['Daily Holding']
TOTALIZERS[totalizer]['Todays'] = 0
TOTALIZERS[totalizer]['Daily Holding'] = val
TOTALIZERS[totalizer]['Lifetime'] = val
TOTALIZERS[totalizer]['Day'] = day
self.sendtodbDev(1, '{}_todays'.format(totalizer), TOTALIZERS[totalizer]['Todays'], 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_yesterdays'.format(totalizer), TOTALIZERS[totalizer]['Yesterdays'], 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_lifetime'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'tenflowmeterskid')
TOTALIZERS[totalizer]['Last Report'] = time.time()
#the day has rolled over if the month also rolls over
if not(month == TOTALIZERS[totalizer]['Month']):
#if a comms error use the stored values else use the latested values
if val == None:
TOTALIZERS[totalizer]['Previous Months'] = TOTALIZERS[totalizer]['Current Months']
TOTALIZERS[totalizer]['Current Months'] = 0
TOTALIZERS[totalizer]['Monthly Holding'] = TOTALIZERS[totalizer]['Lifetime']
else:
TOTALIZERS[totalizer]['Previous Months'] = val - TOTALIZERS[totalizer]['Monthly Holding']
TOTALIZERS[totalizer]['Current Months'] = 0
TOTALIZERS[totalizer]['Monthly Holding'] = val
TOTALIZERS[totalizer]['Month'] = month
self.sendtodbDev(1, '{}_month'.format(totalizer), TOTALIZERS[totalizer]['Current Months'], 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_lastmonth'.format(totalizer), TOTALIZERS[totalizer]['Previous Months'], 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_lifetime'.format(totalizer), TOTALIZERS[totalizer]['Lifetime'], 0, 'tenflowmeterskid')
TOTALIZERS[totalizer]['Last Report'] = time.time()
persistence.store(TOTALIZERS, 'totalizers.json')
def totalizer_null(self, totalizer):
if time.time() - TOTALIZERS[totalizer]['Last Report'] > 3600 or self.force_send:
self.sendtodbDev(1, '{}_todays'.format(totalizer), None, 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_month'.format(totalizer), None, 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_yesterdays'.format(totalizer), None, 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_lastmonth'.format(totalizer), None, 0, 'tenflowmeterskid')
self.sendtodbDev(1, '{}_lifetime'.format(totalizer), None, 0, 'tenflowmeterskid')
TOTALIZERS[totalizer]['Last Report'] = time.time()
persistence.store(TOTALIZERS, 'totalizers.json')