Added Folders

Add all the driver folders
This commit is contained in:
2019-12-13 12:15:30 -06:00
parent 7ea92c19f6
commit 632dcdb3e8
226 changed files with 54771 additions and 0 deletions

297
email_reports/Channel.py Normal file
View File

@@ -0,0 +1,297 @@
"""Define Meshify channel class."""
import time
from pycomm.ab_comm.clx import Driver as ClxDriver
from pycomm.cip.cip_base import CommError, DataError
from file_logger import filelogger as log
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)
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()
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, plc_type="CLX"):
"""Read an array from the PLC."""
direct = plc_type == "Micro800"
clx = ClxDriver()
if clx.open(addr, direct_connection=direct):
arr_vals = []
new_end = 0
try:
for i in range(start, end):
tag_w_index = tag + "[{}]".format(i)
val = clx.read_tag(tag_w_index)
new_end = i
if tag == "TransactionDay" and val[0] == 0:
break
arr_vals.append(round(val[0], 4))
if arr_vals:
return arr_vals,new_end + 1
else:
log.error("No length for {}".format(addr))
return arr_vals, new_end + 1
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])
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):
"""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):
"""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
def read(self, mbsvalue):
"""Return the transformed read value."""
return self.transform_fn(mbsvalue)
class PLCChannel(Channel):
"""PLC Channel Object."""
def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False, plc_type='CLX'):
"""Initialize the channel."""
super(PLCChannel, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled)
self.plc_ip = ip
self.mesh_name = mesh_name
self.plc_tag = plc_tag
self.data_type = data_type
self.last_value = None
self.value = None
self.last_send_time = 0
self.chg_threshold = chg_threshold
self.guarantee_sec = guarantee_sec
self.map_ = map_
self.write_enabled = write_enabled
self.plc_type = plc_type
def read(self):
"""Read the value."""
plc_value = None
if self.plc_tag and self.plc_ip:
read_value = read_tag(self.plc_ip, self.plc_tag, plc_type=self.plc_type)
if read_value:
plc_value = read_value[0]
return plc_value
class BoolArrayChannels(Channel):
"""Hold the configuration for a set of boolean array channels."""
def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False):
"""Initialize the channel."""
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

48
email_reports/Tags.py Normal file
View File

@@ -0,0 +1,48 @@
from Channel import PLCChannel, ModbusChannel
from email_reports import PLC_IP_ADDRESS
tags = [
PLCChannel(PLC_IP_ADDRESS, "driver_id","Driver_ID_This_Transcation","INT", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "transaction_day","TransactionDay","INT", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "transaction_hour","TransactionHour","INT", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "transaction_minute","TransactionMinute","INT", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "transaction_total","Total_Sold","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "today_total","Totalizer.Today_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "yesterdays_total","Totalizer.Yesterdays_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "monthly_total","Totalizer.Monthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "prevmonthly_total","Totalizer.PrevMonthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "year_total","Totalizer.Current_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "previous_year_total","Totalizer.Previous_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_1_today_total","Company_1.Today_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_1_yesterdays_total","Company_1.Yesterdays_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_1_monthly_total","Company_1.Monthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_1_prevmonthly_total","Company_1.PrevMonthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_1_year_total","Company_1.Current_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_1_previous_year_total","Company_1.Previous_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_2_today_total","Company_2.Today_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_2_yesterdays_total","Company_2.Yesterdays_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_2_monthly_total","Company_2.Monthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_2_prevmonthly_total","Company_2.PrevMonthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_2_year_total","Company_2.Current_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_2_previous_year_total","Company_2.Previous_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_3_today_total","Company_3.Today_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_3_yesterdays_total","Company_3.Yesterdays_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_3_monthly_total","Company_3.Monthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_3_prevmonthly_total","Company_3.PrevMonthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_3_year_total","Company_3.Current_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_3_previous_year_total","Company_3.Previous_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_4_today_total","Company_4.Today_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_4_yesterdays_total","Company_4.Yesterdays_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_4_monthly_total","Company_4.Monthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_4_prevmonthly_total","Company_4.PrevMonthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_4_year_total","Company_4.Current_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_4_previous_year_total","Company_4.Previous_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_5_today_total","Company_5.Today_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_5_yesterdays_total","Company_5.Yesterdays_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_5_monthly_total","Company_5.Monthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_5_prevmonthly_total","Company_5.PrevMonthlys_TotalFlow","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_5_year_total","Company_5.Current_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX"),
PLCChannel(PLC_IP_ADDRESS, "company_5_previous_year_total","Company_5.Previous_Year_Total_Sales","REAL", 1, 3600, plc_type="CLX")
]

