Code cleanup

This commit is contained in:
T 2024-10-28 20:48:28 +01:00
parent 8dc0480c18
commit c38a840d42
3 changed files with 283 additions and 228 deletions

View File

@ -2,10 +2,11 @@
import os
import sys
import random
import random
import time
import math
import numpy as np
# Groesse des Bildschirms bestimmen
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
@ -21,18 +22,18 @@ except:
curPixel = 0
phase = np.random.uniform(0, 2*np.pi, (Ny, Nx))
phase = np.random.uniform(0, 2 * np.pi, (Ny, Nx))
t = 0
f_min = 0.08
f_max = 0.10
f = np.random.uniform(f_min, f_max, (Ny, Nx))
while True:
t += time_ms/1000.0
s = 0.80 +0.2*np.sin(phase+2*np.pi*t*f)
t += time_ms / 1000.0
s = 0.80 + 0.2 * np.sin(phase + 2 * np.pi * t * f)
img = np.zeros([Ny, Nx, 4])
img[:,:,3] = 255*s
img[:, :, 3] = 255 * s
# Zeige den Puffer an
out = img.reshape((Nx*Ny*4,)).astype(np.uint8)
out = img.reshape((Nx * Ny * 4,)).astype(np.uint8)
os.write(1, out.tobytes())
# warte time_ms ms
time.sleep(time_ms*0.001)
time.sleep(time_ms * 0.001)

128
config.py
View File

@ -1,8 +1,25 @@
import logging
from dataclasses import dataclass
from typing import Union, List
#width
@dataclass
class AppConfig:
guiname: str
name: str
cmd: Union[List[str], str]
path: str = "./apps/"
persistent: bool = False
white: bool = False
def __post_init__(self):
if not isinstance(self.cmd, list):
self.cmd = [self.cmd]
# width
ScreenX = 80
#height
# height
ScreenY = 40
DefaultBrightness = 0.6
@ -14,81 +31,66 @@ NoDataTimeout = 40
LogLevel = logging.DEBUG
UseGui = True
UseGui = False
GuiFPS = 20
GuiScaleFactor = 15
WebHost = "0.0.0.0"
WebPort = 8000
# first app is always running as default
Apps = [
# first app is always running as default
AppConfig(guiname="Backlight", name="backlight", cmd="./backlight.py", white=True),
{"name": "backlight", "cmd": "apps/backlight.py", "white": True},
{"guiname": "Lines", "name": "lines", "cmd": "./lines.py", "persistent": False, "path": "./apps/"},
{"guiname": "Wolfram", "name": "wolfram", "cmd": "./wolfram.py", "persistent": False, "path": "./apps/"},
{"guiname": "Digi Clock", "name": "digiclock", "cmd": "./digi_clock.py", "persistent": False, "path": "./apps/"},
{"guiname": "Text Scroller MQTT", "name": "textscroll", "cmd": "./textscroll.py", "persistent": False, "path": "./apps/"},
{"guiname": "Flicker", "name": "flicker", "cmd": "apps/flicker"},
{"guiname": "Pixelflut", "name": "pixelflut", "cmd": "apps/pixelflut", "persistent": True},
# {"guiname": "Pong", "name": "pong", "cmd": "apps/pong.py"},
{"guiname": "YoutubeDL", "name": "youtubedl", "cmd": "apps/youtubedl.sh", "persistent": False},
# {"guiname": "Show Framebuffer", "name": "fbcp", "cmd": ["apps/fbcp", "/dev/fb0"]},
{"guiname": "Strobo", "name": "strobo", "cmd": "apps/strobo.py"},
# {"guiname": "Beispiel", "name": "example", "cmd": "apps/example.py"},
# {"guiname": "Beispiel2", "name": "example2", "cmd": "apps/example2.py", "white": True},
{"guiname": "Fibonacci Clock", "name": "fibonacci-clock", "cmd": "apps/fibonacci-clock.py"},
{"guiname": "Wget Video/Gif/Images", "name": "wget", "cmd": "apps/wget.sh", "persistent": False},
AppConfig(guiname="Lines", name="lines", cmd="./lines.py"),
AppConfig(guiname="Wolfram", name="wolfram", cmd="./wolfram.py"),
AppConfig(guiname="Digi Clock", name="digiclock", cmd="./digi_clock.py"),
AppConfig(guiname="Text Scroller MQTT", name="textscroll", cmd="./textscroll.py"),
AppConfig(guiname="Flicker", name="flicker", cmd="./flicker"),
AppConfig(guiname="Pixelflut", name="pixelflut", cmd="./pixelflut", persistent=True),
# App(guiname="Pong", name="pong", cmd="pong.py"),
AppConfig(guiname="YoutubeDL", name="youtubedl", cmd="./youtubedl.sh"),
# App(guiname="Show Framebuffer", name="fbcp", cmd=["fbcp", "/dev/fb0"]),
AppConfig(guiname="Strobo", name="strobo", cmd="./strobo.py"),
# App(guiname="Beispiel", name="example", cmd="example.py"),
# App(guiname="Beispiel2", name="example2", cmd="example2.py", white=True),
AppConfig(guiname="Fibonacci Clock", name="fibonacci-clock", cmd="./fibonacci-clock.py"),
AppConfig(guiname="Wget Video/Gif/Images", name="wget", cmd="./wget.sh"),
# juergen/pixelfoo
{"guiname": "Congress noise", "name": "cnoise", "cmd": "pixelfoo/target/release/cnoise", "persistent": False},
{"guiname": "Game of Life", "name": "life", "cmd": "pixelfoo/target/release/life", "persistent": False},
{"guiname": "Matrix Code", "name": "matrix-code", "cmd": "pixelfoo/target/release/matrix-code", "persistent": False},
{"guiname": "Lorenz Attractor", "name": "lorenz", "cmd": "pixelfoo/target/release/lorenz", "persistent": False},
{"guiname": "Dual Moodlight", "name": "bimood", "cmd": "pixelfoo/target/release/bimood", "persistent": False},
{"guiname": "Maze", "name": "maze", "cmd": "pixelfoo/target/release/maze", "persistent": False},
{"guiname": "Dual Maze", "name": "dualmaze", "cmd": "pixelfoo/target/release/dualmaze", "persistent": False, "persistent": False},
{"guiname": "Predator & Prey", "name": "predprey", "cmd": "pixelfoo/target/release/predprey", "persistent": False},
AppConfig(guiname="Congress noise", name="cnoise", cmd="./cnoise", path="pixelfoo/target/release/"),
AppConfig(guiname="Game of Life", name="life", cmd="./life", path="pixelfoo/target/release/"),
AppConfig(guiname="Matrix Code", name="matrix-code", cmd="./matrix-code", path="pixelfoo/target/release/"),
AppConfig(guiname="Lorenz Attractor", name="lorenz", cmd="./lorenz", path="pixelfoo/target/release/"),
AppConfig(guiname="Dual Moodlight", name="bimood", cmd="./bimood", path="pixelfoo/target/release/"),
AppConfig(guiname="Maze", name="maze", cmd="./maze", path="pixelfoo/target/release/"),
AppConfig(guiname="Dual Maze", name="dualmaze", cmd="./dualmaze", path="pixelfoo/target/release/"),
AppConfig(guiname="Predator & Prey", name="predprey", cmd="./predprey", path="pixelfoo/target/release/"),
# { "guiname": "Beat Saber Ceiling", "name": "beatsaberceiling", "cmd": "./beatsaberceiling.py", "path": "apps/beatsaberceiling" },
# App(guiname="Beat Saber Ceiling", name="beatsaberceiling", cmd="./beatsaberceiling.py", path="beatsaberceiling"),
# mathpixel
{"guiname": "Structure formation", "name": "swifthohenberg", "cmd": "apps/swifthohenberg.py", "persistent": False},
{"guiname": "Quadratisch", "name": "quadratic", "cmd": "apps/quadratic.py"},
{"guiname": "Pendel", "name": "pendulum", "cmd": "apps/pendlum.py"},
{"guiname": "Konvergenz", "name": "convergence", "cmd": "apps/convergence.py"},
{"guiname": "Sinic", "name": "sinic", "cmd": "apps/sinic.py"},
{"guiname": "Sinic 2", "name": "sinic2", "cmd": "apps/sinic2.py"},
AppConfig(guiname="Structure formation", name="swifthohenberg", cmd="./swifthohenberg.py"),
AppConfig(guiname="Quadratisch", name="quadratic", cmd="./quadratic.py"),
AppConfig(guiname="Pendel", name="pendulum", cmd="./pendlum.py"),
AppConfig(guiname="Konvergenz", name="convergence", cmd="./convergence.py"),
AppConfig(guiname="Sinic", name="sinic", cmd="./sinic.py"),
AppConfig(guiname="Sinic 2", name="sinic2", cmd="./sinic2.py"),
# pixelthud
{"guiname": "Fading Pixels", "name": "fadingpxls", "cmd": "apps/fading_pixels.py", "persistent": False},
{"guiname": "Plane Wave", "name": "planewave", "cmd": "apps/plane_wave.py", "persistent": False},
{"guiname": "Rock-paper-scissors-spock-lizard", "name": "rps", "cmd": "apps/rps.py", "persistent": False},
{"guiname": "Doom Fire", "name": "doomfire", "cmd": "apps/doom_fire_psx2.py", "persistent": False},
# {"guiname": "Maxwell FDTD", "name": "fdtd", "cmd": "apps/fdtd.py", "persistent": False},
AppConfig(guiname="Fading Pixels", name="fadingpxls", cmd="./fading_pixels.py"),
AppConfig(guiname="Plane Wave", name="planewave", cmd="./plane_wave.py"),
AppConfig(guiname="Rock-paper-scissors-spock-lizard", name="rps", cmd="./rps.py"),
AppConfig(guiname="Doom Fire", name="doomfire", cmd="./doom_fire_psx2.py"),
# App(guiname="Maxwell FDTD", name="fdtd", cmd="./fdtd.py"),
#{"guiname": "Stream", "name": "stream", "cmd": "apps/stream.sh", "persistent": False},
#{"guiname": "Wget Video/Gif/Images", "name": "wget", "cmd": "apps/wget.sh", "persistent": False},
#{"guiname": "Tetris", "name": "tetris", "cmd": "apps/deckentetris/deckentetris.py", "persistent": False},
#{"guiname": "SkyScrapper", "name": "sky", "cmd": "apps/weather/main.py"},
#{"guiname": "Strobo", "name": "strobo", "cmd": "apps/strobo.py", "persistent": False},
#{"guiname": "Snake", "name": "snake", "cmd": "apps/snake.py", "persistent": False},
#{"name": "gif", "cmd": "apps/gif.sh"},
#{"name": "colormap", "cmd": "apps/colormap.py"},
]
# load additional apps from config/
# import os
# import os.path
# configs = os.listdir("configs/")
# import importlib
# for config in configs:
# file, ext = os.path.splitext(os.path.basename(config))
# if ext != ".py" or file[0] == "_":
# continue
# modname = "configs."+file
# lib = importlib.import_module(modname)
# Apps += lib.Apps
# App(guiname="Stream", name="stream", cmd="./stream.sh"),
# App(guiname="Wget Video/Gif/Images", name="wget", cmd="./wget.sh"),
# App(guiname="Tetris", name="tetris", cmd="./deckentetris/deckentetris.py"),
# App(guiname="SkyScrapper", name="sky", cmd="./weather/main.py"),
# App(guiname="Strobo", name="strobo", cmd="./strobo.py"),
# App(guiname="Snake", name="snake", cmd="./snake.py"),
# App(name="gif", cmd="./gif.sh"),
# App(name="colormap", cmd="./colormap.py"),
]

368
main.py
View File

@ -1,24 +1,26 @@
#!/usr/bin/env python3
import config
import subprocess
import os
import os.path
import serial
import threading
import json
import bottle
from bottle import route, run, request, post
import time
import sys
import logging
import math
import numpy as np
import os
import string
import subprocess
import threading
import time
from collections import OrderedDict
import bottle
import numpy as np
import scipy.misc
import serial
import config
logging.basicConfig(filename='pixelserver.log', level=config.LogLevel)
running = True
########################################################################
# Utils #
########################################################################
@ -26,42 +28,52 @@ class DataSource:
def __init__(self, initial):
self.data = initial
self.listeners = []
def getData(self):
return self.data
def addListener(self, listener):
self.listeners.append(listener)
return self
def pushData(self, data):
self.data = data
for listener in self.listeners:
with listener:
listener.notify_all()
class WatchDog(threading.Thread):
def __init__(self, check, action):
super().__init__()
super().__init__(daemon=True)
self.check = check
self.action = action
self.running = True
def run(self):
while running and self.running:
if self.check():
logging.error("Watchdog: Executed")
self.action()
time.sleep(1)
def stop(self):
self.running = False
class LogReader(threading.Thread):
def __init__(self, runner):
super().__init__()
super().__init__(daemon=True)
self.runner = runner
self.log = ""
self.running = True
def clear(self):
self.log = ""
def getLog(self):
return self.log
def run(self):
logging.info("LogReader started")
while running and self.running:
@ -70,37 +82,44 @@ class LogReader(threading.Thread):
except Exception as e:
print(e)
logging.error(str(e))
time.sleep(1)
time.sleep(1)
logging.info("LogReader closed")
def stop(self):
self.running = False
class Frame:
def __init__(self, buffer, channels=3):
self.buffer = buffer
self.created = time.time()
self.channels = channels
def clone(self):
f = Frame(self.buffer+0, self.channels)
f = Frame(self.buffer + 0, self.channels)
f.created = self.created
return f
########################################################################
# GUI #
########################################################################
if config.UseGui:
import pygame
class Gui(threading.Thread):
def __init__(self, datasource):
super().__init__()
super().__init__(daemon=True)
self.cv = threading.Condition()
self.datasource = datasource.addListener(self.cv)
def run(self):
last_frame = time.time()
logging.info("Starting GUI")
sf = config.GuiScaleFactor
screen = pygame.display.set_mode((sf*config.ScreenX, sf*config.ScreenY))
pygame.init()
screen = pygame.display.set_mode((sf * config.ScreenX, sf * config.ScreenY))
pygame.display.set_caption("Pixelserver - GUI Vis")
while running:
for event in pygame.event.get():
@ -113,35 +132,38 @@ if config.UseGui:
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])
pygame.draw.rect(screen, color, pygame.Rect(sf*x, sf*y, sf, sf))
pygame.draw.rect(screen, color, pygame.Rect(sf * x, sf * y, sf, sf))
elif frame.channels == 4:
for x in range(config.ScreenX):
for y in range(config.ScreenY):
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))
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))
pygame.display.flip()
if time.time() < last_frame+1/config.GuiFPS:
time.sleep(time.time()-(last_frame+1/config.GuiFPS))
#time.sleep(0.01)
if time.time() < last_frame + 1 / config.GuiFPS:
time.sleep(max(0.01, time.time() - (last_frame + 1 / config.GuiFPS)))
# time.sleep(0.01)
last_frame = time.time()
logging.info("Closing GUI")
def join(self):
def join(self, **kwargs):
with self.cv:
self.cv.notify_all()
super().join()
########################################################################
# Serial #
########################################################################
class SerialWriter(threading.Thread):
def __init__(self, datasource):
super().__init__()
super().__init__(daemon=True)
self.cv = threading.Condition()
self.datasource = datasource.addListener(self.cv)
self.updateGamma = False
def run(self):
should_connect = True
ser = None
@ -153,17 +175,17 @@ class SerialWriter(threading.Thread):
should_connect = False
logging.info("Serial Opened")
with self.cv:
self.cv.wait(timeout = 1/30)
self.cv.wait(timeout=1 / 30)
frame = self.datasource.getData()
data = frame.buffer.reshape((config.ScreenX*config.ScreenY*frame.channels,)).astype(np.uint8).tobytes()
data = frame.buffer.reshape((config.ScreenX * config.ScreenY * frame.channels,)).astype(np.uint8).tobytes()
if self.updateGamma:
buf = bytearray(b"\x00")*4*256
buf = bytearray(b"\x00") * 4 * 256
for i in range(256):
apply = lambda x, g: max(0, min(255, int(math.pow(x/255, g)*255)))
buf[i] = apply(i, self.r)
buf[i+256] = apply(i, self.g)
buf[i+512] = apply(i, self.b)
buf[i+512+256] = apply(i, self.w)
apply = lambda x, g: max(0, min(255, int(math.pow(x / 255, g) * 255)))
buf[i] = apply(i, self.r)
buf[i + 256] = apply(i, self.g)
buf[i + 512] = apply(i, self.b)
buf[i + 512 + 256] = apply(i, self.w)
ser.write(b"\x02")
ser.write(buf)
self.updateGamma = False
@ -173,36 +195,37 @@ class SerialWriter(threading.Thread):
elif frame.channels == 4:
ser.write(b"\03")
ser.write(data)
logging.debug("Time to gui: "+str(time.time()-frame.created))
logging.debug("Time to gui: " + str(time.time() - frame.created))
ser.flush()
except Exception as e:
if ser != None:
if ser is not None:
ser.close()
ser = None
logging.warning("Serial was close because: "+str(e))
logging.warning(f"Serial was close because: {e}")
should_connect = True
time.sleep(5)
logging.info("Closing SerialWriter")
def join(self):
def join(self, **kwargs):
with self.cv:
self.cv.notify_all()
super().join()
def setGamma(self, r, g, b, w):
with self.cv:
self.r, self.g, self.b, self.w = r, g, b, w
self.r, self.g, self.b, self.w = r, g, b, w
self.updateGamma = True
self.cv.notify_all()
########################################################################
# App #
########################################################################
class App(threading.Thread):
def __init__(self, cmd, param, listener, is_persistent, is_white=False, path="."):
super().__init__()
#start app
if type(cmd) != list:
cmd = [cmd,]
args = cmd+[str(config.ScreenX), str(config.ScreenY), param]
super().__init__(daemon=True)
# start app
args = cmd + [str(config.ScreenX), str(config.ScreenY), param]
self.app = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, cwd=path)
self.last_update = time.time()
self.cv = threading.Condition()
@ -215,20 +238,21 @@ class App(threading.Thread):
self.listener = listener
self.is_persistent = is_persistent
self.is_white = is_white
def run(self):
while running and self.running and self.alive():
oshandle = self.app.stdout.fileno()
try:
bytes = 4 if self.is_white else 3
data = os.read(oshandle, config.ScreenX*config.ScreenY*bytes)
assert len(data) == config.ScreenX*config.ScreenY*bytes
buffer = np.frombuffer(data, dtype=np.uint8, count=config.ScreenX*config.ScreenY*bytes)
buffer = buffer.reshape((config.ScreenY, config.ScreenX, bytes))
frame = Frame(buffer, channels=bytes)
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)
self.last_update = time.time()
self.datasource.pushData(frame)
except Exception as e:
except:
logging.debug("Exception in App.run")
with self.listener:
self.listener.notify_all()
@ -238,8 +262,10 @@ class App(threading.Thread):
self.logreader.join()
self.app.wait()
logging.debug("App stopped")
def alive(self):
return self.app.poll() == None
return self.app.poll() is None
def stop(self):
self.running = False
self.app.kill()
@ -249,20 +275,24 @@ class App(threading.Thread):
self.logreader.stop()
self.app.wait()
logging.debug("App stopped")
def getLog(self):
return self.logreader.getLog()
def terminateApp(self):
logging.error("Terminate app!")
self.stop()
def isAppTimedOut(self):
return time.time()-self.last_update > config.NoDataTimeout
return time.time() - self.last_update > config.NoDataTimeout
########################################################################
# Main #
########################################################################
class AppRunner(threading.Thread):
def __init__(self):
super().__init__()
super().__init__(daemon=True)
self.last_crashlog = ""
self.currentApp = -1
self.requestedApp = 0
@ -274,137 +304,159 @@ class AppRunner(threading.Thread):
self.serial.start()
self.persistent_apps = {}
self.filters = OrderedDict()
#start persistent apps
for app, i in zip(config.Apps, range(len(config.Apps))):
if app["persistent"]:
# start persistent apps
for i, app in enumerate(config.Apps):
if app.persistent:
self.startApp(i)
if config.UseGui:
self.gui = Gui(self.datasource)
self.gui.start()
def requestApp(self, app, param=""):
with self.cv:
self.requestedApp = app
self.param = param
self.cv.notify_all()
logging.info("Requesting app: "+str(app))
logging.info("Requesting app: " + str(app))
def startApp(self, i, param=""):
app = config.Apps[i]
newapp = App(app["cmd"], param, self.cv, is_persistent=app["persistent"], is_white=app["white"], path=app["path"])
newapp = App(app.cmd, param, self.cv, is_persistent=app.persistent, is_white=app.white, path=app.path)
newapp.datasource.addListener(self.cv)
newapp.start()
if app["persistent"]:
self.persistent_apps[self.currentApp] = newapp
if app.persistent:
self.persistent_apps[self.currentApp] = newapp
return newapp
def updateApp(self):
try:
if self.app != 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()):
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():
self.app = self.persistent_apps[self.currentApp]
else:
self.app = self.startApp(self.requestedApp, self.param)
else:
self.app = self.startApp(self.requestedApp, self.param)
except FileNotFoundError as e:
print(e)
print(e)
def run(self):
logging.info("Starting Apprunner")
while running:
with self.cv:
if self.app == None or not self.app.alive():
if self.app != None:
if self.app is None or not self.app.alive():
if self.app is not None:
self.last_crashlog = self.app.getLog()
self.requestedApp = 0
if self.requestedApp != None:
self.requestedApp = 0
if self.requestedApp is not None:
self.updateApp()
self.requestedApp = 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)
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)
self.cv.wait()
self.serial.join()
if config.UseGui:
self.gui.join()
logging.info("Close Apprunner")
def getLog(self):
if self.app == None:
if self.app is None:
return ""
return self.app.getLog()
def setGamma(self, r, g, b, w):
self.serial.setGamma(r, g, b, w)
def setFilter(self, name, filter):
self.filters[name] = filter
def removeFilter(self, name):
if name in self.filters.keys():
del self.filters[name]
########################################################################
# Filter Api #
########################################################################
def MakeBrightnessFilter(intensity):
def filter(img):
return (img*intensity).astype(np.uint8)
return (img * intensity).astype(np.uint8)
return filter
def FlipXFilter(intensity):
return intensity[:,::-1,:]
return intensity[:, ::-1, :]
def FlipYFilter(intensity):
return intensity[::-1,:,:]
return intensity[::-1, :, :]
def MakeBrightnessImageFilter(name):
img = scipy.misc.imread("filter/"+name+".png", flatten=True)/255
#img = np.transpose(img)
img = scipy.misc.imread("filter/" + name + ".png", flatten=True) / 255
# img = np.transpose(img)
def filter(intensity):
intensity = intensity.astype(float)
for i in range(intensity.shape[2]):
intensity[:,:,i] *= img
intensity[:, :, i] *= img
return intensity.astype(np.uint8)
return filter
def strings(str):
allowed_chars = string.ascii_letters+string.digits+"+-*/()."
def strings(s):
allowed_chars = string.ascii_letters + string.digits + "+-*/()."
i = 0
outlist = []
while i != len(str):
if str[i] not in allowed_chars:
raise Exception("Unexpected char "+str[i])
if str[i] not in string.ascii_letters:
i+=1
while i != len(s):
if s[i] not in allowed_chars:
raise Exception("Unexpected char " + s[i])
if s[i] not in string.ascii_letters:
i += 1
continue
out = ""
while i != len(str) and str[i] in string.ascii_letters+string.digits:
out += str[i]
i+=1
while i != len(s) and s[i] in string.ascii_letters + string.digits:
out += s[i]
i += 1
outlist.append(out)
return outlist
def eval_safer(expr, x, y, t):
symbols = {"x": x, "y": y, "t": t,
symbols = {"x": x, "y": y, "t": t,
"sin": np.sin, "cos": np.cos, "exp": np.exp, "tan": np.tan}
strs = strings(expr)
for s in strs:
if s not in symbols.keys():
raise Exception("unexpected symbol: "+s)
raise Exception("unexpected symbol: " + s)
return eval(expr, {}, symbols)
def MakeBrightnessExprFilter(expr):
t0 = time.time()
x, y = np.meshgrid(np.arange(config.ScreenX), np.arange(config.ScreenY))
eval_safer(expr, 0, 0, 0)
def filter(intensity):
t = time.time()-t0
t = time.time() - t0
intensity = intensity.astype(float)
filter = 0*x+eval_safer(expr, x, y, t)
filter = 0 * x + eval_safer(expr, x, y, t)
filter = np.clip(np.nan_to_num(filter), 0, 1)
for i in range(intensity.shape[2]):
intensity[:,:,i] *= filter
intensity[:, :, i] *= filter
return intensity.astype(np.uint8)
return filter
########################################################################
# Web Api #
########################################################################
@ -412,63 +464,74 @@ def MakeBrightnessExprFilter(expr):
def enable_cors_generic_route():
add_cors_headers()
@bottle.hook('after_request')
def enable_cors_after_request_hook():
add_cors_headers()
def add_cors_headers():
bottle.response.headers['Access-Control-Allow-Origin'] = '*'
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'
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'
def startApp(name, param=""):
for i in range(len(config.Apps)):
if config.Apps[i]["name"] == name:
for i, app in enumerate(config.Apps):
if app.name == name:
runner.requestApp(i, param)
return "ok"
return "not_found"
@route("/apps/list")
@bottle.route("/apps/list")
def apps_list():
s = []
for app in config.Apps:
s.append({"name": app["name"],
"guiname": app["guiname"],
"persistent": app["persistent"]})
return json.dumps(s)
@route("/apps/start/<name>")
return json.dumps([
{
"name": app.name,
"guiname": app.guiname,
"persistent": app.persistent,
} for app in config.Apps
])
@bottle.route("/apps/start/<name>")
def apps_start_param(name):
return startApp(name)
@post("/apps/start/<name>")
@bottle.post("/apps/start/<name>")
def apps_start_post(name):
param = request.forms.get('param')
param = bottle.request.forms.get('param')
return startApp(name, param)
@route("/apps/start/<name>/<param>")
@bottle.route("/apps/start/<name>/<param>")
def apps_start(name, param):
return startApp(name, param)
@route("/apps/log")
@bottle.route("/apps/log")
def apps_log():
return runner.getLog()
@route("/apps/crashlog")
@bottle.route("/apps/crashlog")
def apps_log():
return runner.last_crashlog
@route("/apps/running")
def apps_running():
return config.Apps[runner.currentApp]["name"]
@route("/")
@bottle.route("/apps/running")
def apps_running():
return config.Apps[runner.currentApp].name
@bottle.route("/")
def index():
return bottle.static_file("index.html", root='html')
@route("/setgamma/<r>/<g>/<b>/<w>")
@bottle.route("/setgamma/<r>/<g>/<b>/<w>")
def setGamma(r, g, b, w):
r = float(r)
g = float(g)
@ -477,15 +540,17 @@ def setGamma(r, g, b, w):
runner.setGamma(r, g, b, w)
return "ok"
@route("/setbrightness/<i>")
@bottle.route("/setbrightness/<i>")
def setIntensity(i):
i = float(i)
if i < 0 or i > 1:
if not 0 <= i <= 1:
return "bad_value"
runner.setFilter("0_intensity", MakeBrightnessFilter(i))
return "ok"
@route("/filter/flipx/<do>")
@bottle.route("/filter/flipx/<do>")
def flipx(do):
if do == "true":
runner.setFilter("1_flipx", FlipXFilter)
@ -493,7 +558,8 @@ def flipx(do):
runner.removeFilter("1_flipx")
return "ok"
@route("/filter/flipy/<do>")
@bottle.route("/filter/flipy/<do>")
def flipy(do):
if do == "true":
runner.setFilter("1_flipy", FlipYFilter)
@ -501,48 +567,34 @@ def flipy(do):
runner.removeFilter("1_flipy")
return "ok"
@route("/filter/img/<name>")
@bottle.route("/filter/img/<name>")
def setfilter(name):
if name == "none":
runner.removeFilter("3_imgfilter")
else:
runner.setFilter("3_imgfilter", MakeBrightnessImageFilter(name))
return "ok"
@post("/filter/expr/")
@bottle.post("/filter/expr/")
def filter_expr():
expr = request.forms.get('expr')
expr = bottle.request.forms.get('expr')
if expr == "" or expr == "none":
runner.removeFilter("5_brightnessfunction")
else:
runner.setFilter("5_brightnessfunction", MakeBrightnessExprFilter(expr))
return "ok"
########################################################################
# Startup #
########################################################################
#normalize config
for app in config.Apps:
if "persistent" not in app.keys():
app["persistent"] = False
if "guiname" not in app.keys():
app["guiname"] = app["name"]
if "white" not in app.keys():
app["white"] = False
if "path" not in app.keys():
app["path"] = "."
cmd = app["cmd"]
#remove non existing apps
#if type(cmd) == str and not os.path.isfile(cmd):
#config.Apps.remove(app)
#logging.warning("Removed app "+app["name"])
runner = AppRunner()
runner.start()
#runner.setFilter("5_crazy", MakeBrightnessExprFilter("0.5+0.25*sin(x/3)/x"))
run(host=config.WebHost, port=config.WebPort)
# runner.setFilter("5_crazy", MakeBrightnessExprFilter("0.5+0.25*sin(x/3)/x"))
bottle.run(host=config.WebHost, port=config.WebPort)
########################################################################
# Shutdown #