Merge branch 'master' of repos.ctdo.de:psychose

This commit is contained in:
Stefan Kögl 2014-04-24 10:50:16 +02:00
commit 97d9e8282e
6 changed files with 423 additions and 38 deletions

21
config_files/chaosc Executable file
View File

@ -0,0 +1,21 @@
#!/sbin/runscript
depend() {
need net
use dns localmount
after bootmisc
provide chaosc
}
start() {
ebegin "starting chaosc"
start-stop-daemon --start --pidfile /var/run/chaosc.pid --make-pidfile --user sarah --group sarah --background --exec /usr/bin/chaosc
eend $?
}
stop() {
ebegin "stopping chaosc"
start-stop-daemon --stop --quiet --pidfile /var/run/chaosc.pid
eend $?
}

21
config_files/dump_grabber Executable file
View File

@ -0,0 +1,21 @@
#!/sbin/runscript
depend() {
need net
use dns localmount chaosc
after bootmisc
provide dump_grabber
}
start() {
ebegin "starting dump_grabber"
start-stop-daemon --start --pidfile /var/run/dump_grabber.pid --make-pidfile --user stefan --group stefan --background --exec env DISPLAY=:0 /usr/bin/dump_grabber
eend $?
}
stop() {
ebegin "stopping dump_grabber"
start-stop-daemon --stop --quiet --pidfile /var/run/dump_grabber.pid
eend $?
}

21
config_files/ekgplotter Executable file
View File

@ -0,0 +1,21 @@
#!/sbin/runscript
depend() {
need net
use dns localmount chaosc
after bootmisc
provide ekgplotter
}
start() {
ebegin "starting ekgplotter"
start-stop-daemon --start --pidfile /var/run/ekgplotter.pid --make-pidfile --user stefan --group stefan --background --exec env DISPLAY=:0 /usr/bin/ekgplotter
eend $?
}
stop() {
ebegin "stopping ekgplotter"
start-stop-daemon --stop --quiet --pidfile /var/run/ekgplotter.pid
eend $?
}

View File

