import types import traceback import binascii import threading import time import thread import os import struct import sys import serial import minimalmodbus import minimalmodbusM1 import pickle import json import vpn import subprocess import socket import copy try: from xmodem import XMODEM except: pass import zlib from device_base import deviceBase try: from modbus import modbus_rtu except: pass from struct import * try: import modbus from modbus import defines as cst from modbus import modbus_tcp except: print "no tcp modbus" try: from pycomm.ab_comm.clx import Driver as ClxDriver from pycomm.ab_comm.slc import Driver as SlcDriver except: print "no Ethernet/IP support" try: import logger except: print "Missing logger" pass _NUMBER_OF_BYTES_PER_REGISTER = 2 analogMap = { "uval":"units_high", "lval":"units_low", "uvolts":"volts_high", "lvolts":"volts_low", "low":"alarm_low", "high":"alarm_high", "Not Yet":"action_high", "N/A":"action_low", "change":"change_amount", "minrep":"min_period", "grp":"grp", "mult":"multiplier", "int":"interval", "ch":"change_amount" } analogToChannelMap = { "units_high":"uval", "units_low":"lval", "volts_high":"uvolts", "volts_low":"lvolts", "alarm_low":"low", "alarm_high":"high", "change_amount":"change", "min_period":"minrep", "grp":"grp" } class start(threading.Thread, deviceBase): """\ This class extends one of our base classes and is intended as an example of a concrete, example implementation, but it is not itself meant to be included as part of our developer API. Please consult the base class documentation for the API and the source code for this file for an example implementation. """ # here are the setting defaults DEF_HEXDEC = True DEF_EOLN = None DEF_CHARTOUT = 2.0 # a sanity value to prevent an infinite rcv_buffer creation DEF_MAX_READ = 1000 def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, companyId=None, offset=None, mqtt=None, Nodes=None): 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.vpn = vpn.vpn() self.modbusInterfaces = {} self.lock = threading.Lock() self.analogLock = threading.Lock() self.flyFileLocation = "/root/python_firmware/drivers/output.py" self.lastData = "" self.fileLoading = False #self.mcu.digitalOut1(str(1)) #self.mcu.digitalOut2(str(1)) #self.mcu.digitalOut3(str(1)) #self.mcu.digitalOut4(str(1)) self.version = "37" self.sysReboot = False #check and see if we are going to update the mcu code if os.path.isfile('/root/python_firmware/drivers/gsmgps.py'): print "found new gsmgps file" os.system("/bin/mv -f /root/python_firmware/drivers/gsmgps.py /root/python_firmware/mcu/gsmgps.py") #try and delte the file just in case os.system("/bin/rm -f /root/python_firmware/drivers/gsmgps.py") os.system("/bin/rm -f /root/python_firmware/drivers/gsmgps.pyc") self.sysReboot = True if os.path.isfile('/root/python_firmware/drivers/mcu_main.py'): print "found new mcu_main file" os.system("/bin/mv -f /root/python_firmware/drivers/mcu_main.py /root/python_firmware/mcu/mcu_main.py") #try and delte the file just in case os.system("/bin/rm -f /root/python_firmware/drivers/mcu_main.py") os.system("/bin/rm -f /root/python_firmware/drivers/mcu_main.pyc") self.sysReboot = True if os.path.isfile('/root/python_firmware/drivers/device_base.py'): print "found new device_base file" os.system("/bin/mv -f /root/python_firmware/drivers/device_base.py /root/python_firmware/device_base.py") #try and delte the file just in case os.system("/bin/rm -f /root/python_firmware/drivers/device_base.py") os.system("/bin/rm -f /root/python_firmware/drivers/device_base.pyc") self.sysReboot = True if os.path.isfile('/root/python_firmware/drivers/vpn.py'): print "found new vpn file" os.system("/bin/mv -f /root/python_firmware/drivers/vpn.py /root/python_firmware/vpn.py") #try and delte the file just in case os.system("/bin/rm -f /root/python_firmware/drivers/vpn.py") os.system("/bin/rm -f /root/python_firmware/drivers/vpn.pyc") self.sysReboot = True if os.path.isfile('/root/python_firmware/drivers/gsmWatchdog.py'): print "found new gsmWatchdog file" os.system("/bin/mv -f /root/python_firmware/drivers/gsmWatchdog.py /root/gsmWatchdog.py") #try and delte the file just in case os.system("/bin/rm -f /root/python_firmware/drivers/gsmWatchdog.py") os.system("/bin/rm -f /root/python_firmware/drivers/gsmWatchdog.pyc") self.sysReboot = True if os.path.isfile('/root/python_firmware/drivers/deviceList.txt'): print "found new deviceList.txt file" os.system("/bin/mv -f /root/python_firmware/drivers/deviceList.txt /root/python_firmware/deviceList.txt") #try and delte the file just in case os.system("/bin/rm -f /root/python_firmware/drivers/deviceList.txt") self.sysReboot = True if os.path.isfile('/root/python_firmware/drivers/deviceUrls.p'): print "found new deviceUrls.p file" os.system("/bin/mv -f /root/python_firmware/drivers/deviceUrls.p /root/python_firmware/deviceUrls.p") #try and delte the file just in case os.system("/bin/rm -f /root/python_firmware/drivers/deviceUrls.p") self.sysReboot = True if os.path.isfile('/root/python_firmware/drivers/deviceVersions.p'): print "found new deviceVersions.p file" os.system("/bin/mv -f /root/python_firmware/drivers/deviceVersions.p /root/python_firmware/deviceVersions.p") #try and delte the file just in case os.system("/bin/rm -f /root/python_firmware/drivers/deviceVersions.p") self.sysReboot = True if os.path.isfile('/root/python_firmware/drivers/meshifyData.py'): print "found new meshifyData.py file" os.system("/bin/mv -f /root/python_firmware/drivers/meshifyData.py /root/python_firmware/meshifyData/meshifyData.py") #try and delte the file just in case os.system("/bin/rm -f /root/python_firmware/drivers/meshifyData.py") os.system("/bin/rm -f /root/python_firmware/drivers/meshifyData.pyc") self.sysReboot = True if os.path.isfile('/root/python_firmware/drivers/main.py'): print "found new main.py file" os.system("/bin/mv -f /root/python_firmware/drivers/main.py /root/python_firmware/main.py") #try and delte the file just in case os.system("/bin/rm -f /root/python_firmware/drivers/main.py") os.system("/bin/rm -f /root/python_firmware/drivers/main.pyc") self.sysReboot = True # TODO Stick non driver file updates here #modbus dictionary for the ComBox #multiplier can be multiply or divide or None #changeType = number or percent # self.com1Reads = 0 self.com2Reads = 0 self.com3Reads = 0 self.com4Reads = 0 self.com5Reads = 0 self.com1Errors = 0 self.com2Errors = 0 self.com3Errors = 0 self.com4Errors = 0 self.com5Errors = 0 self.pulses = None self.modbus_logger = logger.Logger(channel_name="log-modbus",message_sender=self.sendtodb,enabled=True) self.IOmap = { "1":"On", 1:"On", 0:"Off", "0":"Off" } #load stored DIO forward rules #load stored location spoofing data: try: with open('/root/python_firmware/drivers/dioForward.p', 'rb') as handle: self.dioForward = pickle.load(handle) print "found pickled dictionary" print self.dioForward except: print "couldn't load locSpoof from pickle" self.dioForward = { "cloop":{"on":"off","deviceName":"M1","channel":"cloop","unitNum":"1"}, "cloop2":{"on":"off","deviceName":"M1","channel":"cloop2","unitNum":"1"}, # only cloop2 or analog4 will exist "din1":{"on":"off","deviceName":"M1","channel":"din1","unitNum":"1"}, # on any given m1 "din2":{"on":"off","deviceName":"M1","channel":"din2","unitNum":"1"}, "din3":{"on":"off","deviceName":"M1","channel":"din3","unitNum":"1"}, "din4":{"on":"off","deviceName":"M1","channel":"din4","unitNum":"1"}, "analog1":{"on":"off","deviceName":"M1","channel":"analog1","unitNum":"1"}, "analog2":{"on":"off","deviceName":"M1","channel":"analog2","unitNum":"1"}, "analog3":{"on":"off","deviceName":"M1","channel":"analog3","unitNum":"1"}, "analog4":{"on":"off","deviceName":"M1","channel":"analog4","unitNum":"1"}, "dout1":{"on":"off","deviceName":"M1","channel":"dout1","unitNum":"1"}, "dout2":{"on":"off","deviceName":"M1","channel":"dout2","unitNum":"1"}, "dout3":{"on":"off","deviceName":"M1","channel":"dout3","unitNum":"1"}, "dout4":{"on":"off","deviceName":"M1","channel":"dout4","unitNum":"1"}, "relay1":{"on":"off","deviceName":"M1","channel":"relay1","unitNum":"1"}, "vin":{"on":"off","deviceName":"M1","channel":"vin","unitNum":"1"}, "bat":{"on":"off","deviceName":"M1","channel":"bat","unitNum":"1"}, "temp":{"on":"off","deviceName":"M1","channel":"temp","unitNum":"1"}, "pulse":{"on":"off","deviceName":"M1","channel":"pulse","unitNum":"1"}, "ver":{"on":"off","deviceName":"M1","channel":"ver","unitNum":"1"}, "modcom":{"on":"off","deviceName":"M1","channel":"ver","unitNum":"1"} } #load stored analog input data try: with open('/root/python_firmware/drivers/analogData.p', 'rb') as handle: self.analogData = pickle.load(handle) print "found analogData dictionary" print self.analogData except: print "couldn't load analogData from pickle" self.analogData = { "vin":{"units_high":30, "units_low":0, "volts_high":30, "volts_low":0, "alarm_low":0, "alarm_high":30, "action_high":None, "action_low":None, "change_amount":0.5, "min_period":600, "grp":(3600*6), "last_value":"", "last_send_time":0 }, "bat":{"units_high":30, "units_low":0, "volts_high":30, "volts_low":0, "alarm_low":0, "alarm_high":30, "action_high":None, "action_low":None, "change_amount":0, "min_period":3600, "grp":None, "last_value":"", "last_send_time":0 }, "analog1":{"units_high":30, "units_low":0, "volts_high":30, "volts_low":0, "alarm_low":0, "alarm_high":30, "action_high":None, "action_low":None, "change_amount":0, "min_period":30, "grp":None, "last_value":"", "last_send_time":0 }, "analog2":{"units_high":30, "units_low":0, "volts_high":30, "volts_low":0, "alarm_low":0, "alarm_high":30, "action_high":None, "action_low":None, "change_amount":0, "min_period":30, "grp":None, "last_value":"", "last_send_time":0 }, "analog3":{"units_high":30, "units_low":0, "volts_high":30, "volts_low":0, "alarm_low":0, "alarm_high":30, "action_high":None, "action_low":None, "change_amount":0, "min_period":30, "grp":None, "last_value":"", "last_send_time":0 }, "analog4":{"units_high":30, "units_low":0, "volts_high":30, "volts_low":0, "alarm_low":0, "alarm_high":30, "action_high":None, "action_low":None, "change_amount":0, "min_period":30, "grp":None, "last_value":"", "last_send_time":0 }, "cloop":{"units_high":20, "units_low":3, "volts_high":20, "volts_low":3, "alarm_low":0, "alarm_high":30, "action_high":None, "action_low":None, "change_amount":0, "min_period":30, "grp":None, "last_value":"", "last_send_time":0 }, "cloop2":{"units_high":20, "units_low":3, "volts_high":20, "volts_low":3, "alarm_low":0, "alarm_high":30, "action_high":None, "action_low":None, "change_amount":0, "min_period":30, "grp":None, "last_value":"", "last_send_time":0 }, "pulse":{"multiplier":1, "change_amount":20, "interval":1800, "last_value":"", "last_send_time":0 } } #load stored location spoofing data: try: with open('/root/python_firmware/drivers/locSpoof.p', 'rb') as handle: self.locSpoof = pickle.load(handle) print "found pickled dictionary" print self.locSpoof except: print "couldn't load locSpoof from pickle" self.locSpoof = { "on":"off","mode":"forward","mac":None} #load stored value for updating device address on boot try: with open('/root/python_firmware/drivers/adrsetonboot.p', 'rb') as handle: self.adrsetonboot = pickle.load(handle) print "found adrsetonboot" print self.adrsetonboot except: print "couldn't load adrsetonboot from pickle" self.adrsetonboot = False #load stored modbus mappings try: with open('/root/python_firmware/drivers/deviceaddress.p', 'rb') as handle: self.deviceaddress = pickle.load(handle) print "found pickled dictionary" print self.deviceaddress except: print "couldn't load deviceaddress from pickle" self.deviceaddress = 1 #load stored modbus mappings try: with open('/root/python_firmware/drivers/modbusMap.p', 'rb') as handle: self.modbusMap = pickle.load(handle) print "found pickled dictionary" print self.modbusMap except: print "couldn't load modbusMap from pickle" self.modbusMap = { } #load stored geo data try: with open('/root/python_firmware/drivers/geoData.p', 'rb') as handle: self.geoData = pickle.load(handle) print "found pickled Geo Data" print self.geoData except: print "couldn't load modbusMap from pickle" self.geoData = {} if self.adrsetonboot or self.adrsetonboot == "True" or self.adrsetonboot == True: self.changeDeviceAddress(int(self.deviceaddress)) print "updating addresses for modbus" self.register() self.finished = threading.Event() threading.Thread.start(self) self.last_xbee_sig_time = 0 #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): self.sync = True self.sendtodb("connected", "true", 0) #{'c':'none','b':'9600','p':'none','s':'1','f':'Off'} self.last_signal = 0 self.last_gps = "" self.last_gps_time = 0 self.last_power = "" self.last_pulses = -21 self.last_xbee_sig_time = 0 self.syncGeoTags() with self.lock: self.syncModbus() for comport in self.modbusMap: for address in self.modbusMap[comport]["addresses"]: for channelName in self.modbusMap[comport]["addresses"][address]: self.modbusMap[comport]["addresses"][address][channelName]["la"] = "" self.forwardingMap = { "1": { "port":"", "externalPort":"", "externalIP":"" }, "2": { "port":"", "externalPort":"", "externalIP":"" }, "3": { "port":"", "externalPort":"", "externalIP":"" }, "4": { "port":"", "externalPort":"", "externalIP":"" } } self.last_alarms = "" self.last_state = "" self.last_tct1 = "" self.last_tsp1 = "" self.last_tmd1 = "" self.last_tst1 = "" self.vpnCheckUp() #self.count = 3001 def stop (self): self.finished.set() self.join() def analogDataSync(self, all=True): with self.analogLock: for item in self.analogData: self.analogData[item]["last_value"] = "" if all: for channel in self.analogData[item]: name = None if item == "analog1" and channel in analogToChannelMap: name = "a1-" + analogToChannelMap[channel] elif item == "analog2" and channel in analogToChannelMap: name = "a2-" + analogToChannelMap[channel] elif item == "analog3" and channel in analogToChannelMap: name = "a3-" + analogToChannelMap[channel] elif item == "analog4" and channel in analogToChannelMap: name = "a4-" + analogToChannelMap[channel] elif item == "cloop" and channel in analogToChannelMap: name = "cl-" + analogToChannelMap[channel] elif item == "cloop2" and channel in analogToChannelMap: name = "cl2-" + analogToChannelMap[channel] if name is not None: self.sendtodb(name, str(self.analogData[item][channel]), 0) #TODO make it chatty def vpnCheckUp(self): #get ip address and subnet mask p = subprocess.Popen("/sbin/uci get network.lan.netmask", stdout=subprocess.PIPE, shell=True) (output, err) = p.communicate() mask = output.replace("\n","") self.sendtodb("getmask", mask, 0) p = subprocess.Popen("/sbin/uci get network.lan.ipaddr", stdout=subprocess.PIPE, shell=True) (output, err) = p.communicate() ip = output.replace("\n","") self.sendtodb("ip", ip, 0) try: for i in range(1,5): for channel in self.vpn.forwardingMap[str(i)]: if self.vpn.forwardingMap[str(i)][channel] != self.forwardingMap[str(i)][channel]: print "new value found in the vpn" if channel == "port": newchannel = ("port" + str(i)) elif channel == "externalPort": newchannel = ("eport" + str(i)) elif channel == "externalIP": newchannel = ("ip" + str(i)) self.sendtodb(str(newchannel), str(self.vpn.forwardingMap[str(i)][channel]), 0) self.forwardingMap[str(i)][channel] = self.vpn.forwardingMap[str(i)][channel] self.sendtodb("vpnip", str(self.vpn.get_ip_address("tun0")), 0) except Exception,e: print e def start(self): # you could add other ddo settings here thread.start_new_thread(self.loop, ()) # def stop(): uses XBeeSerial.stop() ## Locally defined functions: def readCountThread(self): item = "modcom" #here I will calculate the rate of reads and errors from the modbus/ethernet IP initValue = self.dioForward[item]["on"] + "," + self.dioForward[item]["deviceName"] + "," + self.dioForward[item]["unitNum"] + "," + self.dioForward[item]["channel"] self.sendtodb("fwd-modcom", initValue,0) last_newCom1Errors = -100 last_newCom2Errors = -100 last_newCom3Errors = -100 last_newCom4Errors = -100 last_newCom5Errors = -100 last_newCom1Reads = -100 last_newCom2Reads = -100 last_newCom3Reads = -100 last_newCom4Reads = -100 last_newCom5Reads = -100 lastModCom = "" while True: currentCom1Errors = self.com1Errors currentCom2Errors = self.com2Errors currentCom3Errors = self.com3Errors currentCom4Errors = self.com4Errors currentCom5Errors = self.com5Errors currentCom1Reads = self.com1Reads currentCom2Reads = self.com2Reads currentCom3Reads = self.com3Reads currentCom4Reads = self.com4Reads currentCom5Reads = self.com5Reads time.sleep(60) newCom1Errors = (self.com1Errors - currentCom1Errors) newCom2Errors = (self.com2Errors - currentCom2Errors) newCom3Errors = (self.com3Errors - currentCom3Errors) newCom4Errors = (self.com4Errors - currentCom4Errors) newCom5Errors = (self.com5Errors - currentCom5Errors) newCom1Reads = (self.com1Reads - currentCom1Reads) newCom2Reads = (self.com2Reads - currentCom2Reads) newCom3Reads = (self.com3Reads - currentCom3Reads) newCom4Reads = (self.com4Reads - currentCom4Reads) newCom5Reads = (self.com5Reads - currentCom5Reads) self.sendtodb("connected", "true", 0) if abs(last_newCom1Errors - newCom1Errors) > 1 or (newCom1Errors == 0 and last_newCom1Errors != 0): self.sendtodb("c1err", str(newCom1Errors), 0) last_newCom1Errors = newCom1Errors if abs(last_newCom2Errors - newCom2Errors) > 1 or (newCom2Errors == 0 and last_newCom2Errors != 0): self.sendtodb("c2err", str(newCom2Errors), 0) last_newCom2Errors = newCom2Errors if abs(last_newCom3Errors - newCom3Errors) > 1 or (newCom3Errors == 0 and last_newCom3Errors != 0): self.sendtodb("c3err", str(newCom3Errors), 0) last_newCom3Errors = newCom3Errors if abs(last_newCom4Errors - newCom4Errors) > 1 or (newCom4Errors == 0 and last_newCom4Errors != 0): self.sendtodb("c4err", str(newCom4Errors), 0) last_newCom4Errors = newCom4Errors if abs(last_newCom5Errors - newCom5Errors) > 1 or (newCom5Errors == 0 and last_newCom5Errors != 0): self.sendtodb("c5err", str(newCom5Errors), 0) last_newCom5Errors = newCom5Errors if abs(last_newCom1Reads - newCom1Reads) > 1 or (newCom1Reads == 0 and last_newCom1Reads != 0): self.sendtodb("c1rd", str(newCom1Reads), 0) last_newCom1Reads = newCom1Reads if abs(last_newCom2Reads - newCom2Reads) > 1 or (newCom2Reads == 0 and last_newCom2Reads != 0): self.sendtodb("c2rd", str(newCom2Reads), 0) last_newCom2Reads = newCom2Reads if abs(last_newCom3Reads - newCom3Reads) > 1 or (newCom3Reads == 0 and last_newCom3Reads != 0): self.sendtodb("c3rd", str(newCom3Reads), 0) last_newCom3Reads = newCom3Reads if abs(last_newCom4Reads - newCom4Reads) > 1 or (newCom4Reads == 0 and last_newCom4Reads != 0): self.sendtodb("c4rd", str(newCom4Reads), 0) last_newCom4Reads = newCom4Reads if abs(last_newCom5Reads - newCom5Reads) > 1 or (newCom5Reads == 0 and last_newCom5Reads != 0): self.sendtodb("c5rd", str(newCom5Reads), 0) last_newCom5Reads = newCom5Reads if self.dioForward[item]["on"].lower() == "on": if int(newCom1Errors) + int(newCom2Errors) + int(newCom3Errors) + int(newCom4Errors) + int(newCom5Errors) > 3: val = "Off" else: val = "On" if val != lastModCom: lastModCom = val ch = self.dioForward[item]["unitNum"] deviceName = self.dioForward[item]["deviceName"] chName = self.dioForward[item]["channel"] if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, chName, val, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, chName, val, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(item, val, 0) else: self.sendtodbDev(ch, chName, val, 0, deviceName) self.sendtodb(item, val, 0) def run(self): #check and see if you need to update all of the modbus device addresses #example of how to serve modbus rtu data #serial = self.mcu.rs485 #server = modbus_rtu.RtuServer(serial) #server.start() #slave_1 = server.add_slave(1) #slave_1.add_block('0', 3, 3000, 2) #slave_1.set_values("0", 100, [25,99]) self.last = {} #start ftp server os.system("/usr/sbin/pure-ftpd &") #start thread for checking GPIO ports thread.start_new_thread(self.dioThread, ()) #start data counting thread thread.start_new_thread(self.get_network_bytes, ()) #start thread for rs232 for air quaility guys #thread.start_new_thread(self.RS232Thread, ()) #on startup send the version number self.sendtodb("version", str(self.version), 0) self.sendtodb("log", "system start up", 0) #reboot the system to install the latest cpu files, this can cause an infinate reboot cycle if we get the cloud version wrong if self.sysReboot: #sleep a little just incase we get stuck in a reboot cycle, its always good to wait print "waiting for restart" time.sleep(30) os.system("/root/reboot") while self.modbusInterfaces == {}: with self.lock: for com in self.modbusMap: port = self.modbusMap[com]["c"] if port not in self.modbusInterfaces: if port == "M1-232": baud = self.modbusMap[com]["b"] if baud == "": baud = 9600 else: baud = int(baud) #set up rs232 for link to DeapSea connected = False #make sure the baud rate gets set while connected == False: connected = self.mcu.set232Baud(baud) time.sleep(1) serial2 = self.mcu.rs232 self.instrument2 = minimalmodbusM1.Instrument(1, serial2) self.instrument2.address = 1 self.modbusInterfaces[port] = self.instrument2 print "added M1 232 interface" elif port == "M1-485": baud = self.modbusMap[com]["b"] if baud == "": baud = 9600 else: baud = int(baud) #set up rs232 for link to DeapSea connected = False #make sure the baud rate gets set while connected == False: connected = self.mcu.set485Baud(baud) time.sleep(1) serial4 = self.mcu.rs485 self.instrument1 = minimalmodbusM1.Instrument(1, serial4) self.instrument1.address = 1 self.modbusInterfaces[port] = self.instrument1 print "added M1 485 interface" elif port == "M1-MESH": baud = self.modbusMap[com]["b"] if baud == "": baud = 9600 else: baud = int(baud) #set up rs232 for link to DeapSea connected = False #make sure the baud rate gets set serialX = self.mcu.xbee self.instrument1 = minimalmodbusM1.Instrument(1, serialX) self.instrument1.address = 1 self.modbusInterfaces[port] = self.instrument1 print "added M1 XBEE interface" elif port == "TCP": print "adding TCP interface for modbus" ip = self.modbusMap[com]["b"] p = self.modbusMap[com]["p"] self.tcpInstrument = modbus_tcp.TcpMaster(host=ip, port=int(p)) self.modbusInterfaces[port] = self.tcpInstrument print "done tcp" print port print self.modbusInterfaces[port] elif port == "ETHERNET/IP": print "adding Ethernet/IP interface for modbus?" ip = self.modbusMap[com]["b"] self.ethernetInstrument = ClxDriver() try: self.ethernetInstrument.open(ip) except: print "Cannot connect to Ethernet/IP (control Logix) device at: ", ip #p = self.modbusMap[com]["p"] #Since my ethernet IP lib will timeout crazy fast, I plan on just #reconnecting with each loop, so I won't try to make my connection string here self.modbusInterfaces[port] = self.ethernetInstrument print "done Ethernet/IP" print port print self.modbusInterfaces[port] elif port == "ETHERNET/IP-MICRO": print "adding Ethernet/IP interface for modbus?" ip = self.modbusMap[com]["b"] self.ethernetInstrument = SlcDriver() try: self.ethernetInstrument.open(ip) except: print "Cannot connect to Ethernet/IP (micrologix) device at: ", ip #p = self.modbusMap[com]["p"] #Since my ethernet IP lib will timeout crazy fast, I plan on just #reconnecting with each loop, so I won't try to make my connection string here self.modbusInterfaces[port] = self.ethernetInstrument print "done Ethernet/IP-MIRCO" print port print self.modbusInterfaces[port] else: connected = False tries = 0 while not connected: tries += 1 port = self.modbusMap[com]["c"] baud = self.modbusMap[com]["b"] if port == None or port == "none" or port == "None": connected = True continue if baud == "": baud = 9600 else: baud = int(baud) try: instrument = minimalmodbus.Instrument(port, 1) # port name, slave address (in decimal) instrument.serial.baudrate = baud instrument.serial.timeout = 1 # seconds instrument.address = 1 connected = True self.modbusInterfaces[port] = instrument print "added custom interface: " + port break except Exception,e: if tries > 10: connected = True break self.sendtodb("error", str(e), 0) time.sleep(20) time.sleep(30) time.sleep(10) self.count = 3001 hb_count = 0 thread.start_new_thread(self.readCountThread, ()) while not self.finished.isSet(): self.count += 1 time.sleep(5) try: print "################################" print "#############sending data ######" try: self.processModbus() except Exception,e: print(str(e)) self.sendtodb("error2", str(e), 0) except Exception,e: print(str(e)) # We only want the modbus logger to run for one loop, so now that loop is done, catch it here if self.modbus_logger.is_enabled(): self.modbus_logger.disable() self.sendtodb("modbusdebug", "Off", 0) # internal functions & classes def processModbus(self): # "mdl": {"length":16, "address":769, "type":"int", "last_val":"", "multiplier":None, "multiplierVal": "1"}, print "checking modbus" print self.modbusMap self.modbus_logger.log_message("(!) - Starting modbus loop - (!)") for com in self.modbusMap: for address in self.modbusMap[com]["addresses"]: #get the modbus instrument instrument = self.modbusInterfaces[self.modbusMap[com]["c"]] #tcp and ethernet/IP does the device address differently, this only applies to the modbus RTU if self.modbusMap[com]["c"] != "TCP" and self.modbusMap[com]["c"] != "ETHERNET/IP": try: instrument.address = int(address) except: continue ch_un = address self.modbus_logger.log_message("(!) - Reading device number %s - (!)" % str(address)) for chName in self.modbusMap[com]["addresses"][address]: with self.lock: # this while loop is to support modbus retries modbusRetries = 2 while True: #time.sleep(1) #"{'chn':'2-1-v','dn':'M1','da':'1','le':'16','a':'301','f':'3','t':'int','la':null,'m':'none','mv':'1','ct':'number','c':'1','vm':null,'r':'1-200','s':'On'}" #for a bytearray we will have a new argument called bytearray or bytearraybs for byte swapped. #"t":"bytearray","bytary":{1:{"chan":"alm1","val0":"Off","val1":"On"},2:{"chan":"alm2","val0":"Off","val1":"On"},3:{},4:{},5:{},6:{},7:{},8:{},9:{},10:{},11:{},12:{},13:{},14:{},15:{}} try: status = self.modbusMap[com]["addresses"][address][chName]['s'] #if the status is no on, raise an exception so that is indicated on the modbus log if status != "On": modbusRetries = 0 #no point in retrying in this case raise Exception("register status is not on") #see if it has the update for a unit number for each register, if not, use the device address try: unitNumber = int(str(self.modbusMap[com]["addresses"][address][chName]['un'])) ch = unitNumber except: ch = ch_un if int(ch) < 10: ch = "0" + str(ch) deviceName = str(self.modbusMap[com]["addresses"][address][chName]['dn']) functioncode = str(self.modbusMap[com]["addresses"][address][chName]['f']) length = int(str(self.modbusMap[com]["addresses"][address][chName]['le'])) regAddress = str(self.modbusMap[com]["addresses"][address][chName]['a']) type = str(self.modbusMap[com]["addresses"][address][chName]['t']) last_val = self.modbusMap[com]["addresses"][address][chName]['la'] multiplier = str(self.modbusMap[com]["addresses"][address][chName]['m']) try: if multiplier != "function": multiplierVal = float(self.modbusMap[com]["addresses"][address][chName]['mv']) else: multiplierVal = str(self.modbusMap[com]["addresses"][address][chName]['mv']) except: multiplierVal = 1 changeType = str(self.modbusMap[com]["addresses"][address][chName]['ct']) m1Channel = str(self.modbusMap[com]["addresses"][address][chName]['m1ch']) + "-v" channel = str(self.modbusMap[com]["addresses"][address][chName]['chn']) try: vn = self.modbusMap[com]["addresses"][address][chName]['vn'] except: raise ValueError("Could not read vanity name for register " + regAddress) vn = None self.modbus_logger.log_message(" (*) Reading %s on %s " % (vn, regAddress)) try: min_time = int(self.modbusMap[com]["addresses"][address][chName]['mrt']) last_time = int(self.modbusMap[com]["addresses"][address][chName]['lrt']) except: self.modbusMap[com]["addresses"][address][chName]['mrt'] = 60 self.modbusMap[com]["addresses"][address][chName]['lrt'] = 0 min_time = int(self.modbusMap[com]["addresses"][address][chName]['mrt']) last_time = int(self.modbusMap[com]["addresses"][address][chName]['lrt']) #see if there is a period in which you have to get a value, even if it hasn't changed #sudo code: if (current time - last send time ) > period (and period is not None) send up new point try: period = int(self.modbusMap[com]["addresses"][address][chName]['grp']) if period < 10: period = None self.modbus_logger.log_message(" (w) grp set to < 10, will only send on change") except: period = None self.modbus_logger.log_message(" (w) error testing grp, will only send on change") print "here is your period: ", period print time.time() - last_time #check to see if you need to send based on a period that you need a new value try: if period != None: if (time.time() - last_time) > period: send = True else: send = False else: send = False except: send = False self.modbus_logger.log_message(" (w) error testing period, will only send on change") print "here is your send value: ", send try: change = int(str(self.modbusMap[com]["addresses"][address][chName]['c'])) except: change = 0 print "warning: change set to 0" self.modbus_logger.log_message("warning: change set to 0") #load the value map valueMap = self.modbusMap[com]["addresses"][address][chName]['vm'] try: if len(valueMap.keys()) < 1: valueMap = None except: valueMap = None range1 = str(self.modbusMap[com]["addresses"][address][chName]['r']) try: if len(range1) < 3: range1 = None except: range1 = None if (range1 == "None" or range1 == "none"): range1 = None if multiplier == "None" or multiplier == "none": multiplier = None if last_val == None or last_val == "None" or last_val == "none": last_val = "" try: if range1 is not None: numOfDashes = range1.count('-') if numOfDashes == 1: #both positive high = range1.split("-")[1] low = range1.split("-")[0] elif numOfDashes == 2: #first number is negative high = range1.split("-")[2] low = "-" + range1.split("-")[1] elif numOfDashes == 3: #both numbers are negative high = "-" + range1.split("-")[3] low = "-" + range1.split("-")[1] except: range1 = None self.modbus_logger.log_message(" (w) Error parsing range, range set to none") #first check for coils: if type == "coil": if self.modbusMap[com]["c"] == "TCP": lst = instrument.execute(int(address), int(functioncode), int(regAddress), 1) val = lst[0] else: val = instrument.read_bit(int(regAddress), functioncode=int(functioncode)) #now check for byte arrary elif type == "bitarray" or type == "bitarraybs": print "found bytearray" arrayOfBits = self.modbusMap[com]["addresses"][address][chName]['bytary'] if type == "bitarraybs": bs = True else: bs = False #byte arrays are either 16 bit or 32. But mostly 16 will be supported. Thats up to if self.modbusMap[com]["c"] == "TCP": lst = instrument.execute(int(address), int(functioncode), int(regAddress), 1) val = lst[0] elif self.modbusMap[com]["c"] == "ETHERNET/IP": val = self.EthernetIPGET(instrument, com, regAddress) if val == None: raise ValueError('Ethernet IP read Failed') elif self.modbusMap[com]["c"] == "ETHERNET/IP-MICRO": val = self.EthernetIPGETMicro(instrument, com, regAddress) if val == None: raise ValueError('Ethernet IP MICRO read Failed') else: val = instrument.read_register(int(regAddress), functioncode=int(functioncode)) bits = self.byteArray(val, int(length), bs) print "here are the bits" print bits print arrayOfBits for number in range(int(length)): try: if (last_val[number] != bits[number]) or send == True: bitInfo = arrayOfBits[number + 1] if bits[number] == 1 or bits[number] == "1": val = bitInfo["val1"] elif bits[number] == 0 or bits[number] == "0": val = bitInfo["val0"] if bitInfo["chan"] == "": modbusRetries = 0 #no point in retrying in this case raise Exception("bitInfo[\"chan\"] == \"\"") #move on to next register self.sendtodbDev(ch, bitInfo["chan"], val, 0, deviceName) except Exception, e: print "bit error" print e bitInfo = arrayOfBits[number + 1] if bits[number] == 1 or bits[number] == "1": val = bitInfo["val1"] elif bits[number] == 0 or bits[number] == "0": val = bitInfo["val0"] if bitInfo["chan"] == "": modbusRetries = 0 #no point in retrying in this case raise Exception("bitInfo[\"chan\"] == \"\"") #move on to next register self.sendtodbDev(ch, bitInfo["chan"], val, 0, deviceName) if (last_val != bits) or send == True: self.sendtodb(m1Channel, bits, 0) self.modbusMap[com]["addresses"][address][chName]['la'] = bits self.modbusMap[com]["addresses"][address][chName]['lrt'] = time.time() break #break on success elif int(length) == 16 and (type == "int" or type == "ints"): if self.modbusMap[com]["c"] == "TCP": lst = instrument.execute(int(address), int(functioncode), int(regAddress), 1) val = lst[0] if type == "ints": unsigned = val val = unsigned - 65535 if unsigned > 32767 else unsigned elif self.modbusMap[com]["c"] == "ETHERNET/IP": val = self.EthernetIPGET(instrument, com, regAddress) if val == None: raise ValueError('Ethernet IP read Failed') elif self.modbusMap[com]["c"] == "ETHERNET/IP-MICRO": val = self.EthernetIPGETMicro(instrument, com, regAddress) if val == None: raise ValueError('Ethernet IP micro read Failed') else: #must be normal read print "getting int" try: val = instrument.read_register(int(regAddress), functioncode=int(functioncode)) print "success reading" except: print "error reading modbus value" raise Exception("error reading modbus value") #raise exception and retry print val if type == "ints": unsigned = val val = unsigned - 65535 if unsigned > 32767 else unsigned elif int(length) == 32 and (type == "longbs" or type == "intbs" or type == "intsbs"): #must be read long or read float if type == "intbs" or type == "intsbs": if self.modbusMap[com]["c"] == "TCP": lst = instrument.execute(int(address), int(functioncode), int(regAddress), 2) val = int(hex(lst[0]).replace('0x','') + hex(lst[1]).replace('0x',''),16) if type == "intsbs": unsigned = val val = unsigned - 4294967295 if unsigned > 2147483647 else unsigned elif int(length) == 32 and (type == "long" or type == "int" or type == "ints"): #must be read long or read float if type == "int" or type == "ints": if self.modbusMap[com]["c"] == "TCP": lst = instrument.execute(int(address), int(functioncode), int(regAddress), 2) val = int(hex(lst[1]).replace('0x','') + hex(lst[0]).replace('0x',''),16) if type == "ints": unsigned = val val = unsigned - 4294967295 if unsigned > 2147483647 else unsigned elif self.modbusMap[com]["c"] == "ETHERNET/IP": val = self.EthernetIPGET(instrument, com, regAddress) if val == None: raise ValueError('Ethernet IP read Failed') elif self.modbusMap[com]["c"] == "ETHERNET/IP-MICRO": val = self.EthernetIPGETMicro(instrument, com, regAddress) if val == None: raise ValueError('Ethernet IP micro read Failed') else: val = instrument.read_long(int(regAddress), functioncode=int(functioncode)) if type == "ints": val = self.int32(int(val)) elif type == "float" and int(length) == 32: if self.modbusMap[com]["c"] == "TCP": lst = instrument.execute(int(address), int(functioncode), int(regAddress), 2) val = round(unpack('f',pack('>HH',lst[0], lst[1],))[0], 3) elif self.modbusMap[com]["c"] == "ETHERNET/IP": val = self.EthernetIPGET(instrument, com, regAddress) if val == None: raise ValueError('Ethernet IP read Failed') elif self.modbusMap[com]["c"] == "ETHERNET/IP-MICRO": val = self.EthernetIPGETMicro(instrument, com, regAddress) if val == None: raise ValueError('Ethernet IP micro read Failed') else: val = instrument.read_float(int(regAddress), functioncode=int(functioncode)) val = round(val, 3) print val elif type == "floatbs" and int(length) == 32: if self.modbusMap[com]["c"] == "TCP": lst = instrument.execute(int(address), int(functioncode), int(regAddress), 2) val = round(unpack('f',pack('>HH',lst[1], lst[0],))[0], 3) elif self.modbusMap[com]["c"] == "ETHERNET/IP": val = self.EthernetIPGET(instrument, com, regAddress) if val == None: raise ValueError('Ethernet IP read Failed') elif self.modbusMap[com]["c"] == "ETHERNET/IP-MICRO": val = self.EthernetIPGETMicro(instrument, com, regAddress) if val == None: raise ValueError('Ethernet IP micro read Failed') else: t = instrument.read_registers(int(regAddress), 2, functioncode=int(functioncode)) val = round(self.byteSwap32(t), 2) print val elif type == "floatbsw" and int(length) == 32: t = instrument.read_registers(int(regAddress), 2, functioncode=int(functioncode)) val = round(self.byteSwap32(t) ,2) #now write this to registers to address 0 and 1. Because IWI can't figure out how to use 32 bit floats, we are #breaking it down into 2 integers. 0 = 16 bit integer left of the decimal place # 1 = 16 bit integer right of the decimal place try: tm = self.modbusInterfaces[self.modbusMap["1"]["c"]] reg0 = int(str(val).split(".")[0]) reg1 = int(str(val).split(".")[1]) print reg0, reg1 time.sleep(.5) self.EthernetIPSETMicro(tm, 1, "N7:10", reg0) #self.instrument2.write_register(0, reg0) time.sleep(.5) #self.instrument2.write_register(1, reg1) self.EthernetIPSETMicro(tm, 1, "N7:11", reg1) time.sleep(.5) #self.instrument2.write_register(4, 1) print "success setting h2s data to plc" except: print "failed setting registers for IWI" raise Exception("failed setting registers for IWI") print val else: modbusRetries = 0 #no point in retrying, unknown type print "register type does not match any known type" raise Exception("register type does not match any known type") #check if there is a multiplier if multiplier != None: if multiplier == "divide": val = round(float((float(val) / float(multiplierVal))), 2) elif multiplier == "multiply": val = round(float((float(val) * float(multiplierVal))), 2) elif multiplier == "function": x = float(val) print str(multiplierVal) try: val = eval(str(multiplierVal).strip().replace("&", "'")) print "eval value: " print val try: val = float(val) except: type = "str" except Exception, e: print e raise Exception(str(e)) #check and see if we have a range, if its outside the range1 then just ignore the data and continue if range1 != None and type != "coil" and type != "str": #validate the value is in the range try: if float(val) > float(high) or float(val) < float(low): print "validation failed, skipping value" print str(range1) print str(low) + " - " + str(high) raise Exception(" (X) Read invalid value, outside of specified range. -" + str(val)) except Exception, e: print "exception: validation error, skipping value" raise Exception(" (X) Error testing range, skipping register - " + str(e)) #if we get to this point the read must have worked, so we can count up the succussful read counts if int(com) == 1: self.com1Reads +=1 elif int(com) == 2: self.com2Reads += 1 elif int(com) == 3: self.com3Reads += 1 elif int(com) == 4: self.com4Reads += 1 elif int(com) == 5: self.com5Reads += 1 #check if the data has changed if changeType == "number": #compare last value with a change amount to see if we have enough change to send it #first see if you have an empty string as the last value, if so its the first time and we don't need to compare, but just send the value if str(last_val) == "" or last_val == None or str(last_val) == "None" or str(last_val) == "none": #may introduce bug if intentionally pass "None" as value if val != last_val or send == True: if valueMap != None: try: print "found Value map" print valueMap updateVal = valueMap[str(int(val))] print "new Value", val except: self.modbus_logger.log_message(" (w) error applying value map") updateVal = val if deviceName != "M1": if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, channel, updateVal, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, channel, updateVal, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(m1Channel, updateVal, 0) else: self.sendtodbDev(ch, channel, updateVal, 0, deviceName) self.sendtodb(m1Channel, updateVal, 0) else: self.sendtodb(m1Channel, updateVal, 0) else: if deviceName != "M1": if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, channel, val, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, channel, val, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(m1Channel, val, 0) else: self.sendtodbDev(ch, channel, val, 0, deviceName) self.sendtodb(m1Channel, val, 0) else: self.sendtodb(m1Channel, val, 0) self.modbusMap[com]["addresses"][address][chName]['la'] = val self.modbusMap[com]["addresses"][address][chName]['lrt'] = time.time() break #replaced continue with break to preserve original flow (cause added loop) elif type == "str": if (val != last_val and abs(time.time() - last_time) > min_time) or send == True: if valueMap != None: if deviceName != "M1": if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, channel, valueMap[str(val)], 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, channel, valueMap[str(val)], 0, deviceName, self.locSpoof["mac"]) self.sendtodb(m1Channel, valueMap[str(val)], 0) else: self.sendtodbDev(ch, channel, valueMap[str(val)], 0, deviceName) else: self.sendtodb(m1Channel, valueMap[str(val)], 0) else: if deviceName != "M1": if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, channel, val, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, channel, val, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(m1Channel, val, 0) else: self.sendtodbDev(ch, channel, val, 0, deviceName) self.sendtodb(m1Channel, val, 0) else: self.sendtodb(m1Channel, val, 0) self.modbusMap[com]["addresses"][address][chName]['la'] = val self.modbusMap[com]["addresses"][address][chName]['lrt'] = time.time() break #replaced continue with break to preserve original flow (cause added loop) elif type == "coil" : if (val != last_val and abs(time.time() - last_time) > min_time) or send == True: if valueMap != None: if deviceName != "M1": if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, channel, valueMap[str(val)], 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, channel, valueMap[str(val)], 0, deviceName, self.locSpoof["mac"]) self.sendtodb(m1Channel, valueMap[str(val)], 0) else: self.sendtodbDev(ch, channel, valueMap[str(val)], 0, deviceName) self.sendtodb(m1Channel, val, 0) else: self.sendtodb(m1Channel, valueMap[str(val)], 0) else: if deviceName != "M1": if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, channel, val, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, channel, val, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(m1Channel, val, 0) else: self.sendtodbDev(ch, channel, val, 0, deviceName) self.sendtodb(m1Channel, val, 0) else: self.sendtodb(m1Channel, val, 0) self.modbusMap[com]["addresses"][address][chName]['la'] = val self.modbusMap[com]["addresses"][address][chName]['lrt'] = time.time() break #replaced continue with break to preserve original flow (cause added loop) elif type == "float" or type == "int" or type == "floatbs" or type == "floatbsw" or type == "ints": print val if (abs(float(last_val) - float(val)) > change and abs(time.time() - last_time) > min_time) or send == True: if valueMap != None: try: print "found Value map" print valueMap updateVal = valueMap[str(int(val))] print "new Value", val except: updateVal = val if deviceName != "M1": if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, channel, updateVal, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, channel, updateVal, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(m1Channel, updateVal, 0) else: self.sendtodb(m1Channel, val, 0) self.sendtodbDev(ch, channel, updateVal, 0, deviceName) else: self.sendtodb(m1Channel, updateVal, 0) else: if deviceName != "M1": if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, channel, val, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, channel, val, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(m1Channel, val, 0) else: self.sendtodbDev(ch, channel, val, 0, deviceName) self.sendtodb(m1Channel, val, 0) else: self.sendtodb(m1Channel, val, 0) self.modbusMap[com]["addresses"][address][chName]['la'] = val self.modbusMap[com]["addresses"][address][chName]['lrt'] = time.time() break #this level of indent may be important #break from the while True loop on success except Exception,e: modbusRetries = modbusRetries - 1 if(modbusRetries > 0): print "had an error on the modbus loop, retrying read" self.modbus_logger.log_message("Error in modbus loop, retrying") continue #retry processing the register else: print "Error reading register, retries exhausted" print(str(e)) self.modbus_logger.log_message("(X) Retries Exhausted: " + str(e)) #count up on the error if int(com) == 1: self.com1Errors +=1 elif int(com) == 2: self.com2Errors += 1 elif int(com) == 3: self.com3Errors += 1 elif int(com) == 4: self.com4Errors += 1 elif int(com) == 5: self.com5Errors += 1 break #break from the while true loop on failure #end while True def EthernetIPSETMicro(self, instrument, com, regAddress, val): try: success = instrument.write_tag(regAddress, val) except: self.ethernetInstrument = SlcDriver() if self.ethernetInstrument.open(self.modbusMap[com]["b"]): success = self.ethernetInstrument.write_tag(regAddress, val) else: return False return success def EthernetIPGETMicro(self, instrument, com, regAddress): try: val = round(float(instrument.read_tag(regAddress)), 2) except: self.ethernetInstrument = SlcDriver() if self.ethernetInstrument.open(self.modbusMap[com]["b"]): val = round(float(self.ethernetInstrument.read_tag(regAddress)), 2) else: return None return val def EthernetIPGET(self, instrument, com, regAddress): try: val = instrument.read_tag([regAddress])[0][1] except: self.ethernetInstrument = ClxDriver() if self.ethernetInstrument.open(self.modbusMap[com]["b"]): val = self.ethernetInstrument.read_tag([regAddress])[0][1] print val else: return None return val def EthernetIPSET(self, instrument, com, val): try: success = instrument.write_tag(val) except Exception, e: print e self.ethernetInstrument = ClxDriver() if self.ethernetInstrument.open(self.modbusMap[com]["b"]): success = self.ethernetInstrument.write_tag(val) else: return False return success def M1_sync(self, name, value): self.register() self.count = 3001 return True def syncModbus(self): #{'c':'none','b':'9600','p':'none','s':'1','f':'Off'} for comport in self.modbusMap: data = """{"c":"%s", "b":"%s", "p":"%s", "s":"%s", "f":"%s"}""" % (self.modbusMap[comport]["c"],self.modbusMap[comport]["b"],self.modbusMap[comport]["p"],self.modbusMap[comport]["s"],self.modbusMap[comport]["f"] ) name = str(int(comport) * 2) + "s" self.sendtodbJSON(name, data, 0) for address in self.modbusMap[comport]["addresses"]: for channelName in self.modbusMap[comport]["addresses"][address]: name = self.modbusMap[comport]["addresses"][address][channelName]["m1ch"] self.sendtodbJSON(name, json.dumps(self.modbusMap[comport]["addresses"][address][channelName]), 0) try: vn = self.modbusMap[comport]["addresses"][address][channelName]["vn"] self.sendtodb(name+"-vn", vn, 0) hi = self.modbusMap[comport]["addresses"][address][channelName]["ah"] self.sendtodb(name+"-hi", hi, 0) low = self.modbusMap[comport]["addresses"][address][channelName]["al"] self.sendtodb(name+"-low", low, 0) # TODO make it chatty except Exception, e: print "error finding vanity name" print e def handleModbusEntry(self, name, value): #odd naming convention needs to be kept up with #check if there is an integer in on the left side of "-" for validation try: test = int(name.split("-")[0]) com = str(test/2) except: return False newVal = value.replace("'", '"') newJson = json.loads(newVal) #{'s': 'Off'} #check here to see if this is a deletion if "s" in newJson and not "a" in newJson and newJson["s"] == "Off": print "we have a delete request for the channel: " + name try: for comport in self.modbusMap: for address in self.modbusMap[comport]["addresses"]: for channelName in self.modbusMap[comport]["addresses"][address]: if self.modbusMap[comport]["addresses"][address][channelName]["m1ch"] == name: del self.modbusMap[comport]["addresses"][address][channelName] #now see if there are any other channels at this address, if not delete the address also if len(self.modbusMap[comport]["addresses"][address]) < 1: del self.modbusMap[comport]["addresses"][address] self.sendtodb(name, "", 0) return True except: self.sendtodb(name, "", 0) return True #we need to see if this is changing from one address to another, if so delete the old one #if there usage of this channel, delete it, actually just always deleting it could work, #then if there isn't any other regersters at that address, delete that entire address try: for comport in self.modbusMap: for address in self.modbusMap[comport]["addresses"]: for channelName in self.modbusMap[comport]["addresses"][address]: if self.modbusMap[comport]["addresses"][address][channelName]["m1ch"] == name: del self.modbusMap[comport]["addresses"][address][channelName] #now see if there are any other channels at this address, if not delete the address also if len(self.modbusMap[comport]["addresses"][address]) < 1: del self.modbusMap[comport]["addresses"][address] #self.sendtodb(name, "", 0) except Exception, e: try: for comport in self.modbusMap: for address in self.modbusMap[comport]["addresses"]: for channelName in self.modbusMap[comport]["addresses"][address]: if self.modbusMap[comport]["addresses"][address][channelName]["m1ch"] == name: del self.modbusMap[comport]["addresses"][address][channelName] #now see if there are any other channels at this address, if not delete the address also if len(self.modbusMap[comport]["addresses"][address]) < 1: del self.modbusMap[comport]["addresses"][address] #self.sendtodb(name, "", 0) except Exception, e: print "deletion error" print e channelName = name devAddress = newJson["da"] newJson["m1ch"] = name newEntry = {channelName : newJson} #first check and see if the address already exists if devAddress in self.modbusMap[com]["addresses"]: #if it is here, then just add a new item to the dictionary self.modbusMap[com]["addresses"][devAddress][channelName] = newJson pass else: #make a new address entry for this com port self.modbusMap[com]["addresses"][devAddress] = newEntry self.sendtodbJSON(name, json.dumps(newJson), 0) return True def handleComSetup(self, name, value): interfaces = ["2s","4s","6s","8s","10s","12s","14s","16s","18s","20s"] if name in interfaces: try: com = str(int(name.replace("s","")) / 2) except Exception,e: print "error with modbus handleComSetup" print e return False #this is the com settings for 232 #looks like this:{'c':'M1-232','b':'9600','p':'none','s':'1','f':'Off'} #c = com port, b = baud, p = parity, s = stop bit, f = flow control print value #{'c':'none','b':'9600','p':'none','s':'1','f':'Off'} newVal = value.replace("'", '"') newJson = json.loads(newVal) print newJson port = newJson["c"] baud = newJson["b"] parity = newJson["p"] stopbit = newJson["s"] flow = newJson["f"] print "I'm at location 12" #update the interface: connected = False tries = 0 #first check and see if the com port has been set up before if com in self.modbusMap: print "this com port is already here" #if the comport is already here, just change its settings and be on your way! self.modbusMap[com]["c"] = port self.modbusMap[com]["b"] = baud self.modbusMap[com]["p"] = parity self.modbusMap[com]["s"] = stopbit self.modbusMap[com]["f"] = flow else: #if its new we need to add it, but we need to make sure this is done before we try and add devices to it. self.modbusMap[com] = { "c": port, "b": baud, "p":parity, "s":stopbit, "f":flow, "addresses": {} } if port == "M1-232": baud = self.modbusMap[com]["b"] if baud == "": baud = 9600 else: baud = int(baud) #set up rs232 for link to DeapSea connected = False #make sure the baud rate gets set while connected == False: connected = self.mcu.set232Baud(baud) time.sleep(1) serial2 = self.mcu.rs232 self.instrument2 = minimalmodbusM1.Instrument(1, serial2) self.instrument2.address = 1 self.modbusInterfaces[port] = self.instrument2 print "added M1 232 interface" elif port == "M1-485": baud = self.modbusMap[com]["b"] if baud == "": baud = 9600 else: baud = int(baud) #set up rs232 for link to DeapSea connected = False #make sure the baud rate gets set while connected == False: connected = self.mcu.set485Baud(baud) time.sleep(1) serial4 = self.mcu.rs485 self.instrument1 = minimalmodbusM1.Instrument(1, serial4) self.instrument1.address = 1 self.modbusInterfaces[port] = self.instrument1 print "added M1 485 interface" elif port == "M1-MESH": baud = self.modbusMap[com]["b"] if baud == "": baud = 9600 else: baud = int(baud) #set up rs232 for link to DeapSea connected = False #make sure the baud rate gets set serialX = self.mcu.xbee self.instrument1 = minimalmodbusM1.Instrument(1, serialX) self.instrument1.address = 1 self.modbusInterfaces[port] = self.instrument1 print "added M1 XBEE interface" elif port == "TCP": ip = self.modbusMap[com]["b"] p = self.modbusMap[com]["p"] self.tcpInstrument = modbus_tcp.TcpMaster(host=ip, port=int(p)) self.modbusInterfaces[port] = self.tcpInstrument elif port == "ETHERNET/IP": print "adding Ethernet/IP interface for modbus?" ip = self.modbusMap[com]["b"] #p = self.modbusMap[com]["p"] #Since my ethernet IP lib will timeout crazy fast, I plan on just #reconnecting with each loop, so I won't try to make my connection string here self.modbusInterfaces[port] = ip print "done Ethernet/IP" elif port == "ETHERNET/IP-MICRO": print "adding Ethernet/IP interface for modbus?" ip = self.modbusMap[com]["b"] self.ethernetInstrument = SlcDriver() self.ethernetInstrument.open(ip) #p = self.modbusMap[com]["p"] #Since my ethernet IP lib will timeout crazy fast, I plan on just #reconnecting with each loop, so I won't try to make my connection string here self.modbusInterfaces[port] = self.ethernetInstrument print "done Ethernet/IP-MIRCO" print port print self.modbusInterfaces[port] else: connected = False tries = 0 while not connected: tries += 1 port = self.modbusMap[com]["c"] baud = self.modbusMap[com]["b"] if port == None or port == "none" or port == "None": connected = True continue if baud == "": baud = 9600 else: baud = int(baud) try: instrument = minimalmodbus.Instrument(port, 1) # port name, slave address (in decimal) instrument.serial.baudrate = baud instrument.serial.timeout = 1 # seconds instrument.address = 1 connected = True self.modbusInterfaces[port] = instrument print "added custom interface: " + port break except Exception,e: if tries > 10: connected = True break self.sendtodb("error", str(e), 0) print "new com port setup" print "I'm at location 14" print self.modbusMap self.sendtodbJSON(name, json.dumps(newJson), 0) """self.modbusMap = { "M1-485": { "port": "/dev/ttySP4", "baud": "", "parity":"", "stopbits":"", "flowcontrol":"", "addresses": { "1": {"p4": {"reg":"299","length":32, "address":1, "functioncode":3, "type":"int", "last_val":"", "multiplier":None, "multiplierVal": 1, "changeType":"number", "change":0, "valueMap":None, "range":None}} } } } """ def dioThread(self): #get modem id on boot try: mdn = self.mcu.get_modem_id() if mdn is not None: self.sendtodb("mdn", mdn, 0) else: mdn = self.mcu.get_modem_id() if mdn is not None: self.sendtodb("mdn", mdn, 0) time.sleep(1) #sim = self.mcu.get_sim_id() #if sim is not None: # self.sendtodb("sim", sim, 0) #else: # sim = self.mcu.get_sim_id() # if sim is not None: # self.sendtodb("sim", sim, 0) except: pass #items to check for change: changeDict = { "analog1":2, "analog2":2, "analog3":2, "analog4":2, "vin":10, "bat":1, "temp":3, "cloop":0.1 } while True: try: with self.analogLock: self.currentDict = self.mcu.getDict() if self.last_power != "": #check the power state: if float(self.currentDict["vin"]) > 7 and self.last_power == False: print "power coming on, send True" self.last_power = True self.sendtodb("power", "on", 0) elif float(self.currentDict["vin"]) < 7 and self.last_power == True: self.last_power = False self.sendtodb("power", "off", 0) else: #check the initial state of the power if float(self.currentDict["vin"]) > 7: print "power coming on, send True" self.last_power = True self.sendtodb("power", "on", 0) else: self.last_power = False self.sendtodb("power", "off", 0) for item in self.currentDict: try: if self.sync == False: #"analog2":{"units_high":30, #"units_low":0, #"volts_high":30, #"volts_low":0, #"alarm_low":0, #"alarm_high":30, #"action_high":None, #"action_low":None, #"change_amount":0, #"min_period":30, #"grp":None, #"last_value":"", #"last_send_time":0 #}, # ((high units - low units) / (high volts - low volts) * (current reading - low volts)) + low units if item == "pulse": if "pulse" not in self.analogData: self.analogData[item] = {"multiplier":1, "change_amount":20, "interval":1800, "last_value":"", "last_send_time":0 } #last_val = self.analogData[item]["last_value"] change = float(self.analogData[item]["change_amount"]) last_time = float(self.analogData[item]["last_send_time"]) interval = int(self.analogData[item]["interval"]) multiplier = float(self.analogData[item]["multiplier"]) #on trigger event send total pulses, and pulses per hour with units #things to track: interval to check pulses, change amount, multiplier if abs(int(self.last_pulses) - int(self.currentDict[item])) > int(change) or time.time() - int(last_time) > interval: self.analogData[item]["last_send_time"] = time.time() #send up rate per hour perHour = ( 60 / ((time.time() - int(last_time))/60)) * abs(int(self.last_pulses) - int(self.currentDict[item])) self.sendtodb("pph", str(round(perHour * multiplier, 2)), 0) self.sendtodb("ppm", str(round((perHour/60) * multiplier, 2)), 0) self.process_pulses(int(self.currentDict[item]), multiplier) self.last_pulses = int(self.currentDict[item]) continue if item in self.analogData: #get its value, its last value, its required change amount, and its last time it sent val = float(self.currentDict[item]) units_high = float(self.analogData[item]["units_high"]) units_low = float(self.analogData[item]["units_low"]) volts_high = float(self.analogData[item]["volts_high"]) volts_low = float(self.analogData[item]["volts_low"]) val = round(((units_high - units_low) / (volts_high - volts_low) * (val - volts_low)) + units_low, 2) last_val = self.analogData[item]["last_value"] change = float(self.analogData[item]["change_amount"]) last_time = float(self.analogData[item]["last_send_time"]) min_time = float(self.analogData[item]["min_period"]) gauranteed_report_period = self.analogData[item]["grp"] send = False if gauranteed_report_period != None: if gauranteed_report_period < 5: gauranteed_report_period = None if last_val == "": last_val = 0 send = True elif gauranteed_report_period != None: #if the gauranteed period is met, send = True. Or if its the first run (we know its the first run by the last_val = "" if time.time() - last_time > gauranteed_report_period: send = True else: send = False #check to see if you have reached a meaningful change amount and time or a guaranteed report period if (abs(float(last_val) - float(val)) > change and abs(time.time() - last_time) > min_time) or send == True: self.analogData[item]["last_value"] = val self.analogData[item]["last_send_time"] = time.time() val = str(val) if self.dioForward[item]["on"].lower() == "on": ch = self.dioForward[item]["unitNum"] deviceName = self.dioForward[item]["deviceName"] chName = self.dioForward[item]["channel"] if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, chName, val, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, chName, val, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(item, val, 0) else: self.sendtodbDev(ch, chName, val, 0, deviceName) self.sendtodb(item, val, 0) else: self.sendtodb(item, val, 0) #for binary data, check if its changed, that should always be enough, maybe we can get a guarateed report period later for I/O's elif self.currentDict[item] != self.last[item]: if item in changeDict: if abs(float(self.currentDict[item]) - float(self.last[item])) > changeDict[item]: if self.dioForward[item]["on"].lower() == "on": ch = self.dioForward[item]["unitNum"] deviceName = self.dioForward[item]["deviceName"] chName = self.dioForward[item]["channel"] if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, chName, self.currentDict[item], 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, chName, self.currentDict[item], 0, deviceName, self.locSpoof["mac"]) self.sendtodb(item, self.currentDict[item], 0) else: self.sendtodbDev(ch, chName, self.currentDict[item], 0, deviceName) self.sendtodb(item, self.currentDict[item], 0) else: self.sendtodb(item, self.currentDict[item], 0) else: try: print "new value for: " + item if self.dioForward[item]["on"].lower() == "on": ch = self.dioForward[item]["unitNum"] deviceName = self.dioForward[item]["deviceName"] chName = self.dioForward[item]["channel"] if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, chName, self.currentDict[item], 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, chName, self.currentDict[item], 0, deviceName, self.locSpoof["mac"]) self.sendtodb(item, self.currentDict[item], 0) else: self.sendtodbDev(ch, chName, self.currentDict[item], 0, deviceName) self.sendtodb(item, self.currentDict[item], 0) else: self.sendtodb(item, self.currentDict[item], 0) except: pass else: try: if item == "pulse": continue if item in self.analogData: val = float(self.currentDict[item]) units_high = float(self.analogData[item]["units_high"]) units_low = float(self.analogData[item]["units_low"]) volts_high = float(self.analogData[item]["volts_high"]) volts_low = float(self.analogData[item]["volts_low"]) val = round(((units_high - units_low) / (volts_high - volts_low) * (val - volts_low)) + units_low, 2) self.analogData[item]["last_value"] = val self.analogData[item]["last_send_time"] = time.time() self.currentDict[item] = str(val) if self.dioForward[item]["on"].lower() == "on": ch = self.dioForward[item]["unitNum"] deviceName = self.dioForward[item]["deviceName"] chName = self.dioForward[item]["channel"] if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, chName, self.currentDict[item], 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, chName, self.currentDict[item], 0, deviceName, self.locSpoof["mac"]) self.sendtodb(item, self.currentDict[item], 0) else: self.sendtodbDev(ch, chName, self.currentDict[item], 0, deviceName) else: self.sendtodb(item, self.currentDict[item], 0) except: pass except Exception, e: print e if self.sync == True: self.sync = False self.last.update(self.currentDict) time.sleep(.5) #check geo fences and signal strength gps = self.mcu.gps try: lat1 = self.last_gps.split(",")[0] long1 = self.last_gps.split(",")[1] lat2 = gps.split(",")[0] long2 = gps.split(",")[1] distance_traveled = self.mcu.distanceBetween(long1, lat1, long2, lat2) except: distance_traveled = 0 if (gps != self.last_gps and abs(time.time() - self.last_gps_time) > 43200) or distance_traveled > 0.0625: if gps != "0.0,-0.0": self.last_gps = gps self.last_gps_time = time.time() self.sendtodb("gps", gps, 0) try: try: if self.mcu.FLY == True: if (time.time() - self.last_xbee_sig_time) > 3600: self.sendtodb("signal", "0", 0) self.last_xbee_sig_time = time.time() except: print "not a fly" else: signal = self.mcu.signal if signal is not None: if int(signal) != int(self.last_signal): self.sendtodb("signal", str(signal), 0) self.last_signal = int(signal) else: print "signal returned None" except Exception,e: print "error with signal" print e signal = self.mcu.signal if signal is not None: if int(self.last_signal) != int(signal): self.sendtodb("signal", str(signal), 0) self.last_signal = int(signal) #UNCOMMENT AND IMPLEMENT PUSH CONTROL WHEN YOU HAVE MORE THAN 3 seconds #send 3g vs 4g connection status to UI #try: # connType = self.mcu.connType # if connType == 2: # self.sendtodb("connType", "3g", 0) # elif connType == 6: # self.sendtodb("connType", "3g", 0) # elif connType == 7: # self.sendtodb("connType", "4g LTE", 0) # else: # self.sendtodb("connType", "Error", 0) #except Exception, e: # print "error sending 3g vs 4g connection type to db" # print e if len(gps) > 6: for geoTag in self.geoData: lat1 = self.geoData[geoTag]["lat"] long1 = self.geoData[geoTag]["long"] lat2 = gps.split(",")[0] long2 = gps.split(",")[1] distance = self.mcu.distanceBetween(long1, lat1, long2, lat2) if distance > float(self.geoData[geoTag]["distance"]): if self.geoData[geoTag]["alarm"] != "out": self.geoData[geoTag]["alarm"] = "out" self.sendtodb("geo_a1", "out", 0) else: if self.geoData[geoTag]["alarm"] != "in": self.geoData[geoTag]["alarm"] = "in" self.sendtodb("geo_a1", "in", 0) except Exception, e: print "in M1" print e def changeDeviceAddress(self, new): with self.lock: try: for com in self.modbusMap: for address in self.modbusMap[com]["addresses"]: if int(new) != int(address): #this is one we need to repace #1. update all of the device address fields #2. copy the object and replace it back with the correct device address in the main object for chName in self.modbusMap[com]["addresses"][address]: self.modbusMap[com]["addresses"][address][chName]['da'] = str(new) newObj = copy.deepcopy(self.modbusMap[com]["addresses"][address]) #attach the copy at the new address self.modbusMap[com]["addresses"][str(new)] = newObj #delete the old object del self.modbusMap[com]["addresses"][address] #update persistant dictionary with open('/root/python_firmware/drivers/modbusMap.p', 'wb') as handle: pickle.dump(self.modbusMap, handle) except Exception, e: print "error changing address" print e return "Error Changing Address" return True def genericSet(self, name, value, id, longId=None): if name.startswith("fwd-"): self.updateDIOForward(name, value) return True if "adrsetonboot" in name: #save this to a file if value == "On" or value == "on" or value == 1 or value == "1" or value == "true" or value == "True": val = True else: val = False #update persistant value with open('/root/python_firmware/drivers/adrsetonboot.p', 'wb') as handle: pickle.dump(val, handle) self.sendtodb(name, value, 0) return True if "deviceaddress" in name: print "found new device address" success = self.changeDeviceAddress(int(value)) #hardcoded for flowco, need to adjust main.py to include more detials about which device this is self.sendtodbDev("01", "deviceaddress", value, 0, "poc") self.sendtodb("deviceaddress", value, 0) #update persistant value with open('/root/python_firmware/drivers/deviceaddress.p', 'wb') as handle: pickle.dump(int(value), handle) return success # Trigger for turning on modbus debugger. It is turned off after one modbus loop if "modbusdebug" in name: if value == "On" or value == "on" or value == 1 or value == "1" or value == "true" or value == "True": val = "On" self.modbus_logger.enable() else: val = "Off" self.modbus_logger.disable() self.sendtodb("modbusdebug",str(val),0) return True if "reset-pulse" in name: if value == "1" or value == 1 or value == True or value == "True" or value == "true": self.pulses["reset-flag"] = 1 self.sendtodb("reset-pulse", str(self.pulses["reset-flag"]), 0) else: self.pulses["reset-flag"] = 0 self.sendtodb("reset-pulse", str(self.pulses["reset-flag"]), 0) #update persistant dictionary with open('/root/python_firmware/drivers/pulses.p', 'wb') as handle: pickle.dump(self.pulses, handle) return True if "modbuswrite" in name or name == "modbuswrite" or "plcwrite" in name or name == "plcwrite": try: #[{"com":"1","device_address":"1","register_address":"0","values":[1]},{"com":"1","device_address":"1","register_address":"299","type":"32/16/coil","values":[256]}] # [{'com':'1','device_address":'1','register_address':'0','values':[1]}] newVal = value.replace("'", '"') newJson = json.loads(newVal) for set in newJson: com = set["com"] try: regAddress = set["register_address"] device = int(set["device_address"]) except: pass try: type = str(set["type"]) except: type = False values = set["values"] with self.lock: instrument = self.modbusInterfaces[self.modbusMap[com]["c"]] if self.modbusMap[com]["c"] == "TCP": instrument.execute(device, cst.WRITE_MULTIPLE_REGISTERS, int(regAddress), output_value=values) elif self.modbusMap[com]["c"] == "ETHERNET/IP": self.ethernetInstrument = ClxDriver() self.ethernetInstrument.open(self.modbusMap[com]["b"]) instrument = self.ethernetInstrument newLst = [] for i in values: typeOfData = instrument.read_tag([str(i[0])])[0][2] if 'INT' in typeOfData or 'BOOL' in typeOfData: newValue = int(str(i[1])) #elif str(i[2]) == 'BOOL': # newValue = boot(int(i[1])) else: newValue = float(str(i[1])) newLst.append((str(i[0]),newValue,str( typeOfData ) )) print "new List" print newLst self.EthernetIPSET(instrument, com, newLst) elif len(values) == 1 and type == "16": values = values[0] instrument.write_register(int(regAddress), values, functioncode=6) time.sleep(1) newVal = instrument.read_register(int(regAddress)) thread.start_new_thread( self.modbusWriteCheckup, ( int(regAddress), newVal)) elif len(values) == 1 and type == "32": values = float(values[0]) instrument.write_float(int(regAddress), values) time.sleep(1) newVal = instrument.read_float(int(regAddress)) thread.start_new_thread( self.modbusWriteCheckup, ( int(regAddress), newVal)) elif len(values) == 1 and type == "coil": values = values[0] instrument.write_bit(int(regAddress), int(values), functioncode=5) time.sleep(1) newVal = instrument.read_bit(int(regAddress), functioncode=int(1)) thread.start_new_thread( self.modbusWriteCheckup, ( int(regAddress), newVal)) elif len(values) == 1 and type == "long": values = values[0] instrument.write_long(int(regAddress), int(values)) time.sleep(1) newVal = instrument.read_long(int(regAddress)) thread.start_new_thread( self.modbusWriteCheckup, ( int(regAddress), newVal)) else: instrument.write_registers(int(regAddress), values) return True except Exception, e: print "modbus write error" print e if "The slave is indicating an error" in e: e = "The slave is indicating an error, can't write regerister" return str(e) print "I'm at location 1" try: interfaces = ["2s","4s","6s","8s","10s","12s","14s","16s","18s","20s"] if name in interfaces: with self.lock: print "I'm at location 2" self.handleComSetup(name, value) #update persistant dictionary with open('/root/python_firmware/drivers/modbusMap.p', 'wb') as handle: pickle.dump(self.modbusMap, handle) return True try: print "I'm at location 3" if name.split("-")[2] == 'v': #this is a channel set, for future use pass except: pass #2-1","value":"{'chn':'2-1-v','dn':'M1','le':'16','a':'301','f':'3','t':'int','la':null,'m':'none','mv':'1','ct':'number','c':'1','vm':null,'r':'1-200','s':'On'}" interfaces = ["2","4","6","8","10","12","14","16","18","20"] if name.split("-")[0] in interfaces: with self.lock: success = self.handleModbusEntry(name, value) #update persistant dictionary with open('/root/python_firmware/drivers/modbusMap.p', 'wb') as handle: pickle.dump(self.modbusMap, handle) if success: return True else: return False except Exception,e: print(str(e)) return False if name.split("-")[0] == "a1" or name.split("-")[0] == "a2" or name.split("-")[0] == "a3" or name.split("-")[0] == "a4" or name.split("-")[0] == "cl" or name.split("-")[0] == "cl2" or name.split("-")[0] == "pls": try: value = float(value) except: return(" Value must be a float or integer") return self.processAnalog(name, value) #analog inputs (a1-4 and cloop) need 4 data points each set for calibration #Volts Low that ties to Units Low #Volts hight that ties to Units High #if Volts are lower than low, ignore (set to lowest unit value). If Volts are higher than high, ignor (set to highest units value) #formula for displaying value: ((high units - low units) / (high volts - low volts) * (current reading - low volts)) + low units #high / low alarm matrix for digitl outputs and relay #sets from M1 to M1 with I/O mapping, probably dont need def modbusWriteCheckup(self, registerAddress, val): time.sleep(6) print "staring process of modbus ping back" print val, registerAddress for com in self.modbusMap: for address in self.modbusMap[com]["addresses"]: ch_un = address for chName in self.modbusMap[com]["addresses"][address]: #time.sleep(1) regAddress = str(self.modbusMap[com]["addresses"][address][chName]['a']) print regAddress, registerAddress if int(registerAddress) == int(regAddress): print "we found our match for quick update of modbus write" #see if it has the update for a unit number for each register, if not, use the device address try: unitNumber = int(str(self.modbusMap[com]["addresses"][address][chName]['un'])) ch = unitNumber except: ch = ch_un if int(ch) < 10: ch = "0" + str(ch) deviceName = str(self.modbusMap[com]["addresses"][address][chName]['dn']) functioncode = str(self.modbusMap[com]["addresses"][address][chName]['f']) length = int(str(self.modbusMap[com]["addresses"][address][chName]['le'])) regAddress = str(self.modbusMap[com]["addresses"][address][chName]['a']) type = str(self.modbusMap[com]["addresses"][address][chName]['t']) last_val = self.modbusMap[com]["addresses"][address][chName]['la'] multiplier = str(self.modbusMap[com]["addresses"][address][chName]['m']) try: if multiplier != "function": multiplierVal = float(self.modbusMap[com]["addresses"][address][chName]['mv']) else: multiplierVal = str(self.modbusMap[com]["addresses"][address][chName]['mv']) except: multiplierVal = 1 changeType = str(self.modbusMap[com]["addresses"][address][chName]['ct']) m1Channel = str(self.modbusMap[com]["addresses"][address][chName]['m1ch']) + "-v" channel = str(self.modbusMap[com]["addresses"][address][chName]['chn']) try: min_time = int(self.modbusMap[com]["addresses"][address][chName]['mrt']) last_time = int(self.modbusMap[com]["addresses"][address][chName]['lrt']) except: self.modbusMap[com]["addresses"][address][chName]['mrt'] = 60 self.modbusMap[com]["addresses"][address][chName]['lrt'] = 0 min_time = int(self.modbusMap[com]["addresses"][address][chName]['mrt']) last_time = int(self.modbusMap[com]["addresses"][address][chName]['lrt']) #see if there is a period in which you have to get a value, even if it hasn't changed #sudo code: if (current time - last send time ) > period (and period is not None) send up new point try: period = int(self.modbusMap[com]["addresses"][address][chName]['grp']) if period < 10: period = None except: period = None print "here is your period: ", period print time.time() - last_time #load the value map valueMap = self.modbusMap[com]["addresses"][address][chName]['vm'] try: if len(valueMap.keys()) < 1: valueMap = None except: valueMap = None #check if there is a multiplier if multiplier != None: if multiplier == "divide": val = round(float((float(val) / float(multiplierVal))), 2) elif multiplier == "multiply": val = round(float((float(val) * float(multiplierVal))), 2) elif multiplier == "function": x = float(val) print str(multiplierVal) try: val = eval(multiplierVal.replace("&", "'")) print "eval value: " print val try: val = float(val) except: type = "str" except Exception, e: print e if valueMap != None: try: print "found Value map" print valueMap updateVal = valueMap[str(int(val))] print "new Value", val except: updateVal = val if deviceName != "M1": if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, channel, updateVal, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, channel, updateVal, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(m1Channel, updateVal, 0) else: self.sendtodbDev(ch, channel, updateVal, 0, deviceName) self.sendtodb(m1Channel, updateVal, 0) else: self.sendtodb(m1Channel, updateVal, 0) else: if deviceName != "M1": if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, channel, val, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, channel, val, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(m1Channel, val, 0) else: self.sendtodbDev(ch, channel, val, 0, deviceName) self.sendtodb(m1Channel, val, 0) else: self.sendtodb(m1Channel, val, 0) self.modbusMap[com]["addresses"][address][chName]['la'] = val self.modbusMap[com]["addresses"][address][chName]['lrt'] = time.time() def processAnalog(self, name, value): with self.analogLock: try: if name.split("-")[0] == "a1": analogName = "analog1" elif name.split("-")[0] == "a2": analogName = "analog2" elif name.split("-")[0] == "a3": analogName = "analog3" elif name.split("-")[0] == "a4": analogName = "analog4" elif name.split("-")[0] == "cl": analogName = "cloop" elif name.split("-")[0] == "cl2": analogName = "cloop2" elif name.split("-")[0] == "pls": analogName = "pulse" chName = analogMap[name.split("-")[1]] self.analogData[analogName][chName] = float(value) #update persistant dictionary with open('/root/python_firmware/drivers/analogData.p', 'wb') as handle: pickle.dump(self.analogData, handle) self.sendtodb(name, value, 0) except Exception, e: return str(e) return True pass def M1_vpnpf(self, name, value): #value = "1,8080,192.168.1.2,80" v = value.split(",") if v[1] == "del": self.vpn.delForward(1) return True else: number = v[0] port = v[1] ip = v[2] exPort = v[3] self.vpn.addForward(number, port, exPort, ip) self.vpnCheckUp() self.sendtodb("vpnpf", value, 0) return True def M1_vpn(self, name, value): try: #on,lewis@lewis.com,password,subnet,ip,domain values = value.split(",") if values[0] == "On" or values[0] == "on" or values[0] == 1 or values[0] == "1": self.vpn.turnOn("http://54.221.213.207:5000", values[1], values[2], self.mac, values[3], values[4], values[5]) elif values[0] == "Off" or values[0] == "off" or values[0] == 0 or values[0] == "0": self.vpn.turnOff() time.sleep(30) self.vpnCheckUp() #remove the password from the list newValues = values[0] + "," + values[1] + "," + " " + "," + values[3] + "," + values[4] self.sendtodb("vpn", newValues, 0) except Exception,e: print(str(e)) self.sendtodb("error", (str(e) + " most likely wrong username and password"), 0) return str(e) return True def M1_dout1(self, name, value): success = self.mcu.digitalOut1(str(value)) if success: self.sendtodb(name, str(value), 0) #return True/False return success def M1_dout2(self, name, value): success = self.mcu.digitalOut2(str(value)) if success: self.sendtodb(name, str(value), 0) #return True/False return success def M1_dout3(self, name, value): success = self.mcu.digitalOut3(str(value)) if success: self.sendtodb(name, str(value), 0) #return True/False return success def M1_dout4(self, name, value): success = self.mcu.digitalOut4(str(value)) if success: self.sendtodb(name, str(value), 0) #return True/False return success def M1_dout5(self, name, value): success = self.mcu.digitalOut5(str(value)) if success: self.sendtodb(name, str(value), 0) #return True/False return success def M1_relay2(self, name, value): success = self.mcu.relay2(value) if success: self.sendtodb(name, str(value), 0) #return True/False return success def M1_relay1(self, name, value): success = self.mcu.relay1(value) if success: self.sendtodb(name, str(value), 0) #return True/False return success else: return False def M1_reboot(self, name, value): self.sendtodb("log", "success rebooting", 0) time.sleep(5) os.system("reboot") def M1_geo1p(self, name, value): self.addGeo_point(1,value) self.sendtodb(name, value, 0) return True def M1_geo1d(self, name, value): self.addGeo_distance(1, value) self.sendtodb(name, value, 0) return True def M1_rs232baud(self, name, value): try: success = self.mcu.set232Baud(int(value)) self.sendtodb("rs232baud", str(value), 0) except: success = False return success def M1_din2_fw(self, name, value): #update digital in 2 forwarding rules #"cloop":{"on":"off","deviceName":"M1","channel":"cloop","unitNum":"1"}, data = value.split(",") self.dioForward["din2"]["on"] = data[0] self.dioForward["din2"]["deviceName"] = data[1] self.dioForward["din2"]["channel"] = data[2] self.dioForward["din2"]["unitNum"] = data[3] def updateDIOForward(self, name, value): data = value.split(",") name2 = name.split("-")[1] self.dioForward[name2]["on"] = data[0] self.dioForward[name2]["deviceName"] = data[1] self.dioForward[name2]["channel"] = data[2] self.dioForward[name2]["unitNum"] = data[3] #update persistant dictionary with open('/root/python_firmware/drivers/dioForward.p', 'wb') as handle: pickle.dump(self.dioForward, handle) self.sendtodb(name, value, 0) return True def M1_loc_change(self, name, value): data = value.split(",") if len(data) != 3: return "wrong number of values sent, must be exactly 3" { "on":"off","mode":"forward","mac":None} self.locSpoof["on"] = data[0].lower() self.locSpoof["mode"] = data[1].lower() self.locSpoof["mac"] = data[2].upper() #update persistant dictionary with open('/root/python_firmware/drivers/locSpoof.p', 'wb') as handle: pickle.dump(self.locSpoof, handle) self.sendtodb("loc_change", value, 0) return True def M1_rs232(self, name, value): self.mcu.rs232.write(str(value)) return True def RS232Thread(self): while True: data = self.mcu.rs232.read() if len(data) > 2: self.sendtodb("rs232", str(data), 0) time.sleep(1) def get_eoln(self): eoln = SettingsBase.get_setting(self, "eoln") if eoln is None: return None if eoln != self.__eoln_save: # cache result to avoid repeat processing of escapes self.__eoln_save = eoln self._eoln = strip_escapes(eoln) return self._eoln def addGeo_distance(self, number, distance): #self.geoData = { # 1: {"lat":"342.3243, # "long":"343.423", # "distance":43 # } if self.geoData.has_key(int(number)): self.geoData[int(number)]["distance"] = float(distance) else: self.geoData[int(number)] = {"distance":"", "lat":"", "long":"", "alarm":""} self.geoData[int(number)]["distance"] = float(distance) #update persistant dictionary with open('/root/python_firmware/drivers/geoData.p', 'wb') as handle: pickle.dump(self.geoData, handle) def syncGeoTags(self): for number in self.geoData: self.sendtodb(("geo" + str(number) + "p"), (self.geoData[number]["lat"] + "," + self.geoData[number]["long"]), 0) self.sendtodb(("geo" + str(number) + "d"), (self.geoData[number]["distance"]), 0) def addGeo_point(self, number, point): #self.geoData = { # 1: {"lat":"342.3243, # "long":"343.423", # "distance":43 # } if self.geoData.has_key(int(number)): self.geoData[int(number)]["lat"] = point.split(",")[0] self.geoData[int(number)]["long"] = point.split(",")[1] else: self.geoData[int(number)] = {"distance":"", "lat":"", "long":"", "alarm":""} self.geoData[int(number)]["lat"] = point.split(",")[0] self.geoData[int(number)]["long"] = point.split(",")[1] #update persistant dictionary with open('/root/python_firmware/drivers/geoData.p', 'wb') as handle: pickle.dump(self.geoData, handle) def int32(self, x): if x>0xFFFFFFFF: raise OverflowError if x>0x7FFFFFFF: x=int(0x100000000-x) if x<2147483648: return -x else: return -2147483648 return x def byteSwap32(self, 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] def byteArray(self, val, l=16, bs=False): val = ''.join(format(val, '02x')) val = str(val) while len(val) < (l/4): val = "0" + val if bs: val = val[(len(val)/2):] + val[:(len(val)/2)] print val bitStr = bin(int(val, base=16)).replace('0b', '')[::-1] while len(bitStr) < l: bitStr = bitStr + "0" print bitStr return bitStr def M1_fileloc(self, name, value): #@root@python_firmware@main.py,@root@python_firmware@main.py try: spiderFile = value.split(",")[0] flyFile = value.split(",")[1] self.flyFileLocation = flyFile.replace("@", "/") self.sendtodb("log", "starting file transfer", 0) #wait for the data to send, then wait a little more thread.start_new_thread(self.waitforq, (spiderFile,)) except Exception,e: print e def waitforq(self, spiderFile): print "starting to wait" while self.q.qsize() > 0: print "This is the size of the Q" print self.q.qsize() time.sleep(3) print "ending long wait" self.sendtodb("startfile", spiderFile, 0) time.sleep(15) self.M1_getfile('n','n') def M1_uartsoc(self, name, value): #on,ip,port on = value.split(",")[0] ip = value.split(",")[1] port = value.split(",")[2] if on.lower() == "on": #clear old thread if its running: self.runUartSocket = False time.sleep(5) self.runUartSocket = True thread.start_new_thread(self.startUartSoc, (ip, port)) else: self.runUartSocket = False return True def startUartSoc(self, ip, port): print "thread started for socket" self.mcu.xbee.fileTransfer = True while self.runUartSocket: try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (ip, int(port)) sock.connect(server_address) try: while self.runUartSocket: data = self.mcu.xbee.read() if data != "": print "sending data" sock.sendall(data) time.sleep(1) else: time.sleep(2) except Exception, e: print e time.sleep(5) except Exception, e: print e time.sleep(5) self.mcu.xbee.fileTransfer = False def M1_getfile(self, name, value): self.mcu.xbee.fileTransfer = True #clear xbee self.mcu.xbee.read() modem = XMODEM(self.getc, self.putc) stream = file('/tmp/output', 'wb') try: success = (modem.recv(stream, retry=60)) except Exception,e: self.mcu.xbee.fileTransfer = False print e success = None print success self.mcu.xbee.fileTransfer = False if success != None: stream.close() f = open("/tmp/output", "rb") data = f.read() data = zlib.decompress(data) f.close() final = open(self.flyFileLocation, "wb") final.write(data) final.close() self.sendtodb("log", "file trasfer success", 0) else: self.sendtodb("log", "file transfer failed", 0) def getc(self, size, timeout=300): timeout = timeout * 10 data = self.lastData count = 0 while len(data) < size: count += 1 time.sleep(.1) data += self.mcu.xbee.read() if count > timeout and len(data) == 0: return None elif count > timeout: self.lastData = "" return data sndData = data[0:size] self.lastData = data[size:] print "here is the data ", sndData return sndData def putc(self, data, timeout=10): self.mcu.xbee.write(data) return len(data) def process_pulses(self, pulses, multiplier): #load stored pulse data if we don't have any pulse data if self.pulses is None: try: with open('/root/python_firmware/drivers/pulses.p', 'rb') as handle: self.pulses = pickle.load(handle) print "found pickled pulses Data" print self.pulses if ( self.pulses["reset-flag"] == 1 ): #if the reset flag is set, reset the total count self.pulses["lr"] = 0 self.pulses["tr"] = 0 self.pulses["reset-flag"] = 0 self.sendtodb("reset-pulse", str(self.pulses["reset-flag"]), 0) with open('/root/python_firmware/drivers/pulses.p', 'wb') as handle: pickle.dump(self.pulses, handle) print "pickled pulse data reset" except: print "couldn't load pulses from pickle" self.pulses = {"tr":0, "lr":0, "reset-flag":0} with open('/root/python_firmware/drivers/pulses.p', 'wb') as handle: pickle.dump(self.pulses, handle) #grab the initial usage and add it to the other saved totals totals try: #determine how much to add to the total if self.pulses["lr"] <= pulses: print "pulses increased" #add the amount it increased to the total self.pulses["tr"] += (pulses - self.pulses["lr"]) else: #on level decreasing, we can assume the modem has reset, self.pulses["tr"] += pulses #replace last known amount with new one self.pulses["lr"] = pulses val = str(int(self.pulses['tr']) * multiplier) item = "pulse" if self.dioForward[item]["on"].lower() == "on": ch = self.dioForward[item]["unitNum"] deviceName = self.dioForward[item]["deviceName"] chName = self.dioForward[item]["channel"] if self.locSpoof["on"].lower() == "on": if self.locSpoof["mode"].lower() == "forward": self.sendtodbLoc(ch, chName, val, 0, deviceName, self.locSpoof["mac"]) elif self.locSpoof["mode"].lower() == "copy": self.sendtodbLoc(ch, chName, val, 0, deviceName, self.locSpoof["mac"]) self.sendtodb(item, val, 0) else: self.sendtodbDev(ch, chName, val, 0, deviceName) self.sendtodb(item, val, 0) else: self.sendtodb(item, val, 0) #self.sendtodb("pulse", val, 0) #I need pulse multiplier #don't write to flash too often, it will wear the flash out!! #update persistant dictionary with open('/root/python_firmware/drivers/pulses.p', 'wb') as handle: pickle.dump(self.pulses, handle) except Exception, e: print "error pulses" print e time.sleep(60) #add up totals and keep for persistance def get_network_bytes(self): #load stored 3g usage data try: with open('/root/python_firmware/drivers/3G_usage.p', 'rb') as handle: self.usageData = pickle.load(handle) print "found pickled 3g usage Data" print self.usageData except: print "couldn't load 3g usage from pickle" self.usageData = {"lr":0,"lt":0,"tr":0,"tt":0} interface= "3g-3g" #grab the initial usage and add it to the other saved totals totals count = 240 writeCount = 20 while True: count += 1 writeCount += 1 try: for line in open('/proc/net/dev', 'r'): if interface in line: data = line.split('%s:' % interface)[1].split() rx_bytes, tx_bytes = (data[0], data[8]) rx = float(rx_bytes) / 1000 tx = float(tx_bytes) / 1000 #determine how much to add to the total if self.usageData["lr"] <= rx: print "data received increased" #add the amount it increased to the total self.usageData["tr"] += (rx - self.usageData["lr"]) else: #on level decreasing, we can assume the modem has reset, so add all of the data up until now self.usageData["tr"] += rx self.usageData["lr"] = rx continue #replace last known amount with new one self.usageData["lr"] = rx #determine how much to add to the total if self.usageData["lt"] <= tx: print "data sent increased" #add the amount it increased to the total self.usageData["tt"] += (tx - self.usageData["lt"]) else: self.usageData["tt"] += tx self.usageData["lt"] = tx continue #replace last known amount with new one self.usageData["lt"] = tx if count > 240: count = 0 self.sendtodb("rx", str(self.usageData['tr']), 0) self.sendtodb("tx", str(self.usageData['tt']), 0) self.sendtodb("rxtx", str(self.usageData['tr'] + self.usageData['tt'] ), 0) #don't write to flash too often, it will wear the flash out!! if writeCount > 10: writeCount = 0 #update persistant dictionary with open('/root/python_firmware/drivers/3G_usage.p', 'wb') as handle: pickle.dump(self.usageData, handle) time.sleep(60) except Exception, e: print "error in 3g data usage" print e time.sleep(60) #add up totals and keep for persistance def M1_cutoff(self, name, value): if value == "True" or value == "true" or value == "on" or value == "On" or value == "1" or value == 1: self.mcu.cutAllDataOff = True else: self.mcu.cutAllDataOff = False return True def M1_setmask(self, name, value): mask = value #lets do some simple validation: ip = ip.strip() test = ip.split(".") if len(test) == 4: for i in test: if int(i) < 256: continue else: return False cmd = "/sbin/uci get network.lan.netmask=" + mask os.system(cmd) os.system("/sbin/uci commit network") os.system("/sbin/ifup lan") self.sendtodb("setmask", mask, 0) return True else: return False def M1_setip(self, name, value): ip = value #lets do some simple validation: ip = ip.strip() test = ip.split(".") if len(test) == 4: for i in test: if int(i) < 256: continue else: return False cmd = "/sbin/uci set network.lan.ipaddr=" + ip os.system(cmd) os.system("/sbin/uci commit network") os.system("/sbin/ifup lan") self.sendtodb("setip", ip, 0) return True else: return False