rpiarduinomusings

Raspberry Pi, Arduino, Sensors and the data they produce

A Module to Test When Time is Within a Defined Range

Introduction

This post presents a Python module that determines when the current time falls between a time range for given days of the week. The days and times are in a configuration file that makes it easy to make scheduling changes.

 

The Module

This module is designed to be imported into an existing application that requires scheduled processing that is governed using a from and to time boundary. In other words, if the current time expressed as HHMMSS (as integer) is within the specified range, then the process execution cab be allowed.

The location of the configuration file is defined in function ‘readConfigFileValues()’. There, you will see two paths. One is for a Linux OS and the other is for Windows. Comment out the line not being used and ensure the path is correct for your machine.

#!/usr/bin/python #********************************************************************** # Module : ModTimeRange.py # Date : 20160830 # Uses Modules : No # # This module is designed to be imported into an existing application # that requires scheduled processing that is governed using a from and # to time boundary. In other words, if the current time expressed as # HHMMSS (as integer) is within the specified range, then the process # execution is allowed. # # This module relies upon a configuration file, 'modTimeRange.txt'. It # contains name/value pairs that define from and to time pairs for each # day of the week. # # The function init() loads the values from the configuration file. # The function testTime() will compare the current time against each # from-to time pairs for the current day. These functions do not # require parameters as everything is contained within and governed by # time ranges defined by the config file. # # There is no limit to the number of from-to time pairs for a given day. # # To use: # # import ModTimeRange # # ModTimeRange.init() # # while True: # # if ModTimeRange.testTime(): # print "Current time is within range." # else: # print "Current time is not within range." # # ModTimeRange.checkConfigReadFreq() # time.sleep(10) # # #********************************************************************** import os import sys import platform import datetime from ConfigParser import SafeConfigParser import time from datetime import date import calendar #********************************************************************** #***** VARIABLES ***** #********************************************************************** progName = os.path.basename(__file__) # Full path of this python script mypath=os.path.abspath(__file__) # Path location only (excluding script name) baseDir=mypath[0:mypath.rfind("/")+1] baseFileName=mypath[mypath.rfind("/")+1:mypath.rfind(".")] #config file variables activeSchedule = None configReadFreq = None mon = None tue = None wed = None thur = None fri = None sat = None sun = None dayOfWeek = None timeRange = None printTimeInfo = None #Miscellaneous variables. saveConfigStartTime = None #********************************************************************** #***** FUNCTION DECLARATIONS ***** #********************************************************************** #---------------------------------------------------------------------- def init(): readConfigFileValues() logTimeInfo() #---------------------------------------------------------------------- def readConfigFileValues(): #use the global variables global activeSchedule, configReadFreq, printTimeInfo global mon, tue, wed, thur, fri, sat, sun, dayOfWeek, timeRange function = "checkConfigReadFreq()" #Establish location of configuration file for OS being used. #configFileHome = '/home/pi/PythonDev/modTimeRange.txt' configFileHome = 'C:\Desk\AppDevPython\modTimeRange.txt' #Initialize SafeConfigParser using value in configFileHome parser = SafeConfigParser() parser.read(configFileHome) #Read general config values that indicate schedule to use and #frequency of reading config file for potential changes. activeSchedule = parser.get('general', 'activeSchedule') configReadFreq = parser.get('general', 'configReadFreq') printTimeInfo = parser.get('general', 'printTimeInfo') #Isolate the schedule to be sensitive to movement for each day mon = parser.get(activeSchedule, 'Monday') tue = parser.get(activeSchedule, 'Tuesday') wed = parser.get(activeSchedule, 'Wednesday') thur = parser.get(activeSchedule, 'Thursday') fri = parser.get(activeSchedule, 'Friday') sat = parser.get(activeSchedule, 'Saturday') sun = parser.get(activeSchedule, 'Sunday') #Determine day of week. This value is used to lookup the schedule. dateToday = date.today() dayOfWeek = calendar.day_name[dateToday.weekday()] #The schedule of times to be active is placed into timeRange. if dayOfWeek == "Monday" : timeRange = mon.split(",",3 ) if dayOfWeek == "Tuesday" : timeRange = tue.split(",",3 ) if dayOfWeek == "Wednesday" : timeRange = wed.split(",",3 ) if dayOfWeek == "Thursday" : timeRange = thur.split(",",3 ) if dayOfWeek == "Friday" : timeRange = fri.split(",",3 ) if dayOfWeek == "Saturday" : timeRange = sat.split(",",3 ) if dayOfWeek == "Sunday" : timeRange = sun.split(",",3 ) return #---------------------------------------------------------------------- def checkConfigReadFreq(): global configReadFreq, saveConfigStartTime function = "checkConfigReadFreq()" if saveConfigStartTime == None: saveConfigStartTime = time.time() timeNow = time.time() elapsed_time = timeNow - saveConfigStartTime #This will print the message similar to this: # "checkConfigReadFreq(): 1472561461.18 - 1472561441.12 = 20.0679998398" # to reveal the format type of timeNow, saveConfigStartTime and elapsed_time showMessage(function, str(timeNow) + " - " + str(saveConfigStartTime) + " = " + str(elapsed_time) ) if elapsed_time >= configReadFreq: showMessage(function, "Calling readConfigFileValues()") readConfigFileValues() saveConfigStartTime = time.time() return #---------------------------------------------------------------------- def logTimeInfo(): global activeSchedule, configReadFreq, printTimeInfo global mon, tue, wed, thur, fri, sat, sun, dayOfWeek function = "logTimeInfo()" if printTimeInfo == "True": showMessage(function, "activeSchedule = " + str(activeSchedule) ) showMessage(function, "configReadFreq = " + str(configReadFreq ) ) showMessage(function, "Monday = " + mon ) showMessage(function, "Tuesday = " + tue ) showMessage(function, "Wednesday = " + wed ) showMessage(function, "Thursday = " + thur ) showMessage(function, "Friday = " + fri ) showMessage(function, "Saturday = " + sat ) showMessage(function, "Sunday = " + sun ) showMessage(function, "Day of week number (0=Mon, 6=Sun) is " + str(datetime.datetime.today().weekday()) ) showMessage(function, "Day of week name is " + dayOfWeek ) dateTimeNow = datetime.datetime.now() showMessage(function, "Date/Time now is " + str(dateTimeNow)) return #---------------------------------------------------------------------- def testTime(): global dayOfWeek, timeRange function = "testTime()" timeNow = time.strftime("%H%M%S") showMessage(function, "Time now is " + timeNow ) for index in range(len(timeRange)): timeFromTo = timeRange[index] if str2bool(printTimeInfo) == True: showMessage(function, " timeFromTo :" + timeFromTo) showMessage(function, " from time :" + timeFromTo[0:6]) showMessage(function, " to time :" + timeFromTo[7:13]) inRange=isTimeInRange(timeFromTo[0:6], timeNow, timeFromTo[7:13]) if(inRange): return inRange return inRange #---------------------------------------------------------------------- def isTimeInRange(start, timeNow, end): function = "isTimeInRange()" #Return true if x is in the range [start, end] inRange = False if start <= end: inRange = start <= timeNow <= end else: inRange = start <= timeNow or timeNow <= end return inRange #---------------------------------------------------------------------- def str2bool(v): return v.lower() in ("yes", "true", "t", "1") #---------------------------------------------------------------------- def processMovement(): function = "processMovement()" showMessage(function, "Call processMovement(): ") #---------------------------------------------------------------------- def showMessage(functionName, messageStr): if printTimeInfo == "True": now = showTime() print ("%s %s : %s " % (now, messageStr.ljust(70), functionName.ljust(25)) ) return #---------------------------------------------------------------------- def showTime(): rightNow = datetime.datetime.now() rn=rightNow.strftime("[%Y%m%d %H%M%S]") return rn #---------------------------------------------------------------------- # This function is provided for testing only. It is executed only when # this module is called as a program. #---------------------------------------------------------------------- def execute(): function = "execute()" readConfigFileValues() logTimeInfo() while True: if testTime(): showMessage(function, "Current time is within range.") else: showMessage(function, "Current time is not within range.") checkConfigReadFreq() time.sleep(10) return #********************************************************************** #***** M A I N L I N E C O N T R O L L E R ***** #********************************************************************** if __name__ == '__main__': print("+++++++++++++++++++++++++++++++++++") print("%s - Entering Program" % progName) print "Path to program = " + mypath print sys.version_info print "The O/S = " + platform.system() try: execute() finally: print("%s - Exiting Program" % progName) print("+++++++++++++++++++++++++++++++++++") print("") #********************************************************************** #********************************************************************** #***** E N D O F S O U R C E ***** #********************************************************************** #**********************************************************************

 

