class DeviceManager(): INSTANCE = None # Singleton pattern def __new__(cls, *args, **kwargs): if not cls.INSTANCE: cls.INSTANCE = super(DeviceManager, cls).__new__( cls, *args, **kwargs) return cls.INSTANCE def __init__(self): # Instantiate Logger self.LOGGER = Debug.getLogger("energyathome.datalogger.serialcomm") self.COMM = serial.Serial() # Create Config manager self.CONFIG = ConfigManager() # Open serial connection def open(self): '''Open connection to USB to serial port''' self.COMM.port = self.CONFIG.getConfig("SerialConnection", "serialport") self.COMM.baudrate = self.CONFIG.getIntConfig("SerialConnection", "baudrate") self.COMM.parity = self.CONFIG.getConfig("SerialConnection", "parity") self.COMM.timeout = None self.COMM.bytesize = self.CONFIG.getIntConfig("SerialConnection", "bytesize") if(self.COMM.isOpen() == False): self.LOGGER.info('Establishing connection to device') self.COMM.open() else: self.LOGGER.info('Connection already opened') self.LOGGER.info('Device connected:' + str(self.COMM.isOpen())) # Close serial connection def close(self): '''Close connection to USB to Serial port''' if(self.COMM.isOpen): self.LOGGER.info('Closing device connection') self.COMM.close() self.LOGGER.info('Device connection closed') # Get data from serial connection def read(self): '''Read data from USB to Serial device. Connection must be established.''' data = self.COMM.readline() if(len(data) > 0): self.LOGGER.debug('serial data: ' + data) return data
class BackupRestore: def __init__(self): # Instantiate Logger self.LOGGER = Debug.getLogger("energyathome.datalogger.offlinehandler") # Configuration Manager self.CONFIG = ConfigManager() # Data validation class self.VALIDATOR = CheckLiveData() # Data trigger class self.TRIGGER = CheckLiveTriggers() def backup(self, historicalData): '''Writes historical data to file''' #If true exit due to exception exit = False # Get file path from config location = self.CONFIG.getConfig("Application", "offlineFile") #Check if config is empty if len(location) == 0: # Use current directory location = os.path.join(os.getcwd(), "backup.p") self.LOGGER.warning("offlineFile is empty. Using default: '" + location + "'") try: # Append to file path = file(location, "a") # Ensure object has data if historicalData is not None: self.LOGGER.info("Writing data to file:" + str(historicalData.__dict__)) pickle.dump(historicalData, path) except IOError: #Debug.writeOut("Unable to write to backup file: '" + location + "'") # No point running program if it's unable to write to file exit = True finally: # Close file try: path.close() except UnboundLocalError: # File was not opened pass # Check to exit if exit: Core.shutdown() def restore(self): '''Starts the restore process from a file.''' backup = self.restoreFromFile() recordCount = len(backup) if(recordCount > 0): self.LOGGER.info(str(recordCount) + " record(s) found. Saving to DB") for record in backup: # Set data as valid by default hDataValid = True # Get backup record hData = record if self.VALIDATOR is not None and self.CONFIG.getBooleanConfig("Tolerence", "enabled"): try: validatedData = self.VALIDATOR.validateData(hData) except Exception as e: raise e hDataValid = validatedData[0] if hDataValid is True: hData = validatedData[1] if hDataValid and self.CONFIG.getBooleanConfig("Trigger", "enabled"): # Check trigger conditions which return true or false if it's valid try: hDataValid = self.TRIGGER.checkTriggers(hData) except Exception as e: raise e # Insert the first element in list and remove it if hDataValid: self.LOGGER.info("Inserting: " + str(record.__dict__)) #HistoricalData.insertData(record) HistoricalData.insertData(hData) else: self.LOGGER.info("Skipped backup record") # Remove backup file to prevent duplicate data from being restored. self.LOGGER.info("Restore from backup complete.") self.LOGGER.info("Removing backup file.") self.LOGGER.info("File deleted? " + str(self.deleteBackupFile())) def restoreFromFile(self): '''Reads a file and returns an array of objects.''' # List to store restored objects list = [] # Get file path from config location = self.CONFIG.getConfig("Application", "offlineFile") #Check if config is empty if len(location) == 0: # Use current directory location = os.path.join(os.getcwd(), "backup.p") self.LOGGER.info("offlineFile is empty. Using default: '" + location + "'") # Check file exists before deleting if os.path.exists(location): # Create file object path = file(location, "r") # Read file till end of file try: while True: list.append(pickle.load(path)) except EOFError: # Ignore end of file error pass finally: # Close file path.close() self.LOGGER.info("Found " + str(len(list)) + " record(s) in '" + location + "'") return list def deleteBackupFile(self): '''Removed backup file if it exists.''' success = False; # Get file path from config location = self.CONFIG.getConfig("Application", "offlineFile") #Check if config is empty if len(location) == 0: # Use current directory location = os.path.join(os.getcwd(), "backup.p") self.LOGGER.info("offlineFile is empty. Using default: '" + location + "'") # Check file exists before deleting if os.path.exists(location): # Delete file try: os.remove(location) success = True; except OSError: self.LOGGER.error("Unable to remove file: '" + location + "'") return success;
class CheckLiveData: def __init__(self): # Instantiate config manager self.CONFIG = ConfigManager() # Get logger instance self.LOGGER = Debug.getLogger("energyathome.datalogger.datavalidation") # Validate data captured from device def validateData(self, historicalData): '''Checks historical data fall within parameter which is customised in the config file. Returns results in a tuple size of 2. [0] = True or False depending if validation was successful or not [1] = HistoricalData.HistoricalData class of sanitised data''' valid = True # Get maximum appliance Id value maxAppId = self.CONFIG.getIntConfig("Tolerence", "maxApplianceId") try: # If max app id is greater than 0 then checking for app id is enabled if maxAppId is not None and maxAppId >= 0: if self.checkApplianceId(historicalData) is False: valid = False # Check device name matches in the config deviceNames = self.CONFIG.getConfig("Tolerence", "allowedDeviceNames") # If device names are defined then perform a match if deviceNames is not None and deviceNames != "": if self.checkDeviceName(historicalData) is False: valid = False if self.CONFIG.getBooleanConfig("Tolerence", "allowNewAppliances") is False: # If it returns true then it's a new appliance and should be ignored if self.checkNewAppliance(historicalData) is True: valid = False self.LOGGER.info("Appliance ID " + str(historicalData.applianceId) + " was not stored due to allowNewAppliances = False") # Check channel names if self.CONFIG.getBooleanConfig("Tolerence", "allowBlankChannelNames") is False: test = self.checkChannelNames(historicalData) # If test returned None then it failed validation. # Otherwise assign returned Historical Data because it may have changed some attributes if test is None: valid = False else: historicalData = test # Check channels if self.CONFIG.getBooleanConfig("Tolerence", "checkChannels") is True and historicalData.applianceId is not None: test = self.checkChannels(historicalData) # If test returned None then it failed validation. # Otherwise assign returned Historical Data because it may have changed some attributes if test is None: valid = False else: historicalData = test except ConnectionException as ce: raise ce return (valid, historicalData) def checkApplianceId(self, historicalData): '''Checks if appliance ID falls within a set range''' # Get maximum appliance Id value maxAppId = self.CONFIG.getIntConfig("Tolerence", "maxApplianceId") try: # Check if appliance number exceeds maximum if int(historicalData.applianceId) > maxAppId: self.LOGGER.info("Appliance ID " + str(historicalData.applianceId) + " > " + str(maxAppId)) return False else: return True except ValueError, ve: self.LOGGER.info("Check max App ID failed: " + str(historicalData.applianceId) + " > " + str(maxAppId)) return False except AttributeError as ae: self.LOGGER.info("Check max App ID failed: No attribute Error:" + str(ae)) return False