@ -2,15 +2,62 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys, os, random # This file is part of chaosc and psychosis
#
from PyQt4 import QtCore, QtGui # chaosc is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# chaosc is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with chaosc. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2014 Stefan Kögl
from __future__ import absolute_import
from dump_grabber_ui import Ui_MainWindow import os
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from chaosc.argparser_groups import *
from chaosc.lib import logger, resolve_host
from collections import deque
from datetime import datetime
from dump_grabber.dump_grabber_ui import Ui_MainWindow
from os import curdir, sep
from PyKDE4.kdecore import ki18n, KCmdLineArgs, KAboutData from PyKDE4.kdecore import ki18n, KCmdLineArgs, KAboutData
from PyKDE4.kdeui import KMainWindow, KApplication from PyKDE4.kdeui import KMainWindow, KApplication
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import QBuffer, QByteArray, QIODevice
from SocketServer import ThreadingMixIn, ForkingMixIn
import logging
import numpy as np
import os.path
import Queue
import random
import re
import select
import socket
import string
import sys
import threading
import time
import traceback
try:
from chaosc.c_osc_lib import OSCMessage, decode_osc
except ImportError as e:
from chaosc.osc_lib import OSCMessage, decode_osc
appName = "dump_grabber" appName = "dump_grabber"
catalog = "dump_grabber" catalog = "dump_grabber"
@ -22,8 +69,13 @@ aboutData = KAboutData(appName, catalog, programName, version)
KCmdLineArgs.init (sys.argv, aboutData) KCmdLineArgs.init (sys.argv, aboutData)
app = KApplication() app = KApplication()
fh = logging.FileHandler(os.path.expanduser("~/.chaosc/dump_grabber.log"))
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)
class MainWindow(KMainWindow, Ui_MainWindow): class MainWindow(KMainWindow, Ui_MainWindow):
def __init__(self, parent=None): def __init__(self, parent=None, columns=3):
super(MainWindow, self).__init__(parent) super(MainWindow, self).__init__(parent)
self.setupUi(self) self.setupUi(self)
self.graphics_view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.graphics_view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
@ -36,28 +88,247 @@ class MainWindow(KMainWindow, Ui_MainWindow):
self.default_font = QtGui.QFont("Monospace", 14) self.default_font = QtGui.QFont("Monospace", 14)
self.default_font.setStyleHint(QtGui.QFont.Monospace) self.default_font.setStyleHint(QtGui.QFont.Monospace)
self.default_font.setBold(True) self.default_font.setBold(True)
self.blue_color = QtGui.QColor(47,147,235)
self.font_metrics = QtGui.QFontMetrics(self.default_font) self.font_metrics = QtGui.QFontMetrics(self.default_font)
self.line_height = self.font_metrics.height() self.line_height = self.font_metrics.height()
self.num_lines = 775/self.line_height self.num_lines = 775/self.line_height
self.graphics_scene.setFont(self.default_font) self.graphics_scene.setFont(self.default_font)
print "font", self.default_font.family(), self.default_font.pixelSize(), self.default_font.pointSize()
self.brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) self.brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
self.brush.setStyle(QtCore.Qt.SolidPattern) self.brush.setStyle(QtCore.Qt.SolidPattern)
pos_y = 0 self.column_width = 775 / columns
for i in range(self.num_lines):
text = self.graphics_scene.addSimpleText("osc address:/test/foo/bar arguments:[%d] types=[\"i\"]" % random.randint(0,255), self.default_font)
text.setBrush(self.brush)
text.setPos(0, i * self.line_height)
pos_y += self.line_height
self.column_count = columns
self.columns = list()
for i in range(columns):
column = list()
for j in range(self.num_lines):
text_item = self.graphics_scene.addSimpleText("", self.default_font)
if column == 0:
text_item.setBrush(QtCore.Qt.red)
elif column == 1:
text_item.setBrush(QtCore.Qt.green)
elif column == 2:
text_item.setBrush(self.blue_color)
text_item.setPos(j * self.line_height, i * self.column_width)
column.append(text_item)
self.columns.append(column)
self.graphics_view.show() self.graphics_view.show()
def main(): def add_text(self, column, text):
window = MainWindow() text_item = self.graphics_scene.addSimpleText(text, self.default_font)
window.show() if column == 0:
app.exec_() text_item.setBrush(QtCore.Qt.red)
elif column == 1:
text_item.setBrush(QtCore.Qt.green)
elif column == 2:
text_item.setBrush(self.blue_color)
old_item = self.columns[column].pop(0)
self.graphics_scene.removeItem(old_item)
self.columns[column].append(text_item)
for ix, text_item in enumerate(self.columns[column]):
text_item.setPos(column * self.column_width, ix * self.line_height)
def render(self):
image = QtGui.QImage(768, 576, QtGui.QImage.Format_ARGB32)
image.fill(QtCore.Qt.black)
painter = QtGui.QPainter(image)
#painter.setPen(QtCore.Qt.white)
painter.setFont(self.default_font)
self.graphics_view.render(painter, target=QtCore.QRectF(0,0,768,576),source=QtCore.QRect(0,0,768,576))
return image
class OSCThread(threading.Thread):
def __init__(self, args):
super(OSCThread, self).__init__()
self.args = args
self.running = True
self.client_address = resolve_host(args.client_host, args.client_port, args.address_family)
self.chaosc_address = chaosc_host, chaosc_port = resolve_host(args.chaosc_host, args.chaosc_port, args.address_family)
self.osc_sock = socket.socket(args.address_family, 2, 17)
self.osc_sock.bind(self.client_address)
self.osc_sock.setblocking(0)
logger.info("starting up osc receiver on '%s:%d'", self.client_address[0], self.client_address[1])
self.subscribe_me()
def subscribe_me(self):
"""Use this procedure for a quick'n dirty subscription to your chaosc instance.
:param chaosc_address: (chaosc_host, chaosc_port)
:type chaosc_address: tuple
:param receiver_address: (host, port)
:type receiver_address: tuple
:param token: token to get authorized for subscription
:type token: str
"""
logger.info("%s: subscribing to '%s:%d' with label %r", datetime.now().strftime("%x %X"), self.chaosc_address[0], self.chaosc_address[1], self.args.subscriber_label)
msg = OSCMessage("/subscribe")
msg.appendTypedArg(self.client_address[0], "s")
msg.appendTypedArg(self.client_address[1], "i")
msg.appendTypedArg(self.args.authenticate, "s")
if self.args.subscriber_label is not None:
msg.appendTypedArg(self.args.subscriber_label, "s")
self.osc_sock.sendto(msg.encode_osc(), self.chaosc_address)
def unsubscribe_me(self):
if self.args.keep_subscribed:
return
logger.info("unsubscribing from '%s:%d'", self.chaosc_address[0], self.chaosc_address[1])
msg = OSCMessage("/unsubscribe")
msg.appendTypedArg(self.client_address[0], "s")
msg.appendTypedArg(self.client_address[1], "i")
msg.appendTypedArg(self.args.authenticate, "s")
self.osc_sock.sendto(msg.encode_osc(), self.chaosc_address)
def run(self):
while self.running:
try:
reads, writes, errs = select.select([self.osc_sock], [], [], 0.01)
except Exception, e:
pass
else:
if reads:
try:
osc_input, address = self.osc_sock.recvfrom(8192)
osc_address, typetags, messages = decode_osc(osc_input, 0, len(osc_input))
queue.put_nowait((osc_address, messages))
except Exception, e:
pass
else:
pass
self.unsubscribe_me()
logger.info("OSCThread is going down")
queue = Queue.Queue()
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
try:
self.path=re.sub('[^.a-zA-Z0-9]', "",str(self.path))
if self.path=="" or self.path==None or self.path[:1]==".":
self.send_error(403,'Forbidden')
if self.path.endswith(".html"):
directory = os.path.dirname(os.path.abspath(__file__))
data = open(os.path.join(directory, self.path), "rb").read()
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(data)
elif self.path.endswith(".mjpeg"):
self.thread = thread = OSCThread(self.server.args)
thread.daemon = True
thread.start()
window = MainWindow()
window.hide()
self.send_response(200)
self.send_header("Content-Type", "multipart/x-mixed-replace; boundary=--aaboundary")
self.end_headers()
event_loop = QtCore.QEventLoop()
while 1:
event_loop.processEvents()
app.sendPostedEvents(None, 0)
while 1:
try:
osc_address, args = queue.get_nowait()
except Queue.Empty:
break
else:
if "merle" in osc_address:
window.add_text(0, "%s = %s" % (osc_address[7:], ", ".join([str(i) for i in args])))
elif "uwe" in osc_address:
window.add_text(1, "%s = %s" % (osc_address[5:], ", ".join([str(i) for i in args])))
elif "bjoern" in osc_address:
window.add_text(2, "%s = %s" % (osc_address[8:], ", ".join([str(i) for i in args])))
img = window.render()
buffer = QBuffer()
buffer.open(QIODevice.WriteOnly)
img.save(buffer, "JPG", 50)
img.save("/tmp/test.jpg", "JPG", 50)
JpegData = buffer.data()
self.wfile.write("--aaboundary\r\nContent-Type: image/jpeg\r\nContent-length: %d\r\n\r\n%s\r\n\r\n\r\n" % (len(JpegData), JpegData))
JpegData = None
buffer = None
img = None
time.sleep(0.06)
elif self.path.endswith(".jpeg"):
directory = os.path.dirname(os.path.abspath(__file__))
data = open(os.path.join(directory, self.path), "rb").read()
self.send_response(200)
self.send_header('Content-type','image/jpeg')
self.end_headers()
self.wfile.write(data)
return
except (KeyboardInterrupt, SystemError):
#print "queue size", queue.qsize()
if hasattr(self, "thread") and self.thread is not None:
self.thread.running = False
self.thread.join()
self.thread = None
except IOError, e:
#print "ioerror", e, e[0]
#print dir(e)
if e[0] in (32, 104):
if hasattr(self, "thread") and self.thread is not None:
self.thread.running = False
self.thread.join()
self.thread = None
else:
pass
#print '-'*40
#print 'Exception happened during processing of request from'
#traceback.print_exc() # XXX But this goes to stderr!
#print '-'*40
#self.send_error(404,'File Not Found: %s' % self.path)
class JustAHTTPServer(HTTPServer):
pass
def main():
arg_parser = ArgParser("dump_grabber")
arg_parser.add_global_group()
client_group = arg_parser.add_client_group()
arg_parser.add_argument(client_group, '-x', "--http_host", default="::",
help='my host, defaults to "::"')
arg_parser.add_argument(client_group, '-X', "--http_port", default=9001,
type=int, help='my port, defaults to 9001')
arg_parser.add_chaosc_group()
arg_parser.add_subscriber_group()
args = arg_parser.finalize()
http_host, http_port = resolve_host(args.http_host, args.http_port, args.address_family)
server = JustAHTTPServer((http_host, http_port), MyHandler)
server.address_family = args.address_family
server.args = args
logger.info("starting up http server on '%s:%d'", http_host, http_port)
server.serve_forever()
if ( __name__ == '__main__' ): if ( __name__ == '__main__' ):
main() main()

