CAMP can capture canals
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

286 lines
7.7 KiB

#!/usr/bin/python3
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'
with open('camera.json') as fd:
CAMERA = json.load(fd)
state = {
'time': {},
'status': 'Idle',
'sequence': [],
}
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, command):
if self.q.empty():
self.camera.abort = False
self.q.put(command)
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 setPreset(self, **data):
result = {}
print('setPreset', data)
ctl.camera.set_preset(data['id'], data['name'])
return result
def setPresets(self, **data):
result = {}
ctl.camera.set_presets(data['presets'])
result['presets'] = ctl.camera.get_presets(True)
return result
def camera(self, **data):
result = {}
for key, value in data.items():
result[key] = getattr(ctl.camera, key)(**value)
return result
def run(self, **data):
result = {}
ctl.put(data)
with open('last_run.json', 'w') as fd:
json.dump(data, fd, indent=4)
return result
def move(self, **data):
result = {}
if data.get('direction') in (
'LEFT', 'RIGHT', 'UP', 'DOWN',
'LEFT_UP', 'LEFT_DOWN',
'RIGHT_UP', 'RIGHT_DOWN',
'IN', 'OUT',
):
direction = getattr(ctl.camera, data['direction']).copy()
speed = int(data.get('speed', 1))
for key in direction:
direction[key] *= speed
duration = data.get('duration', 1)
try:
duration = float(duration)
except:
duration = 1.0
print('move', direction, duration)
ctl.camera.momentary(direction, duration)
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
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
def wipe(self, **data):
result = {}
ctl.camera.wipe()
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:
future = tornado.concurrent.Future()
api_task(request, callback=future.set_result)
response = yield future
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)
def load_sequence():
data = []
if os.path.exists('sequence.json'):
try:
with open('sequence.json') as fd:
data = json.load(fd)
except:
data = []
state['sequence'] = data
def main():
global ctl
ctl = ControlQueue()
load_sequence()
handlers = [
(r'/api/', RPCHandler),
(r'/static/(.*)', StaticFileHandler, {'path': STATIC_PATH}),
(r"(.*)", MainHandler),
]
options = {
'debug': False,
'gzip': True,
}
app = Application(handlers, **options)
print('listening on http://%s:%s/' % (ADDRESS, PORT))
app.listen(PORT, ADDRESS)
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)
main = IOLoop.instance()
try:
main.start()
except:
print('shutting down...')
ctl.join()
if __name__ == '__main__':
main()