14
email_reports/config.txt Normal file
View File

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

View File

@@ -0,0 +1,360 @@
import types
import traceback
import binascii
import threading
import time
import thread
import os
import struct
import sys
import textwrap
import Queue
import json
class deviceBase():
def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, companyId=None, offset=None, mqtt=None, Nodes=None):
self.offset = offset
self.company = companyId
self.name = name
self.number = number
self.q = Q
self.deviceName = name + '_[' + mac + ':' + number[0:2] + ':' + number[2:] + ']!'
self.chName = "M1" + '_[' + mac + ':'
self.chName2 = '_[' + mac + ':'
print 'device name is:'
print self.deviceName
mac2 = mac.replace(":", "")
self.mac = mac2.upper()
self.address = 1
self.debug = True
self.mcu = mcu
self.firstRun = True
self.mqtt = mqtt
self.nodes = Nodes
#local dictionary of derived nodes ex: localNodes[tank_0199] = self
self.localNodes = {}
os.system("chmod 777 /root/reboot")
os.system("echo nameserver 8.8.8.8 > /etc/resolv.conf")
#Queue for imcoming sets
self.loraQ = Queue.Queue()
self.knownIDs = []
thread.start_new_thread(self.getSetsThread, ())
def getSetsThread(self):
while True:
try:
item = self.loraQ.get(block=True, timeout=600)
try:
print "here is the item from the sets q"
print item
if len(item) == 2:
techname = str(json.loads(item[1])[0]['payload']['name'].split(".")[0])
channel = str(json.loads(item[1])[0]['payload']['name'].split(".")[1])
name = techname.split("_")[0]
id = techname.split("_")[1][1:-2].replace(":","").upper()
value = json.loads(item[1])[0]['payload']['value']
msgId = json.loads(item[1])[0]['msgId']
print channel, value, id, name, msgId
success = self.specificSets(channel, value, id, name)
if success == True:
print "SUCCESS ON SET"
if int(msgId) == 0:
return
lc = self.getTime()
value = str(self.mac) + " Success Setting: " + channel + " To: " + value
msg = """[ { "value":"%s", "timestamp":"%s", "msgId":"%s" } ]""" % (value, str(lc), msgId)
print value
print msg
topic = "meshify/responses/" + str(msgId)
print topic
self.q.put([topic, str(msg), 2])
else:
lc = self.getTime()
if success == False:
reason = "(Internal Gateway/Device Error)"
else:
reason = success
value = str(self.mac) + " Failed Setting: " + channel + " To: " + value + " " + reason
msg = """[ { "value":"%s", "timestamp":"%s", "msgId":"%s" } ]""" % (value, str(lc), msgId)
topic = "meshify/responses/" + msgId
self.q.put([topic, str(msg), 2])
except:
if int(msgId) == 0:
return
lc = self.getTime()
value = str(self.mac) + " Failed Setting: " + channel + " To: " + value + " (No Callback Found)"
msg = """[ { "value":"%s", "timestamp":"%s", "msgId":"%s" } ]""" % (value, str(lc), msgId)
topic = "meshify/responses/" + msgId
self.q.put([topic, str(msg), 2])
print 'no Set callback found for channel: ' + funcName
except:
print "sets queue timeout, restarting..."
def sendtodbDevLora(self, id, channel, value, timestamp, deviceName):
mac = self.mac
if deviceName == "mainMeshify":
zigmac = "_[01:00:00:00:00:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
else:
zigmac = "_[00:00:00:00:00:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
dname = deviceName + zigmac
#define dname, make id into techname and mac
if id not in self.knownIDs:
self.knownIDs.append(id)
self.mcu.xbees[dname] = self.loraQ
#meshify/db/330/C493000354FB/ilora/c493000354fb2A6E/a1-v
#[ { "value":"0.5635", "timestamp":"1486039316" } ]
if int(timestamp) == 0:
timestamp = self.getTime()
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel)
print topic
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
def sendtodbLocLora(self, id, channel, value, timestamp, deviceName):
mac = id
while len(mac) < 12:
mac = "0" + mac
if deviceName == "mainMeshify":
zigmac = "_[01:00:00:00:00:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
else:
zigmac = "_[00:00:00:00:00:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
dname = deviceName + zigmac
#define dname, make id into techname and mac
if id not in self.knownIDs:
self.knownIDs.append(id)
topic = str(("meshify/sets/" + str(self.company) + "/" + mac + "/#"))
self.mqtt.subscribe(topic, 0)
topic = str(("meshify/sets/" + "1" + "/" + mac + "/#"))
self.mqtt.subscribe(topic, 0)
self.mcu.xbees[dname] = self.loraQ
#meshify/db/330/C493000354FB/ilora/c493000354fb2A6E/a1-v
#[ { "value":"0.5635", "timestamp":"1486039316" } ]
if int(timestamp) == 0:
timestamp = self.getTime()
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel)
print topic
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
def sendtodbLocLoraCom(self, id, channel, value, timestamp, deviceName):
mac = "1" + id
while len(mac) < 12:
mac = "0" + mac
if deviceName == "mainMeshify":
zigmac = "_[01:00:00:00:00:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
else:
zigmac = "_[00:00:00:00:01:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
dname = deviceName + zigmac
#define dname, make id into techname and mac
if id not in self.knownIDs:
self.knownIDs.append(id)
topic = str(("meshify/sets/" + str(self.company) + "/" + mac + "/#"))
self.mqtt.subscribe(topic, 0)
topic = str(("meshify/sets/" + "1" + "/" + mac + "/#"))
self.mqtt.subscribe(topic, 0)
self.mcu.xbees[dname] = self.loraQ
#meshify/db/330/C493000354FB/ilora/c493000354fb2A6E/a1-v
#[ { "value":"0.5635", "timestamp":"1486039316" } ]
if int(timestamp) == 0:
timestamp = self.getTime()
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel)
print topic
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
def sendtodbLoc(self, ch, channel, value, timestamp, deviceName, mac):
#this will add your derived nodes the master nodes list, allowing them to receive sets!!
localNodesName = deviceName + "_" + str(ch) + "99"
if not self.localNodes.has_key(localNodesName):
self.localNodes[localNodesName] = True
self.nodes[localNodesName] = self
#make the techname
lst = textwrap.wrap(str(mac), width=2)
tech = ""
for i in range(len(lst)):
tech += lst[i].lower() + ":"
chName2 = '_[' + tech
if int(ch) < 10:
ch = "0" + str(int(ch))
if len(ch) > 2:
ch = ch[:-2]
dname = deviceName + chName2 + str(ch) + ":98]!"
if int(timestamp) == 0:
timestamp = self.getTime()
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel)
print topic
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
def sendtodbDevJSON(self, ch, channel, value, timestamp, deviceName):
if int(ch) < 10:
ch = "0" + str(int(ch))
dname = deviceName + self.chName2 + str(ch) + ":99]!"
if int(timestamp) == 0:
timestamp = self.getTime()
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel)
print topic
msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
def sendtodbLora(self, ch, channel, value, timestamp, deviceName):
if ":" not in ch:
ch = ch[0:2] + ":" + ch[2:4]
#this will add your derived nodes the master nodes list, allowing them to receive sets!!
localNodesName = deviceName + "_" + str(ch).replace(':', "")
if not self.localNodes.has_key(localNodesName):
self.localNodes[localNodesName] = True
self.nodes[localNodesName] = self
dname = deviceName + self.chName2 + str(ch) + "]!"
if int(timestamp) == 0:
timestamp = self.getTime()
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel)
print topic
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
def sendtodbDev(self, ch, channel, value, timestamp, deviceName):
#this will add your derived nodes the master nodes list, allowing them to receive sets!!
localNodesName = deviceName + "_" + str(ch) + "99"
if not self.localNodes.has_key(localNodesName):
self.localNodes[localNodesName] = True
self.nodes[localNodesName] = self
if int(ch) < 10:
ch = "0" + str(int(ch))
dname = deviceName + self.chName2 + str(ch) + ":99]!"
if int(timestamp) == 0:
timestamp = self.getTime()
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel)
print topic
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
def sendtodbCH(self, ch, channel, value, timestamp):
if int(ch) < 10:
ch = "0" + str(ch)
dname = self.chName + str(ch) + ":99]!"
if int(timestamp) == 0:
timestamp = self.getTime()
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel)
print topic
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
def sendtodb(self, channel, value, timestamp):
if int(timestamp) == 0:
timestamp = self.getTime()
if timestamp < 1400499858:
return
else:
timestamp = str(int(timestamp) + int(self.offset))
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel)
print topic
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
def sendtodbJSON(self, channel, value, timestamp):
if int(timestamp) == 0:
timestamp = self.getTime()
if timestamp < 1400499858:
return
else:
timestamp = str(int(timestamp) + int(self.offset))
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel)
print topic
msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
def getTime(self):
return str(int(time.time() + int(self.offset)))

