def isTime(lastTime, frequency): """ Will calculate whether to do something based on how frequent the action should be. E.g. if something should happen every 6 seconds and the last time it was completed was 5 seconds ago this will return False. Inputs: * lastTime => The timestamp of the last time the action was completed. [datetime.datetime object] * frequency => The frequency at which to do the action. [datetime.timedelta] Outputs: * boolean (whether to do the action), (datetime.datetime) the last time the action was carried out. """ if type(lastTime) != dt.datetime: err.printLog("ERROR: Wrong Format of datetime") return False, False timeNow = dt.datetime.now() if lastTime > timeNow: err.printLog("ERROR: lastTime more than current time!") return False, False timeDiff = timeNow - lastTime if timeDiff > frequency: return True, timeNow else: return False, lastTime
def moveCSVData(currData): """ Will move the current data file for permanent storage on the USB pen. Outputs: * returns new data object (or bad exit code) """ currPath = const.dataFilepath # Let this error slide a couple of times if not os.path.isfile(currPath): const.badPathWarnings += 1 return currData if const.badPathWarnings > 3: err.printLog("ERROR: The data file hasn't been created") return False # Format the date and time to make a filename timeNow = tutils.strTimeNow().replace(" ", "_") timeNow = tutils.strTimeNow().replace(":", "x") timeNow = timeNow.replace("/", "-") newPath = "%s/%s.csv" % (const.permDataStoragePath, timeNow) carryOn = moveFile(currPath, newPath) if carryOn is False: return False return getCurrData()
def takePic(test=False): """ Will take a picture using the picamera and the picamera module. This involves switching off the grow light, switching on the flash finding the correct filename and then taking the pic and storing it under that filename. """ # Turn off grow lights and turn on flash lUt.switchLight('GrowLight', 'off') lUt.switchLight('Flash', 'on') # The with statement seems to work fine being called in a loop. # Memory leaks will be seen if picam is not properly closed and the # code will crash. with picam.PiCamera() as cam: cam.resolution = (1640, 1232) cam.start_preview() time.sleep(5) fileName = getNewFileNumber(const.imgFolder) if test: fileName = "testPic.jpg" if os.path.isfile(fileName): os.remove(fileName) # Actually take the picture cam.capture(fileName) cam.stop_preview() err.printLog("INFO: Taking pic `%s'" % fileName) lUt.switchLight('Flash', 'off')
def initBluetooth(macAddress, port): """ Will initialise the bluetooth module. """ s = bluetooth.BluetoothSocket(bluetooth.RFCOMM) # no need for a try and except here as errors are already handled s.connect((macAddress, port)) err.printLog("INFO: Bluetooth Connected") return s
def testUSBAddress(): """ Will check if the USB drive is recognised and mounted. It must be mounted at const.permDataStoragePath. """ if not os.path.isdir(const.permDataStoragePath): err.printLog("ERROR: USB drive not mounted at %s" % const.permDataStoragePath) return False else: err.printLog("INFO: USB drive found at %s" % const.permDataStoragePath) return True
def moveFile(currFile, newFile): """ Will move a file to the USB. Inputs: * currFile => the current file that need moving * newFile => the new filepath for the file Outputs: exit_code """ if not os.path.isfile(currFile): msg = "ERROR: Can't find file %s" % currFile err.printLog(msg) return False newFolder = newFile[:newFile.rfind('/')] if not os.path.isdir(newFolder): msg = "ERROR: Can't find folder %s" % newFolder err.printLog(msg) return False try: shutil.move(currFile, newFile) except PermissionError as e: msg = "ERROR: you don't have permissions to move file " msg += "%s to %s" % (currFile, newFile) msg += "\n\terror = %s" % (str(e)) err.printLog(msg) return False except Exception as e: msg = "ERROR: an error occured when trying to move" msg += " file %s to %s" % (currFile, newFile) msg += "\n\terror = %s" % (str(e)) err.printLog(msg) return False finally: msg = "ERROR: Unknown in moveFile" err.printLog(msg + "\n\targ1 = '%s' arg2 = '%s'" % (currFile, newFile)) return False return True
def setLastTimes(key, value): """ Will set the lastTimes dictionaries Inputs: * key => the name of the last time to change [str] * value => the value to which the last time should be changed. [datetime.datetime] Outputs: * exit_code """ if type(value) != dt.datetime: err.printLog("ERROR: wrong type given for value in lastTimes") return False if key not in const.allLastTimeVals: err.printLog("ERROR: key `%s` not in the allLastTimeVals" % key) msg = "\n\tKeys are:\n\t* %s" % "\n\t* ".join(const.allLastTimeVals) err.printLog(msg) return False lastTimes = getLastTimes() if lastTimes[key] != value: lastTimes[key] = value # set new val # Should be strings for writing lastTimes = { i: dt.datetime.strftime(lastTimes[i], const.timeFormat) for i in lastTimes } with open(const.lastTimeFilepath, 'w') as f: json.dump(lastTimes, f) return True
def getTempHumid(): """ Will get the temperature and humidity from the DHT's in the grow tent. """ # Get readings allHumid, allTemp = [], [] failedPins = [] for sensor, pin in const.sensorPins['DHT']: humidity, temperature = Adafruit_DHT.read_retry(sensor, pin) if humidity is None or temperature is None: failedPins.append((pin, sensor, 'humidity')) failedPins.append((pin, sensor, 'temperature')) else: allHumid.append(humidity) allTemp.append(temperature) # Report any failed pins for pin, sensor, measurement in failedPins: msg = "WARNING | SENSOR BROKEN\n" msg += "\t Pin = %i\n" % pin msg += "\t Type = DHT%s" % (str(sensor)) msg += "\t Measurement = %s" % measurement err.printLog(msg) # Do some analysis (exclude outliers and average) tMean, hMean = allTemp[0], allHumid[0] tBest, hBest = None, None if len(allHumid) > 1: tStd, hStd = np.std(allHumid) * 2, np.std(allTemp) * 2 tMean, hMean = np.mean(allTemp), np.mean(allHumid) tBest = [t for t in allTemp if tMean - tStd < t < tMean + tStd] hBest = [h for h in allHumid if hMean - hStd < h < hMean + hStd] tBest = np.mean(tBest) hBest = np.mean(hBest) humidData = {'all': allHumid, 'mean': hMean, 'real': hBest} tempData = {'all': allTemp, 'mean': tMean, 'real': tBest} return humidData, tempData
def getLastTimes(key=False): """ Will read the lastTimes filepath. If it is not there then this will return a dictionary with all values set to the current time. Inputs: * key => the name of the lastTime variable to get Outputs: * dependent on the value of key, either dict of all lastTimes or single lastTime. """ if os.path.isfile(const.lastTimeFilepath): with open(const.lastTimeFilepath, 'r') as f: try: data = json.load(f) except json.JSONDecodeError: err.printLog( "WARN: Reseting lastTimes dict as json is corrupted.") data = {i: strTimeNow() for i in const.allLastTimeVals} else: # defaults to current time data = {i: strTimeNow() for i in const.allLastTimeVals} for mKey in const.allLastTimeVals: if mKey not in data: data[mKey] = strTimeNow() # If a specific key is requested then return that data if key: if key in data: return dt.datetime.strptime(data[key], const.timeFormat) elif key in const.allLastTimeVals: timeNow = dt.datetime.now() setLastTimes(key, timeNow) return timeNow else: err.printLog( "ERROR: key `%s` not available in the lastTimes dictionary" % key) msg = "\n\tKeys are:\n\t* %s" % "\n\t* ".join( const.allLastTimeVals) err.printLog(msg) return False data = {i: dt.datetime.strptime(data[i], const.timeFormat) for i in data} return data
def doEvent(eventName, function, timeDelta, *args): """ Will check if the event `eventName' needs doing (by checking the lastTimes dict). If it does need doing the function will be called. Inputs: * eventName => the name of the event in the lastTimes dictionary [str] * function => the action to be carried out if the event needs doing. This function must return a dictionary with one key for an 'exit_code', and one key for 'values'. [func] * timeDelta => How often the event needs doing [dt.timedelta] * *args => The arguments to pass to the function [tuple] Outputs: * Exit code (False means quit loop, True is OK) """ carryOn = True if eventName not in const.allLastTimeVals: msg = "ERROR: Can't find the event called %s" % eventName msg += " in the allLastTimeVals list." err.printLog(msg) return False, False allGood, msg = err.typeCheck(timeDelta, dt.timedelta, "timeDelta (func doEvent)") if allGood is False: err.printLog("ERROR: %s" % msg) return False, False # Get the last time the event occured and check if it is time for it again lastTime = getLastTimes(eventName) doEvent, newLastTime = isTime(lastTime, timeDelta) # Actually carry out the func carryOn *= bool(newLastTime) if doEvent: funcDict = function(*args) else: funcDict = {'exit_code': True} allGood, msg = err.typeCheck(funcDict, dict, "funcDict (func doEvent)") if allGood is False: err.printLog("ERROR: %s" % msg) return False, False # Handle exit codes if 'exit_code' in funcDict: carryOn *= funcDict['exit_code'] # Catch any naughty functions not returning with an exit code else: msg = "WARN: The function `%s` returns no exit code. Code this up!" % str( function) err.printLog(msg) # Will get the values returned if 'values' in funcDict: returners = funcDict['values'] else: returners = False # Set the new 'lastTime' the event occured carryOn *= setLastTimes(eventName, newLastTime) return carryOn, returners