-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprogramrunner.py
152 lines (117 loc) · 6.58 KB
/
programrunner.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/user/bin/python
from datetime import datetime, timedelta, date, time
from workerthread import WorkerThread
import db
from constants import ProgramCodes, MessageTypes, MessageCodes, Statuses, Sensors, Devices, DebugLevels
class ProgramRunner(WorkerThread):
RUNTIME = 3600
EXCEPTION_TIMEOUT = 120
DEBUG_LEVEL = DebugLevels.NONE
scheduledTaskTime = datetime.now()
def dowork(self):
programID = self.program[ProgramCodes.PROGRAM_ID]
programCode = self.program[ProgramCodes.CODE]
relative = self.program[ProgramCodes.RELATIVE_TIMES]
repeat = self.program[ProgramCodes.REPEAT_PROGRAM]
self.starttime = self.program[ProgramCodes.START_TIME]
if self.program[ProgramCodes.MESSAGE] != "":
self.information['Information'] = {
MessageCodes.NAME:"Info",
MessageCodes.VALUE:self.program[ProgramCodes.MESSAGE]
}
elif 'Information' in self.information:
del self.information['Information']
self.debug("Running program {0}".format(programCode))
sql = """SELECT device, action_time, action_time_milliseconds, value
FROM program_actions
WHERE program_id = {0}
ORDER BY action_time, action_time_milliseconds""".format(programID)
programActions = db.read(sql)
deviceActions = {}
# Find the initial state for absolute programs by iterating once through the array.
if not relative:
for programAction in programActions:
device = programAction['device']
action = programAction['value']
deviceActions[device] = {'value':action, 'message':'Setup {0} to {1}'.format(device, action)}
self.debug("Iterating initial state {0} {1}".format(programAction['device'], programAction['value']))
for deviceName, deviceAction in deviceActions.iteritems():
self.debug("Initial state {0} {1}".format(deviceName, deviceAction['message']))
if self.status == Statuses.UNDEFINED:
self.showstatus()
for programAction in programActions:
if self.looprequest or self.stoprequest:
return
device = programAction['device']
actionTime = self.getabsoluteactiontime(programAction['action_time'] + timedelta(milliseconds=programAction['action_time_milliseconds']), relative)
action = programAction['value']
if actionTime > datetime.now():
self.switchdevices(deviceActions)
self.debug("Sleeping until next action at {0}".format(actionTime))
self.sleep(actionTime)
self.debug("Program specifies {0} {1} at {2}".format(device, action, actionTime))
actionText = "ON" if action else "OFF"
deviceActions[device] = {'value':action, 'message':'Switch {0} {1} at {2}'.format(device, actionText, actionTime)}
# Perform the final action
if not self.stoprequest and not self.looprequest:
self.switchdevices(deviceActions)
if repeat and not relative:
# We've finished our program for the day. We can sleep until midnight.
self.debug("Sleeping until midnight {0}".format(date.today() + timedelta(days=1)))
self.sleep(datetime.combine(date.today() + timedelta(days=1), time()))
if not repeat and not self.looprequest and not self.stoprequest:
if len(self.programStack) > 0:
self.program = self.programStack.pop()
self.loginformation("Program Complete", "Program {0} has finished. Resuming {1}.".format(programCode, self.program[ProgramCodes.CODE]))
self.looprequest = True
else:
self.loginformation("Program Complete", "Program {0} has finished. Resuming default.".format(programCode))
self.setdefaultprogram()
return
def sleep(self, actionTime=None):
self.debug("actionTime: {0} {1}".format(actionTime, type(actionTime)))
if actionTime is None:
pass
elif type(actionTime) is int and actionTime == 0:
pass
elif type(actionTime) is int:
pass
elif type(actionTime) is datetime:
self.debug("actionTime = {0}, scheduledTaskTime = {1}".format(actionTime, self.scheduledTaskTime))
while actionTime > self.scheduledTaskTime and not (self.looprequest or self.stoprequest):
self.debug("Sleeping until next scheduled tasks at {0}".format(self.scheduledTaskTime))
super(ProgramRunner, self).sleep(self.scheduledTaskTime)
if not (self.looprequest or self.stoprequest):
self.scheduledTaskTime = datetime.now() + timedelta(seconds=self.RUNTIME)
self.resetstatus()
self.runscheduledtasks()
self.debug("Sleeping until action time at {0}".format(actionTime))
else:
raise Exception("Program runner sleep argument must be 0 or of type datetime. {0} ({1}) supplied.".format(actionTime, type(actionTime)))
super(ProgramRunner, self).sleep(actionTime)
def runscheduledtasks(self):
""" Override this method in subclasses to provide regular tasks, run at RUNTIME intervals """
self.debug("Running scheduled tasks")
def switchdevices(self, deviceActions):
self.debug("Switching devices")
for deviceName, deviceAction in deviceActions.iteritems():
self.debug("Switch {0} {1} ({2})".format(deviceName, deviceAction['value'], deviceAction['message']))
self.deviceoutput(deviceName, deviceAction['value'], logMessage=deviceAction['message'])
def getabsoluteactiontime(self, actionTime, relative):
"""Returns a datetime specifying at what time today the actionTime should run"""
if relative:
self.debug("Getting endtime for relative {0} from {1}".format(actionTime, self.starttime))
result = self.starttime + actionTime
self.debug("Endtime is {0}".format(result))
return result
else:
return datetime.combine(date.today(), time()) + actionTime
def setdefaultprogram(self):
super(ProgramRunner, self).setdefaultprogram()
self.programStack = []
if not self.program:
raise Exception("No default program is defined for ProgramRunner {0}".format(self.name))
def onprogramchanged(self):
self.looprequest = True
def setup(self):
pass