def sensorreading(sensorname, MinutesOfAverage, operation): isok = False # operation "average", "min" , "max" , "sum" if sensorname: timelist = hardwaremod.gettimedata(sensorname) theinterval = timelist[1] # minutes if theinterval > 0: samplesnumber = int(MinutesOfAverage / theinterval + 1) else: samplesnumber = 1 theinterval = 15 quantity = 0 MinutesOfAverage = samplesnumber * theinterval if samplesnumber > 0: sensordata = [] sensordbmod.getsensordbdatasamplesN(sensorname, sensordata, samplesnumber) datenow = datetime.now() # dont put UTC here !!! starttimecalc = datenow - timedelta( minutes=(MinutesOfAverage + theinterval) ) #if Minutes of average is zero, it allows to have at least one sample isok, quantitylist = sensordbmod.EvaluateDataPeriod( sensordata, starttimecalc, datenow) quantity = quantitylist[operation] # sensor reading value logger.info('Sensor reading <%s>=<%s>', sensorname, str(quantity)) print "sensor Reading ", sensorname, "=", quantity return isok, quantity
def sensorlisttriggertime(): tablelist=hardwaremod.searchdatalist(hardwaremod.HW_INFO_IOTYPE,"input",hardwaremod.HW_INFO_NAME) timetriggerlist=[] for item in tablelist: timelist=hardwaremod.gettimedata(item) theinterval=timelist[1] # minutes timetriggerlist.append(theinterval) return timetriggerlist
def sensorlisttriggertime(): tablelist=hardwaremod.searchdatalist(hardwaremod.HW_INFO_IOTYPE,"input",hardwaremod.HW_INFO_NAME) timetriggerlist=[] for item in tablelist: timelist=hardwaremod.gettimedata(item) theinterval=timelist[1] # minutes timetriggerlist.append(theinterval) return timetriggerlist
def sensorreading(sensorname,MinutesOfAverage,operation): isok=False # operation "average", "min" , "max" , "sum" if sensorname: timelist=hardwaremod.gettimedata(sensorname) theinterval=timelist[1] # minutes if theinterval>0: samplesnumber=int(MinutesOfAverage/theinterval+1) else: samplesnumber=1 theinterval=15 quantity=0 MinutesOfAverage=samplesnumber*theinterval if samplesnumber>0: sensordata=[] sensordbmod.getsensordbdatasamplesN(sensorname,sensordata,samplesnumber) starttimecalc=datetime.now()-timedelta(minutes=(MinutesOfAverage+theinterval)) #if Minutes of average is zero, it allows to have at least one sample quantity=sensordbmod.EvaluateDataPeriod(sensordata,starttimecalc,datetime.now())[operation] isok=True return isok , quantity
def sensorreading(sensorname): MinutesOfAverage=70 #about one hour, 4 samples at 15min samples rate if sensorname: # old #sensordata=[] #sensordbmod.getsensordbdata(sensorname,sensordata) # get number of samples timelist=hardwaremod.gettimedata(sensorname) theinterval=timelist[1] # minutes if theinterval>0: samplesnumber=int(MinutesOfAverage/theinterval+1) else: samplesnumber=1 # new procedure should be faster on database reading for large amount of data sensordata=[] sensordbmod.getsensordbdatasamplesN(sensorname,sensordata,samplesnumber) # still necessary to filter the sample based on timestamp, due to the possibility of missing samples starttimecalc=datetime.now()-timedelta(minutes=int(MinutesOfAverage)) quantity=sensordbmod.EvaluateDataPeriod(sensordata,starttimecalc,datetime.now())["max"] return quantity
def sensorreading(sensorname): MinutesOfAverage = 70 #about one hour, 4 samples at 15min samples rate if sensorname: # old #sensordata=[] #sensordbmod.getsensordbdata(sensorname,sensordata) # get number of samples timelist = hardwaremod.gettimedata(sensorname) theinterval = timelist[1] # minutes if theinterval > 0: samplesnumber = int(MinutesOfAverage / theinterval + 1) else: samplesnumber = 1 # new procedure should be faster on database reading for large amount of data sensordata = [] sensordbmod.getsensordbdatasamplesN(sensorname, sensordata, samplesnumber) # still necessary to filter the sample based on timestamp, due to the possibility of missing samples starttimecalc = datetime.now() - timedelta( minutes=int(MinutesOfAverage)) quantity = sensordbmod.EvaluateDataPeriod(sensordata, starttimecalc, datetime.now())["max"] return quantity
def sensorreading(sensorname, MinutesOfAverage, operation): isok = False # operation "average", "min" , "max" , "sum" if sensorname: timelist = hardwaremod.gettimedata(sensorname) theinterval = timelist[1] # minutes if theinterval > 0: samplesnumber = int(MinutesOfAverage / theinterval + 1) else: samplesnumber = 1 theinterval = 15 quantity = 0 MinutesOfAverage = samplesnumber * theinterval if samplesnumber > 0: sensordata = [] sensordbmod.getsensordbdatasamplesN(sensorname, sensordata, samplesnumber) starttimecalc = datetime.now() - timedelta( minutes=(MinutesOfAverage + theinterval) ) #if Minutes of average is zero, it allows to have at least one sample quantity = sensordbmod.EvaluateDataPeriod( sensordata, starttimecalc, datetime.now())[operation] isok = True return isok, quantity
def mastercallback(fromscheduledtime=False): print("master callback") if fromscheduledtime: # check if the time this function is called is the same as the expected time it should be called (solar/legal time might mess with it) logger.info( 'Master scheduler call at scheduled local time, expected time %s', MASTERSCHEDULERTIME) thedateloc = datetime.now() timelist = hardwaremod.separatetimestringint(MASTERSCHEDULERTIME) masterschedtime = thedateloc.replace(hour=timelist[0], minute=timelist[1], second=timelist[2]) secondsdifference = abs((thedateloc - masterschedtime).total_seconds()) logger.info( 'Master scheduler, difference between expected time and local time %d', secondsdifference) if secondsdifference > 300: # more than 5 minutes logger.warning( 'Master scheduler, difference between expected time and local time greater than 5 minutes, RESET MASTER SCHEDULER' ) resetmastercallback() return # clean old data of the database (pastdays) logger.info('Remove data exceeding one year') pastdays = hardwaremod.toint(STOREPASTDAYS, 364) logger.info('Sensor Remove data exceeding one year') sensordbmod.RemoveSensorDataPeriod(pastdays) logger.info('Actuator Remove data exceeding one year') actuatordbmod.RemoveActuatorDataPeriod(pastdays) logger.info('Photo Remove data exceeding one year') hardwaremod.removephotodataperiod(pastdays) else: logger.info('Master scheduler call not coming from scheduler') logger.info('Scheduling main activities') # remove all jobs except masterscheduler #for job in SchedulerMod.sched.get_jobs(): # if job.name != "master": # try: # job.remove() # except: # logger.error('Not able to remove Job %s', job.name) # set the individual callback of the day logger.info('Start other scheduler activities - heartbeat') # info file dedicate call-back --------------------------------------------- (heartbeat) #this callback is used only for system status periodic check calltype = "periodic" global HEARTBEATINTERVAL interval = HEARTBEATINTERVAL timelist = [0, interval, 900] # 20 indicates to start after 15 minutes callback = "heartbeat" argument = [] setschedulercallback(calltype, timelist, argument, callback, callback) logger.info('Start other scheduler activities - sensor') # info file dedicate call-back --------------------------------------------- (sensor) hwnamelist = hardwaremod.searchdatalist(hardwaremod.HW_INFO_IOTYPE, "input", hardwaremod.HW_INFO_NAME) # GPIO input can be considered at all the aspects a binary input, therefor their stat collection are possible as any other input # the input should be time periodic and time should be more than one minute callback = "sensor" timeshift = 300 # after wifi activation because of possible clock adjustment shiftstep = 7 #seconds # IMPORTANT # the shiftstep is necessary to avoid thread collision which brings to sqlite3 db failure "database is locked" # this number is giving a limit to the sensor reading that should be higher than 1min for hwname in hwnamelist: calltype = hardwaremod.searchdata(hardwaremod.HW_INFO_NAME, hwname, hardwaremod.HW_FUNC_SCHEDTYPE) if calltype == "periodic": #check the input to be periodic timelist = hardwaremod.gettimedata(hwname) timelist[ 2] = timeshift # avoid all the sensor thread to be called in the same time argument = [] argument.append(hwname) setschedulercallback(calltype, timelist, argument, callback, hwname) timeshift = timeshift + shiftstep else: logger.warning( 'The scheduler for the input %s is not set to PERIODIC, no log record of this input will be taken', hwname) logger.info('Start other scheduler activities - photo') #<------> # info file dedicate quinto call-back ----------------------------------(takephoto) usedfor = "photocontrol" callback = "photo" hwnamelist = hardwaremod.searchdatalist(hardwaremod.HW_FUNC_USEDFOR, usedfor, hardwaremod.HW_INFO_NAME) for hwname in hwnamelist: calltype = hardwaremod.searchdata(hardwaremod.HW_INFO_NAME, hwname, hardwaremod.HW_FUNC_SCHEDTYPE) timelist = hardwaremod.gettimedata(hwname) argument = [] argument.append(hwname) setschedulercallback(calltype, timelist, argument, callback, hwname) logger.info('Start other scheduler activities - mail') # info ne file dedicate quarto call-back ---------------------------------------(sendmail) usedfor = "mailcontrol" callback = "mail" hwnamelist = hardwaremod.searchdatalist(hardwaremod.HW_FUNC_USEDFOR, usedfor, hardwaremod.HW_INFO_NAME) for hwname in hwnamelist: calltype = hardwaremod.searchdata(hardwaremod.HW_INFO_NAME, hwname, hardwaremod.HW_FUNC_SCHEDTYPE) timelist = hardwaremod.gettimedata(hwname) argument = [] argument.append(hwname) setschedulercallback(calltype, timelist, argument, callback, hwname) # info ne file dedicate quinto call-back ---------------------------------------(lightcheck) # empty #<----> logger.info('Start other scheduler activities - pump') # info file dedicate call-back ------------------------------------------------ (waterpump) callback = "waterpump" #water schedule table paramlist = wateringdbmod.getparamlist() # name of the months ordered elementlist = wateringdbmod.getelementlist( ) # pump ordered (based on "watercontrol" field) table = wateringdbmod.gettable( 1 ) # table, each row is a pump, each column is a month, value is watering time multiplieer table1 = wateringdbmod.gettable( 0 ) # table, each row is a pump, each column is a month, value is watering scheme table2 = wateringdbmod.gettable( 2 ) # table, each row is a pump, each column is a month, value is time delay #print paramlist #print elementlistly #print table paramlistdrop = advancedmod.getparamlist() # day of the week elementlistdrop = advancedmod.getelementlist() # drops ordered tabledrop = advancedmod.gettable( ) # table, each row is a schema number (drop number), each column is a weekday for pumpnumber in range(len(elementlist)): #print "number =",pumpnumber pumpname = elementlist[pumpnumber] todaydate = date.today() # Monday is 0 and Sunday is 6 weekday = todaydate.weekday() month = todaydate.month try: waterschemanumber = table1[pumpnumber][month - 1] waterdropnumber = hardwaremod.toint(table[pumpnumber][month - 1], 0) watertimedelaysec = hardwaremod.toint( table2[pumpnumber][month - 1], 0) except IndexError: print("EXCEPTION: index out of range") waterdropnumber = 0 watertimedelaysec = 0 if waterdropnumber > 0: #print " month " , month, " drop " , waterdropnumber calltype = hardwaremod.searchdata(hardwaremod.HW_INFO_NAME, pumpname, hardwaremod.HW_FUNC_SCHEDTYPE) for todayevent in tabledrop[waterschemanumber - 1][weekday]: timelist = hardwaremod.separatetimestringint(todayevent[0]) timelist[2] = timelist[2] + watertimedelaysec argument = [] argument.append(pumpname) durationinseconds = hardwaremod.toint(todayevent[1], 0) * waterdropnumber argument.append(durationinseconds) for i in range(2, len(todayevent)): argument.append(todayevent[i]) if durationinseconds > 0: #check if the duration in second is >0 setschedulercallback(calltype, timelist, argument, callback, pumpname) logger.info('Start other scheduler activities - doser') # info file dedicate call-back ------------------------------------------------ (pulsenutrient) callback = "doser" #fertilizer schedule table paramlist = fertilizerdbmod.getparamlist() # name of the months ordered elementlist = fertilizerdbmod.getelementlist( ) # element with "fertilizercontrol field" ordered table = fertilizerdbmod.gettable( 1 ) # table, each row is a doser, each column is a month, value is number of times in a month table1 = fertilizerdbmod.gettable( 0 ) # table, each row is a doser, each column is a month, value is pulse seconds for dosernumber in range(len(elementlist)): #print "number =",dosernumber dosername = elementlist[dosernumber] calltype = hardwaremod.searchdata(hardwaremod.HW_INFO_NAME, dosername, hardwaremod.HW_FUNC_SCHEDTYPE) todaydate = date.today() # Monday is 0 and Sunday is 6 year = todaydate.year month = todaydate.month day = todaydate.day fertilizerpulsenumber = hardwaremod.toint( table[dosernumber][month - 1], 0) fertilizerpulsesecond = hardwaremod.toint( table1[dosernumber][month - 1], 0) if (fertilizerpulsenumber > 0) and (fertilizerpulsesecond > 0): themonthdays = 30 #approximate number of days in a month dayinterval = old_div(themonthdays, fertilizerpulsenumber) halfinterval = old_div((dayinterval + 1), 2) print("day=", day, " dayinterval=", dayinterval, " half=", halfinterval) if ((day + int(halfinterval)) % int(dayinterval)) == 0: #timelist=hardwaremod.gettimedata("06:00:00") timelist = autofertilizermod.timelist(dosername) argument = [] argument.append(dosername) argument.append(fertilizerpulsesecond) if (fertilizerpulsesecond ) > 0: #check if the duration in second is >0 setschedulercallback(calltype, timelist, argument, callback, dosername) logger.info('Start other scheduler activities - finish') return True
def mastercallback(): # clean old data of the database (pastdays) pastdays = 364 sensordbmod.RemoveSensorDataPeriod(pastdays) actuatordbmod.RemoveActuatorDataPeriod(pastdays) hardwaremod.removephotodataperiod(364) # remove all jobs except masterscheduler for job in SchedulerMod.sched.get_jobs(): if job.name != "master": try: job.remove() except: logger.error('Not able to remove Job %s', job.name) # set the individual callback of the day # info file dedicate call-back --------------------------------------------- (heartbeat) #this callback is used only for system status periodic check calltype = "periodic" global HEARTBEATINTERVAL interval = HEARTBEATINTERVAL timelist = [0, interval, 900] # 900 indicates to start after 15 minutes callback = "heartbeat" argument = [] setschedulercallback(calltype, timelist, argument, callback, callback) # info file dedicate call-back --------------------------------------------- (sensor) hwnamelist = sensordbmod.gettablelist() callback = "sensor" timeshift = 300 shiftstep = 2 #seconds for hwname in hwnamelist: calltype = hardwaremod.searchdata(hardwaremod.HW_INFO_NAME, hwname, hardwaremod.HW_FUNC_SCHEDTYPE) timelist = hardwaremod.gettimedata(hwname) timelist[2] = timelist[ 2] + timeshift # avoid all the sensor thread to be called in the same time argument = [] argument.append(hwname) setschedulercallback(calltype, timelist, argument, callback, hwname) timeshift = timeshift + shiftstep #<------> # info file dedicate quinto call-back ----------------------------------(takephoto) usedfor = "photocontrol" callback = "photo" hwnamelist = hardwaremod.searchdatalist(hardwaremod.HW_FUNC_USEDFOR, usedfor, hardwaremod.HW_INFO_NAME) for hwname in hwnamelist: calltype = hardwaremod.searchdata(hardwaremod.HW_INFO_NAME, hwname, hardwaremod.HW_FUNC_SCHEDTYPE) timelist = hardwaremod.gettimedata(hwname) argument = [] argument.append(hwname) setschedulercallback(calltype, timelist, argument, callback, hwname) # info ne file dedicate quarto call-back ---------------------------------------(sendmail) usedfor = "mailcontrol" callback = "mail" hwnamelist = hardwaremod.searchdatalist(hardwaremod.HW_FUNC_USEDFOR, usedfor, hardwaremod.HW_INFO_NAME) for hwname in hwnamelist: calltype = hardwaremod.searchdata(hardwaremod.HW_INFO_NAME, hwname, hardwaremod.HW_FUNC_SCHEDTYPE) timelist = hardwaremod.gettimedata(hwname) argument = [] argument.append(hwname) setschedulercallback(calltype, timelist, argument, callback, hwname) # info ne file dedicate quinto call-back ---------------------------------------(lightcheck) # empty #<----> # info file dedicate call-back ------------------------------------------------ (waterpump) callback = "waterpump" #water schedule table paramlist = wateringdbmod.getparamlist() # name of the months ordered elementlist = wateringdbmod.getelementlist( ) # pump ordered (based on "watercontrol" field) table = wateringdbmod.gettable( 1 ) # table, each row is a pump, each column is a month, value is watering time multiplieer table1 = wateringdbmod.gettable( 0 ) # table, each row is a pump, each column is a month, value is watering scheme table2 = wateringdbmod.gettable( 2 ) # table, each row is a pump, each column is a month, value is time delay #print paramlist #print elementlistly #print table paramlistdrop = advancedmod.getparamlist() # day of the week elementlistdrop = advancedmod.getelementlist() # drops ordered tabledrop = advancedmod.gettable( ) # table, each row is a schema number (drop number), each column is a weekday for pumpnumber in range(len(elementlist)): #print "number =",pumpnumber pumpname = elementlist[pumpnumber] todaydate = date.today() # Monday is 0 and Sunday is 6 weekday = todaydate.weekday() month = todaydate.month try: waterschemanumber = table1[pumpnumber][month - 1] waterdropnumber = hardwaremod.toint(table[pumpnumber][month - 1], 0) watertimedelaysec = hardwaremod.toint( table2[pumpnumber][month - 1], 0) except IndexError: print "EXCEPTION: index out of range" waterdropnumber = 0 watertimedelaysec = 0 if waterdropnumber > 0: #print " month " , month, " drop " , waterdropnumber calltype = hardwaremod.searchdata(hardwaremod.HW_INFO_NAME, pumpname, hardwaremod.HW_FUNC_SCHEDTYPE) for todayevent in tabledrop[waterschemanumber - 1][weekday]: timelist = hardwaremod.separatetimestringint(todayevent[0]) timelist[2] = timelist[2] + watertimedelaysec argument = [] argument.append(pumpname) durationinseconds = hardwaremod.toint(todayevent[1], 0) * waterdropnumber argument.append(durationinseconds) for i in range(2, len(todayevent)): argument.append(todayevent[i]) if durationinseconds > 0: #check if the duration in second is >0 setschedulercallback(calltype, timelist, argument, callback, pumpname) # info file dedicate call-back ------------------------------------------------ (pulsenutrient) callback = "doser" #fertilizer schedule table paramlist = fertilizerdbmod.getparamlist() # name of the months ordered elementlist = fertilizerdbmod.getelementlist( ) # element with "fertilizercontrol field" ordered table = fertilizerdbmod.gettable( 1 ) # table, each row is a doser, each column is a month, value is number of times in a month table1 = fertilizerdbmod.gettable( 0 ) # table, each row is a doser, each column is a month, value is pulse seconds for dosernumber in range(len(elementlist)): #print "number =",dosernumber dosername = elementlist[dosernumber] calltype = hardwaremod.searchdata(hardwaremod.HW_INFO_NAME, dosername, hardwaremod.HW_FUNC_SCHEDTYPE) todaydate = date.today() # Monday is 0 and Sunday is 6 year = todaydate.year month = todaydate.month day = todaydate.day fertilizerpulsenumber = hardwaremod.toint( table[dosernumber][month - 1], 0) fertilizerpulsesecond = hardwaremod.toint( table1[dosernumber][month - 1], 0) if (fertilizerpulsenumber > 0) and (fertilizerpulsesecond > 0): themonthdays = 30 #approximate number of days in a month dayinterval = themonthdays / fertilizerpulsenumber halfinterval = (dayinterval + 1) / 2 print "day=", day, " dayinterval=", dayinterval, " half=", halfinterval if ((day + int(halfinterval)) % int(dayinterval)) == 0: timelist = hardwaremod.gettimedata("06:00:00") argument = [] argument.append(dosername) argument.append(fertilizerpulsesecond) if (fertilizerpulsesecond ) > 0: #check if the duration in second is >0 setschedulercallback(calltype, timelist, argument, callback, dosername)
def autowateringexecute(refsensor, element): global AUTO_data sensor = autowateringdbmod.searchdata("element", element, "sensor") # check the sensor if refsensor == sensor: print "auto watering check -----------------------------------------> ", element logger.info('auto watering check --------------------------> %s', element) # check the watering mode modelist = ["None", "Full Auto", "Emergency Activation", "Alert Only"] workmode = checkworkmode(element) if not (sensor in sensordbmod.gettablelist()): print "Sensor does not exist ", sensor, ", element: ", element logger.error("Sensor does not exist %s , element: %s ", sensor, element) return "sensor not Exist" maxthreshold = hardwaremod.tonumber( autowateringdbmod.searchdata("element", element, "threshold")[1], 0) minthreshold = hardwaremod.tonumber( autowateringdbmod.searchdata("element", element, "threshold")[0], maxthreshold) # exit condition in case of data inconsistency if minthreshold >= maxthreshold: print "Data inconsistency , element: ", element logger.error("Data inconsistency , element: %s ", element) return "data inconsistency" now = datetime.now() nowtime = now.time() starttimeh = hardwaremod.toint( autowateringdbmod.searchdata("element", element, "allowedperiod")[0].split(":")[0], 0) starttimem = hardwaremod.toint( autowateringdbmod.searchdata("element", element, "allowedperiod")[0].split(":")[1], 0) endtimeh = hardwaremod.toint( autowateringdbmod.searchdata("element", element, "allowedperiod")[1].split(":")[0], 1) endtimem = hardwaremod.toint( autowateringdbmod.searchdata("element", element, "allowedperiod")[1].split(":")[1], 0) starttime = time(starttimeh, starttimem) endtime = time(endtimeh, endtimem) duration = 1000 * hardwaremod.toint( autowateringdbmod.searchdata("element", element, "wtstepsec"), 0) maxstepnumber = hardwaremod.toint( autowateringdbmod.searchdata("element", element, "maxstepnumber"), 0) maxdays = hardwaremod.toint( autowateringdbmod.searchdata("element", element, "maxdaysbetweencycles"), 0) waitingtime = hardwaremod.toint( autowateringdbmod.searchdata("element", element, "pausebetweenwtstepsmin"), 0) mailtype = autowateringdbmod.searchdata("element", element, "mailalerttype") minaccepted = hardwaremod.tonumber( autowateringdbmod.searchdata("element", element, "sensorminacceptedvalue"), 0.1) # ------------------------ Workmode split if workmode == "Full Auto": # block the wateringplan activation as by definition of "Full Auto" allowwateringplan[element] = False # check if inside the allowed time period print "full Auto Mode" logger.info('full auto mode --> %s', element) timeok = isNowInTimePeriod(starttime, endtime, nowtime) print "inside allowed time ", timeok, " starttime ", starttime, " endtime ", endtime logger.info('full auto mode') if timeok: logger.info('inside allowed time') belowthr, valid = checkminthreshold(sensor, minthreshold, minaccepted) if valid: if belowthr: status = "lowthreshold" logger.info('below threshold') # wait to seek a more stable reading of hygrometer # check if time between watering events is larger that the waiting time (minutes) lastwateringtime = statusdataDBmod.read_status_data( AUTO_data, element, "lastwateringtime") print ' Previous watering: ', lastwateringtime, ' Now: ', datetime.now( ) timedifference = sensordbmod.timediffinminutes( lastwateringtime, datetime.now()) print 'Time interval between watering steps', timedifference, '. threshold', waitingtime logger.info( 'Time interval between watering steps %d threshold %d', timedifference, waitingtime) if timedifference > waitingtime: print " Sufficient waiting time" logger.info('Sufficient waiting time') # activate watering in case the maxstepnumber is not exceeded watercounter = statusdataDBmod.read_status_data( AUTO_data, element, "watercounter") if maxstepnumber > watercounter: #activate pump activatewater(element, duration) # invia mail, considered as info, not as alert if mailtype != "warningonly": textmessage = "INFO: " + sensor + " value below the minimum threshold " + str( minthreshold ) + ", activating the watering :" + element emailmod.sendallmail("alert", textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", watercounter + 1) statusdataDBmod.write_status_data( AUTO_data, element, "lastwateringtime", datetime.now()) else: # critical, sensor below minimum after all watering activations are done logger.info( 'Number of watering time per cycle has been exceeeded' ) # read hystory data and calculate the slope timelist = hardwaremod.gettimedata(sensor) cyclestartdate = statusdataDBmod.read_status_data( AUTO_data, element, "cyclestartdate") lastwateringtime = statusdataDBmod.read_status_data( AUTO_data, element, "lastwateringtime") startdate = cyclestartdate - timedelta( minutes=timelist[1]) enddate = lastwateringtime + timedelta( minutes=waitingtime) isslopeok = checkinclination( sensor, startdate, enddate ) # still to decide if use the enddate if isslopeok: # invia mail if couner alert is lower than 1 alertcounter = statusdataDBmod.read_status_data( AUTO_data, element, "alertcounter") if alertcounter < 1: textmessage = "WARNING: Please consider to increase the amount of water per cycle, the " + sensor + " value below the MINIMUM threshold " + str( minthreshold ) + " still after activating the watering :" + element + " for " + str( maxstepnumber ) + " times. System will automatically reset the watering cycle to allow more water" print textmessage #send alert mail notification emailmod.sendallmail( "alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", alertcounter + 1) # reset watering cycle status = "done" statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", -1) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "cyclestartdate", datetime.now()) else: # slope not OK, probable hardware problem alertcounter = statusdataDBmod.read_status_data( AUTO_data, element, "alertcounter") if alertcounter < 3: textmessage = "CRITICAL: Possible hardware problem, " + sensor + " value below the MINIMUM threshold " + str( minthreshold ) + " still after activating the watering :" + element + " for " + str( maxstepnumber) + " times" print textmessage #send alert mail notification emailmod.sendallmail( "alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", alertcounter + 1) # update the status checkcounter = statusdataDBmod.read_status_data( AUTO_data, element, "checkcounter") statusdataDBmod.write_status_data( AUTO_data, element, "cyclestatus", status) statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", checkcounter + 1) # RAMPUP case above threshold but below maxthreshold elif sensorreading( sensor ) < maxthreshold: # intermediate state where the sensor is above the minthreshold but lower than the max threshold # check the status of the automatic cycle cyclestatus = statusdataDBmod.read_status_data( AUTO_data, element, "cyclestatus") if cyclestatus != "done": status = "rampup" # wait to seek a more stable reading of hygrometer # check if time between watering events is larger that the waiting time (minutes) lastwateringtime = statusdataDBmod.read_status_data( AUTO_data, element, "lastwateringtime") if sensordbmod.timediffinminutes( lastwateringtime, datetime.now()) > waitingtime: watercounter = statusdataDBmod.read_status_data( AUTO_data, element, "watercounter") if maxstepnumber > watercounter: #activate pump activatewater(element, duration) # invia mail, considered as info, not as alert if mailtype != "warningonly": textmessage = "INFO: " + sensor + " value below the Maximum threshold " + str( maxthreshold ) + ", activating the watering :" + element emailmod.sendallmail( "alert", textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", watercounter + 1) statusdataDBmod.write_status_data( AUTO_data, element, "lastwateringtime", datetime.now()) else: # give up to reache the maximum threshold, proceed as done, send alert logger.info( 'Number of watering time per cycle has been exceeeded' ) # invia mail if couner alert is lower than 1 -------------- # only if the info is activated if mailtype != "warningonly": alertcounter = statusdataDBmod.read_status_data( AUTO_data, element, "alertcounter") if alertcounter < 2: textmessage = "INFO " + sensor + " value below the Maximum threshold " + str( maxthreshold ) + " still after activating the watering :" + element + " for " + str( maxstepnumber) + " times" print textmessage #send alert mail notification emailmod.sendallmail( "alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", alertcounter + 1) # reset watering cycle status = "done" statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", -1) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "cyclestartdate", datetime.now()) # update the status checkcounter = statusdataDBmod.read_status_data( AUTO_data, element, "checkcounter") statusdataDBmod.write_status_data( AUTO_data, element, "cyclestatus", status) statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", checkcounter + 1) else: # update the status, reset cycle statusdataDBmod.write_status_data( AUTO_data, element, "cyclestatus", "done") statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", 0) elif workmode == "Emergency Activation": # check if inside the allow time period logger.info('Emergency Mode') timeok = isNowInTimePeriod(starttime, endtime, nowtime) print "inside allowed time ", timeok, " starttime ", starttime, " endtime ", endtime if timeok: belowthr, valid = checkminthreshold(sensor, minthreshold, minaccepted) if valid: if belowthr: # wait to seek a more stable reading of hygrometer # check if time between watering events is larger that the waiting time (minutes) lastwateringtime = statusdataDBmod.read_status_data( AUTO_data, element, "lastwateringtime") if sensordbmod.timediffinminutes( lastwateringtime, datetime.now()) > waitingtime: # activate watering in case the maxstepnumber is not exceeded watercounter = statusdataDBmod.read_status_data( AUTO_data, element, "watercounter") if maxstepnumber > watercounter: #activate pump activatewater(element, duration) # invia mail, considered as info, not as alert if mailtype != "warningonly": textmessage = "INFO: " + sensor + " value below the minimum threshold " + str( minthreshold ) + ", activating the watering :" + element emailmod.sendallmail("alert", textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", watercounter + 1) statusdataDBmod.write_status_data( AUTO_data, element, "lastwateringtime", datetime.now()) else: logger.info( 'Number of watering time per cycle has been exceeeded' ) # read hystory data and calculate the slope timelist = hardwaremod.gettimedata(sensor) cyclestartdate = statusdataDBmod.read_status_data( AUTO_data, element, "cyclestartdate") lastwateringtime = statusdataDBmod.read_status_data( AUTO_data, element, "lastwateringtime") startdate = cyclestartdate - timedelta( minutes=timelist[1]) enddate = lastwateringtime + timedelta( minutes=waitingtime) isslopeok = checkinclination( sensor, startdate, enddate) if isslopeok: # invia mail if couner alert is lower than 1 alertcounter = statusdataDBmod.read_status_data( AUTO_data, element, "alertcounter") if alertcounter < 1: textmessage = "WARNING: Please consider to increase the amount of water per cycle, the " + sensor + " value below the MINIMUM threshold " + str( minthreshold ) + " still after activating the watering :" + element + " for " + str( maxstepnumber ) + " times. System will automatically reset the watering cycle to allow more water" print textmessage #send alert mail notification alertcounter emailmod.sendallmail( "alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", alertcounter + 1) # reset watering cycle status = "done" statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", -1) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "cyclestartdate", datetime.now()) else: # slope not OK, probable hardware problem alertcounter = statusdataDBmod.read_status_data( AUTO_data, element, "alertcounter") if alertcounter < 3: textmessage = "CRITICAL: Possible hardware problem, " + sensor + " value below the MINIMUM threshold " + str( minthreshold ) + " still after activating the watering :" + element + " for " + str( maxstepnumber) + " times" print textmessage #send alert mail notification emailmod.sendallmail( "alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", alertcounter + 1) # update the status checkcounter = statusdataDBmod.read_status_data( AUTO_data, element, "checkcounter") statusdataDBmod.write_status_data( AUTO_data, element, "cyclestatus", "lowthreshold") statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", checkcounter + 1) else: # update the status statusdataDBmod.write_status_data( AUTO_data, element, "cyclestatus", "done") statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", 0) elif workmode == "under MIN over MAX": logger.info('under MIN over MAX') # normally watering plan is allowed unless over MAX threshold allowwateringplan[element] = True # check if inside the allow time period timeok = isNowInTimePeriod(starttime, endtime, nowtime) print "inside allowed time ", timeok, " starttime ", starttime, " endtime ", endtime if timeok: logger.info('Insede operative time') belowthr, valid = checkminthreshold(sensor, minthreshold, minaccepted) if valid: logger.info('valid sensor reading') if belowthr: logger.info('sensor reading below threshold') # wait to seek a more stable reading of hygrometer # check if time between watering events is larger that the waiting time (minutes) lastwateringtime = statusdataDBmod.read_status_data( AUTO_data, element, "lastwateringtime") if sensordbmod.timediffinminutes( lastwateringtime, datetime.now()) > waitingtime: # activate watering in case the maxstepnumber is not exceeded watercounter = statusdataDBmod.read_status_data( AUTO_data, element, "watercounter") if maxstepnumber > watercounter: logger.info('water Count not exceeded') #activate pump activatewater(element, duration) # invia mail, considered as info, not as alert if mailtype != "warningonly": textmessage = "INFO: " + sensor + " value below the minimum threshold " + str( minthreshold ) + ", activating the watering :" + element emailmod.sendallmail("alert", textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", watercounter + 1) statusdataDBmod.write_status_data( AUTO_data, element, "lastwateringtime", datetime.now()) else: logger.info( 'Number of watering time per cycle has been exceeeded' ) # read hystory data and calculate the slope timelist = hardwaremod.gettimedata(sensor) cyclestartdate = statusdataDBmod.read_status_data( AUTO_data, element, "cyclestartdate") lastwateringtime = statusdataDBmod.read_status_data( AUTO_data, element, "lastwateringtime") startdate = cyclestartdate - timedelta( minutes=timelist[1]) enddate = lastwateringtime + timedelta( minutes=waitingtime) isslopeok = checkinclination( sensor, startdate, enddate) if isslopeok: # invia mail if couner alert is lower than 1 alertcounter = statusdataDBmod.read_status_data( AUTO_data, element, "alertcounter") if alertcounter < 1: textmessage = "WARNING: Please consider to increase the amount of water per cycle, the " + sensor + " value below the MINIMUM threshold " + str( minthreshold ) + " still after activating the watering :" + element + " for " + str( maxstepnumber ) + " times. System will automatically reset the watering cycle to allow more water" print textmessage #send alert mail notification emailmod.sendallmail( "alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", alertcounter + 1) # reset watering cycle status = "done" statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", -1) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "cyclestartdate", datetime.now()) else: # slope not OK, probable hardware problem alertcounter = statusdataDBmod.read_status_data( AUTO_data, element, "alertcounter") if alertcounter < 3: textmessage = "CRITICAL: Possible hardware problem, " + sensor + " value below the MINIMUM threshold " + str( minthreshold ) + " still after activating the watering :" + element + " for " + str( maxstepnumber) + " times" print textmessage #send alert mail notification emailmod.sendallmail( "alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", alertcounter + 1) # update the status checkcounter = statusdataDBmod.read_status_data( AUTO_data, element, "checkcounter") statusdataDBmod.write_status_data( AUTO_data, element, "cyclestatus", "lowthreshold") statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", checkcounter + 1) else: # above minimum threshold logger.info('sensor reading above min threshold') # update the status statusdataDBmod.write_status_data( AUTO_data, element, "cyclestatus", "done") statusdataDBmod.write_status_data( AUTO_data, element, "checkcounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "watercounter", 0) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", 0) if sensorreading(sensor) > maxthreshold: logger.info( 'sensor reading above MAX threshold, deactivate scheduled irrigation' ) # do not activate the irrigation scheduled in the time plan allowwateringplan[element] = False elif workmode == "Alert Only": belowthr, valid = checkminthreshold(sensor, minthreshold, minaccepted) if valid: if belowthr: # invia mail if couter alert is lower than alertcounter = statusdataDBmod.read_status_data( AUTO_data, element, "alertcounter") if alertcounter < 2: textmessage = "WARNING " + sensor + " value below the minimum threshold " + str( minthreshold) + " watering system: " + element print textmessage #send alert mail notification emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", alertcounter + 1) # update the status checkcounter = statusdataDBmod.read_status_data( AUTO_data, element, "checkcounter") statusdataDBmod.write_status_data(AUTO_data, element, "cyclestatus", "lowthreshold") statusdataDBmod.write_status_data(AUTO_data, element, "checkcounter", checkcounter + 1) else: # update the status statusdataDBmod.write_status_data(AUTO_data, element, "cyclestatus", "done") statusdataDBmod.write_status_data(AUTO_data, element, "checkcounter", 0) statusdataDBmod.write_status_data(AUTO_data, element, "watercounter", 0) statusdataDBmod.write_status_data(AUTO_data, element, "alertcounter", 0) else: # None case print "No Action required, workmode set to None, element: ", element logger.info( "No Action required, workmode set to None, element: %s ", element) cyclestatus = statusdataDBmod.read_status_data(AUTO_data, element, "cyclestatus") if cyclestatus == "lowthreshold": checkcounter = statusdataDBmod.read_status_data( AUTO_data, element, "checkcounter") if checkcounter == 1: statusdataDBmod.write_status_data(AUTO_data, element, "cyclestartdate", datetime.now()) # implment alert message for the cycle exceeding days, and reset the cycle if workmode != "None": cyclestartdate = statusdataDBmod.read_status_data( AUTO_data, element, "cyclestartdate") timedeltadays = sensordbmod.timediffdays(datetime.now(), cyclestartdate) if (timedeltadays > maxdays ): #the upper limit is set in case of abrupt time change textmessage = "WARNING " + sensor + " watering cycle is taking too many days, watering system: " + element + ". Reset watering cycle" print textmessage # in case of full Auto, activate pump for minimum pulse period if workmode == "Full Auto": if ( timedeltadays < maxdays + 2 ): #the upper limit is set in case of abrupt time change textmessage = "WARNING " + sensor + " watering cycle is taking too many days, watering system: " + element + ". Activate Min pulse + Reset watering cycle" activatewater(element, duration) #send alert mail notification if mailtype != "warningonly": emailmod.sendallmail("alert", textmessage) logger.error(textmessage) logger.error("Cycle started %s, Now is %s ", cyclestartdate.strftime("%Y-%m-%d %H:%M:%S"), datetime.now().strftime("%Y-%m-%d %H:%M:%S")) # reset cycle statusdataDBmod.write_status_data(AUTO_data, element, "cyclestatus", "done") statusdataDBmod.write_status_data(AUTO_data, element, "checkcounter", 0) statusdataDBmod.write_status_data(AUTO_data, element, "watercounter", 0) statusdataDBmod.write_status_data(AUTO_data, element, "alertcounter", 0) statusdataDBmod.write_status_data(AUTO_data, element, "cyclestartdate", datetime.now()) # implment Critical alert message in case the threshold is below the 0.5 of the minimum if workmode != "None": belowthr, valid = checkminthreshold(sensor, minthreshold * 0.5, minaccepted) if valid: if belowthr: logger.info( 'sensor %s below half of the actual set threshold', sensor) textmessage = "CRITICAL: Plant is dying, " + sensor + " reading below half of the minimum threshold, need to check the " + element print textmessage #send alert mail notification alertcounter = statusdataDBmod.read_status_data( AUTO_data, element, "alertcounter") if alertcounter < 5: emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data( AUTO_data, element, "alertcounter", alertcounter + 1) else: logger.info('sensor %s below valid data', sensor) textmessage = "WARNING: " + sensor + " below valid data range, need to check sensor" print textmessage #send alert mail notification alertcounter = statusdataDBmod.read_status_data( AUTO_data, element, "alertcounter") if alertcounter < 3: emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data, element, "alertcounter", alertcounter + 1) return
def mastercallback(): # clean old data of the database (pastdays) logger.info('Remove data in exceed of one year') pastdays=364 #sensordbmod.RemoveSensorDataPeriod(pastdays) logger.info('Sensor Remove data in exceed of one year') #actuatordbmod.RemoveActuatorDataPeriod(pastdays) logger.info('Actuator Remove data in exceed of one year') #hardwaremod.removephotodataperiod(364) logger.info('Photo Remove data in exceed of one year') logger.info('Start other scheduler activities') # remove all jobs except masterscheduler #for job in SchedulerMod.sched.get_jobs(): # if job.name != "master": # try: # job.remove() # except: # logger.error('Not able to remove Job %s', job.name) # set the individual callback of the day # info file dedicate call-back --------------------------------------------- (heartbeat) #this callback is used only for system status periodic check calltype="periodic" global HEARTBEATINTERVAL interval=HEARTBEATINTERVAL timelist=[0,interval,900] # 900 indicates to start after 15 minutes callback="heartbeat" argument=[] setschedulercallback(calltype,timelist,argument,callback,callback) logger.info('Start other scheduler activities - sensor') # info file dedicate call-back --------------------------------------------- (sensor) hwnamelist=sensordbmod.gettablelist() callback="sensor" timeshift=300 shiftstep=5 #seconds # IMPORTANT # the shiftstep is necessary to avoid thread collision which brings to sqlite3 db failure "database is locked" # this number is giving a limit to the sensor reading that should be higher than 1min for hwname in hwnamelist: calltype=hardwaremod.searchdata(hardwaremod.HW_INFO_NAME,hwname,hardwaremod.HW_FUNC_SCHEDTYPE) timelist=hardwaremod.gettimedata(hwname) timelist[2]=timeshift # avoid all the sensor thread to be called in the same time argument=[] argument.append(hwname) setschedulercallback(calltype,timelist,argument,callback,hwname) timeshift=timeshift+shiftstep logger.info('Start other scheduler activities - photo') #<------> # info file dedicate quinto call-back ----------------------------------(takephoto) usedfor="photocontrol" callback="photo" hwnamelist=hardwaremod.searchdatalist(hardwaremod.HW_FUNC_USEDFOR,usedfor,hardwaremod.HW_INFO_NAME) for hwname in hwnamelist: calltype=hardwaremod.searchdata(hardwaremod.HW_INFO_NAME,hwname,hardwaremod.HW_FUNC_SCHEDTYPE) timelist=hardwaremod.gettimedata(hwname) argument=[] argument.append(hwname) setschedulercallback(calltype,timelist,argument,callback,hwname) logger.info('Start other scheduler activities - mail') # info ne file dedicate quarto call-back ---------------------------------------(sendmail) usedfor="mailcontrol" callback="mail" hwnamelist=hardwaremod.searchdatalist(hardwaremod.HW_FUNC_USEDFOR,usedfor,hardwaremod.HW_INFO_NAME) for hwname in hwnamelist: calltype=hardwaremod.searchdata(hardwaremod.HW_INFO_NAME,hwname,hardwaremod.HW_FUNC_SCHEDTYPE) timelist=hardwaremod.gettimedata(hwname) argument=[] argument.append(hwname) setschedulercallback(calltype,timelist,argument,callback,hwname) # info ne file dedicate quinto call-back ---------------------------------------(lightcheck) # empty #<----> logger.info('Start other scheduler activities - pump') # info file dedicate call-back ------------------------------------------------ (waterpump) callback="waterpump" #water schedule table paramlist= wateringdbmod.getparamlist() # name of the months ordered elementlist= wateringdbmod.getelementlist() # pump ordered (based on "watercontrol" field) table=wateringdbmod.gettable(1)# table, each row is a pump, each column is a month, value is watering time multiplieer table1=wateringdbmod.gettable(0)# table, each row is a pump, each column is a month, value is watering scheme table2=wateringdbmod.gettable(2)# table, each row is a pump, each column is a month, value is time delay #print paramlist #print elementlistly #print table paramlistdrop= advancedmod.getparamlist() # day of the week elementlistdrop= advancedmod.getelementlist() # drops ordered tabledrop=advancedmod.gettable() # table, each row is a schema number (drop number), each column is a weekday for pumpnumber in range(len(elementlist)): #print "number =",pumpnumber pumpname=elementlist[pumpnumber] todaydate = date.today() # Monday is 0 and Sunday is 6 weekday = todaydate.weekday() month = todaydate.month try: waterschemanumber=table1[pumpnumber][month-1] waterdropnumber=hardwaremod.toint(table[pumpnumber][month-1],0) watertimedelaysec=hardwaremod.toint(table2[pumpnumber][month-1],0) except IndexError: print "EXCEPTION: index out of range" waterdropnumber=0 watertimedelaysec=0 if waterdropnumber>0: #print " month " , month, " drop " , waterdropnumber calltype=hardwaremod.searchdata(hardwaremod.HW_INFO_NAME,pumpname,hardwaremod.HW_FUNC_SCHEDTYPE) for todayevent in tabledrop[waterschemanumber-1][weekday]: timelist=hardwaremod.separatetimestringint(todayevent[0]) timelist[2]=timelist[2]+watertimedelaysec argument=[] argument.append(pumpname) durationinseconds=hardwaremod.toint(todayevent[1],0)*waterdropnumber argument.append(durationinseconds) for i in range(2,len(todayevent)): argument.append(todayevent[i]) if durationinseconds>0: #check if the duration in second is >0 setschedulercallback(calltype,timelist,argument,callback,pumpname) logger.info('Start other scheduler activities - doser') # info file dedicate call-back ------------------------------------------------ (pulsenutrient) callback="doser" #fertilizer schedule table paramlist= fertilizerdbmod.getparamlist() # name of the months ordered elementlist= fertilizerdbmod.getelementlist() # element with "fertilizercontrol field" ordered table=fertilizerdbmod.gettable(1)# table, each row is a doser, each column is a month, value is number of times in a month table1=fertilizerdbmod.gettable(0)# table, each row is a doser, each column is a month, value is pulse seconds for dosernumber in range(len(elementlist)): #print "number =",dosernumber dosername=elementlist[dosernumber] calltype=hardwaremod.searchdata(hardwaremod.HW_INFO_NAME,dosername,hardwaremod.HW_FUNC_SCHEDTYPE) todaydate = date.today() # Monday is 0 and Sunday is 6 year = todaydate.year month = todaydate.month day = todaydate.day fertilizerpulsenumber=hardwaremod.toint(table[dosernumber][month-1],0) fertilizerpulsesecond=hardwaremod.toint(table1[dosernumber][month-1],0) if (fertilizerpulsenumber>0) and (fertilizerpulsesecond>0): themonthdays=30 #approximate number of days in a month dayinterval=themonthdays/fertilizerpulsenumber halfinterval=(dayinterval+1)/2 print "day=" , day , " dayinterval=", dayinterval, " half=", halfinterval if ((day+int(halfinterval)) % int(dayinterval)) == 0: #timelist=hardwaremod.gettimedata("06:00:00") timelist=autofertilizermod.timelist(dosername) argument=[] argument.append(dosername) argument.append(fertilizerpulsesecond) if (fertilizerpulsesecond)>0: #check if the duration in second is >0 setschedulercallback(calltype,timelist,argument,callback,dosername) logger.info('Start other scheduler activities - finish') return True
def getsensordbdatadaysV2(selsensor, sensordata, startdate, enddate): # V2 try to optimize access to database fieldlist = [] fieldlist.append(TIMEFIELD) fieldlist.append(DATAFIELD) #sampletime=hardwaremod.searchdata(hardwaremod.HW_INFO_NAME,selsensor,hardwaremod.HW_FUNC_TIME) timelist = hardwaremod.gettimedata( selsensor) # return list of int [0] sec, [1] min, [2] hours samplingintervalminutes = timelist[0] * 60 + timelist[1] schedtype = hardwaremod.searchdata( hardwaremod.HW_INFO_NAME, selsensor, hardwaremod.HW_FUNC_SCHEDTYPE ) # ["oneshot", "periodic"] #scheduling type #samplingintervalminutes=hardwaremod.toint(sampletime.split(":")[1],0) # return zero in case of problems if (samplingintervalminutes >= 1) and (schedtype == "periodic"): #minutessperiod=((enddate-startdate).total_seconds()+1 ) // 60 minutessperiod = ((datetime.now() - startdate).total_seconds() + 1) // 60 # sub optima approach # in this case we know the number of samples per day, so we query only the last samples of the database, this makes query way faster samplesnumber = minutessperiod // samplingintervalminutes # WARNING: with offset, there is an issue, every day the system loose some saple at midnight rescheduling only if the sample interval is less than 5 min. # in case of holes in the data sampling, it also provides wrong offset. Decided to use the non optimal approach offset = 0 #lastdata=databasemod.returnrowdatafromfieldslimitV2(DBFILENAME,selsensor,fieldlist,1,0) #if lastdata: # dateref=datetime.strptime(lastdata[0][0].split(".")[0],'%Y-%m-%d %H:%M:%S') # print( " dataref ", dateref , " enddate ", enddate) # if dateref>enddate: # minutessoffset=int( (dateref-enddate).total_seconds() / 60) # dayssoffset=minutessoffset//(24*60) # print ("dayssoffset", dayssoffset , " minutessoffset ", minutessoffset) # offset=int(minutessoffset/samplingintervalminutes) - (dayssoffset*10) # assume to loose max 10 samples per day # samplesnumber=samplesnumber+ 2 * (dayssoffset*10) # allunga il periodo di samples # offset=max(offset,0) # print ("Defined samplesnumber ", samplesnumber , " offset ", offset) print(selsensor, " Get Database Data : samplesnumber ", samplesnumber, " offset ", offset, " Type ", schedtype, "samplingintervalminutes ", samplingintervalminutes) rowdata = databasemod.returnrowdatafromfieldslimitV2( DBFILENAME, selsensor, fieldlist, samplesnumber, offset) #print( " rowdata ", rowdata) else: # no info about number of samples per day try with a number samplingintervalminutes = 5 # assumption #samplesnumber=int(days*24*60/samplingintervalminutes) samplesnumber = 1000 offset = 0 lastitem = 2 dateref = enddate rowdata = [] while (dateref > startdate) and (lastitem > 0): print(selsensor, " Get Database Data : samplesnumber ", samplesnumber, " offset ", offset) partrowdata = databasemod.returnrowdatafromfieldslimitV2( DBFILENAME, selsensor, fieldlist, samplesnumber, offset) lastitem = len(partrowdata) if lastitem >= samplesnumber: offset = offset + lastitem dateref = datetime.strptime( partrowdata[lastitem - 1][0].split(".")[0], '%Y-%m-%d %H:%M:%S') else: lastitem = 0 #print( " dataref ", dateref) #print( " rowdata ", rowdata) print(selsensor, " Get Database Data : dateref ", dateref, " DB part lenght ", lastitem) rowdata.extend(partrowdata) # check the last value of the returned array # return only the data in right datetime interval and in list form (instead of tuple) for data in rowdata: try: dateref = datetime.strptime(data[0].split(".")[0], '%Y-%m-%d %H:%M:%S') if (dateref >= startdate) and (dateref <= enddate): value = float(data[1]) templist = [data[0], value] sensordata.append(templist) except: print("Error in database reading ")
def autowateringexecute(refsensor,element): global AUTO_data sensor=autowateringdbmod.searchdata("element",element,"sensor") # check the sensor if refsensor==sensor: print "auto watering check -----------------------------------------> ", element logger.info('auto watering check --------------------------> %s', element) # check the watering mode modelist=["None", "Full Auto" , "Emergency Activation" , "Alert Only"] workmode=checkworkmode(element) if not(sensor in sensordbmod.gettablelist()): print "Sensor does not exist " ,sensor , ", element: " , element logger.error("Sensor does not exist %s , element: %s " ,sensor, element) return "sensor not Exist" maxthreshold=hardwaremod.tonumber(autowateringdbmod.searchdata("element",element,"threshold")[1],0) minthreshold=hardwaremod.tonumber(autowateringdbmod.searchdata("element",element,"threshold")[0],maxthreshold) # exit condition in case of data inconsistency if minthreshold>=maxthreshold: print "Data inconsistency , element: " , element logger.error("Data inconsistency , element: %s " , element) return "data inconsistency" now = datetime.now() nowtime = now.time() starttimeh=hardwaremod.toint(autowateringdbmod.searchdata("element",element,"allowedperiod")[0].split(":")[0],0) starttimem=hardwaremod.toint(autowateringdbmod.searchdata("element",element,"allowedperiod")[0].split(":")[1],0) endtimeh=hardwaremod.toint(autowateringdbmod.searchdata("element",element,"allowedperiod")[1].split(":")[0],1) endtimem=hardwaremod.toint(autowateringdbmod.searchdata("element",element,"allowedperiod")[1].split(":")[1],0) starttime=time(starttimeh,starttimem) endtime=time(endtimeh,endtimem) duration=1000*hardwaremod.toint(autowateringdbmod.searchdata("element",element,"wtstepsec"),0) maxstepnumber=hardwaremod.toint(autowateringdbmod.searchdata("element",element,"maxstepnumber"),0) maxdays=hardwaremod.toint(autowateringdbmod.searchdata("element",element,"maxdaysbetweencycles"),0) waitingtime=hardwaremod.toint(autowateringdbmod.searchdata("element",element,"pausebetweenwtstepsmin"),0) mailtype=autowateringdbmod.searchdata("element",element,"mailalerttype") minaccepted=hardwaremod.tonumber(autowateringdbmod.searchdata("element",element,"sensorminacceptedvalue"),0.1) # ------------------------ Workmode split if workmode=="Full Auto": # block the wateringplan activation as by definition of "Full Auto" allowwateringplan[element]=False # check if inside the allowed time period print "full Auto Mode" logger.info('full auto mode --> %s', element) timeok=isNowInTimePeriod(starttime, endtime, nowtime) print "inside allowed time ", timeok , " starttime ", starttime , " endtime ", endtime logger.info('full auto mode') if timeok: logger.info('inside allowed time') belowthr,valid=checkminthreshold(sensor,minthreshold,minaccepted) if valid: if belowthr: status="lowthreshold" logger.info('below threshold') # wait to seek a more stable reading of hygrometer # check if time between watering events is larger that the waiting time (minutes) lastwateringtime=statusdataDBmod.read_status_data(AUTO_data,element,"lastwateringtime") print ' Previous watering: ' , lastwateringtime , ' Now: ', datetime.now() timedifference=sensordbmod.timediffinminutes(lastwateringtime,datetime.now()) print 'Time interval between watering steps', timedifference ,'. threshold', waitingtime logger.info('Time interval between watering steps %d threshold %d', timedifference,waitingtime) if timedifference>waitingtime: print " Sufficient waiting time" logger.info('Sufficient waiting time') # activate watering in case the maxstepnumber is not exceeded watercounter=statusdataDBmod.read_status_data(AUTO_data,element,"watercounter") if maxstepnumber>watercounter: #activate pump activatewater(element, duration) # invia mail, considered as info, not as alert if mailtype!="warningonly": textmessage="INFO: " + sensor + " value below the minimum threshold " + str(minthreshold) + ", activating the watering :" + element emailmod.sendallmail("alert", textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",watercounter+1) statusdataDBmod.write_status_data(AUTO_data,element,"lastwateringtime",datetime.now()) else: # critical, sensor below minimum after all watering activations are done logger.info('Number of watering time per cycle has been exceeeded') # read hystory data and calculate the slope timelist=hardwaremod.gettimedata(sensor) cyclestartdate=statusdataDBmod.read_status_data(AUTO_data,element,"cyclestartdate") lastwateringtime=statusdataDBmod.read_status_data(AUTO_data,element,"lastwateringtime") startdate=cyclestartdate - timedelta(minutes=timelist[1]) enddate=lastwateringtime + timedelta(minutes=waitingtime) isslopeok=checkinclination(sensor,startdate,enddate) # still to decide if use the enddate if isslopeok: # invia mail if couner alert is lower than 1 alertcounter=statusdataDBmod.read_status_data(AUTO_data,element,"alertcounter") if alertcounter<1: textmessage="WARNING: Please consider to increase the amount of water per cycle, the "+ sensor + " value below the MINIMUM threshold " + str(minthreshold) + " still after activating the watering :" + element + " for " + str(maxstepnumber) + " times. System will automatically reset the watering cycle to allow more water" print textmessage #send alert mail notification emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",alertcounter+1) # reset watering cycle status="done" statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",-1) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"cyclestartdate",datetime.now()) else: # slope not OK, probable hardware problem alertcounter=statusdataDBmod.read_status_data(AUTO_data,element,"alertcounter") if alertcounter<3: textmessage="CRITICAL: Possible hardware problem, "+ sensor + " value below the MINIMUM threshold " + str(minthreshold) + " still after activating the watering :" + element + " for " + str(maxstepnumber) + " times" print textmessage #send alert mail notification emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",alertcounter+1) # update the status checkcounter=statusdataDBmod.read_status_data(AUTO_data,element,"checkcounter") statusdataDBmod.write_status_data(AUTO_data,element,"cyclestatus",status) statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",checkcounter+1) # RAMPUP case above threshold but below maxthreshold elif sensorreading(sensor)<maxthreshold: # intermediate state where the sensor is above the minthreshold but lower than the max threshold # check the status of the automatic cycle cyclestatus=statusdataDBmod.read_status_data(AUTO_data,element,"cyclestatus") if cyclestatus!="done": status="rampup" # wait to seek a more stable reading of hygrometer # check if time between watering events is larger that the waiting time (minutes) lastwateringtime=statusdataDBmod.read_status_data(AUTO_data,element,"lastwateringtime") if sensordbmod.timediffinminutes(lastwateringtime,datetime.now())>waitingtime: watercounter=statusdataDBmod.read_status_data(AUTO_data,element,"watercounter") if maxstepnumber>watercounter: #activate pump activatewater(element, duration) # invia mail, considered as info, not as alert if mailtype!="warningonly": textmessage="INFO: " + sensor + " value below the Maximum threshold " + str(maxthreshold) + ", activating the watering :" + element emailmod.sendallmail("alert", textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",watercounter+1) statusdataDBmod.write_status_data(AUTO_data,element,"lastwateringtime",datetime.now()) else: # give up to reache the maximum threshold, proceed as done, send alert logger.info('Number of watering time per cycle has been exceeeded') # invia mail if couner alert is lower than 1 -------------- # only if the info is activated if mailtype!="warningonly": alertcounter=statusdataDBmod.read_status_data(AUTO_data,element,"alertcounter") if alertcounter<2: textmessage="INFO "+ sensor + " value below the Maximum threshold " + str(maxthreshold) + " still after activating the watering :" + element + " for " + str(maxstepnumber) + " times" print textmessage #send alert mail notification emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",alertcounter+1) # reset watering cycle status="done" statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",-1) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"cyclestartdate",datetime.now()) # update the status checkcounter=statusdataDBmod.read_status_data(AUTO_data,element,"checkcounter") statusdataDBmod.write_status_data(AUTO_data,element,"cyclestatus",status) statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",checkcounter+1) else: # update the status, reset cycle statusdataDBmod.write_status_data(AUTO_data,element,"cyclestatus","done") statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",0) elif workmode=="Emergency Activation": # check if inside the allow time period logger.info('Emergency Mode') timeok=isNowInTimePeriod(starttime, endtime, nowtime) print "inside allowed time ", timeok , " starttime ", starttime , " endtime ", endtime if timeok: belowthr,valid=checkminthreshold(sensor,minthreshold,minaccepted) if valid: if belowthr: # wait to seek a more stable reading of hygrometer # check if time between watering events is larger that the waiting time (minutes) lastwateringtime=statusdataDBmod.read_status_data(AUTO_data,element,"lastwateringtime") if sensordbmod.timediffinminutes(lastwateringtime,datetime.now())>waitingtime: # activate watering in case the maxstepnumber is not exceeded watercounter=statusdataDBmod.read_status_data(AUTO_data,element,"watercounter") if maxstepnumber>watercounter: #activate pump activatewater(element, duration) # invia mail, considered as info, not as alert if mailtype!="warningonly": textmessage="INFO: " + sensor + " value below the minimum threshold " + str(minthreshold) + ", activating the watering :" + element emailmod.sendallmail("alert", textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",watercounter+1) statusdataDBmod.write_status_data(AUTO_data,element,"lastwateringtime",datetime.now()) else: logger.info('Number of watering time per cycle has been exceeeded') # read hystory data and calculate the slope timelist=hardwaremod.gettimedata(sensor) cyclestartdate=statusdataDBmod.read_status_data(AUTO_data,element,"cyclestartdate") lastwateringtime=statusdataDBmod.read_status_data(AUTO_data,element,"lastwateringtime") startdate=cyclestartdate - timedelta(minutes=timelist[1]) enddate=lastwateringtime + timedelta(minutes=waitingtime) isslopeok=checkinclination(sensor,startdate,enddate) if isslopeok: # invia mail if couner alert is lower than 1 alertcounter=statusdataDBmod.read_status_data(AUTO_data,element,"alertcounter") if alertcounter<1: textmessage="WARNING: Please consider to increase the amount of water per cycle, the "+ sensor + " value below the MINIMUM threshold " + str(minthreshold) + " still after activating the watering :" + element + " for " + str(maxstepnumber) + " times. System will automatically reset the watering cycle to allow more water" print textmessage #send alert mail notification alertcounter emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",alertcounter+1) # reset watering cycle status="done" statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",-1) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"cyclestartdate",datetime.now()) else: # slope not OK, probable hardware problem alertcounter=statusdataDBmod.read_status_data(AUTO_data,element,"alertcounter") if alertcounter<3: textmessage="CRITICAL: Possible hardware problem, "+ sensor + " value below the MINIMUM threshold " + str(minthreshold) + " still after activating the watering :" + element + " for " + str(maxstepnumber) + " times" print textmessage #send alert mail notification emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",alertcounter+1) # update the status checkcounter=statusdataDBmod.read_status_data(AUTO_data,element,"checkcounter") statusdataDBmod.write_status_data(AUTO_data,element,"cyclestatus","lowthreshold") statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",checkcounter+1) else: # update the status statusdataDBmod.write_status_data(AUTO_data,element,"cyclestatus","done") statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",0) elif workmode=="under MIN over MAX": logger.info('under MIN over MAX') # normally watering plan is allowed unless over MAX threshold allowwateringplan[element]=True # check if inside the allow time period timeok=isNowInTimePeriod(starttime, endtime, nowtime) print "inside allowed time ", timeok , " starttime ", starttime , " endtime ", endtime if timeok: logger.info('Insede operative time') belowthr,valid=checkminthreshold(sensor,minthreshold,minaccepted) if valid: logger.info('valid sensor reading') if belowthr: logger.info('sensor reading below threshold') # wait to seek a more stable reading of hygrometer # check if time between watering events is larger that the waiting time (minutes) lastwateringtime=statusdataDBmod.read_status_data(AUTO_data,element,"lastwateringtime") if sensordbmod.timediffinminutes(lastwateringtime,datetime.now())>waitingtime: # activate watering in case the maxstepnumber is not exceeded watercounter=statusdataDBmod.read_status_data(AUTO_data,element,"watercounter") if maxstepnumber>watercounter: logger.info('water Count not exceeded') #activate pump activatewater(element, duration) # invia mail, considered as info, not as alert if mailtype!="warningonly": textmessage="INFO: " + sensor + " value below the minimum threshold " + str(minthreshold) + ", activating the watering :" + element emailmod.sendallmail("alert", textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",watercounter+1) statusdataDBmod.write_status_data(AUTO_data,element,"lastwateringtime",datetime.now()) else: logger.info('Number of watering time per cycle has been exceeeded') # read hystory data and calculate the slope timelist=hardwaremod.gettimedata(sensor) cyclestartdate=statusdataDBmod.read_status_data(AUTO_data,element,"cyclestartdate") lastwateringtime=statusdataDBmod.read_status_data(AUTO_data,element,"lastwateringtime") startdate=cyclestartdate - timedelta(minutes=timelist[1]) enddate=lastwateringtime + timedelta(minutes=waitingtime) isslopeok=checkinclination(sensor,startdate,enddate) if isslopeok: # invia mail if couner alert is lower than 1 alertcounter=statusdataDBmod.read_status_data(AUTO_data,element,"alertcounter") if alertcounter<1: textmessage="WARNING: Please consider to increase the amount of water per cycle, the "+ sensor + " value below the MINIMUM threshold " + str(minthreshold) + " still after activating the watering :" + element + " for " + str(maxstepnumber) + " times. System will automatically reset the watering cycle to allow more water" print textmessage #send alert mail notification emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",alertcounter+1) # reset watering cycle status="done" statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",-1) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"cyclestartdate",datetime.now()) else: # slope not OK, probable hardware problem alertcounter=statusdataDBmod.read_status_data(AUTO_data,element,"alertcounter") if alertcounter<3: textmessage="CRITICAL: Possible hardware problem, "+ sensor + " value below the MINIMUM threshold " + str(minthreshold) + " still after activating the watering :" + element + " for " + str(maxstepnumber) + " times" print textmessage #send alert mail notification emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",alertcounter+1) # update the status checkcounter=statusdataDBmod.read_status_data(AUTO_data,element,"checkcounter") statusdataDBmod.write_status_data(AUTO_data,element,"cyclestatus","lowthreshold") statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",checkcounter+1) else: # above minimum threshold logger.info('sensor reading above min threshold') # update the status statusdataDBmod.write_status_data(AUTO_data,element,"cyclestatus","done") statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",0) if sensorreading(sensor)>maxthreshold: logger.info('sensor reading above MAX threshold, deactivate scheduled irrigation') # do not activate the irrigation scheduled in the time plan allowwateringplan[element]=False elif workmode=="Alert Only": belowthr,valid=checkminthreshold(sensor,minthreshold,minaccepted) if valid: if belowthr: # invia mail if couter alert is lower than alertcounter=statusdataDBmod.read_status_data(AUTO_data,element,"alertcounter") if alertcounter<2: textmessage="WARNING "+ sensor + " value below the minimum threshold " + str(minthreshold) + " watering system: " + element print textmessage #send alert mail notification emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",alertcounter+1) # update the status checkcounter=statusdataDBmod.read_status_data(AUTO_data,element,"checkcounter") statusdataDBmod.write_status_data(AUTO_data,element,"cyclestatus","lowthreshold") statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",checkcounter+1) else: # update the status statusdataDBmod.write_status_data(AUTO_data,element,"cyclestatus","done") statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",0) else: # None case print "No Action required, workmode set to None, element: " , element logger.info("No Action required, workmode set to None, element: %s " , element) cyclestatus=statusdataDBmod.read_status_data(AUTO_data,element,"cyclestatus") if cyclestatus=="lowthreshold": checkcounter=statusdataDBmod.read_status_data(AUTO_data,element,"checkcounter") if checkcounter==1: statusdataDBmod.write_status_data(AUTO_data,element,"cyclestartdate",datetime.now()) # implment alert message for the cycle exceeding days, and reset the cycle if workmode!="None": cyclestartdate=statusdataDBmod.read_status_data(AUTO_data,element,"cyclestartdate") timedeltadays=sensordbmod.timediffdays(datetime.now(),cyclestartdate) if (timedeltadays > maxdays): #the upper limit is set in case of abrupt time change textmessage="WARNING "+ sensor + " watering cycle is taking too many days, watering system: " + element + ". Reset watering cycle" print textmessage # in case of full Auto, activate pump for minimum pulse period if workmode=="Full Auto": if (timedeltadays < maxdays+2): #the upper limit is set in case of abrupt time change textmessage="WARNING "+ sensor + " watering cycle is taking too many days, watering system: " + element + ". Activate Min pulse + Reset watering cycle" activatewater(element, duration) #send alert mail notification if mailtype!="warningonly": emailmod.sendallmail("alert", textmessage) logger.error(textmessage) logger.error("Cycle started %s, Now is %s ", cyclestartdate.strftime("%Y-%m-%d %H:%M:%S"), datetime.now().strftime("%Y-%m-%d %H:%M:%S")) # reset cycle statusdataDBmod.write_status_data(AUTO_data,element,"cyclestatus","done") statusdataDBmod.write_status_data(AUTO_data,element,"checkcounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"watercounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",0) statusdataDBmod.write_status_data(AUTO_data,element,"cyclestartdate",datetime.now()) # implment Critical alert message in case the threshold is below the 0.5 of the minimum if workmode!="None": belowthr,valid=checkminthreshold(sensor,minthreshold*0.5,minaccepted) if valid: if belowthr: logger.info('sensor %s below half of the actual set threshold', sensor) textmessage="CRITICAL: Plant is dying, "+ sensor + " reading below half of the minimum threshold, need to check the " + element print textmessage #send alert mail notification alertcounter=statusdataDBmod.read_status_data(AUTO_data,element,"alertcounter") if alertcounter<5: emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",alertcounter+1) else: logger.info('sensor %s below valid data', sensor) textmessage="WARNING: "+ sensor + " below valid data range, need to check sensor" print textmessage #send alert mail notification alertcounter=statusdataDBmod.read_status_data(AUTO_data,element,"alertcounter") if alertcounter<3: emailmod.sendallmail("alert", textmessage) logger.error(textmessage) statusdataDBmod.write_status_data(AUTO_data,element,"alertcounter",alertcounter+1) return