[go: up one dir, main page]

Menu

[r219]: / calise / daemonmodule.py  Maximize  Restore  History

Download this file

336 lines (314 with data), 13.4 kB

  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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# Copyright (C) 2011 Nicolo' Barbon
#
# This file is part of Calise.
#
# Calise is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# Calise is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Calise. If not, see <http://www.gnu.org/licenses/>.
import os
import time
import threading
import logging
from wsgiref.simple_server import make_server
from cgi import parse_qs, escape
import socket
from calise import objects
from calise.infos import __LowerName__
# Execute func and log out as DEBUG information the return code
def debug_exec(func, logger):
r = func()
logger.debug("Function '%s' returned %d" % (func.__name__, r))
def strToBool(string):
retVal = None
if string.lower() in ["0","false","no"]:
retVal = False
elif string.lower() in ["1","true","yes"]:
retVal = True
return retVal
# Main execution Thread
class whatsmyname(threading.Thread):
def __init__(self, settings):
self.logger = logging.getLogger('calise.root')
self.logger.info("Starting main Thread...")
self.objectClass = objects.objects(settings)
self.cbs = self.objectClass.getCbs()
# control "flags"
self.stop = False
self.pause = False
# exec-options
# TODO: move weather to settings
self.weather = True
# threading.Thread initialization
threading.Thread.__init__(self)
def run(self):
self.logger.info("Main Thread successfully started")
while self.stop is False:
self.objectClass.resetComers()
self.cycle_sleeptime = self.objectClass.executer(
wcheck=self.weather)
self.objectClass.append_data()
self.event_logger()
# the cycle below sleeps Nth time for 1 sec and meanwhile checks
# if the stop flag has been set, in that case breaks (and so
# terminates the thread)
while self.cycle_sleeptime > time.time():
# if %pause% then sleep indefinitely (until NOT %pause%)
if self.pause is None:
self.pause = True
while self.pause and not self.stop:
time.sleep(1)
self.pause = False
else:
time.sleep(1)
# if %stop% then close thread
if self.stop is True:
break
def event_logger(self):
objc = self.objectClass
if len(objc.oldies) == 1:
if objc.oldies[-1]["cbs"] != self.cbs:
self.logger.info(
"Backlight step changed from %d to %d"
% (self.cbs, objc.oldies[-1]["cbs"]))
del self.cbs
self.logger.info(
"Time of the day is \"%s\"" % (objc.oldies[-1]["css"]))
self.logger.info(
"Sleeptime set to %.2f" % (objc.oldies[-1]["slp"]))
elif len(objc.oldies) > 1:
if objc.oldies[-1]["cbs"] != objc.oldies[-2]["cbs"]:
self.logger.info(
"Backlight step changed from %d to %d"
% (objc.oldies[-2]["cbs"], objc.oldies[-1]["cbs"]))
if objc.oldies[-1]["css"] != objc.oldies[-2]["css"]:
self.logger.info(
"Time of the day changed from \"%s\" to \"%s\""
% (objc.oldies[-2]["css"], objc.oldies[-1]["css"]))
if objc.oldies[-1]["slp"] != objc.oldies[-2]["slp"]:
self.logger.info(
"Sleeptime between captures changed from %.2f to %.2f"
% (objc.oldies[-2]["slp"], objc.oldies[-1]["slp"]))
return 0
# Create a listening server and indefinitely check for inputs
# Should be executed as thread
class DaemonLike():
def __init__(self, settings, dport=30780, dhost="localhost"):
# Init vars
self.th = None
self.stop = False
self.settings = settings
# Init functions
self.httpd = make_server(dhost, dport, self.application)
self.logger = logging.getLogger(".".join([__LowerName__, "server"]))
# Start functions
self.logger.info("Process started with PID %d" % os.getpid())
self.logger.info("Daemon listening on port %d" % dport)
debug_exec(self.start_thread, self.logger)
# wsgiref.simple_server basic application implementation
def application(self, environ, start_response):
# Returns a dictionary containing lists as values
d = parse_qs(environ['QUERY_STRING'])
# Escape and check value format (to avoid code injection)
for key in d.keys():
if type(d[key]) != list:
d[key] = [d[key]]
for x in range(len(d[key])):
d[key][x] = escape(d[key][x])
# Simple parse & response (None if not an "output" request)
d = self.gurreato_parser(d)
# Server-answer section
response_body = str(d)
status = '200 OK'
response_headers = [('Content-Type', 'text/html'),
('Content-Length', str(len(response_body)))]
start_response(status, response_headers)
return [response_body]
# start thread execution
# returns True if thread started correctly, elsewhere returns False
def start_thread(self):
if self.th is not None:
if self.th.isAlive():
return 1
self.th = whatsmyname(self.settings)
self.th.start()
return 0
# stop thread execution
# returns True if thread stopped correctly or there is no thread at all
def stop_thread(self):
if self.th is not None:
if self.th.isAlive():
self.th.stop = True
self.th.join(
self.th.objectClass.arguments["capnum"] * \
self.th.objectClass.arguments["capint"] * 3)
if self.th.isAlive():
self.th = None
return 1
return 0
# pause thread execution
def pause_thread(self):
if self.th is not None:
if self.th.isAlive() and self.th.pause is False:
self.th.pause = None
while self.th.pause is not True:
time.sleep(0.1)
return 0
return 1
# resume thread execution
def resume_thread(self):
if self.th is not None:
if self.th.isAlive() and self.th.pause is True:
self.th.pause = None
while self.th.pause is not False:
time.sleep(0.1)
return 0
return 1
# check thread execution
# If alive but paused returns 2, else if Alive and not paused, 0.
def check_thread(self):
if self.th is not None:
if self.th.isAlive():
if self.th.pause is True:
return 2
else:
return 0
else:
return 3
return 1
# Set thread variable's value (one per call)
# every and only settings stated there can be changed during execution
# TODO: simplify removing actual key names if you can
def set_thread(self, idx, value):
value = value[0]
if idx in ["weather"]:
self.th.weather = strToBool(value)
elif idx in ["latitude"]:
self.settings["latitude"] = float(value)
self.th.objectClass.arguments["latitude"] = float(value)
elif idx in ["longitude"]:
self.settings["longitude"] = float(value)
self.th.objectClass.arguments["longitude"] = float(value)
elif idx in ["capnum"]:
self.settings["capnum"] = int(value)
self.th.objectClass.arguments["capnum"] = int(value)
elif idx in ["capint"]:
self.settings["capint"] = float(value)
self.th.objectClass.arguments["capint"] = float(value)
return 0
# Manual camera capture
# If an existing running thread is found it will be paused and then
# resumed else, a temporary thread is initialized (but not started)
def manual_capture(self):
r = self.check_thread()
if r in (0, 2):
if r == 0:
self.pause_thread()
self.th.objectClass.resetComers()
old = self.th.objectClass.oldies[-1]
w = self.th.objectClass.writeStep(standalone=True)
ts = self.th.objectClass.newcomers["cts"] - old["cts"]
self.th.objectClass.newcomers["nss"] = old["nss"] - ts
self.th.objectClass.newcomers["slp"] = old["slp"] - ts
self.th.objectClass.newcomers["css"] = old["css"]
self.th.objectClass.append_data()
self.th.event_logger()
if r == 0:
self.resume_thread()
else:
if r == 1:
self.th = whatsmyname()
w = self.th.objectClass.writeStep(standalone=True)
self.th.event_logger()
self.logger.debug("Function objects.writeStep returned %d" % w)
return 0
# Parse args and executes corresponding operations, also logs processing
# data and returns messages according to the requests
def gurreato_parser(self, getd):
retMsg = []
# Kill sequence
if getd.keys().count("kill"):
self.logger.debug("Client requested kill. Stopping thread...")
retCode = self.stop_thread()
if retCode > 0:
self.logger.error("Failed to stop thread.")
retMsg.append(("error", "Failed to stop thread."))
else:
self.logger.info("Thread stopped successfully.")
self.logger.info("Sending kill to server.")
retMsg.append(("info", "Kill sent succesfully."))
#os.kill(os.getpid(), 15)
self.httpd.socket.close()
# Restart sequence
elif getd.keys().count("restart"):
self.logger.debug("Client requested kill. Stopping thread...")
debug_exec(self.stop_thread, self.logger)
debug_exec(self.start_thread, self.logger)
# Pause sequence
elif getd.keys().count("pause"):
self.logger.debug("Client requested pause. Pausing thread...")
retCode = self.pause_thread()
if retCode > 0:
self.logger.error("Failed to pause thread.")
retMsg.append(("error", "Failed to pause thread."))
else:
self.logger.info("Thread paused successfully.")
retMsg.append(("info", "Pause sent succesfully."))
# Resume sequence
elif getd.keys().count("resume"):
self.logger.debug("Client requested resume. Resuming thread...")
retCode = self.resume_thread()
if retCode > 0:
self.logger.error("Failed to resume thread.")
retMsg.append(("error", "Failed to resume thread."))
else:
self.logger.info("Thread resumed successfully.")
retMsg.append(("info", "Resume sent succesfully."))
# Check sequence
elif getd.keys().count("check"):
debug_exec(self.check_thread, self.logger)
# Other execution commands
elif getd.keys().count("capture"):
debug_exec(self.manual_capture, self.logger)
# Output triggered by "dump", output everything in dictionary format
if getd.keys().count("dump") and self.th is not None:
vals = self.th.objectClass.dumpValues()
retMsg += [(k, vals[k]) for k in vals.keys()]
elif getd.keys().count("dumpall") and self.th is not None:
vl = self.th.objectClass.dumpValues(allv=True)
# Format multiple values as list of values for every key
vals = {}
for idx in range(len(vl)):
for key in vl[idx].keys():
if vals.keys().count(key):
vals[key] += [vl[idx][key]]
else:
vals[key] = [vl[idx][key]]
retMsg += [(k, vals[k]) for k in vals.keys()]
if getd.keys().count("dumpsettings") and self.th is not None:
args = self.th.objectClass.arguments
retMsg += [(k, args[k]) for k in args.keys()]
# Settings set commands
settingsKeys = [
"weather",
"latitude", "longitude",
"capnum", "capint"]
if sum([x in settingsKeys for x in getd.keys()]) > 0:
if self.check_thread() in (0, 2):
for key in getd.keys():
self.set_thread(key, getd[key])
return retMsg
def runrunrun(self):
try:
self.httpd.serve_forever()
except socket.error as err:
if err.errno != 9:
raise