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

This commit is contained in:
Stefan Kögl 2014-03-12 04:02:47 +01:00
commit fd64ce55db
5 changed files with 397 additions and 1 deletions

View File

View File

@ -0,0 +1,151 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import pyqtgraph as pg
import select
import socket
import cStringIO
import subprocess
import threading
import time
from PyQt4.QtCore import QBuffer, QByteArray, QIODevice
from collections import deque
"""This module implements the standalone filtering tool in the chaosc framework.
It uses the chaosc osc_lib but does not depend on chaosc features, so it can
be used with other osc compatible gear.
We provide here osc message filtering based on python regex defined in a file
and a very flexible transcoding toolchain, but it's up to your python skills
to master them. The TranscoderBaseHandler subclasses should be defined in the
appropriate python module you place in the config directory. Please refer for
a howto/examples to our comprehensive docs or look into the provided example
transcoding.py file.
"""
# This file is part of chaosc
#
# 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) 2012-2014 Stefan Kögl
import atexit
import sys, argparse
from datetime import datetime
from chaosc.simpleOSCServer import SimpleOSCServer
import chaosc._version
class ChaoscLogger(SimpleOSCServer):
"""OSC filtering/transcoding middleware
"""
def __init__(self, args):
"""ctor for filter server
starts the server, loads scene filters and transcoders and chooses
the request handler, which is one of
forward only, forward and dump, dump only.
:param result: return value of argparse.parse_args
:type result: namespace object
"""
d = datetime.now().strftime("%x %X")
print "%s: starting up chaosc_dump-%s..." % (d, chaosc._version.__version__)
SimpleOSCServer.__init__(self, (args.own_host, args.own_port))
self.args = args
self.chaosc_address = (args.chaosc_host, args.chaosc_port)
if args.subscribe:
self.subscribe_me(self.chaosc_address, (args.own_host, args.own_port),
args.token, args.subscriber_label)
self.plot_data = deque([0] * 100)
self.plt = pg.plot()
def dispatchMessage(self, osc_address, typetags, args, packet,
client_address):
"""Handles this filtering, transcoding steps and forwards the result
:param osc_address: the OSC address string.
:type osc_address: str
:param typetags: the typetags of args
:type typetags: list
:param args: the osc message args
:type args: list
:param packet: the binary representation of a osc message
:type packet: str
:param client_address: (host, port) of the requesting client
:type client_address: tuple
"""
if osc_address == "/uwe/ekg":
self.plot_data.appendleft(args[0])
self.plot_data.pop()
self.plt.plot(self.plot_data, clear=True)
exporter = pg.exporters.ImageExporter.ImageExporter(self.plt.plotItem)
exporter.parameters()['width'] = 1024
name = '/tmp/plotImage.jpg'
exporter.export(name)
def unsubscribe(self):
self.unsubscribe_me(self.chaosc_address, (self.args.own_host, self.args.own_port),
self.args.token)
def main():
parser = argparse.ArgumentParser(prog='ekgplotter')
main_args_group = parser.add_argument_group('main flags', 'flags for chaosc_transcoder')
chaosc_args_group = parser.add_argument_group('chaosc', 'flags relevant for interacting with chaosc')
main_args_group.add_argument('-o', "--own_host", required=True,
type=str, help='my host')
main_args_group.add_argument('-p', "--own_port", required=True,
type=int, help='my port')
chaosc_args_group.add_argument('-s', '--subscribe', action="store_true",
help='if True, this transcoder subscribes itself to chaosc. If you use this, you need to provide more flags in this group')
chaosc_args_group.add_argument('-S', '--subscriber_label', type=str, default="chaosc_transcoder",
help='the string to use for subscription label, default="chaosc_transcoder"')
chaosc_args_group.add_argument('-t', '--token', type=str, default="sekret",
help='token to authorize subscription command, default="sekret"')
chaosc_args_group.add_argument("-H", '--chaosc_host',
type=str, help='host of chaosc instance')
chaosc_args_group.add_argument("-P", '--chaosc_port',
type=int, help='port of chaosc instance')
server = ChaoscLogger(parser.parse_args(sys.argv[1:]))
atexit.register(server.unsubscribe)
server.serve_forever()

55
ekgplotter/setup.py Normal file
View File

@ -0,0 +1,55 @@
#!/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='ekgplotter',
version="0.1",
packages=find_packages(exclude=["scripts",]),
include_package_data = True,
package_data = {
"chaosc" : ["config/*",]},
exclude_package_data = {'': ['.gitignore']},
install_requires=[],
# installing unzipped
zip_safe = False,
# predefined extension points, e.g. for plugins
entry_points = """
[console_scripts]
ekgplotter = ekgplotter.main:main
""",
# pypi metadata
author = "Stefan Kögl",
# FIXME: add author email
author_email = "",
description = "osc filtering application level gateway",
# 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

