class GenTankData(MySupport): #------------ GenTankData::init--------------------------------------------- def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, console=None): super(GenTankData, self).__init__() self.LogFileName = os.path.join(loglocation, "gentankdiy.log") self.AccessLock = threading.Lock() self.log = log self.console = console self.MonitorAddress = host configfile = os.path.join(ConfigFilePath, 'gentankdiy.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gentankdiy', log=self.log) self.gauge_type = self.config.ReadValue('gauge_type', return_type=int, default=1) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) try: if self.gauge_type == 1: self.gauge = GaugeDIY1(self.config, log=self.log, console=self.console) elif self.gauge_type == 2: self.gauge = GaugeDIY2(self.config, log=self.log, console=self.console) else: self.LogError("Invalid guage type: " + str(self.gauge_type)) sys.exit(1) self.debug = self.gauge.debug self.Generator = ClientInterface(host=self.MonitorAddress, port=port, log=self.log) # start thread monitor time for exercise self.Threads["TankCheckThread"] = MyThread(self.TankCheckThread, Name="TankCheckThread", start=False) if not self.gauge.InitADC(): self.LogError("InitADC failed, exiting") sys.exit(1) self.Threads["TankCheckThread"].Start() signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) self.console.error("Error in GenTankData init: " + str(e1)) sys.exit(1) #---------- GenTankData::SendCommand -------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data # ---------- GenTankData::TankCheckThread----------------------------------- def TankCheckThread(self): time.sleep(1) while True: try: dataforgenmon = {} tankdata = self.gauge.GetGaugeData() if tankdata != None: dataforgenmon["Tank Name"] = "External Tank" dataforgenmon["Capacity"] = 0 dataforgenmon["Percentage"] = tankdata retVal = self.SendCommand("generator: set_tank_data=" + json.dumps(dataforgenmon)) self.LogDebug(retVal) if self.WaitForExit("TankCheckThread", float(self.gauge.PollTime * 60)): return except Exception as e1: self.LogErrorLine("Error in TankCheckThread: " + str(e1)) if self.WaitForExit("TankCheckThread", float(self.gauge.PollTime * 60)): return # ----------GenTankData::SignalClose---------------------------------------- def SignalClose(self, signum, frame): self.Close() sys.exit(1) # ----------GenTankData::Close---------------------------------------------- def Close(self): self.KillThread("TankCheckThread") self.gauge.Close() self.Generator.Close()
class GenNotify(MyCommon): def __init__(self, host="127.0.0.1", port=9082, log=None, onready=None, onexercise=None, onrun=None, onrunmanual=None, onalarm=None, onservice=None, onoff=None, onmanual=None, onutilitychange=None): super(GenNotify, self).__init__() self.AccessLock = threading.Lock() self.Threads = {} self.LastEvent = None self.LastOutageStatus = None self.Events = {} # Dict for handling events if log != None: self.log = log else: # log errors in this module to a file self.log = SetupLogger("client", "/var/log/myclient.log") self.console = SetupLogger("notify_console", log_file="", stream=True) try: # init event callbacks if onready != None: self.Events["READY"] = onready if onexercise != None: self.Events["EXERCISING"] = onexercise if onrun != None: self.Events["RUNNING"] = onrun if onrunmanual != None: self.Events["RUNNING-MANUAL"] = onrunmanual if onalarm != None: self.Events["ALARM"] = onalarm if onservice != None: self.Events["SERVICEDUE"] = onservice if onoff != None: self.Events["OFF"] = onoff if onmanual != None: self.Events["MANUAL"] = onmanual if onutilitychange != None: self.Events["OUTAGE"] = onutilitychange startcount = 0 while startcount <= 10: try: self.Generator = ClientInterface(host=host, log=log) break except Exception as e1: startcount += 1 if startcount >= 10: self.console.info("genmon not loaded.") sys.exit(1) time.sleep(1) continue # start thread to accept incoming sockets for nagios heartbeat self.Threads["PollingThread"] = MyThread(self.MainPollingThread, Name="PollingThread") except Exception as e1: self.LogErrorLine("Error in mynotify init: " + str(e1)) # ---------- GenNotify::MainPollingThread----------------------------------- def MainPollingThread(self): while True: try: data = self.SendCommand("generator: getbase") outagedata = self.SendCommand("generator: outage_json") try: OutageDict = collections.OrderedDict() OutageDict = json.loads(outagedata) OutageState = True if OutageDict["Outage"][ "System In Outage"].lower() == "yes" else False except Exception as e1: # The system does no support outage tracking (i.e. H-100) #self.LogErrorLine("Unable to get outage state: " + str(e1)) OutageState = None if OutageState != None: self.ProcessOutageState(OutageState) if self.LastEvent == data: time.sleep(3) continue if self.LastEvent != None: self.console.info("Last : <" + self.LastEvent + ">, New : <" + data + ">") self.CallEventHandler(False) # end last event self.LastEvent = data self.CallEventHandler(True) # begin new event time.sleep(3) except Exception as e1: self.LogErrorLine("Error in mynotify:MainPollingThread: " + str(e1)) time.sleep(3) #---------- GenNotify::CallEventHandler ----------------------------------- def CallEventHandler(self, Status): try: if self.LastEvent == None: return EventCallback = self.Events.get(self.LastEvent, None) # Event has ended if EventCallback != None: if callable(EventCallback): EventCallback(Status) else: self.LogError("Invalid Callback in CallEventHandler : " + str(EventCallback)) else: self.LogError("Invalid Callback in CallEventHandler : None") except Exception as e1: self.LogErrorLine("Error in CallEventHandler: " + str(e1)) #---------- GenNotify::ProcessOutageState --------------------------------- def ProcessOutageState(self, outagestate): try: if self.LastOutageStatus == outagestate: return self.LastOutageStatus = outagestate EventCallback = self.Events.get("OUTAGE", None) if EventCallback != None: if callable(EventCallback): EventCallback(self.LastOutageStatus) else: self.LogError("Invalid Callback in ProcessOutageState : " + str(EventCallback)) else: self.LogError("Invalid Callback in ProcessOutageState : None") except Exception as e1: self.LogErrorLine("Error in ProcessOutageState: " + str(e1)) #---------- GenNotify::SendCommand ---------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data #---------- GenNotify::Close ---------------------------------------------- def Close(self): self.Generator.Close() return False
class GenTankData(MySupport): #------------ GenTankData::init--------------------------------------------- def __init__(self, log = None, loglocation = ProgramDefaults.LogPath, ConfigFilePath = MyCommon.DefaultConfPath, host = ProgramDefaults.LocalHost, port = ProgramDefaults.ServerPort, console = None): super(GenTankData, self).__init__() self.LogFileName = os.path.join(loglocation, "gentankutil.log") self.AccessLock = threading.Lock() self.log = log self.console = console self.MonitorAddress = host self.PollTime = 2 self.TankID = "" self.debug = False configfile = os.path.join(ConfigFilePath, 'gentankutil.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename = configfile, section = 'gentankutil', log = self.log) self.PollTime = self.config.ReadValue('poll_frequency', return_type = float, default = 60) self.debug = self.config.ReadValue('debug', return_type = bool, default = False) self.username = self.config.ReadValue('username', default = "") self.password = self.config.ReadValue('password', default = "") self.tank_name = self.config.ReadValue('tank_name', default = "") if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) if self.username == "" or self.username == None or self.password == "" or self.password == None: self.LogError("Invalid user name or password, exiting") sys.exit(1) try: self.Generator = ClientInterface(host = self.MonitorAddress, port = port, log = self.log) #if not self.CheckGeneratorRequirement(): # self.LogError("Requirements not met. Exiting.") # sys.exit(1) self.tank = tankutility(self.username, self.password, self.log, debug = self.debug) # start thread monitor time for exercise self.Threads["TankCheckThread"] = MyThread(self.TankCheckThread, Name = "TankCheckThread", start = False) self.Threads["TankCheckThread"].Start() signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) self.console.error("Error in GenTankData init: " + str(e1)) sys.exit(1) #---------- GenTankData::SendCommand -------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data #---------- GenTankData::CheckGeneratorRequirement ------------------------ def CheckGeneratorRequirement(self): try: data = self.SendCommand("generator: start_info_json") StartInfo = {} StartInfo = json.loads(data) if not "evolution" in StartInfo["Controller"].lower() and not "nexus" in StartInfo["Controller"].lower(): self.LogError("Error: Only Evolution or Nexus controllers are supported for this feature: " + StartInfo["Controller"]) return False return True except Exception as e1: self.LogErrorLine("Error in CheckGeneratorRequirement: " + str(e1)) return False # ---------- GenTankData::Login--------------------------------------------- def Login(self, force = False): if force: self.TankID = "" if len(self.TankID): # already logged in return True if not self.tank.Login(): return False self.TankID = self.tank.GetIDFromName(self.tank_name) if not len(self.TankID): return False return True # ---------- GenTankData::TankCheckThread----------------------------------- def TankCheckThread(self): time.sleep(1) LastLoginTime = datetime.datetime.now() while True: try: NUMBER_OF_SECONDS = 60 * 60 * 12 # 12 hours if ((datetime.datetime.now() - LastLoginTime).total_seconds() > NUMBER_OF_SECONDS) or not len(self.TankID): self.LogDebug("Login ") if not self.Login(force = True): self.LogError("Error logging in in TankCheckThread, retrying") dataforgenmon = {} tankdata = self.tank.GetData(self.TankID) if tankdata != None: dataforgenmon["Tank Name"] = tankdata["name"] dataforgenmon["Capacity"] = self.tank.GetCapacity() dataforgenmon["Percentage"] = self.tank.GetPercentage() retVal = self.SendCommand("generator: set_tank_data=" + json.dumps(dataforgenmon)) self.LogDebug(retVal) if self.WaitForExit("TankCheckThread", float(self.PollTime * 60)): return except Exception as e1: self.LogErrorLine("Error in TankCheckThread: " + str(e1)) if self.WaitForExit("TankCheckThread", float(self.PollTime * 60)): return # ----------GenTankData::SignalClose---------------------------------------- def SignalClose(self, signum, frame): self.Close() sys.exit(1) # ----------GenTankData::Close---------------------------------------------- def Close(self): self.KillThread("TankCheckThread") self.Generator.Close()
class MyGenPush(MySupport): #------------ MyGenPush::init----------------------------------------------- def __init__(self, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, log = None, callback = None, polltime = None, blacklist = None, flush_interval = float('inf'), use_numeric = False, debug = False, loglocation = ProgramDefaults.LogPath): super(MyGenPush, self).__init__() self.Callback = callback self.UseNumeric = use_numeric self.Debug = debug if polltime == None: self.PollTime = 3 else: self.PollTime = float(polltime) if log != None: self.log = log else: # log errors in this module to a file self.log = SetupLogger("client", loglocation + "mygenpush.log") self.console = SetupLogger("mygenpush_console", log_file = "", stream = True) self.AccessLock = threading.Lock() self.BlackList = blacklist self.LastValues = {} self.FlushInterval = flush_interval self.LastChange = {} try: startcount = 0 while startcount <= 10: try: self.Generator = ClientInterface(host = host, port = port, log = log) break except Exception as e1: startcount += 1 if startcount >= 10: self.console.info("genmon not loaded.") self.LogError("Unable to connect to genmon.") sys.exit(1) time.sleep(1) continue # start thread to accept incoming sockets for nagios heartbeat self.Threads["PollingThread"] = MyThread(self.MainPollingThread, Name = "PollingThread") except Exception as e1: self.LogErrorLine("Error in mygenpush init: " + str(e1)) #---------- MyGenPush::SendCommand ---------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data # ---------- MyGenPush::MainPollingThread----------------------------------- def MainPollingThread(self): while True: try: if not self.UseNumeric: statusdata = self.SendCommand("generator: status_json") else: statusdata = self.SendCommand("generator: status_num_json") outagedata = self.SendCommand("generator: outage_json") monitordata = self.SendCommand("generator: monitor_json") maintdata = self.SendCommand("generator: maint_json") try: GenmonDict = {} TempDict = {} TempDict = json.loads(statusdata) GenmonDict["Status"] = TempDict["Status"] TempDict = json.loads(maintdata) GenmonDict["Maintenance"] = TempDict["Maintenance"] TempDict = json.loads(outagedata) GenmonDict["Outage"] = TempDict["Outage"] TempDict = json.loads(monitordata) GenmonDict["Monitor"] = TempDict["Monitor"] self.CheckDictForChanges(GenmonDict, "generator") except Exception as e1: self.LogErrorLine("Unable to get status: " + str(e1)) if self.WaitForExit("PollingThread", float(self.PollTime)): return except Exception as e1: self.LogErrorLine("Error in mynotify:MainPollingThread: " + str(e1)) if self.WaitForExit("PollingThread", float(self.PollTime)): return #------------ MySupport::CheckDictForChanges ------------------------------- # This function is recursive, it will turn a nested dict into a flat dict keys # that have a directory structure with corrposonding values and determine if # anyting changed. If it has then call our callback function def CheckDictForChanges(self, node, PathPrefix): CurrentPath = PathPrefix if not isinstance(PathPrefix, str): return "" if isinstance(node, dict): for key, item in node.items(): if isinstance(item, dict): CurrentPath = PathPrefix + "/" + str(key) self.CheckDictForChanges(item, CurrentPath) elif isinstance(item, list): CurrentPath = PathPrefix + "/" + str(key) for listitem in item: if isinstance(listitem, dict): self.CheckDictForChanges(listitem, CurrentPath) elif isinstance(listitem, str) or isinstance(listitem, unicode): CurrentPath = PathPrefix + "/" + str(key) #todo list support pass else: self.LogError("Invalid type in CheckDictForChanges: %s %s (2)" % (key, str(type(listitem)))) else: CurrentPath = PathPrefix + "/" + str(key) self.CheckForChanges(CurrentPath, item) else: self.LogError("Invalid type in CheckDictForChanges %s " % str(type(node))) # ---------- MyGenPush::CheckForChanges------------------------------------- def CheckForChanges(self, Path, Value): try: if self.BlackList != None: for BlackItem in self.BlackList: if BlackItem.lower() in Path.lower(): return LastValue = self.LastValues.get(str(Path), None) LastChange = self.LastChange.get(str(Path), 0) if LastValue == None or LastValue != Value or (time.time() - LastChange) > self.FlushInterval: self.LastValues[str(Path)] = Value self.LastChange[str(Path)] = time.time() if self.Callback != None: self.Callback(str(Path), Value) except Exception as e1: self.LogErrorLine("Error in mygenpush:CheckForChanges: " + str(e1)) # ---------- MyGenPush::Close----------------------------------------------- def Close(self): self.KillThread("PollingThread") self.Generator.Close()
class GenCTHat(MySupport): #------------ GenCTHat::init------------------------------------------------ def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, console=None): super(GenCTHat, self).__init__() #https://tutorials-raspberrypi.com/mcp3008-read-out-analog-signals-on-the-raspberry-pi/ #https://forums.raspberrypi.com/viewtopic.php?t=237182 self.LogFileName = os.path.join(loglocation, "gencthat.log") self.AccessLock = threading.Lock() self.log = log self.console = console self.MonitorAddress = host self.PollTime = 2 self.SampleTimeMS = 34 self.debug = False self.ConfigFileName = 'gencthat.conf' configfile = os.path.join(ConfigFilePath, self.ConfigFileName) try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gencthat', log=self.log) self.Multiplier = self.config.ReadValue('multiplier', return_type=float, default=0.488) # this checks to see if an old version of the conf file is in use and replaces it if self.Multiplier == 0.218: self.ConfPath = os.path.join( os.path.dirname(os.path.realpath(__file__)), "conf") if os.path.isfile( os.path.join(self.ConfPath, self.ConfigFileName)): copyfile(os.path.join(self.ConfPath, self.ConfigFileName), configfile) self.LogError( "Copied " + os.path.join(self.ConfPath, self.ConfigFileName) + " to " + configfile) self.config = MyConfig(filename=configfile, section='gencthat', log=self.log) else: self.LogError( "Error: unable to find config file: " + os.path.join(self.ConfPath, self.ConfigFileName)) self.SampleTimeMS = self.config.ReadValue('sample_time_ms', return_type=int, default=34) self.Multiplier = self.config.ReadValue('multiplier', return_type=float, default=0.488) self.PollTime = self.config.ReadValue('poll_frequency', return_type=float, default=60) self.powerfactor = self.config.ReadValue('powerfactor', return_type=float, default=1.0) self.bus = self.config.ReadValue('bus', return_type=int, default=1) self.device = self.config.ReadValue('device', return_type=int, default=0) self.strict = self.config.ReadValue('strict', return_type=bool, default=False) self.singlelegthreshold = self.config.ReadValue( 'singlelegthreshold', return_type=float, default=0.6) self.debug = self.config.ReadValue('debug', return_type=bool, default=False) self.LogDebug("Multiplier: " + str(self.Multiplier)) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) try: self.adc = MCP3008(bus=self.bus, device=self.device, log=self.log) self.adc.open() self.Generator = ClientInterface(host=self.MonitorAddress, port=port, log=self.log) #if not self.CheckGeneratorRequirement(): # self.LogError("Requirements not met. Exiting.") # sys.exit(1) # start thread monitor time for exercise self.Threads["SensorCheckThread"] = MyThread( self.SensorCheckThread, Name="SensorCheckThread", start=False) self.Threads["SensorCheckThread"].Start() signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenCTHat init: " + str(e1)) self.console.error("Error in GenCTHat init: " + str(e1)) sys.exit(1) #---------- GenCTHat::SendCommand ----------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data #---------- GenCTHat::CheckGeneratorRequirement --------------------------- def CheckGeneratorRequirement(self): try: data = self.SendCommand("generator: start_info_json") StartInfo = {} StartInfo = json.loads(data) if not "evolution" in StartInfo["Controller"].lower( ) and not "nexus" in StartInfo["Controller"].lower(): self.LogError( "Error: Only Evolution or Nexus controllers are supported for this feature: " + StartInfo["Controller"]) return False return True except Exception as e1: self.LogErrorLine("Error in CheckGeneratorRequirement: " + str(e1)) return False # ---------- GenCTHat::MillisecondsElapsed---------------------------------- def MillisecondsElapsed(self, ReferenceTime): CurrentTime = datetime.datetime.now() Delta = CurrentTime - ReferenceTime return Delta.total_seconds() * 1000 # ---------- GenCTHat::SensorCheckThread------------------------------------ def SensorCheckThread(self): time.sleep(1) while True: try: CT1 = None CT2 = None CTReading1 = [] CTReading2 = [] for i in range(5): CT1 = self.GetCTReading(channel=0) if CT1 != None: CTReading1.append(CT1) CT2 = self.GetCTReading(channel=1) if CT2 != None: CTReading2.append(CT2) if len(CTReading1): CT1 = min(CTReading1) else: CT1 = None if len(CTReading2): CT2 = min(CTReading2) else: CT2 = None if CT1 == None or CT2 == None: if self.WaitForExit("SensorCheckThread", float(self.PollTime)): return continue if CT1 <= self.singlelegthreshold: CT1 = 0 if CT2 <= self.singlelegthreshold: CT2 = 0 self.LogDebug("CT1: %.2f, CT2: %.2f" % (CT1, CT2)) data = {} data['strict'] = self.strict data['current'] = CT1 + CT2 data['ctdata'] = [CT1, CT2] data['powerfactor'] = self.powerfactor return_string = json.dumps(data) self.Generator.ProcessMonitorCommand( "generator: set_power_data=" + return_string) if self.WaitForExit("SensorCheckThread", float(self.PollTime)): return except Exception as e1: self.LogErrorLine("Error in SensorCheckThread: " + str(e1)) if self.WaitForExit("SensorCheckThread", float(self.PollTime)): return # ----------GenCTHat::GetCTReading------------------------------------------ def GetCTReading(self, channel=0): try: StartTime = datetime.datetime.now() num_samples = 0 max = 0 min = 512 return_data = 0 while True: sample = self.adc.read(channel=channel) if sample > max: max = sample if sample < min: min = sample num_samples += 1 msElapsed = self.MillisecondsElapsed(StartTime) if msElapsed > self.SampleTimeMS: break if max == 0 and min == 512: self.LogDebug("No data read in GetCTSample") return 0 else: offset = max - 512 if 511 - min > offset: offset = 511 - min if offset <= 2: offset = 0 #1 or 2 is most likely just noise on the clamps or in the traces on the board self.LogDebug( "channel: %d, sample: %d, max: %d, min: %d, ms elapsed: %d, num samples %d" % (channel, sample, max, min, msElapsed, num_samples)) if max == min == 0: self.LogDebug("NULL readings, device not responding") return None return_data = offset * self.Multiplier return return_data except Exception as e1: self.LogErrorLine("Error in GetCTReading: " + str(e1)) return 0 # ----------GenCTHat::SignalClose------------------------------------------- def SignalClose(self, signum, frame): self.Close() sys.exit(1) # ----------GenCTHat::Close------------------------------------------------- def Close(self): self.KillThread("SensorCheckThread") self.Generator.Close()
class GenTemp(MySupport): #------------ GenTemp::init------------------------------------------------- def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort): super(GenTemp, self).__init__() self.LogFileName = os.path.join(loglocation, "gentemp.log") self.AccessLock = threading.Lock() # log errors in this module to a file self.log = SetupLogger("gentemp", self.LogFileName) self.console = SetupLogger("gentemp_console", log_file="", stream=True) self.LastValues = {} self.MonitorAddress = host self.debug = False self.PollTime = 1 self.BlackList = None configfile = os.path.join(ConfigFilePath, 'gentemp.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gentemp', log=self.log) self.UseMetric = self.config.ReadValue('use_metric', return_type=bool, default=False) self.PollTime = self.config.ReadValue('poll_frequency', return_type=float, default=1) self.debug = self.config.ReadValue('debug', return_type=bool, default=False) self.DeviceLabels = self.GetParamList( self.config.ReadValue('device_labels', default=None)) self.BlackList = self.GetParamList( self.config.ReadValue('blacklist', default=None)) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) try: try: startcount = 0 while startcount <= 10: try: self.Generator = ClientInterface( host=self.MonitorAddress, port=port, log=self.log) break except Exception as e1: startcount += 1 if startcount >= 10: self.LogConsole("genmon not loaded.") self.LogError("Unable to connect to genmon.") sys.exit(1) time.sleep(1) continue except Exception as e1: self.LogErrorLine("Error in GenTempThread init: " + str(e1)) self.DeviceList = self.EnumDevices() if not len(self.DeviceList): self.LogConsole("No sensors found.") self.LogError("No sensors found.") sys.exit(1) # start thread monitor time for exercise self.Threads["GenTempThread"] = MyThread(self.GenTempThread, Name="GenTempThread", start=False) self.Threads["GenTempThread"].Start() atexit.register(self.Close) except Exception as e1: self.LogErrorLine("Error in GenTemp init: " + str(e1)) self.LogConsole("Error in GenTemp init: " + str(e1)) sys.exit(1) #---------- GenTemp::GetParamList ----------------------------------------- def GetParamList(self, input_string): ReturnValue = [] try: if input_string != None: if len(input_string): ReturnList = input_string.strip().split(",") if len(ReturnList): for Items in ReturnList: Items = Items.strip() if len(Items): ReturnValue.append(Items) if len(ReturnValue): return ReturnValue return None except Exception as e1: self.LogErrorLine("Error in GetParamList: " + str(e1)) return None #---------- GenTemp::EnumDevices ------------------------------------------ def EnumDevices(self): DeviceList = [] try: # enum DS18B20 temp sensors for sensor in glob.glob("/sys/bus/w1/devices/28-*/w1_slave"): if not self.CheckBlackList(sensor) and self.DeviceValid( sensor): self.LogDebug("Found DS18B20 : " + sensor) DeviceList.append(sensor) # enum type K thermocouples #for sensor in glob.glob("/sys/bus/w1/devices/w1_bus_master*/3b-*/w1_slave"): for sensor in glob.glob("/sys/bus/w1/devices/3b-*/w1_slave"): if not self.CheckBlackList(sensor) and self.DeviceValid( sensor): self.LogDebug("Found type K thermocouple : " + sensor) DeviceList.append(sensor) return DeviceList except Exception as e1: self.LogErrorLine("Error in EnumDevices: " + str(e1)) return DeviceList #------------ GenTemp::ReadData -------------------------------------------- def ReadData(self, device): try: f = open(device, "r") data = f.read() f.close() return data except Exception as e1: self.LogErrorLine("Error in ReadData for " + device + " : " + str(e1)) return None #------------ GenTemp::DeviceValid ----------------------------------------- def DeviceValid(self, device): try: data = self.ReadData(device) if isinstance( data, str ) and "YES" in data and " crc=" in data and " t=" in data: return True return False except Exception as e1: self.LogErrorLine("Error in DeviceValid for " + device + " : " + str(e1)) return False #------------ GenTemp::ParseData ------------------------------------------- def ParseData(self, data): try: if self.UseMetric: units = "C" else: units = "F" if not isinstance(data, str): return None, units if not len(data): return None, units (discard, sep, reading) = data.partition(' t=') t = float(reading) / 1000.0 if not self.UseMetric: return self.ConvertCelsiusToFahrenheit(t), units else: return t, units except Exception as e1: self.LogErrorLine("Error in ParseData: " + str(e1)) return None, units #------------ GenTemp::GetIDFromDeviceName --------------------------------- def GetIDFromDeviceName(self, device): try: if "28-" in device or "3b-" in device: id = device.split("/")[5] return id except Exception as e1: self.LogErrorLine("Error in GetIDFromDeviceName for " + device + " : " + str(e1)) return "UNKNOWN_ID" #---------- GenTemp::SendCommand ------------------------------------------ def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data # ---------- GenTemp::CheckBlackList----------------------------------------- def CheckBlackList(self, device): if not isinstance(self.BlackList, list): return False return any(blacklistitem in device for blacklistitem in self.BlackList) # ---------- GenTemp::GenTempThread----------------------------------------- def GenTempThread(self): time.sleep(1) while True: try: labelIndex = 0 ReturnDeviceData = [] for sensor in self.DeviceList: temp, units = self.ParseData(self.ReadData(sensor)) if temp != None: self.LogDebug( "Device: %s Reading: %.2f %s" % (self.GetIDFromDeviceName(sensor), temp, units)) if isinstance(self.DeviceLabels, list) and len( self.DeviceLabels) and (labelIndex < len( self.DeviceLabels)): device_label = self.DeviceLabels[labelIndex] else: device_label = self.GetIDFromDeviceName(sensor) ReturnDeviceData.append( {device_label: "%.2f %s" % (temp, units)}) labelIndex += 1 return_string = json.dumps( {"External Temperature Sensors": ReturnDeviceData}) self.SendCommand("generator: set_temp_data=" + return_string) self.LogDebug(return_string) if self.WaitForExit("GenTempThread", float(self.PollTime)): return except Exception as e1: self.LogErrorLine("Error in GenTempThread: " + str(e1)) if self.WaitForExit("GenTempThread", float(self.PollTime * 60)): return # ----------GenTemp::Close---------------------------------------------- def Close(self): self.LogError("GenTemp Exit") self.KillThread("GenTempThread") self.Generator.Close()
class GenExercise(MySupport): #------------ GenExercise::init--------------------------------------------- def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, console=None): super(GenExercise, self).__init__() self.AccessLock = threading.Lock() self.log = log self.console = console self.MonitorAddress = host self.PollTime = 2 self.ExerciseActive = False self.Debug = False try: self.config = MyConfig(filename=os.path.join( ConfigFilePath, 'genexercise.conf'), section='genexercise', log=self.log) self.ExerciseType = self.config.ReadValue('exercise_type', default="Normal") self.ExerciseHour = self.config.ReadValue('exercise_hour', return_type=int, default=12) self.ExerciseMinute = self.config.ReadValue('exercise_minute', return_type=int, default=0) self.ExerciseDayOfMonth = self.config.ReadValue( 'exercise_day_of_month', return_type=int, default=1) self.ExerciseDayOfWeek = self.config.ReadValue( 'exercise_day_of_week', default="Monday") self.ExerciseDuration = self.config.ReadValue('exercise_duration', return_type=float, default=12) self.ExerciseWarmup = self.config.ReadValue('exercise_warmup', return_type=float, default=0) self.ExerciseFrequency = self.config.ReadValue( 'exercise_frequency', default="Monthly") self.MonitorAddress = self.config.ReadValue( 'monitor_address', default=ProgramDefaults.LocalHost) self.LastExerciseTime = self.config.ReadValue('last_exercise', default=None) self.UseGeneratorTime = self.config.ReadValue('use_gen_time', return_type=bool, default=False) self.Debug = self.config.ReadValue('debug', return_type=bool, default=False) # Validate settings if not self.ExerciseType.lower() in [ "normal", "quiet", "transfer" ]: self.ExerciseType = "normal" if self.ExerciseHour > 23 or self.ExerciseHour < 0: self.ExerciseHour = 12 if self.ExerciseMinute > 59 or self.ExerciseMinute < 0: self.ExerciseMinute = 0 if not self.ExerciseDayOfWeek.lower() in [ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ]: self.ExerciseDayOfWeek = "Monday" if self.ExerciseDayOfMonth > 28 or self.ExerciseDayOfMonth < 1: self.ExerciseDayOfMonth = 1 if self.ExerciseDuration > 60: self.ExerciseDuration = 60 if self.ExerciseDuration < 5: self.ExerciseDuration = 5 if self.ExerciseWarmup > 30: self.ExerciseWarmup = 30 if self.ExerciseWarmup < 0: self.ExerciseWarmup = 0 if not self.ExerciseFrequency.lower() in [ "weekly", "biweekly", "monthly" ]: self.ExerciseFrequency = "Monthly" if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine( "Error reading " + os.path.join(ConfigFilePath, "genexercise.conf") + ": " + str(e1)) self.console.error( "Error reading " + os.path.join(ConfigFilePath, "genexercise.conf") + ": " + str(e1)) sys.exit(1) try: self.Generator = ClientInterface(host=self.MonitorAddress, port=port, log=self.log) if not self.CheckGeneratorRequirement(): self.LogError("Requirements not met. Exiting.") sys.exit(1) # start thread monitor time for exercise self.Threads["ExerciseThread"] = MyThread(self.ExerciseThread, Name="ExerciseThread", start=False) self.Threads["ExerciseThread"].Start() try: if self.ExerciseFrequency.lower() == "monthly": DayStr = "Day " + str(self.ExerciseDayOfMonth) else: DayStr = str(self.ExerciseDayOfWeek) self.LogError("Execise: " + self.ExerciseType + ", " + self.ExerciseFrequency + " at " + str(self.ExerciseHour) + ":" + str(self.ExerciseMinute) + " on " + DayStr + " for " + str(self.ExerciseDuration) + " min. Warmup: " + str(self.ExerciseWarmup)) self.DebugOutput("Debug Enabled") except Exception as e1: self.LogErrorLine(str(e1)) signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenExercise init: " + str(e1)) self.console.error("Error in GenExercise init: " + str(e1)) sys.exit(1) #---------- GenExercise::SendCommand -------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data #---------- GenExercise::CheckGeneratorRequirement ------------------------ def CheckGeneratorRequirement(self): try: data = self.SendCommand("generator: start_info_json") StartInfo = {} StartInfo = json.loads(data) if not "evolution" in StartInfo["Controller"].lower( ) and not "nexus" in StartInfo["Controller"].lower(): self.LogError( "Error: Only Evolution or Nexus controllers are supported for this feature: " + StartInfo["Controller"]) return False return True except Exception as e1: self.LogErrorLine("Error in CheckGeneratorRequirement: " + str(e1)) return False # ---------- GenExercise::PostWarmup---------------------------------------- def PostWarmup(self): # check to see if the generator is running status = self.SendCommand("generator: getbase") if not status.lower() in ["running", "exercising"]: self.LogError( "WARNING: generator not running post warmup. Transfer switch not activated." ) self.SendCommand("generator: setremote=stop") return self.SendCommand("generator: setremote=starttransfer") self.DebugOutput("Starting transfer exercise cycle (post warmup).") # set timer to stop self.StopTimer = threading.Timer(float(self.ExerciseDuration * 60.0), self.StopExercise) self.StopTimer.start() # ---------- GenExercise::ReadyToExercise----------------------------------- def ReadyToExercise(self): status = self.SendCommand("generator: getbase") if not status.lower() in ["ready", "servicedue"]: self.LogError( "Generator not in Ready state, exercise cycle not started: " + str(status)) return False return True # ---------- GenExercise::StartExercise------------------------------------- def StartExercise(self): if self.ExerciseActive: # already active return # Start generator if self.ExerciseType.lower() == "normal" and self.ReadyToExercise(): self.SendCommand("generator: setremote=start") self.DebugOutput("Starting normal exercise cycle.") self.StopTimer = threading.Timer( float(self.ExerciseDuration * 60.0), self.StopExercise) self.StopTimer.start() elif self.ExerciseType.lower() == "quiet" and self.ReadyToExercise(): self.SendCommand("generator: setremote=startexercise") self.DebugOutput("Starting quiet exercise cycle.") self.StopTimer = threading.Timer( float(self.ExerciseDuration * 60.0), self.StopExercise) self.StopTimer.start() elif self.ExerciseType.lower() == "transfer" and self.ReadyToExercise( ): if self.ExerciseWarmup == 0: self.SendCommand("generator: setremote=starttransfer") self.DebugOutput("Starting transfer exercise cycle.") self.StopTimer = threading.Timer( float(self.ExerciseDuration * 60.0), self.StopExercise) self.StopTimer.start() else: self.SendCommand("generator: setremote=start") self.DebugOutput( "Starting warmup for transfer exercise cycle.") # start timer for post warmup transition to starttransfer command self.WarmupTimer = threading.Timer( float(self.ExerciseWarmup * 60.0), self.PostWarmup) self.WarmupTimer.start() else: self.LogError("Invalid mode in StartExercise: " + str(self.ExerciseType)) return self.WriteLastExerciseTime() self.ExerciseActive = True # ---------- GenExercise::StopExercise-------------------------------------- def StopExercise(self): if self.ExerciseActive: self.SendCommand("generator: setremote=stop") self.DebugOutput("Stopping exercise cycle.") self.ExerciseActive = False else: self.DebugOutput("Calling Stop Exercise (not needed)") # ---------- GenExercise::DebugOutput----------------------------- def DebugOutput(self, Message): if self.Debug: self.LogError(Message) # ---------- GenExercise::WriteLastExerciseTime----------------------------- def WriteLastExerciseTime(self): try: NowString = datetime.datetime.now().strftime( "%A %B %d, %Y %H:%M:%S") if self.ExerciseFrequency.lower() == "biweekly": self.config.WriteValue("last_exercise", NowString) self.config.LastExerciseTime = NowString self.DebugOutput("Last Exercise Cycle: " + NowString) except Exception as e1: self.LogErrorLine("Error in WriteLastExerciseTime: " + str(e1)) # ---------- GenExercise::TimeForExercise----------------------------------- def TimeForExercise(self): try: if self.UseGeneratorTime: TimeNow = self.GetGeneratorTime() else: TimeNow = datetime.datetime.now() if TimeNow.hour != self.ExerciseHour or TimeNow.minute != self.ExerciseMinute: return False weekDays = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday") WeekDayString = weekDays[TimeNow.weekday()] if not self.ExerciseFrequency.lower() in [ "weekly", "biweekly", "monthly" ]: self.LogError( "Invalid Exercise Frequency in TimeForExercise: " + str(self.ExerciseFrequency)) return False if self.ExerciseFrequency.lower( ) == "weekly" and self.ExerciseDayOfWeek.lower( ) == WeekDayString.lower(): return True elif self.ExerciseFrequency.lower( ) == "biweekly" and self.ExerciseDayOfWeek.lower( ) == WeekDayString.lower(): if self.LastExerciseTime == None: return True LastExerciseTime = datetime.datetime.strptime( self.LastExerciseTime, "%A %B %d, %Y %H:%M:%S") if (TimeNow - LastExerciseTime).days >= 14: return True return False elif self.ExerciseFrequency.lower( ) == "monthly" and TimeNow.day == self.ExerciseDayOfMonth: return True else: return False except Exception as e1: self.LogErrorLine("Error in TimeForExercise: " + str(e1)) return False # ---------- GenExercise::GetGeneratorTime---------------------------------- def GetGeneratorTime(self): try: GenTimeStr = "" data = self.SendCommand("generator: status_json") Status = {} Status = json.loads(data) TimeDict = self.FindDictValueInListByKey("Time", Status["Status"]) if TimeDict != None: TimeDictStr = self.FindDictValueInListByKey( "Generator Time", TimeDict) if TimeDictStr != None or not len(TimeDictStr): GenTimeStr = TimeDictStr # Format is "Wednesday March 6, 2019 13:10" or " "Friday May 3, 2019 11:11" GenTime = datetime.datetime.strptime( GenTimeStr, "%A %B %d, %Y %H:%M") else: self.LogError( "Error getting generator time! Genmon may be starting up." ) GenTime = datetime.datetime.now() else: self.LogError("Error getting generator time (2)!") GenTime = datetime.datetime.now() return GenTime except Exception as e1: self.LogErrorLine("Error in GetGeneratorTime: " + str(e1) + ": " + GenTimeStr) return datetime.datetime.now() # ---------- GenExercise::ExerciseThread------------------------------------ def ExerciseThread(self): time.sleep(1) while True: try: if not self.ExerciseActive: if self.TimeForExercise(): self.StartExercise() if self.WaitForExit("ExerciseThread", float(self.PollTime)): return except Exception as e1: self.LogErrorLine("Error in ExerciseThread: " + str(e1)) if self.WaitForExit("ExerciseThread", float(self.PollTime)): return # ----------GenExercise::SignalClose---------------------------------------- def SignalClose(self, signum, frame): self.Close() sys.exit(1) # ----------GenExercise::Close---------------------------------------------- def Close(self): self.KillThread("ExerciseThread") if self.ExerciseActive: try: self.WarmupTimer.cancel() except: pass try: self.StopTimer.cancel() except: pass self.StopExercise() self.Generator.Close()
class GenTankData(MySupport): # The device is the ADS1115 I2C ADC # reference python http://www.smartypies.com/projects/ads1115-with-raspberrypi-and-python/ads1115runner/ RESET_ADDRESS = 0b0000000 RESET_COMMAND = 0b00000110 POINTER_CONVERSION = 0x0 POINTER_CONFIGURATION = 0x1 POINTER_LOW_THRESHOLD = 0x2 POINTER_HIGH_THRESHOLD = 0x3 #------------ GenTankData::init--------------------------------------------- def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort): super(GenTankData, self).__init__() self.LogFileName = os.path.join(loglocation, "gentankdiy.log") self.AccessLock = threading.Lock() # log errors in this module to a file self.log = SetupLogger("gentankdiy", self.LogFileName) self.console = SetupLogger("gentankdiy_console", log_file="", stream=True) self.MonitorAddress = host self.PollTime = 2 self.debug = False configfile = os.path.join(ConfigFilePath, 'gentankdiy.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gentankdiy', log=self.log) self.PollTime = self.config.ReadValue('poll_frequency', return_type=float, default=60) self.debug = self.config.ReadValue('debug', return_type=bool, default=False) self.i2c_address = self.config.ReadValue('i2c_address', return_type=int, default=72) self.mv_per_step = self.config.ReadValue('mv_per_step', return_type=int, default=125) self.Multiplier = self.config.ReadValue( 'volts_to_percent_multiplier', return_type=float, default=20.0) # I2C channel 1 is connected to the GPIO pins self.i2c_channel = self.config.ReadValue('i2c_channel', return_type=int, default=1) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) try: try: startcount = 0 while startcount <= 10: try: self.Generator = ClientInterface( host=self.MonitorAddress, port=port, log=self.log) break except Exception as e1: startcount += 1 if startcount >= 10: self.console.info("genmon not loaded.") self.LogError("Unable to connect to genmon.") sys.exit(1) time.sleep(1) continue except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) # start thread monitor time for exercise self.Threads["TankCheckThread"] = MyThread(self.TankCheckThread, Name="TankCheckThread", start=False) if not self.InitADC(): self.LogError("InitADC failed, exiting") sys.exit(1) self.Threads["TankCheckThread"].Start() atexit.register(self.Close) signal.signal(signal.SIGTERM, self.Close) signal.signal(signal.SIGINT, self.Close) except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) self.console.error("Error in GenTankData init: " + str(e1)) sys.exit(1) #---------- GenTankData::SendCommand -------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data # ---------- GenTankData::InitADC------------------------------------------- def InitADC(self): try: # I2C channel 1 is connected to the GPIO pins self.I2Cbus = smbus.SMBus(self.i2c_channel) # Reset ADC self.I2Cbus.write_byte(self.RESET_ADDRESS, self.RESET_COMMAND) # set config register and start conversion # ANC1 and GND, 4.096v, 128s/s # Customized - Port A0 and 4.096 V input # 0b11000011; # bit 15-8 = 0xC3 # bit 15 flag bit for single shot # Bits 14-12 input selection: # 100 ANC0; 101 ANC1; 110 ANC2; 111 ANC3 # Bits 11-9 Amp gain. Default to 010 here 001 P19 # Bit 8 Operational mode of the ADS1115. # 0 : Continuous conversion mode # 1 : Power-down single-shot mode (default) CONFIG_VALUE_1 = 0xC3 # bits 7-0 0b10000101 = 0x85 # Bits 7-5 data rate default to 100 for 128SPS # Bits 4-0 comparator functions see spec sheet. CONFIG_VALUE_2 = 0x85 self.I2Cbus.write_i2c_block_data(self.i2c_address, self.POINTER_CONFIGURATION, [CONFIG_VALUE_1, CONFIG_VALUE_2]) self.LogDebug("I2C Init complete: success") except Exception as e1: self.LogErrorLine("Error calling InitADC: " + str(e1)) return False return True # ---------- GenTankData::GetGaugeData-------------------------------------- def GetGaugeData(self): try: val = self.I2Cbus.read_i2c_block_data(self.i2c_address, self.POINTER_CONVERSION, 2) self.LogDebug(str(val)) # convert display results reading = val[0] << 8 | val[1] if (reading < 0): reading = 0 #reading = self.I2Cbus.read_word_data(self.i2c_address, self.i2c_channel) volts = round( float(reading * (float(self.mv_per_step) / 1000000.0)), 2) gauge_data = float(self.Multiplier) * volts self.LogDebug("Reading Gauge Data: %4.2f%%" % gauge_data) return gauge_data except Exception as e1: self.LogErrorLine("Error calling GetGaugeData: " + str(e1)) return 0.0 # ---------- GenTankData::TankCheckThread----------------------------------- def TankCheckThread(self): time.sleep(1) while True: try: dataforgenmon = {} tankdata = self.GetGaugeData() if tankdata != None: dataforgenmon["Tank Name"] = "External Tank" dataforgenmon["Capacity"] = 0 dataforgenmon["Percentage"] = self.GetGaugeData() retVal = self.SendCommand("generator: set_tank_data=" + json.dumps(dataforgenmon)) self.LogDebug(retVal) if self.WaitForExit("TankCheckThread", float(self.PollTime * 60)): return except Exception as e1: self.LogErrorLine("Error in TankCheckThread: " + str(e1)) if self.WaitForExit("TankCheckThread", float(self.PollTime * 60)): return # ----------GenTankData::Close---------------------------------------------- def Close(self): self.KillThread("TankCheckThread") self.Generator.Close()
MyClientInterface = ClientInterface(host=address, port=port, log=log) break except Exception as e1: startcount += 1 if startcount >= 2: console.error("Error: genmon not loaded.") sys.exit(1) time.sleep(1) continue try: while True: try: line = raw_input(">") except NameError: pass line = input(">") if line.lower() == "exit": break if len(line): data = MyClientInterface.ProcessMonitorCommand(line) print(data) except Exception as e1: console.error("Error: " + str(e1)) MyClientInterface.Close()
class GenNotify(MyCommon): def __init__(self, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, log = None, loglocation = ProgramDefaults.LogPath, onready = None, onexercise = None, onrun = None, onrunmanual = None, onalarm = None, onservice = None, onoff = None, onmanual = None, onutilitychange = None, start = True, console = None): super(GenNotify, self).__init__() self.AccessLock = threading.Lock() self.Threads = {} self.LastEvent = None self.LastOutageStatus = None self.Events = {} # Dict for handling events self.log = log self.console = console try: # init event callbacks if onready != None: self.Events["READY"] = onready if onexercise != None: self.Events["EXERCISING"] = onexercise if onrun != None: self.Events["RUNNING"] = onrun if onrunmanual != None: self.Events["RUNNING-MANUAL"] = onrunmanual if onalarm != None: self.Events["ALARM"] = onalarm if onservice != None: self.Events["SERVICEDUE"] = onservice if onoff != None: self.Events["OFF"] = onoff if onmanual != None: self.Events["MANUAL"] = onmanual if onutilitychange != None: self.Events["OUTAGE"] = onutilitychange self.Generator = ClientInterface(host = host, port = port, log = log, loglocation = loglocation) self.Threads["PollingThread"] = MyThread(self.MainPollingThread, Name = "PollingThread", start = start) self.Started = start except Exception as e1: self.LogErrorLine("Error in mynotify init: " + str(e1)) # ---------- GenNotify::MainPollingThread----------------------------------- def StartPollThread(self): if not self.Started: self.Threads["PollingThread"].Start() self.Started = True # ---------- GenNotify::MainPollingThread----------------------------------- def MainPollingThread(self): while True: try: data = self.SendCommand("generator: getbase") OutageState = self.GetOutageState() if OutageState != None: self.ProcessOutageState(OutageState) if self.LastEvent == data: time.sleep(3) continue if self.LastEvent != None: self.console.info( "Last : <" + self.LastEvent + ">, New : <" + data + ">") self.CallEventHandler(False) # end last event self.LastEvent = data self.CallEventHandler(True) # begin new event time.sleep(3) except Exception as e1: self.LogErrorLine("Error in mynotify:MainPollingThread: " + str(e1)) time.sleep(3) #---------- GenNotify::GetOutageState ------------------------------------- def GetOutageState(self): OutageState = None outagedata = self.SendCommand("generator: outage_json") try: OutageDict = collections.OrderedDict() OutageDict = json.loads(outagedata) OutageList = OutageDict["Outage"] for Items in OutageList: for key, value in Items.items(): if key == "System In Outage": if value.lower() == "yes": return True else: return False except Exception as e1: # The system does no support outage tracking (i.e. H-100) self.LogErrorLine("Unable to get outage state: " + str(e1)) OutageState = None return OutageState #---------- GenNotify::CallEventHandler ----------------------------------- def CallEventHandler(self, Status): try: if self.LastEvent == None: return EventCallback = self.Events.get(self.LastEvent, None) # Event has ended if EventCallback != None: if callable(EventCallback): EventCallback(Status) else: self.LogError("Invalid Callback in CallEventHandler : " + str(EventCallback)) else: self.LogError("Invalid Callback in CallEventHandler : None") except Exception as e1: self.LogErrorLine("Error in CallEventHandler: " + str(e1)) #---------- GenNotify::ProcessOutageState --------------------------------- def ProcessOutageState(self, outagestate): try: if self.LastOutageStatus == outagestate: return self.LastOutageStatus = outagestate EventCallback = self.Events.get("OUTAGE", None) if EventCallback != None: if callable(EventCallback): EventCallback(self.LastOutageStatus) else: self.LogError("Invalid Callback in ProcessOutageState : " + str(EventCallback)) else: self.LogError("Invalid Callback in ProcessOutageState : None") except Exception as e1: self.LogErrorLine("Error in ProcessOutageState: " + str(e1)) #---------- GenNotify::SendCommand ---------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data #---------- GenNotify::Close ---------------------------------------------- def Close(self): try: self.KillThread("PollingThread") self.Generator.Close() except Exception as e1: pass return False
class GenNotify(MyCommon): def __init__(self, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, log=None, loglocation=ProgramDefaults.LogPath, onready=None, onexercise=None, onrun=None, onrunmanual=None, onalarm=None, onservice=None, onoff=None, onmanual=None, onutilitychange=None, onsoftwareupdate=None, onsystemhealth=None, onfuelstate=None, start=True, console=None): super(GenNotify, self).__init__() self.AccessLock = threading.Lock() self.Threads = {} self.LastEvent = None self.LastOutageStatus = None self.LastSoftwareUpdateStatus = None self.LastSystemHealth = None self.LastFuelWarningStatus = None self.Events = {} # Dict for handling events self.log = log self.console = console try: # init event callbacks if onready != None: self.Events["READY"] = onready if onexercise != None: self.Events["EXERCISING"] = onexercise if onrun != None: self.Events["RUNNING"] = onrun if onrunmanual != None: self.Events["RUNNING-MANUAL"] = onrunmanual if onalarm != None: self.Events["ALARM"] = onalarm if onservice != None: self.Events["SERVICEDUE"] = onservice if onoff != None: self.Events["OFF"] = onoff if onmanual != None: self.Events["MANUAL"] = onmanual if onutilitychange != None: self.Events["OUTAGE"] = onutilitychange if onsoftwareupdate != None: self.Events["SOFTWAREUPDATE"] = onsoftwareupdate if onsystemhealth != None: self.Events["SYSTEMHEALTH"] = onsystemhealth if onfuelstate != None: self.Events["FUELWARNING"] = onfuelstate self.Generator = ClientInterface(host=host, port=port, log=log, loglocation=loglocation) self.Threads["PollingThread"] = MyThread(self.MainPollingThread, Name="PollingThread", start=start) self.Started = start except Exception as e1: self.LogErrorLine("Error in mynotify init: " + str(e1)) # ---------- GenNotify::MainPollingThread----------------------------------- def StartPollThread(self): if not self.Started: self.Threads["PollingThread"].Start() self.Started = True # ---------- GenNotify::MainPollingThread----------------------------------- def MainPollingThread(self): while True: try: OutageState = self.GetOutageState() self.GetMonitorState() self.GetMaintState() data = self.SendCommand("generator: getbase") if self.LastEvent == data: time.sleep(3) continue if self.LastEvent != None: self.console.info("Last : <" + self.LastEvent + ">, New : <" + data + ">") self.CallEventHandler(False) # end last event self.LastEvent = data self.CallEventHandler(True) # begin new event time.sleep(3) except Exception as e1: self.LogErrorLine("Error in mynotify:MainPollingThread: " + str(e1)) time.sleep(3) #---------- GenNotify::GetOutageState ------------------------------------- def GetOutageState(self): OutageState = None outagedata = self.SendCommand("generator: outage_json") try: OutageDict = collections.OrderedDict() OutageDict = json.loads(outagedata) OutageList = OutageDict["Outage"] for Items in OutageList: for key, value in Items.items(): if key == "Status" and value == "Not Supported": return None if key == "System In Outage": if value.lower() == "yes": OutageState = True else: OutageState = False except Exception as e1: # The system does no support outage tracking (i.e. H-100) self.LogErrorLine("Unable to get outage state: " + str(e1)) OutageState = None if OutageState != None: self.ProcessEventData("OUTAGE", OutageState, self.LastOutageStatus) self.LastOutageStatus = OutageState return OutageState #---------- GenNotify::GetMonitorState ------------------------------------ def GetMonitorState(self): UpdateAvailable = None try: monitordata = self.SendCommand("generator: monitor_json") GenDict = collections.OrderedDict() GenDict = json.loads(monitordata) GenList = GenDict["Monitor"][0]["Generator Monitor Stats"] for Items in GenList: for key, value in Items.items(): if key == "Update Available": if value.lower() == "yes": UpdateAvailable = True else: UpdateAvailable = False self.ProcessEventData("SOFTWAREUPDATE", UpdateAvailable, self.LastSoftwareUpdateStatus) self.LastSoftwareUpdateStatus = UpdateAvailable if key == "Monitor Health": self.ProcessEventData("SYSTEMHEALTH", value, self.LastSystemHealth) self.LastSystemHealth = value except Exception as e1: # The system does no support outage tracking (i.e. H-100) self.LogErrorLine("Unable to get moniotr state: " + str(e1)) UpdateAvailable = None return UpdateAvailable #---------- GenNotify::GetMaintState -------------------------------------- def GetMaintState(self): FuelOK = None try: maintdata = self.SendCommand("generator: maint_json") GenDict = collections.OrderedDict() GenDict = json.loads(maintdata) GenList = GenDict["Maintenance"] for Items in GenList: for key, value in Items.items(): if key == "Fuel Level State": if value.lower() == "OK": FuelOK = True else: FuelOK = False self.ProcessEventData("FUELWARNING", FuelOK, self.LastFuelWarningStatus) self.LastFuelWarningStatus = FuelOK except Exception as e1: # The system does no support outage tracking (i.e. H-100) self.LogErrorLine("Unable to get maint state: " + str(e1)) FuelOK = None return FuelOK #---------- GenNotify::ProcessEventData -------------------------------- def ProcessEventData(self, name, eventdata, lastvalue): try: if eventdata == None: return if lastvalue == eventdata: return lastvalue = eventdata EventCallback = self.Events.get(name, None) if EventCallback != None: if callable(EventCallback): EventCallback(lastvalue) else: self.LogError("Invalid Callback in ProcessEventData : " + name + ": " + str(EventCallback)) else: self.LogError( "Invalid Callback in ProcessEventData : None : " + name) except Exception as e1: self.LogErrorLine("Error in ProcessEventData: " + name + ": " + str(e1)) #---------- GenNotify::CallEventHandler ----------------------------------- def CallEventHandler(self, Status): try: if self.LastEvent == None: return EventCallback = self.Events.get(self.LastEvent, None) # Event has ended if EventCallback != None: if callable(EventCallback): EventCallback(Status) else: self.LogError("Invalid Callback in CallEventHandler : " + str(EventCallback)) else: self.LogError("Invalid Callback in CallEventHandler : None") except Exception as e1: self.LogErrorLine("Error in CallEventHandler: " + str(e1)) #---------- GenNotify::SendCommand ---------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data #---------- GenNotify::Close ---------------------------------------------- def Close(self): try: self.KillThread("PollingThread") self.Generator.Close() except Exception as e1: pass return False
class MyGenPush(MySupport): #------------ MyGenPush::init----------------------------------------------- def __init__(self, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, log = None, callback = None, polltime = None, blacklist = None, flush_interval = float('inf'), use_numeric = False, debug = False, loglocation = ProgramDefaults.LogPath, console = None): super(MyGenPush, self).__init__() self.Callback = callback self.UseNumeric = use_numeric self.debug = debug self.Exiting = False if polltime == None: self.PollTime = 3 else: self.PollTime = float(polltime) if log != None: self.log = log else: # log errors in this module to a file self.log = SetupLogger("client", os.path.join(loglocation, "mygenpush.log")) self.console = console self.AccessLock = threading.Lock() self.BlackList = blacklist self.LastValues = {} self.FlushInterval = flush_interval self.LastChange = {} try: self.Generator = ClientInterface(host = host, port = port, log = log) self.GetGeneratorStartInfo() # start thread to accept incoming sockets for nagios heartbeat self.Threads["MainPollingThread"] = MyThread(self.MainPollingThread, Name = "MainPollingThread", start = False) self.Threads["MainPollingThread"].Start() except Exception as e1: self.LogErrorLine("Error in mygenpush init: " + str(e1)) #---------- MyGenPush::ControllerIsEvolution2 ----------------------------- def ControllerIsEvolution2(self): try: if "evolution 2.0" in self.StartInfo["Controller"].lower(): return True return False except Exception as e1: self.LogErrorLine("Error in ControllerIsEvolution2: " + str(e1)) return False #---------- MyGenPush::ControllerIsEvolutionNexus ------------------------- def ControllerIsEvolutionNexus(self): try: if self.ControllerIsEvolution() or self.ControllerIsNexus: return True return False except Exception as e1: self.LogErrorLine("Error in ControllerIsEvolutionNexus: " + str(e1)) return False #---------- MyGenPush::ControllerIsEvolution ------------------------------ def ControllerIsEvolution(self): try: if "evolution" in self.StartInfo["Controller"].lower(): return True return False except Exception as e1: self.LogErrorLine("Error in ControllerIsEvolution: " + str(e1)) return False #---------- MyGenPush::ControllerIsNexius --------------------------------- def ControllerIsNexius(self): try: if "nexus" in self.StartInfo["Controller"].lower(): return True return False except Exception as e1: self.LogErrorLine("Error in ControllerIsNexius: " + str(e1)) return False #---------- MyGenPush::ControllerIsGeneracH100 ---------------------------- def ControllerIsGeneracH100(self): try: if "h-100" in self.StartInfo["Controller"].lower() or "g-panel" in self.StartInfo["Controller"].lower(): return True return False except Exception as e1: self.LogErrorLine("Error in ControllerIsGeneracH100: " + str(e1)) return False #---------- MyGenPush::ControllerIsGeneracPowerZone ----------------------- def ControllerIsGeneracPowerZone(self): try: if "powerzone" in self.StartInfo["Controller"].lower(): return True return False except Exception as e1: self.LogErrorLine("Error in ControllerIsGeneracPowerZone: " + str(e1)) return False #---------- MyGenPush::GetGeneratorStartInfo ------------------------------ def GetGeneratorStartInfo(self): try: data = self.SendCommand("generator: start_info_json") self.StartInfo = {} self.StartInfo = json.loads(data) return True except Exception as e1: self.LogErrorLine("Error in GetGeneratorStartInfo: " + str(e1)) return False #---------- MyGenPush::SendCommand ---------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data # ---------- MyGenPush::MainPollingThread----------------------------------- def MainPollingThread(self): while True: try: if not self.UseNumeric: statusdata = self.SendCommand("generator: status_json") maintdata = self.SendCommand("generator: maint_json") outagedata = self.SendCommand("generator: outage_json") monitordata = self.SendCommand("generator: monitor_json") else: statusdata = self.SendCommand("generator: status_num_json") maintdata = self.SendCommand("generator: maint_num_json") outagedata = self.SendCommand("generator: outage_num_json") monitordata = self.SendCommand("generator: monitor_num_json") try: GenmonDict = {} TempDict = {} TempDict = json.loads(statusdata) GenmonDict["Status"] = TempDict["Status"] TempDict = json.loads(maintdata) GenmonDict["Maintenance"] = TempDict["Maintenance"] TempDict = json.loads(outagedata) GenmonDict["Outage"] = TempDict["Outage"] TempDict = json.loads(monitordata) GenmonDict["Monitor"] = TempDict["Monitor"] self.CheckDictForChanges(GenmonDict, "generator") except Exception as e1: self.LogErrorLine("Unable to get status: " + str(e1)) if self.WaitForExit("MainPollingThread", float(self.PollTime)): return except Exception as e1: self.LogErrorLine("Error in mynotify:MainPollingThread: " + str(e1)) if self.WaitForExit("MainPollingThread", float(self.PollTime)): return #------------ MyGenPush::CheckDictForChanges ------------------------------- # This function is recursive, it will turn a nested dict into a flat dict keys # that have a directory structure with corrposonding values and determine if # anyting changed. If it has then call our callback function def CheckDictForChanges(self, node, PathPrefix): CurrentPath = PathPrefix if not isinstance(PathPrefix, str): return "" if isinstance(node, dict): for key, item in node.items(): if isinstance(item, dict): CurrentPath = PathPrefix + "/" + str(key) self.CheckDictForChanges(item, CurrentPath) elif isinstance(item, list): CurrentPath = PathPrefix + "/" + str(key) if self.ListIsStrings(item): # if this is a list of strings, the join the list to one comma separated string self.CheckForChanges(CurrentPath, ', '.join(item)) else: for listitem in item: if isinstance(listitem, dict): self.CheckDictForChanges(listitem, CurrentPath) else: self.LogError("Invalid type in CheckDictForChanges: %s %s (2)" % (key, str(type(listitem)))) else: CurrentPath = PathPrefix + "/" + str(key) self.CheckForChanges(CurrentPath, item) else: self.LogError("Invalid type in CheckDictForChanges %s " % str(type(node))) # ---------- MyGenPush::ListIsStrings--------------------------------------- # return true if every element of list is a string def ListIsStrings(self, listinput): try: if not isinstance(listinput, list): return False for item in listinput: if sys.version_info[0] < 3: if not (isinstance(item, str) or isinstance(item, unicode)): return False else: if not (isinstance(item, str) or isinstance(item, bytes)): return False return True except Exception as e1: self.LogErrorLine("Error in ListIsStrings: " + str(e1)) return False # ---------- MyGenPush::CheckForChanges------------------------------------- def CheckForChanges(self, Path, Value): try: if self.BlackList != None: for BlackItem in self.BlackList: if BlackItem.lower() in Path.lower(): return LastValue = self.LastValues.get(str(Path), None) LastChange = self.LastChange.get(str(Path), 0) if LastValue == None or LastValue != Value or (time.time() - LastChange) > self.FlushInterval: self.LastValues[str(Path)] = Value self.LastChange[str(Path)] = time.time() if self.Callback != None: self.Callback(str(Path), Value) except Exception as e1: self.LogErrorLine("Error in mygenpush:CheckForChanges: " + str(e1)) # ---------- MyGenPush::Close----------------------------------------------- def Close(self): self.Exiting = True self.KillThread("MainPollingThread") self.Generator.Close()