pixelserver2/main.py

532 lines
17 KiB
Python
Raw Normal View History

2018-12-30 19:45:11 +00:00
#!/usr/bin/env python3
2024-10-28 19:48:28 +00:00
2018-08-19 18:47:08 +00:00
import json
2018-08-22 18:48:26 +00:00
import logging
2018-10-21 13:03:56 +00:00
import math
2024-10-28 19:48:28 +00:00
import os
import subprocess
import threading
import time
2024-10-28 17:10:03 +00:00
from collections import OrderedDict
2024-10-28 19:48:28 +00:00
import bottle
import numpy as np
import serial
import config
2024-11-10 01:27:36 +00:00
import filters
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
logging.basicConfig(filename='pixelserver.log', level=config.LogLevel)
2018-08-22 17:43:16 +00:00
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
########################################################################
# Utils #
########################################################################
2018-08-25 17:01:09 +00:00
class DataSource:
def __init__(self, initial):
self.data = initial
self.listeners = []
2024-10-28 19:48:28 +00:00
2018-08-25 17:01:09 +00:00
def getData(self):
return self.data
2024-10-28 19:48:28 +00:00
2018-08-25 17:01:09 +00:00
def addListener(self, listener):
self.listeners.append(listener)
2018-12-30 20:23:05 +00:00
return self
2024-10-28 19:48:28 +00:00
2018-08-25 17:01:09 +00:00
def pushData(self, data):
self.data = data
for listener in self.listeners:
with listener:
listener.notify_all()
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
class WatchDog(threading.Thread):
def __init__(self, check, action):
2024-10-28 19:48:28 +00:00
super().__init__(daemon=True)
2018-12-30 19:45:11 +00:00
self.check = check
self.action = action
self.running = True
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
def run(self):
while running and self.running:
if self.check():
logging.error("Watchdog: Executed")
self.action()
time.sleep(1)
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
def stop(self):
self.running = False
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
class LogReader(threading.Thread):
def __init__(self, runner):
2024-10-28 19:48:28 +00:00
super().__init__(daemon=True)
2018-12-30 19:45:11 +00:00
self.runner = runner
self.log = ""
self.running = True
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
def clear(self):
self.log = ""
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
def getLog(self):
return self.log
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
def run(self):
logging.info("LogReader started")
while running and self.running:
try:
self.log += self.runner.app.stderr.read(1).decode("utf-8")
except Exception as e:
print(e)
logging.error(str(e))
2024-10-28 19:48:28 +00:00
time.sleep(1)
2018-12-30 19:45:11 +00:00
logging.info("LogReader closed")
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
def stop(self):
self.running = False
2024-10-28 19:48:28 +00:00
2024-10-28 17:10:03 +00:00
class Frame:
def __init__(self, buffer, channels=3):
self.buffer = buffer
self.created = time.time()
self.channels = channels
2024-10-28 19:48:28 +00:00
2024-10-28 17:10:03 +00:00
def clone(self):
2024-10-28 19:48:28 +00:00
f = Frame(self.buffer + 0, self.channels)
2024-10-28 17:10:03 +00:00
f.created = self.created
return f
2018-12-30 19:45:11 +00:00
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
########################################################################
# GUI #
########################################################################
2018-08-22 17:23:36 +00:00
if config.UseGui:
import pygame
2024-10-28 19:48:28 +00:00
2018-08-22 17:23:36 +00:00
class Gui(threading.Thread):
2018-08-25 17:01:09 +00:00
def __init__(self, datasource):
2024-10-28 19:48:28 +00:00
super().__init__(daemon=True)
2018-08-25 17:01:09 +00:00
self.cv = threading.Condition()
2018-12-30 20:23:05 +00:00
self.datasource = datasource.addListener(self.cv)
2024-10-28 19:48:28 +00:00
2018-08-22 17:23:36 +00:00
def run(self):
2024-10-28 17:10:03 +00:00
last_frame = time.time()
2018-08-22 18:48:26 +00:00
logging.info("Starting GUI")
2018-08-22 17:23:36 +00:00
sf = config.GuiScaleFactor
2024-10-28 19:48:28 +00:00
pygame.init()
screen = pygame.display.set_mode((sf * config.ScreenX, sf * config.ScreenY))
2018-08-22 17:23:36 +00:00
pygame.display.set_caption("Pixelserver - GUI Vis")
2018-08-22 17:43:16 +00:00
while running:
2018-08-22 17:23:36 +00:00
for event in pygame.event.get():
2018-08-25 17:01:09 +00:00
pass
with self.cv:
self.cv.wait()
2024-10-28 17:10:03 +00:00
frame = self.datasource.getData()
screen.fill((0, 0, 0))
if frame.channels == 3:
for x in range(config.ScreenX):
for y in range(config.ScreenY):
color = (frame.buffer[y, x, 0], frame.buffer[y, x, 1], frame.buffer[y, x, 2])
2024-10-28 19:48:28 +00:00
pygame.draw.rect(screen, color, pygame.Rect(sf * x, sf * y, sf, sf))
2024-10-28 17:10:03 +00:00
elif frame.channels == 4:
for x in range(config.ScreenX):
for y in range(config.ScreenY):
2024-10-28 19:48:28 +00:00
w = frame.buffer[y, x, 3] // 2
color = (frame.buffer[y, x, 0] // 2 + w, frame.buffer[y, x, 1] // 2 + w, frame.buffer[y, x, 2] // 2 + w)
pygame.draw.rect(screen, color, pygame.Rect(sf * x, sf * y, sf, sf))
# logging.debug("Time to gui: " + str(time.time() - frame.created))
2018-08-22 17:23:36 +00:00
pygame.display.flip()
2024-10-28 19:48:28 +00:00
if time.time() < last_frame + 1 / config.GuiFPS:
time.sleep(max(0.01, time.time() - (last_frame + 1 / config.GuiFPS)))
# time.sleep(0.01)
2024-10-28 17:10:03 +00:00
last_frame = time.time()
2018-08-22 18:48:26 +00:00
logging.info("Closing GUI")
2024-10-28 19:48:28 +00:00
def join(self, **kwargs):
2018-08-25 17:01:09 +00:00
with self.cv:
self.cv.notify_all()
2024-11-10 01:27:36 +00:00
super().join(**kwargs)
2018-08-22 16:04:04 +00:00
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
########################################################################
# Serial #
########################################################################
2018-08-22 16:04:04 +00:00
class SerialWriter(threading.Thread):
2018-08-25 17:01:09 +00:00
def __init__(self, datasource):
2024-10-28 19:48:28 +00:00
super().__init__(daemon=True)
2018-08-25 17:01:09 +00:00
self.cv = threading.Condition()
2018-12-30 20:23:05 +00:00
self.datasource = datasource.addListener(self.cv)
2024-11-10 01:27:36 +00:00
self.gamma_rgbw = 0, 0, 0, 0
2018-10-21 13:03:56 +00:00
self.updateGamma = False
2024-10-28 19:48:28 +00:00
2018-08-22 16:04:04 +00:00
def run(self):
should_connect = True
ser = None
2018-08-22 18:48:26 +00:00
logging.info("Starting SerialWriter")
2018-08-22 17:43:16 +00:00
while running:
try:
if should_connect:
ser = serial.Serial(config.Serial)
should_connect = False
2018-08-22 18:48:26 +00:00
logging.info("Serial Opened")
2018-08-25 17:01:09 +00:00
with self.cv:
2024-10-28 19:48:28 +00:00
self.cv.wait(timeout=1 / 30)
2024-10-28 17:10:03 +00:00
frame = self.datasource.getData()
2024-10-28 19:48:28 +00:00
data = frame.buffer.reshape((config.ScreenX * config.ScreenY * frame.channels,)).astype(np.uint8).tobytes()
2018-10-21 13:03:56 +00:00
if self.updateGamma:
2024-11-10 01:27:36 +00:00
r, g, b, w = self.gamma_rgbw
apply = lambda x, g: max(0, min(255, int(math.pow(x / 255, g) * 255)))
buf = bytearray(4 * 256)
2018-10-21 13:03:56 +00:00
for i in range(256):
2024-11-10 01:27:36 +00:00
buf[i] = apply(i, r)
buf[i + 256] = apply(i, g)
buf[i + 256 * 2] = apply(i, b)
buf[i + 256 * 3] = apply(i, w)
2018-10-21 13:03:56 +00:00
ser.write(b"\x02")
ser.write(buf)
self.updateGamma = False
2024-10-28 17:10:03 +00:00
if frame.channels == 3:
ser.write(b"\01")
ser.write(data)
elif frame.channels == 4:
ser.write(b"\03")
ser.write(data)
2024-11-10 01:27:36 +00:00
logging.debug(f"Time to gui: {time.time() - frame.created}")
2018-10-21 13:19:51 +00:00
ser.flush()
2018-08-22 18:48:26 +00:00
except Exception as e:
2024-10-28 19:48:28 +00:00
if ser is not None:
ser.close()
ser = None
2024-10-28 19:48:28 +00:00
logging.warning(f"Serial was close because: {e}")
should_connect = True
2018-08-25 17:01:09 +00:00
time.sleep(5)
2018-08-22 18:48:26 +00:00
logging.info("Closing SerialWriter")
2024-10-28 19:48:28 +00:00
def join(self, **kwargs):
2018-12-30 19:48:46 +00:00
with self.cv:
self.cv.notify_all()
2024-11-10 01:27:36 +00:00
super().join(**kwargs)
2024-10-28 19:48:28 +00:00
2024-10-28 17:10:03 +00:00
def setGamma(self, r, g, b, w):
2018-10-21 13:03:56 +00:00
with self.cv:
2024-11-10 01:27:36 +00:00
self.gamma_rgbw = r, g, b, w
2018-10-21 13:03:56 +00:00
self.updateGamma = True
self.cv.notify_all()
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
########################################################################
# App #
########################################################################
2018-08-25 17:01:09 +00:00
class App(threading.Thread):
2024-10-28 17:10:03 +00:00
def __init__(self, cmd, param, listener, is_persistent, is_white=False, path="."):
2024-10-28 19:48:28 +00:00
super().__init__(daemon=True)
# start app
args = cmd + [str(config.ScreenX), str(config.ScreenY), param]
2024-10-28 17:10:03 +00:00
self.app = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, cwd=path)
2018-08-25 17:01:09 +00:00
self.last_update = time.time()
self.cv = threading.Condition()
self.watchdog = WatchDog(lambda: self.isAppTimedOut(), lambda: self.terminateApp())
self.watchdog.start()
self.logreader = LogReader(self)
self.logreader.start()
2024-10-28 17:10:03 +00:00
self.datasource = DataSource(Frame(np.zeros((config.ScreenY, config.ScreenX, 3))))
2018-12-30 20:23:05 +00:00
self.running = True
2018-08-25 17:01:09 +00:00
self.listener = listener
2018-08-26 18:31:48 +00:00
self.is_persistent = is_persistent
2024-10-28 17:10:03 +00:00
self.is_white = is_white
2024-10-28 19:48:28 +00:00
2018-08-25 17:01:09 +00:00
def run(self):
while running and self.running and self.alive():
oshandle = self.app.stdout.fileno()
try:
2024-10-28 19:48:28 +00:00
channels = 4 if self.is_white else 3
data = os.read(oshandle, config.ScreenX * config.ScreenY * channels)
assert len(data) == config.ScreenX * config.ScreenY * channels
buffer = np.frombuffer(data, dtype=np.uint8, count=config.ScreenX * config.ScreenY * channels)
buffer = buffer.reshape((config.ScreenY, config.ScreenX, channels))
frame = Frame(buffer, channels=channels)
2018-08-25 17:01:09 +00:00
self.last_update = time.time()
2024-10-28 17:10:03 +00:00
self.datasource.pushData(frame)
2024-10-28 19:48:28 +00:00
except:
2018-08-25 17:01:09 +00:00
logging.debug("Exception in App.run")
with self.listener:
self.listener.notify_all()
self.watchdog.stop()
self.logreader.stop()
self.watchdog.join()
self.logreader.join()
2024-10-28 17:10:03 +00:00
self.app.wait()
logging.debug("App stopped")
2024-10-28 19:48:28 +00:00
2018-08-25 17:01:09 +00:00
def alive(self):
2024-10-28 19:48:28 +00:00
return self.app.poll() is None
2018-08-25 17:01:09 +00:00
def stop(self):
self.running = False
self.app.kill()
self.app.stdout.close()
self.app.stderr.close()
self.watchdog.stop()
self.logreader.stop()
2024-10-28 17:10:03 +00:00
self.app.wait()
logging.debug("App stopped")
2024-10-28 19:48:28 +00:00
2018-08-25 17:01:09 +00:00
def getLog(self):
return self.logreader.getLog()
2024-10-28 19:48:28 +00:00
2018-08-25 17:01:09 +00:00
def terminateApp(self):
logging.error("Terminate app!")
self.stop()
2024-10-28 19:48:28 +00:00
2018-08-25 17:01:09 +00:00
def isAppTimedOut(self):
2024-10-28 19:48:28 +00:00
return time.time() - self.last_update > config.NoDataTimeout
2018-08-25 17:01:09 +00:00
2018-12-30 19:45:11 +00:00
########################################################################
# Main #
########################################################################
2018-08-19 18:47:08 +00:00
class AppRunner(threading.Thread):
def __init__(self):
2024-10-28 19:48:28 +00:00
super().__init__(daemon=True)
2024-10-28 17:10:03 +00:00
self.last_crashlog = ""
2018-08-19 18:47:08 +00:00
self.currentApp = -1
self.requestedApp = 0
self.app = None
2018-08-25 17:01:09 +00:00
self.cv = threading.Condition()
2018-08-19 18:47:08 +00:00
self.param = ""
2024-10-28 17:10:03 +00:00
self.datasource = DataSource(Frame(np.zeros((config.ScreenY, config.ScreenX, 3))))
2018-08-25 17:01:09 +00:00
self.serial = SerialWriter(self.datasource)
2018-08-22 16:04:04 +00:00
self.serial.start()
2018-08-26 18:31:48 +00:00
self.persistent_apps = {}
2024-10-28 17:10:03 +00:00
self.filters = OrderedDict()
2024-10-28 19:48:28 +00:00
# start persistent apps
for i, app in enumerate(config.Apps):
if app.persistent:
2018-12-30 20:23:05 +00:00
self.startApp(i)
2018-08-22 17:23:36 +00:00
if config.UseGui:
2018-08-25 17:01:09 +00:00
self.gui = Gui(self.datasource)
2018-08-22 17:23:36 +00:00
self.gui.start()
2024-10-28 19:48:28 +00:00
2018-08-19 18:47:08 +00:00
def requestApp(self, app, param=""):
2018-08-25 17:01:09 +00:00
with self.cv:
2018-08-19 18:47:08 +00:00
self.requestedApp = app
self.param = param
2018-08-25 17:01:09 +00:00
self.cv.notify_all()
2024-11-10 01:27:36 +00:00
logging.info(f"Requesting app: {app}")
2024-10-28 19:48:28 +00:00
2018-12-30 20:23:05 +00:00
def startApp(self, i, param=""):
app = config.Apps[i]
2024-10-28 19:48:28 +00:00
newapp = App(app.cmd, param, self.cv, is_persistent=app.persistent, is_white=app.white, path=app.path)
2018-12-30 20:23:05 +00:00
newapp.datasource.addListener(self.cv)
newapp.start()
2024-10-28 19:48:28 +00:00
if app.persistent:
self.persistent_apps[self.currentApp] = newapp
2018-12-30 20:23:05 +00:00
return newapp
2024-10-28 19:48:28 +00:00
2018-08-19 18:47:08 +00:00
def updateApp(self):
2024-10-28 17:10:03 +00:00
try:
2024-10-28 19:48:28 +00:00
if self.app is not None and not self.app.is_persistent:
self.app.stop()
self.currentApp = self.requestedApp
if self.currentApp in self.persistent_apps.keys() and self.persistent_apps[self.currentApp].alive():
2024-10-28 17:10:03 +00:00
self.app = self.persistent_apps[self.currentApp]
2024-10-28 19:48:28 +00:00
else:
self.app = self.startApp(self.requestedApp, self.param)
2024-10-28 17:10:03 +00:00
except FileNotFoundError as e:
2024-10-28 19:48:28 +00:00
print(e)
2018-08-19 18:47:08 +00:00
def run(self):
2018-08-22 18:48:26 +00:00
logging.info("Starting Apprunner")
2018-08-22 17:43:16 +00:00
while running:
2018-08-25 17:01:09 +00:00
with self.cv:
2024-10-28 19:48:28 +00:00
if self.app is None or not self.app.alive():
if self.app is not None:
2024-10-28 17:10:03 +00:00
self.last_crashlog = self.app.getLog()
2024-10-28 19:48:28 +00:00
self.requestedApp = 0
if self.requestedApp is not None:
2018-08-19 18:47:08 +00:00
self.updateApp()
2018-08-22 16:21:59 +00:00
self.requestedApp = None
2024-10-28 19:48:28 +00:00
if self.app is not None:
frame = self.app.datasource.getData().clone()
# logging.debug("Runner in time: " + str(time.time() - frame.created))
for _, f in self.filters.items():
frame.buffer = f(frame.buffer)
# logging.debug("Runner out time: " + str(time.time() - frame.created))
self.datasource.pushData(frame)
2018-12-30 20:23:05 +00:00
self.cv.wait()
2018-08-22 17:43:16 +00:00
self.serial.join()
if config.UseGui:
self.gui.join()
2018-08-22 18:48:26 +00:00
logging.info("Close Apprunner")
2024-10-28 19:48:28 +00:00
def getLog(self):
2024-10-28 19:48:28 +00:00
if self.app is None:
2018-08-25 17:01:09 +00:00
return ""
return self.app.getLog()
2024-10-28 19:48:28 +00:00
2024-10-28 17:10:03 +00:00
def setGamma(self, r, g, b, w):
self.serial.setGamma(r, g, b, w)
2024-10-28 19:48:28 +00:00
2024-11-10 01:27:36 +00:00
def setFilter(self, name, filter_):
self.filters[name] = filter_
2024-10-28 19:48:28 +00:00
2024-10-28 17:10:03 +00:00
def removeFilter(self, name):
if name in self.filters.keys():
del self.filters[name]
2024-10-28 19:48:28 +00:00
2018-12-30 19:45:11 +00:00
########################################################################
# Web Api #
########################################################################
2018-10-21 14:09:48 +00:00
@bottle.route('/<:re:.*>', method='OPTIONS')
def enable_cors_generic_route():
add_cors_headers()
2024-10-28 19:48:28 +00:00
2018-10-21 14:09:48 +00:00
@bottle.hook('after_request')
def enable_cors_after_request_hook():
add_cors_headers()
2024-10-28 19:48:28 +00:00
2018-10-21 14:09:48 +00:00
def add_cors_headers():
bottle.response.headers['Access-Control-Allow-Origin'] = '*'
2024-10-28 19:48:28 +00:00
bottle.response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS'
bottle.response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
2018-10-21 14:09:48 +00:00
2018-12-30 20:23:05 +00:00
def startApp(name, param=""):
2024-10-28 19:48:28 +00:00
for i, app in enumerate(config.Apps):
if app.name == name:
2018-12-30 20:23:05 +00:00
runner.requestApp(i, param)
return "ok"
return "not_found"
2018-12-30 19:45:11 +00:00
2024-10-28 19:48:28 +00:00
@bottle.route("/apps/list")
2018-08-19 18:47:08 +00:00
def apps_list():
2024-10-28 19:48:28 +00:00
return json.dumps([
{
"name": app.name,
"guiname": app.guiname,
"persistent": app.persistent,
} for app in config.Apps
])
@bottle.route("/apps/start/<name>")
2018-08-26 20:07:06 +00:00
def apps_start_param(name):
2018-12-30 20:23:05 +00:00
return startApp(name)
2018-08-19 18:47:08 +00:00
2024-10-28 19:48:28 +00:00
@bottle.post("/apps/start/<name>")
2018-08-26 20:07:06 +00:00
def apps_start_post(name):
2024-10-28 19:48:28 +00:00
param = bottle.request.forms.get('param')
2018-12-30 20:23:05 +00:00
return startApp(name, param)
2024-10-28 19:48:28 +00:00
@bottle.route("/apps/start/<name>/<param>")
2018-08-19 18:47:08 +00:00
def apps_start(name, param):
2018-12-30 20:23:05 +00:00
return startApp(name, param)
2018-08-19 18:47:08 +00:00
2024-10-28 19:48:28 +00:00
@bottle.route("/apps/log")
def apps_log():
return runner.getLog()
2018-08-19 18:47:08 +00:00
2024-10-28 19:48:28 +00:00
@bottle.route("/apps/crashlog")
2024-10-28 17:10:03 +00:00
def apps_log():
return runner.last_crashlog
2018-08-19 18:47:08 +00:00
2024-10-28 19:48:28 +00:00
@bottle.route("/apps/running")
def apps_running():
return config.Apps[runner.currentApp].name
@bottle.route("/")
2018-08-22 19:15:32 +00:00
def index():
2018-08-26 20:07:06 +00:00
return bottle.static_file("index.html", root='html')
2018-12-30 19:45:11 +00:00
2024-11-10 01:27:36 +00:00
@bottle.route("/<filepath:path>")
def serve_static(filepath):
return bottle.static_file(filepath, root='html')
2024-10-28 19:48:28 +00:00
2024-11-10 01:27:36 +00:00
2024-10-28 19:48:28 +00:00
@bottle.route("/setgamma/<r>/<g>/<b>/<w>")
2024-10-28 17:10:03 +00:00
def setGamma(r, g, b, w):
2024-11-10 01:27:36 +00:00
runner.setGamma(float(r), float(g), float(b), float(w))
2018-10-21 14:09:48 +00:00
return "ok"
2018-08-22 17:43:16 +00:00
2024-10-28 19:48:28 +00:00
@bottle.route("/setbrightness/<i>")
2024-10-28 17:10:03 +00:00
def setIntensity(i):
2019-01-01 11:49:26 +00:00
i = float(i)
2024-10-28 19:48:28 +00:00
if not 0 <= i <= 1:
2019-01-01 11:49:26 +00:00
return "bad_value"
2024-11-10 01:27:36 +00:00
runner.setFilter("0_intensity", filters.MakeBrightnessFilter(i))
2024-10-28 17:10:03 +00:00
return "ok"
2024-10-28 19:48:28 +00:00
@bottle.route("/filter/flipx/<do>")
2024-10-28 17:10:03 +00:00
def flipx(do):
if do == "true":
2024-11-10 01:27:36 +00:00
runner.setFilter("1_flipx", filters.FlipXFilter)
2024-10-28 17:10:03 +00:00
else:
runner.removeFilter("1_flipx")
return "ok"
2024-10-28 19:48:28 +00:00
@bottle.route("/filter/flipy/<do>")
2024-10-28 17:10:03 +00:00
def flipy(do):
if do == "true":
2024-11-10 01:27:36 +00:00
runner.setFilter("1_flipy", filters.FlipYFilter)
2024-10-28 17:10:03 +00:00
else:
runner.removeFilter("1_flipy")
2019-01-01 11:49:26 +00:00
return "ok"
2024-10-28 19:48:28 +00:00
@bottle.route("/filter/img/<name>")
2024-10-28 17:10:03 +00:00
def setfilter(name):
if name == "none":
runner.removeFilter("3_imgfilter")
else:
2024-11-10 01:27:36 +00:00
runner.setFilter("3_imgfilter", filters.MakeBrightnessImageFilter(name))
2024-10-28 17:10:03 +00:00
return "ok"
2024-10-28 19:48:28 +00:00
@bottle.post("/filter/expr/")
2024-10-28 17:10:03 +00:00
def filter_expr():
2024-10-28 19:48:28 +00:00
expr = bottle.request.forms.get('expr')
2024-10-28 17:10:03 +00:00
if expr == "" or expr == "none":
runner.removeFilter("5_brightnessfunction")
else:
2024-11-10 01:27:36 +00:00
runner.setFilter("5_brightnessfunction", filters.MakeBrightnessExprFilter(expr))
2024-10-28 17:10:03 +00:00
return "ok"
2024-10-28 19:48:28 +00:00
2024-10-28 17:10:03 +00:00
2024-11-10 01:27:36 +00:00
if __name__ == '__main__':
########################################################################
# Startup #
########################################################################
running = True
runner = AppRunner()
runner.start()
2018-12-30 19:48:46 +00:00
2024-11-10 01:27:36 +00:00
# runner.setFilter("5_crazy", MakeBrightnessExprFilter("0.5+0.25*sin(x/3)/x"))
bottle.run(host=config.WebHost, port=config.WebPort)
2018-12-30 19:48:46 +00:00
2024-11-10 01:27:36 +00:00
########################################################################
# Shutdown #
########################################################################
running = False
runner.join()