View File

@@ -0,0 +1,232 @@
"""Driver for email_reports"""
import threading
import json
import time
from random import randint
from datetime import datetime as dt
from device_base import deviceBase
from Channel import PLCChannel, ModbusChannel,read_tag, read_array, write_tag, TAG_DATAERROR_SLEEPTIME
import persistence
from utilities import get_public_ip_address, generate_report, send_email
from file_logger import filelogger as log
PLC_IP_ADDRESS = "192.168.1.10"
from Tags import tags
_ = None
log.info("email_reports startup")
# GLOBAL VARIABLES
WAIT_FOR_CONNECTION_SECONDS = 60
IP_CHECK_PERIOD = 60
WATCHDOG_ENABLE = False
WATCHDOG_CHECK_PERIOD = 60
WATCHDOG_SEND_PERIOD = 3600 # Seconds, the longest amount of time before sending the watchdog status
CHANNELS = tags
# PERSISTENCE FILE
PERSIST = persistence.load()
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 = "5"
self.finished = threading.Event()
self.force_send = False
self.public_ip_address = ""
self.public_ip_address_last_checked = 0
self.watchdog = False
self.watchdog_last_checked = 0
self.watchdog_last_sent = 0
threading.Thread.start(self)
# this is a required function for all drivers, its goal is to upload some piece of data
# about your device so it can be seen on the web
def register(self):
"""Register the driver."""
# self.sendtodb("log", "BOOM! Booted.", 0)
pass
def run(self):
"""Actually run the driver."""
for i in range(0, WAIT_FOR_CONNECTION_SECONDS):
print("email_reports driver will start in {} seconds".format(WAIT_FOR_CONNECTION_SECONDS - i))
time.sleep(1)
log.info("BOOM! Starting email_reports driver...")
if not PERSIST:
PERSIST = {'yesterday': dt.today().day - 1,
'start': 0,
'end': 10000,
'recipients': ['nmelone@henry-pump.com']}
persistence.store(PERSIST)
#self._check_watchdog()
self._check_ip_address()
self.nodes["email_reports_0199"] = self
send_loops = 0
while True:
now = time.time()
if self.force_send:
log.warning("FORCE SEND: TRUE")
if self.isReportTime():
#read a full array from PLC
log.info("Gathering Timestamp between {} and {}".format(PERSIST['start'], PERSIST['end']))
trans_day, PERSIST['end'] = read_array(CHANNELS[1].plc_ip,CHANNELS[1].plc_tag,PERSIST['start'],PERSIST['end'],CHANNELS[1].plc_type)
trans_hour, PERSIST['end'] = read_array(CHANNELS[2].plc_ip,CHANNELS[1].plc_tag,PERSIST['start'],PERSIST['end'],CHANNELS[2].plc_type)
trans_min, PERSIST['end'] = read_array(CHANNELS[3].plc_ip,CHANNELS[3].plc_tag,PERSIST['start'],PERSIST['end'],CHANNELS[3].plc_type)
log.info("Gathering Driver IDs between {} and {}".format(PERSIST['start'], PERSIST['end']))
driver_id, PERSIST['end'] = read_array(CHANNELS[0].plc_ip, CHANNELS[0].plc_tag, PERSIST['start'], PERSIST['end'], CHANNELS[0].plc_type) #addr, tag, start, end, plc_type="CLX"
log.info("Gather Transaction Data between {} and {}".format(PERSIST['start'], PERSIST['end']))
trans_total, PERSIST['end'] = read_array(CHANNELS[4].plc_ip,CHANNELS[4].plc_tag,PERSIST['start'],PERSIST['end'],CHANNELS[4].plc_type)
PERSIST['start'] = PERSIST['end']
overall = {
'today_total': CHANNELS[5].read(),
'yesterday_total': CHANNELS[6].read(),
'monthly_total': CHANNELS[7].read(),
'prevmonthly_total': CHANNELS[8].read(),
'current_year_total': CHANNELS[9].read(),
'prev_year_total': CHANNELS[10].read()
}
company_1 = {
'today_total': CHANNELS[11].read(),
'yesterday_total': CHANNELS[12].read(),
'monthly_total': CHANNELS[13].read(),
'prevmonthly_total': CHANNELS[14].read(),
'current_year_total': CHANNELS[15].read(),
'prev_year_total': CHANNELS[16].read()
}
company_2 = {
'today_total': CHANNELS[17].read(),
'yesterday_total': CHANNELS[18].read(),
'monthly_total': CHANNELS[19].read(),
'prevmonthly_total': CHANNELS[20].read(),
'current_year_total': CHANNELS[21].read(),
'prev_year_total': CHANNELS[22].read()
}
company_3 = {
'today_total': CHANNELS[23].read(),
'yesterday_total': CHANNELS[24].read(),
'monthly_total': CHANNELS[25].read(),
'prevmonthly_total': CHANNELS[26].read(),
'current_year_total': CHANNELS[27].read(),
'prev_year_total': CHANNELS[28].read()
}
company_4 = {
'today_total': CHANNELS[29].read(),
'yesterday_total': CHANNELS[30].read(),
'monthly_total': CHANNELS[31].read(),
'prevmonthly_total': CHANNELS[32].read(),
'current_year_total': CHANNELS[33].read(),
'prev_year_total': CHANNELS[34].read()
}
company_5 = {
'today_total': CHANNELS[35].read(),
'yesterday_total': CHANNELS[36].read(),
'monthly_total': CHANNELS[37].read(),
'prevmonthly_total': CHANNELS[38].read(),
'current_year_total': CHANNELS[39].read(),
'prev_year_total': CHANNELS[40].read()
}
log.info("Generating Spreadsheet")
generate_report(driver_id,trans_day,trans_hour,trans_min, trans_total, overall, company_1, company_2, company_3, company_4, company_5)
log.info("Sending Emails")
send_email(PERSIST['recipients'])
PERSIST['yesterday'] = dt.today().day
persistence.store(PERSIST)
# print("email_reports driver still alive...")
if self.force_send:
if send_loops > 2:
log.warning("Turning off force_send")
self.force_send = False
send_loops = 0
else:
send_loops += 1
if WATCHDOG_ENABLE:
if (now - self.watchdog_last_checked) > WATCHDOG_CHECK_PERIOD:
self._check_watchdog()
if (now - self.public_ip_address_last_checked) > IP_CHECK_PERIOD:
self._check_ip_address()
def _check_watchdog(self):
"""Check the watchdog and send to Meshify if changed or stale."""
test_watchdog = self.email_reports_watchdog()
now = time.time()
self.watchdog_last_checked = now
if test_watchdog != self.watchdog or (now - self.watchdog_last_sent) > WATCHDOG_SEND_PERIOD:
self.sendtodbDev(1, 'watchdog', test_watchdog, 0, 'email_reports')
self.watchdog = test_watchdog
self.watchdog_last_sent = now
def _check_ip_address(self):
"""Check the public IP address and send to Meshify if changed."""
self.public_ip_address_last_checked = time.time()
test_public_ip = get_public_ip_address()
if not test_public_ip == self.public_ip_address:
self.sendtodbDev(1, 'public_ip_address', test_public_ip, 0, 'email_reports')
self.public_ip_address = test_public_ip
def email_reports_watchdog(self):
"""Write a random integer to the PLC and then 1 seconds later check that it has been decremented by 1."""
randval = randint(0, 32767)
write_tag(str(PLC_IP_ADDRESS), 'watchdog_INT', randval, plc_type="CLX")
time.sleep(1)
watchdog_val = read_tag(str(PLC_IP_ADDRESS), 'watchdog_INT', plc_type="CLX")
try:
return (randval - 1) == watchdog_val[0]
except (KeyError, TypeError):
return False
def email_reports_sync(self, name, value):
"""Sync all data from the driver."""
self.force_send = True
# self.sendtodb("log", "synced", 0)
return True
def email_reports_writeplctag(self, name, value):
"""Write a value to the PLC."""
new_val = json.loads(str(value).replace("'", '"'))
tag_n = str(new_val['tag']) # "cmd_Start"
val_n = new_val['val']
write_res = write_tag(str(PLC_IP_ADDRESS), tag_n, val_n, plc_type="CLX")
print("Result of email_reports_writeplctag(self, {}, {}) = {}".format(name, value, write_res))
if write_res is None:
write_res = "Error writing to PLC..."
return write_res
def isReportTime(self):
right_now = dt.today()
if right_now.hour == 23 and right_now.minute == 0 and not(right_now.day == PERSIST['yesterday']):
if right_now.day == 1:
PERSIST['start'] = 0
PERSIST['end'] = 10000
persistence.store(PERSIST)
return True
return False