The Configuration File

The contents presented below are contained in the module’s configuration file, ‘modTimeRange.txt’. It is read when ModTimeRange.init() is called by the host program. The name/value pairs should be easy to understand. Just know that the times should always appear in pairs separated by commas only, as shown.

 
[general] activeSchedule = schedule01 configReadFreq = 000015 printTimeInfo = True [schedule01] Monday = 000000-041500,051500-160000,211500-235959 Tuesday = 000000-041500,051500-160000,211500-235959 Wednesday = 000000-041500,051500-160000,211500-235959 Thursday = 000000-041500,051500-160000,211500-235959 Friday = 000000-041500,051500-160000,211500-235959 Saturday = 000000-050000,220000-235959 Sunday = 000000-050000,220000-235959 [schedule02] Monday = 000000-041500,051500-160000,211500-235959 Tuesday = 000000-041500,051500-160000,211500-235959 Wednesday = 000000-041500,051500-160000,211500-235959 Thursday = 000000-041500,051500-160000,211500-235959 Friday = 000000-041500,051500-160000,211500-235959 Saturday = 000000-050000,220000-235959 Sunday = 000000-050000,220000-235959

Impetus

This module was written to replace existing code in PIRLEDeMail.py that monitors movement during certain times of the day and night. In that program, there is an if-statement (Use the link above to see this under section title “The main processing loop”) that governs sensor operations for certain time ranges. It was hastily written just to get the job done. The resulting logic is cryptic, hard to maintain and is prone to making errors when the days and times need adjustment.

Benefits

The following features of this module address these concerns and others.

  • This module is useful for applications that have processing tasks spanning specific times of specific days of the week.
  • Placing the time range values used by time range testing into a configuration file provides scheduling flexibility while removing the requirement to modify Python code.
  • Potential editing errors made to the Python program are eliminated.
  • The intuitive nature of the externalized scheduling data minimize errors.
  • There can now be a single version of an application that is installed across different Raspberry Pi. The time range data externalized into a configuration file is the only component that may be different between them.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: