cccc/server.py

287 lines
7.7 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/python3
2018-12-04 08:50:29 +00:00
from functools import wraps
from urllib.parse import unquote
import json
import logging
import os
import queue
import shutil
import threading
import time
import sys
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 camera import Camera
logger = logging.getLogger(__name__)
STATIC_PATH = 'static'
PORT = 8000
ADDRESS = '127.0.0.1'
ADDRESS = '0.0.0.0'
2018-12-04 08:50:29 +00:00
with open('camera.json') as fd:
CAMERA = json.load(fd)
state = {
'time': {},
2020-12-06 12:42:13 +00:00
'status': 'Idle',
'sequence': [],
2018-12-04 08:50:29 +00:00
}
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()
2020-12-05 10:31:14 +00:00
def put(self, command):
if self.q.empty():
self.camera.abort = False
2020-12-05 10:31:14 +00:00
self.q.put(command)
2018-12-04 08:50:29 +00:00
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 = {}
2020-12-06 16:11:47 +00:00
result['presets'] = ctl.camera.get_presets(True)
2018-12-04 08:50:29 +00:00
return result
2020-12-05 11:55:59 +00:00
def setPreset(self, **data):
result = {}
2020-12-05 11:56:56 +00:00
print('setPreset', data)
2020-12-05 11:55:59 +00:00
ctl.camera.set_preset(data['id'], data['name'])
return result
2018-12-04 08:50:29 +00:00
def setPresets(self, **data):
result = {}
ctl.camera.set_presets(data['presets'])
result['presets'] = ctl.camera.get_presets(True)
return result
2018-12-04 08:50:29 +00:00
def camera(self, **data):
result = {}
for key, value in data.items():
2018-12-05 12:58:12 +00:00
result[key] = getattr(ctl.camera, key)(**value)
2018-12-04 08:50:29 +00:00
return result
def run(self, **data):
result = {}
ctl.put(data)
with open('last_run.json', 'w') as fd:
json.dump(data, fd, indent=4)
2018-12-04 08:50:29 +00:00
return result
2020-12-05 10:39:05 +00:00
def move(self, **data):
result = {}
if data.get('direction') in (
'LEFT', 'RIGHT', 'UP', 'DOWN',
'LEFT_UP', 'LEFT_DOWN',
'RIGHT_UP', 'RIGHT_DOWN',
'IN', 'OUT',
):
2020-12-05 11:24:41 +00:00
direction = getattr(ctl.camera, data['direction']).copy()
speed = int(data.get('speed', 1))
for key in direction:
direction[key] *= speed
2021-11-23 09:48:08 +00:00
duration = data.get('duration', 1)
try:
duration = float(duration)
except:
duration = 1.0
print('move', direction, duration)
ctl.camera.momentary(direction, duration)
2020-12-05 10:39:05 +00:00
return result
2018-12-04 08:50:29 +00:00
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
2020-12-06 12:42:13 +00:00
def updateSequence(self, **data):
print('updateSequence')
state['sequence'] = data['sequence']
with open('sequence.json', 'w') as fd:
json.dump(state['sequence'], fd, indent=4, ensure_ascii=False)
def getSequence(self, **data):
result = {}
result['sequence'] = state['sequence']
return result
2021-09-13 09:23:43 +00:00
def wipe(self, **data):
result = {}
ctl.camera.wipe()
return result
2018-12-04 08:50:29 +00:00
#@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:
2023-08-18 07:26:44 +00:00
future = tornado.concurrent.Future()
api_task(request, callback=future.set_result)
response = yield future
2018-12-04 08:50:29 +00:00
except KeyboardInterrupt:
raise
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)
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)
2020-12-06 12:42:13 +00:00
def load_sequence():
2021-10-14 08:53:20 +00:00
data = []
2020-12-06 12:42:13 +00:00
if os.path.exists('sequence.json'):
try:
with open('sequence.json') as fd:
data = json.load(fd)
except:
data = []
state['sequence'] = data
2018-12-04 08:50:29 +00:00
def main():
global ctl
ctl = ControlQueue()
2020-12-06 12:42:13 +00:00
load_sequence()
2018-12-04 08:50:29 +00:00
handlers = [
(r'/api/', RPCHandler),
(r'/static/(.*)', StaticFileHandler, {'path': STATIC_PATH}),
(r"(.*)", MainHandler),
]
options = {
'debug': False,
'gzip': True,
}
app = Application(handlers, **options)
2020-12-06 12:42:13 +00:00
print('listening on http://%s:%s/' % (ADDRESS, PORT))
2018-12-04 08:50:29 +00:00
app.listen(PORT, ADDRESS)
2022-01-26 09:31:02 +00:00
if os.path.exists('last_run.json'):
with open('last_run.json', 'r') as fd:
data = json.load(fd)
if data and data.get('hour_loop'):
ctl.put(data)
2018-12-04 08:50:29 +00:00
main = IOLoop.instance()
try:
main.start()
except:
print('shutting down...')
ctl.join()
if __name__ == '__main__':
main()