View File

@@ -0,0 +1,18 @@
"""Logging setup for email_reports"""
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 = './email_reports.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('email_reports')
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,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

192
email_reports/utilities.py Normal file
View File

@@ -0,0 +1,192 @@
"""Utility functions for the driver."""
import socket
import struct
import xlsxwriter
from datetime import datetime as dt
import email, smtplib, ssl
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def get_public_ip_address():
"""Find the public IP Address of the host device."""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect(("8.8.8.8", 80))
ip_address = sock.getsockname()[0]
sock.close()
return ip_address
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
def generate_report(driver_id,trans_day,trans_hour,trans_min, trans_total,overall, company_1,company_2, company_3, company_4, company_5):
workbook = xlsxwriter.Workbook('daily_report.xlsx')
worksheet = workbook.add_worksheet()
bold = workbook.add_format({'bold': True})
row = 1
col = 0
worksheet.write('A1', 'Driver', bold)
worksheet.write('B1', 'Day', bold)
worksheet.write('C1', 'Time',bold)
worksheet.write('D1', 'Total', bold)
for i in range(len(trans_day)):
worksheet.write(row,col, driver_id[i])
worksheet.write(row,col+1,str(round(trans_day[i])).rstrip('0').rstrip('.') )
worksheet.write(row,col+2, ""+ str(round(trans_hour[i])).rstrip('0').rstrip('.')+":"+str(round(trans_min[i])).rstrip('0').rstrip('.'))
worksheet.write(row,col+3, trans_total[i])
row += 1
worksheet.write(row, col+1, "Today's Total", bold)
worksheet.write(row, col+2, "Yesterday's Total", bold)
worksheet.write(row, col+3, "Month's Total", bold)
worksheet.write(row, col+4, "Previous Month's Total", bold)
worksheet.write(row, col+5, "Current Year's Total", bold)
worksheet.write(row, col+6, "Previous Year's Total", bold)
row += 1
worksheet.write(row, col, "Company 1", bold)
worksheet.write(row, col+1, company_1['today_total'])
worksheet.write(row, col+2, company_1['yesterday_total'])
worksheet.write(row, col+3, company_1['monthly_total'])
worksheet.write(row, col+4, company_1['prevmonthly_total'])
worksheet.write(row, col+5, company_1['current_year_total'])
worksheet.write(row, col+6, company_1['prev_year_total'])
row += 1
worksheet.write(row, col, "Company 2", bold)
worksheet.write(row, col+1, company_2['today_total'])
worksheet.write(row, col+2, company_2['yesterday_total'])
worksheet.write(row, col+3, company_2['monthly_total'])
worksheet.write(row, col+4, company_2['prevmonthly_total'])
worksheet.write(row, col+5, company_2['current_year_total'])
worksheet.write(row, col+6, company_2['prev_year_total'])
row += 1
worksheet.write(row, col, "Company 3", bold)
worksheet.write(row, col+1, company_3['today_total'])
worksheet.write(row, col+2, company_3['yesterday_total'])
worksheet.write(row, col+3, company_3['monthly_total'])
worksheet.write(row, col+4, company_3['prevmonthly_total'])
worksheet.write(row, col+5, company_3['current_year_total'])
worksheet.write(row, col+6, company_3['prev_year_total'])
row += 1
worksheet.write(row, col, "Company 4", bold)
worksheet.write(row, col+1, company_4['today_total'])
worksheet.write(row, col+2, company_4['yesterday_total'])
worksheet.write(row, col+3, company_4['monthly_total'])
worksheet.write(row, col+4, company_4['prevmonthly_total'])
worksheet.write(row, col+5, company_4['current_year_total'])
worksheet.write(row, col+6, company_4['prev_year_total'])
row += 1
worksheet.write(row, col, "Company 5", bold)
worksheet.write(row, col+1, company_5['today_total'])
worksheet.write(row, col+2, company_5['yesterday_total'])
worksheet.write(row, col+3, company_5['monthly_total'])
worksheet.write(row, col+4, company_5['prevmonthly_total'])
worksheet.write(row, col+5, company_1['current_year_total'])
worksheet.write(row, col+6, company_1['prev_year_total'])
row +=1
worksheet.write(row, col, "Grand Total", bold)
worksheet.write(row, col+1, overall['today_total'])
worksheet.write(row, col+2, overall['yesterday_total'])
worksheet.write(row, col+3, overall['monthly_total'])
worksheet.write(row, col+4, overall['prevmonthly_total'])
worksheet.write(row, col+5, overall['current_year_total'])
worksheet.write(row, col+6, overall['prev_year_total'])
workbook.close()
def send_email(recipients):
subject = "Daily Sales Report"
body = "This is an automate email for M&W sales report"
smtp_user = "AKIA4QSVRJTZ4GXTPTMX"
smtp_pass = "BCodXCh7VY126D3/C3lu5SCSmq6pjjF2uxMxZmd4nQFJ"
sender_email = "alerts@henry-pump.com"
receiver_email = ", ".join(recipients)
# Create a multipart message and set headers
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject
message["Body"] = body
filename = "daily_report.xlsx"
# Open XLSX file in binary mode
with open(filename, "rb") as attachment:
# Add file as application/octet-stream
# Email client can usually download this automatically as attachment
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
# Encode file in ASCII characters to send by email
encoders.encode_base64(part)
months = {
1: 'JAN',
2: 'FEB',
3: 'MAR',
4: 'APR',
5: 'MAY',
6: 'JUN',
7: 'JUL',
8: 'AUG',
9: 'SEP',
10: 'OCT',
11: 'NOV',
12: 'DEC'
}
# Add header as key/value pair to attachment part
part.add_header(
"Content-Disposition",
"attachment; filename= daily_report_{}_{}_{}.xlsx".format(dt.today().day, months.get(dt.today().month), dt.today().year),
)
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
# Log in to server using secure context and send email
server = smtplib.SMTP("email-smtp.us-east-1.amazonaws.com", 587)
server.ehlo()
server.starttls()
server.ehlo()
server.login(smtp_user,smtp_pass)
server.sendmail(sender_email, receiver_email.split(","), text)
server.quit()