cccc/server.py
2018-12-04 09:50:29 +01:00

229 lines
6.1 KiB
Python

from functools import wraps
from urllib.parse import unquote
import json
import logging
import os
import queue
import shutil
import threading
import time
import requests
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.web import StaticFileHandler, Application, HTTPError
import tornado.gen
import tornado.web
from action import Camera
logger = logging.getLogger(__name__)
STATIC_PATH = 'static'
PORT = 8000
ADDRESS = '127.0.0.1'
with open('camera.json') as fd:
CAMERA = json.load(fd)
state = {
'time': {},
'status': 'Idle'
}
def run_async(func):
@wraps(func)
def async_func(*args, **kwargs):
func_hl = Thread(target=func, args=args, kwargs=kwargs)
func_hl.start()
return func_hl
return async_func
def _to_json(python_object):
if isinstance(python_object, datetime.datetime):
if python_object.year < 1900:
tt = python_object.timetuple()
return '%d-%02d-%02dT%02d:%02d%02dZ' % tuple(list(tt)[:6])
return python_object.strftime('%Y-%m-%dT%H:%M:%SZ')
raise TypeError('%s %s is not JSON serializable' % (repr(python_object), type(python_object)))
def json_dumps(obj):
return json.dumps(obj, indent=4, default=_to_json, ensure_ascii=False, sort_keys=True).encode()
class ControlQueue:
shutdown = False
def worker(self):
while True:
item = self.q.get()
if item is None or self.shutdown:
break
state['status'] = 'Active'
self.camera.sequence(**item)
if self.camera.abort:
self.camera.abort = False
state['status'] = 'Idle'
def __init__(self):
self.q = queue.Queue()
self.camera = Camera(**CAMERA)
self._worker = threading.Thread(target=self.worker)
self._worker.start()
def put(self, filename):
self.q.put(filename)
def join(self):
self.shutdown = True
self.camera.abort = True
# block until all tasks are done
self.q.join()
self.q.put(None)
self._worker.join()
class API(object):
def __call__(self, method, kwargs):
return getattr(self, method)(**kwargs)
def getPresets(self, **data):
result = {}
result['presets'] = ctl.camera.get_presets(True)
return result
def camera(self, **data):
result = {}
for key, value in data.items():
getattr(ctl.camera, key)(**value)
return result
def run(self, **data):
result = {}
ctl.put(data)
return result
def stop(self, **data):
result = {}
ctl.camera.abort = True
ctl.camera.continuous(ctl.camera.STOP)
return result
def status(self, **data):
result = {}
result['time'] = ctl.camera.segment_times
result['status'] = state['status']
if result['status'] == 'Active':
if ctl.camera.sequence_start:
result['duration'] = int(time.time() - ctl.camera.sequence_start)
else:
result['duration'] = '...'
result['next'] = ctl.camera.next_target
else:
result['duration'] = ctl.camera.sequence_time
result['position'] = ctl.camera.last_position
return result
#@run_async
def api_task(request, callback):
api = API()
response = {
'result': api(request['method'], request.get('params', {}))
}
callback(response)
class RPCHandler(tornado.web.RequestHandler):
def get(self):
self.write(BANNER)
@tornado.gen.coroutine
def post(self):
error = None
request = None
try:
request = json.loads(self.request.body.decode())
if request['method'][0] == '_' or not hasattr(API, request['method']):
raise Exception('unknown method')
except:
error = {'error': {'code': -32700, 'message': 'Parse error'}}
if not error:
try:
response = yield tornado.gen.Task(api_task, request)
except:
logger.error("ERROR: %s", request, exc_info=True)
error = {'error': {'code': -32000, 'message': 'Server error'}}
if error:
response = error
if request and 'id' in request:
response['id'] = request['id']
response['jsonrpc'] = '2.0'
response = json_dumps(response)
self.write(response)
def prepare(encoding):
if not os.path.exists(settings['prefix']):
print('please create "%s" and start again' % settings['prefix'])
sys.exit(1)
index = os.path.join(settings['prefix'], 'index.html')
if not os.path.exists(index):
try:
with open(index, 'w') as fd:
fd.write(BANNER_PUBLIC)
except:
print('can not write to "%s"' % settings['prefix'])
sys.exit(1)
load_files(encoding)
registered = False
while not registered:
try:
register_server()
except:
logging.error('failed to register')
time.sleep(10)
registered = True
class MainHandler(tornado.web.RequestHandler):
def get(self, path):
path = os.path.join(STATIC_PATH, 'index.html')
with open(path) as fd:
content = fd.read()
self.set_header('Content-Type', 'text/html')
self.set_header('Content-Length', str(len(content)))
self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate')
self.set_header('Pragma', 'no-cache')
self.set_header('Expires', '0')
self.write(content)
def main():
global ctl
ctl = ControlQueue()
handlers = [
(r'/api/', RPCHandler),
(r'/static/(.*)', StaticFileHandler, {'path': STATIC_PATH}),
(r"(.*)", MainHandler),
]
options = {
'debug': False,
'gzip': True,
}
app = Application(handlers, **options)
app.listen(PORT, ADDRESS)
main = IOLoop.instance()
try:
main.start()
except:
print('shutting down...')
ctl.join()
if __name__ == '__main__':
main()