@ -0,0 +1,190 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# This file is part of chaosc
#
# 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
import argparse
import os.path
import select
import serial
import socket
import sys
import datetime
try:
from chaosc.c_osc_lib import OSCMessage
except ImportError as e:
print(e)
from chaosc.osc_lib import OSCMessage
class Forwarder(object):
def __init__(self, actor, platform, device):
self.actor = actor
self.platform = platform
self.device = device
self.serial = serial.Serial()
self.serial.port = device
self.serial.baudrate = 115200
self.serial.timeout = 0
self.buf_ser2osc = ""
self.serial.open()
def close(self):
"""Close all resources and unpublish service"""
print "%s: closing..." % (self.device, )
self.serial.close()
class EHealth2OSC(Forwarder):
def __init__(self, actor, platform, device):
super(EHealth2OSC, self).__init__(actor, platform, device)
def handle_read(self, osc_sock):
data = self.serial.readline()[:-2]
print repr(data)
try:
airFlow, emg, temp = data.split(";")
except ValueError:
return
try:
airFlow = int(airFlow)
emg = int(emg)
temp = int(temp);
except ValueError:
return
osc_message = OSCMessage("/%s/airFlow" % self.actor)
osc_message.appendTypedArg(airFlow, "i")
osc_sock.sendall(osc_message.encode_osc())
osc_message = OSCMessage("/%s/emg" % self.actor)
osc_message.appendTypedArg(emg, "i")
osc_sock.sendall(osc_message.encode_osc())
osc_message = OSCMessage("/%s/temperatur" % self.actor)
osc_message.appendTypedArg(temp, "i")
osc_sock.sendall(osc_message.encode_osc())
class EKG2OSC(Forwarder):
def __init__(self, actor, platform, device):
super(EKG2OSC, self).__init__(actor, platform, device)
def handle_read(self, osc_sock):
t = ord(self.serial.read(1))
osc_message = OSCMessage("/%s/ekg" % self.actor)
osc_message.appendTypedArg(t, "i")
osc_sock.sendall(osc_message.encode_osc())
class RingBuffer(object):
def __init__(self, length):
self.length = length
self.ring_buf = [-1 for i in xrange(length)]
self.head = 0
def append(self, value):
self.ring_buf[self.head] = value
self.head = (self.head + 1) % self.length
def getData(self):
print "getData", self.ring_buf, self.head
data = list()
for i in range(7, 1, -1):
value = self.ring_buf[(self.head - i) % self.length]
if value == -1:
raise ValueError("not complete")
data.append(value)
if data[0] != 0x0 or data[1] != 0xff:
raise ValueError("not synced")
return data[2:]
class Pulse2OSC(Forwarder):
def __init__(self, actor, platform, device):
super(Pulse2OSC, self).__init__(actor, platform, device)
self.buf = RingBuffer(6)
self.heartbeat_on = False
def handle_read(self, osc_sock):
t = ord(self.serial.read(1))
self.buf.append(t)
if t == 0:
try:
heart_signal, heart_rate, o2, pulse = self.buf.getData()
if pulse == 245 and not self.heartbeat_on:
osc_message = OSCMessage("/%s/heartbeat" % self.actor)
osc_message.appendTypedArg(1, "i")
osc_message.appendTypedArg(heart_rate, "i")
osc_message.appendTypedArg(o2, "i")
osc_sock.sendall(osc_message.encode_osc())
print "heartbeat", datetime.datetime.now(), heart_signal
self.heartbeat_on = True
elif pulse == 1 and self.heartbeat_on:
#print "off heartbeat", datetime.datetime.now(), heart_signal
self.heartbeat_on = False
osc_message = OSCMessage("/%s/heartbeat" % self.actor)
osc_message.appendTypedArg(0, "i")
osc_message.appendTypedArg(heart_rate, "i")
osc_message.appendTypedArg(o2, "i")
osc_sock.sendall(osc_message.encode_osc())
except ValueError, e:
print e
def main():
parser = argparse.ArgumentParser(prog='psychose_actor')
parser.add_argument("-H", '--chaosc_host', required=True,
type=str, help='host of chaosc instance to control')
parser.add_argument("-p", '--chaosc_port', required=True,
type=int, help='port of chaosc instance to control')
parser.add_argument("-t", '--type', required=True,
type=str, help='ekg, pulse, ehealth')
parser.add_argument("-d", '--device', required=True,
type=str, help='device node under /dev')
parser.add_argument("-a", '--actor', required=True,
type=str, help='actor name')
args = parser.parse_args(sys.argv[1:])
osc_sock = socket.socket(2, 2, 17)
osc_sock.connect((args.chaosc_host, args.chaosc_port))
used_devices = dict()
actor = args.actor
if args.type == "ehealth":
used_devices[device] = EHealth2OSC(actor, "ehealth", args.device)
elif args.type == "ekg":
used_devices[device] = EKG2OSC(actor, "ekg", args.device)
elif args.type == "pulse":
used_devices[device] = Pulse2OSC(actor, "pulse", args.device)
else:
raise ValueError("unknown description %r for device %r" % (description, device))
while 1:
read_map = {}
for forwarder in used_devices.values():
read_map[forwarder.serial] = forwarder.handle_read
readers, writers, errors = select.select(read_map, [], [], 0.1)
for reader in readers:
read_map[reader](osc_sock)

View File

@ -4,7 +4,6 @@
from distribute_setup import use_setuptools
use_setuptools()
from scripts.version import get_git_version
import sys
from setuptools import find_packages, setup
@ -33,6 +32,7 @@ setup(
entry_points = """
[console_scripts]
sensors2osc = sensors2osc.main:main
sensorTest = sensors2osc.sensorTest:main
""",
# pypi metadata
author = "Stefan Kögl",