50
dump_grabber/setup.py Normal file
View File

@ -0,0 +1,50 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from distribute_setup import use_setuptools
use_setuptools()
import sys
from setuptools import find_packages, setup
if sys.version_info >= (3,):
extras['use_2to3'] = True
setup(
name='dump_grabber',
version="0.1",
packages=find_packages(exclude=["scripts",]),
include_package_data = True,
exclude_package_data = {'': ['.gitignore']},
# installing unzipped
zip_safe = False,
# predefined extension points, e.g. for plugins
entry_points = """
[console_scripts]
dump_grabber = dump_grabber.main:main
""",
# pypi metadata
author = "Stefan Kögl",
# FIXME: add author email
author_email = "",
description = "osc messages logging terminal as mjpeg stream, uses 3 columns",
# FIXME: add long_description
long_description = """
""",
# FIXME: add license
license = "LGPL",
# FIXME: add keywords
keywords = "",
# FIXME: add download url
url = "",
test_suite='tests'
)

View File

@ -29,6 +29,7 @@ from datetime import datetime
import threading import threading
import Queue import Queue
import traceback import traceback
import logging
import numpy as np import numpy as np
import string import string
import time import time
@ -52,20 +53,18 @@ import pyqtgraph as pg
from pyqtgraph.widgets.PlotWidget import PlotWidget from pyqtgraph.widgets.PlotWidget import PlotWidget
from chaosc.argparser_groups import * from chaosc.argparser_groups import *
from chaosc.lib import resolve_host from chaosc.lib import logger, resolve_host
fh = logging.FileHandler(os.path.expanduser("~/.chaosc/ekgplotter.log"))
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)
try: try:
from chaosc.c_osc_lib import * from chaosc.c_osc_lib import OSCMessage, decode_osc
except ImportError:
from chaosc.osc_lib import *
try:
from chaosc.c_osc_lib import decode_osc
except ImportError as e: except ImportError as e:
print(e) print(e)
from chaosc.osc_lib import decode_osc from chaosc.osc_lib import OSCMessage, decode_osc
@ -98,7 +97,7 @@ class OSCThread(threading.Thread):
print "%s: starting up osc receiver on '%s:%d'" % ( print "%s: starting up osc receiver on '%s:%d'" % (
datetime.now().strftime("%x %X"), self.client_address[0], self.client_address[1]) datetime.now().strftime("%x %X"), self.client_address[0], self.client_address[1])
self.subscribe_me() #self.subscribe_me()
def subscribe_me(self): def subscribe_me(self):
"""Use this procedure for a quick'n dirty subscription to your chaosc instance. """Use this procedure for a quick'n dirty subscription to your chaosc instance.
@ -137,7 +136,7 @@ class OSCThread(threading.Thread):
while self.running: while self.running:
try: try:
reads, writes, errs = select.select([self.osc_sock], [], [], 0.05) reads, writes, errs = select.select([self.osc_sock], [], [], 0.01)
except Exception, e: except Exception, e:
print "select error", e print "select error", e
pass pass
@ -146,16 +145,15 @@ class OSCThread(threading.Thread):
try: try:
osc_input, address = self.osc_sock.recvfrom(8192) osc_input, address = self.osc_sock.recvfrom(8192)
osc_address, typetags, messages = decode_osc(osc_input, 0, len(osc_input)) osc_address, typetags, messages = decode_osc(osc_input, 0, len(osc_input))
if osc_address.find("ekg") != -1 or osc_address.find("plot") != -1:
queue.put_nowait((osc_address, messages)) queue.put_nowait((osc_address, messages))
except Exception, e: except Exception, e:
print "recvfrom error", e print "recvfrom error", e
else: #else:
queue.put_nowait(("/bjoern/ekg", [0])) #queue.put_nowait(("/bjoern/ekg", [0]))
queue.put_nowait(("/merle/ekg", [0])) #queue.put_nowait(("/merle/ekg", [0]))
queue.put_nowait(("/uwe/ekg", [0])) #queue.put_nowait(("/uwe/ekg", [0]))
self.unsubscribe_me() #self.unsubscribe_me()
print "OSCThread is going down" print "OSCThread is going down"
@ -252,7 +250,7 @@ class EkgPlot(object):
self.plot.showGrid(False, False) self.plot.showGrid(False, False)
self.plot.setYRange(0, 255) self.plot.setYRange(0, 255)
self.plot.setXRange(0, num_data) self.plot.setXRange(0, num_data)
self.plot.resize(1280, 720) self.plot.resize(768, 576)
ba = self.plot.getAxis("bottom") ba = self.plot.getAxis("bottom")
bl = self.plot.getAxis("left") bl = self.plot.getAxis("left")
@ -386,10 +384,12 @@ class MyHandler(BaseHTTPRequestHandler):
plotter.update(osc_address, args[0]) plotter.update(osc_address, args[0])
exporter = pg.exporters.ImageExporter.ImageExporter(plotter.plot.plotItem) exporter = pg.exporters.ImageExporter.ImageExporter(plotter.plot.plotItem)
exporter.parameters()['width'] = 768
img = exporter.export("tmpfile", True) img = exporter.export("tmpfile", True)
buffer = QBuffer() buffer = QBuffer()
buffer.open(QIODevice.WriteOnly) buffer.open(QIODevice.WriteOnly)
img.save(buffer, "JPG", 100) img.save(buffer, "JPG")
img.save("/tmp/test2.jpg", "JPG")
JpegData = buffer.data() JpegData = buffer.data()
self.wfile.write("--aaboundary\r\nContent-Type: image/jpeg\r\nContent-length: %d\r\n\r\n%s\r\n\r\n\r\n" % (len(JpegData), JpegData)) self.wfile.write("--aaboundary\r\nContent-Type: image/jpeg\r\nContent-length: %d\r\n\r\n%s\r\n\r\n\r\n" % (len(JpegData), JpegData))
@ -407,6 +407,7 @@ class MyHandler(BaseHTTPRequestHandler):
#s = np.clip(dt*3., 0, 1) #s = np.clip(dt*3., 0, 1)
#fps = fps * (1-s) + (1.0/dt) * s #fps = fps * (1-s) + (1.0/dt) * s
#print '%0.2f fps' % fps #print '%0.2f fps' % fps
time.sleep(0.05)
elif self.path.endswith(".jpeg"): elif self.path.endswith(".jpeg"):
directory = os.path.dirname(os.path.abspath(__file__)) directory = os.path.dirname(os.path.abspath(__file__))