diff --git a/.gitignore b/.gitignore index d2d6f36..8c64017 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,9 @@ nosetests.xml .mr.developer.cfg .project .pydevproject + +*.elf +*.hex +*.map +*.d +*.o diff --git a/healthdisplay/.idea/compiler.xml b/healthdisplay/.idea/compiler.xml new file mode 100644 index 0000000..fa7a277 --- /dev/null +++ b/healthdisplay/.idea/compiler.xml @@ -0,0 +1,30 @@ + + + + + + diff --git a/healthdisplay/.idea/copyright/profiles_settings.xml b/healthdisplay/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..3572571 --- /dev/null +++ b/healthdisplay/.idea/copyright/profiles_settings.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/healthdisplay/.idea/encodings.xml b/healthdisplay/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/healthdisplay/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/healthdisplay/.idea/libraries/Maven__com_illposed_osc_javaosc_core_0_2.xml b/healthdisplay/.idea/libraries/Maven__com_illposed_osc_javaosc_core_0_2.xml new file mode 100644 index 0000000..a12fb77 --- /dev/null +++ b/healthdisplay/.idea/libraries/Maven__com_illposed_osc_javaosc_core_0_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/healthdisplay/.idea/libraries/Maven__org_jboss_netty_netty_3_2_7_Final.xml b/healthdisplay/.idea/libraries/Maven__org_jboss_netty_netty_3_2_7_Final.xml new file mode 100644 index 0000000..f065feb --- /dev/null +++ b/healthdisplay/.idea/libraries/Maven__org_jboss_netty_netty_3_2_7_Final.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/healthdisplay/.idea/misc.xml b/healthdisplay/.idea/misc.xml new file mode 100644 index 0000000..8a80acb --- /dev/null +++ b/healthdisplay/.idea/misc.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/healthdisplay/.idea/modules.xml b/healthdisplay/.idea/modules.xml new file mode 100644 index 0000000..662d397 --- /dev/null +++ b/healthdisplay/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/healthdisplay/.idea/scopes/scope_settings.xml b/healthdisplay/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/healthdisplay/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/healthdisplay/.idea/uiDesigner.xml b/healthdisplay/.idea/uiDesigner.xml new file mode 100644 index 0000000..fbab987 --- /dev/null +++ b/healthdisplay/.idea/uiDesigner.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/healthdisplay/.idea/vcs.xml b/healthdisplay/.idea/vcs.xml new file mode 100644 index 0000000..21cbaa6 --- /dev/null +++ b/healthdisplay/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/healthdisplay/healthdisplay.iml b/healthdisplay/healthdisplay.iml new file mode 100644 index 0000000..e6e4b05 --- /dev/null +++ b/healthdisplay/healthdisplay.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/healthdisplay/pom.xml b/healthdisplay/pom.xml new file mode 100644 index 0000000..fe39b78 --- /dev/null +++ b/healthdisplay/pom.xml @@ -0,0 +1,36 @@ + + 4.0.0 + Maven Default Project + 1.0 + + psychose + psy + + + + com.illposed.osc + javaosc-core + 0.2 + + + + org.jboss.netty + netty + 3.2.7.Final + + + + + + + central + Maven Repository Switchboard + default + http://repo1.maven.org/maven2 + + false + + + + + \ No newline at end of file diff --git a/healthdisplay/src/ActorDisplay.form b/healthdisplay/src/ActorDisplay.form new file mode 100644 index 0000000..0506171 --- /dev/null +++ b/healthdisplay/src/ActorDisplay.form @@ -0,0 +1,155 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/healthdisplay/src/ActorDisplay.java b/healthdisplay/src/ActorDisplay.java new file mode 100644 index 0000000..cfcd95a --- /dev/null +++ b/healthdisplay/src/ActorDisplay.java @@ -0,0 +1,117 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.Random; + +/** + * @author: lucas + * @date: 14.04.14 21:44 + */ +public class ActorDisplay { + private final static Color onColor = Color.WHITE; + private final static Color offColor = Color.RED; + + private JPanel actorPanel; + private JLabel lblCaption; + private JLabel lblHeartbeat; + private JLabel lblPulse; + private JLabel lblOxy; + private JLabel lblEkg; + private JLabel lblEmg; + private JLabel lblTemperature; + + private int counterHeartbeat = 0; + private int counterPulse = 0; + private int counterOxy = 0; + private int counterEkg = 0; + private int counterEmg = 0; + private int counterTemperature = 0; + + private int timeout = 20; // 20 * 100ms + + public void setCaption(String caption) { + lblCaption.setText(caption); + } + + public void setTemperature(String temperature) { + lblTemperature.setText(temperature); + counterTemperature = 0; + } + + public void setEkg(String value) { + lblEkg.setText(value); + counterEkg = 0; + } + + public void setPulse(String pulse) { + lblPulse.setText(pulse); + counterPulse = 0; + } + + public void setEmg(String emg) { + lblEmg.setText(emg); + counterEmg = 0; + } + + public void setOxy(String oxy) { + lblOxy.setText(oxy); + counterOxy = 0; + } + + public void setHeartbeat(String heartbeat) { + lblHeartbeat.setText(heartbeat); + counterHeartbeat = 0; + } + + public ActorDisplay() { + final Random r = new Random(); + + final Timer timer = new Timer(100, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + +// actorPanel.setBackground(new Color(r.nextInt(), false)); + + if(++counterTemperature > timeout) { + lblTemperature.setForeground(offColor); + } else { + lblTemperature.setForeground(onColor); + } + + if(++counterPulse > timeout) { + lblPulse.setForeground(offColor); + } else { + lblPulse.setForeground(onColor); + } + + if(++counterOxy > timeout) { + lblOxy.setForeground(offColor); + } else { + lblOxy.setForeground(onColor); + } + + if(++counterEkg > timeout) { + lblEkg.setForeground(offColor); + } else { + lblEkg.setForeground(onColor); + } + + if(++counterEmg > timeout) { + lblEmg.setForeground(offColor); + } else { + lblEmg.setForeground(onColor); + } + + if(++counterHeartbeat > timeout) { + lblHeartbeat.setForeground(offColor); + } else { + lblHeartbeat.setForeground(onColor); + } + + } + }); + timer.setRepeats(true); + timer.start(); + } +} + diff --git a/healthdisplay/src/ChaOSCclient.java b/healthdisplay/src/ChaOSCclient.java new file mode 100644 index 0000000..a277421 --- /dev/null +++ b/healthdisplay/src/ChaOSCclient.java @@ -0,0 +1,86 @@ +import com.illposed.osc.OSCListener; +import com.illposed.osc.OSCMessage; +import com.illposed.osc.OSCPortIn; +import com.illposed.osc.OSCPortOut; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Enumeration; + +/** + * @author: lucas + * @date: 13.04.14 17:03 + */ +public class ChaOSCclient { + private final static int OSC_CLIENT_PORT = 8123; + private OSCPortIn portIn; + private OSCPortOut portOut; + + public ChaOSCclient(String host, int port) throws UnknownHostException, SocketException { + portOut = new OSCPortOut(InetAddress.getByName(host), port); + } + + public void addListener(String address, OSCListener listener) { + portIn.addListener(address, listener); + } + + public boolean stopReceiver() { + portIn.stopListening(); + return changeChaoscSubscription(false); + } + + public boolean startReceiver() { + try { + portIn = new OSCPortIn(OSC_CLIENT_PORT); + portIn.startListening(); + + return changeChaoscSubscription(true); + } catch (SocketException e) { + System.out.println("could not create listening socket"); + e.printStackTrace(); + } + + return false; + } + + private boolean changeChaoscSubscription(boolean subscribe) { + try { + OSCMessage subscribeMessage = new OSCMessage("/" + (subscribe ? "subscribe" : "unsubscribe")); + subscribeMessage.addArgument(getLocalAddress().getHostAddress()); + subscribeMessage.addArgument(OSC_CLIENT_PORT); + subscribeMessage.addArgument("sekret"); + subscribeMessage.addArgument("statusmonitor"); + + portOut.send(subscribeMessage); + return true; + } catch (SocketException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + System.out.println("could not change chaosc subscription message"); + return false; + } + + private InetAddress getLocalAddress() throws SocketException { + final Enumeration n = NetworkInterface.getNetworkInterfaces(); + while (n.hasMoreElements()) { + NetworkInterface e = n.nextElement(); + + Enumeration a = e.getInetAddresses(); + for (; a.hasMoreElements(); ) { + InetAddress addr = a.nextElement(); + + if (addr.isSiteLocalAddress()) { + return addr; + } + } + } + throw new SocketException(); + } + +} diff --git a/healthdisplay/src/MainForm.form b/healthdisplay/src/MainForm.form new file mode 100644 index 0000000..aa1bed6 --- /dev/null +++ b/healthdisplay/src/MainForm.form @@ -0,0 +1,49 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/healthdisplay/src/MainForm.java b/healthdisplay/src/MainForm.java new file mode 100644 index 0000000..c5f52db --- /dev/null +++ b/healthdisplay/src/MainForm.java @@ -0,0 +1,105 @@ +import com.illposed.osc.OSCListener; +import com.illposed.osc.OSCMessage; + +import javax.swing.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Date; + +/** + * @author: lucas + * @date: 14.04.14 21:43 + */ +public class MainForm { + private ChaOSCclient osCclient; + + private JPanel mainPanel; + private ActorDisplay actor1; + private ActorDisplay actor2; + private ActorDisplay actor3; + + public MainForm(ChaOSCclient client) { + osCclient = client; + osCclient.startReceiver(); + + addActor("merle", "Merle", actor1); + addActor("uwe", "Uwe", actor2); + addActor("bjoern", "Björn", actor3); + } + + + private void addActor(final String actor, final String label, final ActorDisplay actorDisplay) { + actorDisplay.setCaption(label); + osCclient.addListener("/" + actor.toLowerCase() + "/heartbeat", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + if (message.getArguments().length == 3) { + actorDisplay.setHeartbeat(message.getArguments()[0].toString()); + actorDisplay.setPulse(message.getArguments()[1].toString()); + actorDisplay.setOxy(message.getArguments()[2].toString()); + } + } + }); + + osCclient.addListener("/" + actor.toLowerCase() + "/ekg", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + if (message.getArguments().length == 1) { + actorDisplay.setEkg(message.getArguments()[0].toString()); + } + } + }); + + osCclient.addListener("/" + actor.toLowerCase() + "/emg", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + if (message.getArguments().length == 1) { + actorDisplay.setEmg(message.getArguments()[0].toString()); + } + } + }); + + osCclient.addListener("/" + actor.toLowerCase() + "/temperatur", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + if (message.getArguments().length == 1) { + actorDisplay.setTemperature(message.getArguments()[0].toString()); + } + } + }); + } + + public static void main(String[] args) { + + try { + final ChaOSCclient chaOSCclient = new ChaOSCclient("localhost", 7110); + + final MainForm mainForm = new MainForm(chaOSCclient); + final JFrame frame = new JFrame("MainForm"); + frame.setContentPane(mainForm.mainPanel); + frame.setResizable(false); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.pack(); + + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + chaOSCclient.stopReceiver(); + super.windowClosing(e); + } + }); + + frame.setVisible(true); + + new Streamer(8888, mainForm.mainPanel).run(); + + } catch (UnknownHostException e) { + e.printStackTrace(); + } catch (SocketException e) { + e.printStackTrace(); + } + } + +} diff --git a/healthdisplay/src/Streamer.java b/healthdisplay/src/Streamer.java new file mode 100644 index 0000000..1796b55 --- /dev/null +++ b/healthdisplay/src/Streamer.java @@ -0,0 +1,254 @@ +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.*; +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; +import org.jboss.netty.handler.codec.frame.TooLongFrameException; +import org.jboss.netty.handler.codec.http.*; +import org.jboss.netty.handler.stream.ChunkedInput; +import org.jboss.netty.handler.stream.ChunkedWriteHandler; +import org.jboss.netty.util.CharsetUtil; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.ClosedChannelException; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE; +import static org.jboss.netty.handler.codec.http.HttpMethod.GET; +import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*; +import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1; + +/** + * @author Maurus Cuelenaere + * taken from http://blog.maurus.be/2012/05/09/netty-mjpeg-streamer/ + */ +public class Streamer { + private final int port; + private final Component view; + private final ViewRenderer renderer; + + private class ViewRenderer implements Runnable { + private final BufferedImage image; + private final AtomicReference currentBuffer; + private final AtomicInteger listenerCount; + private final int napTime; + private final ByteArrayOutputStream outputStream; + + public ViewRenderer() { + this(10); + } + + public ViewRenderer(int fps) { + this.napTime = 1000 / fps; + this.listenerCount = new AtomicInteger(); + this.currentBuffer = new AtomicReference(); + this.image = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_RGB); + this.outputStream = new ByteArrayOutputStream(); + } + + @Override + public void run() { + while (!Thread.interrupted()) { + long sleepTill = System.currentTimeMillis() + napTime; + + if (listenerCount.get() > 0) { + Graphics g = image.createGraphics(); + view.paint(g); + g.dispose(); + + byte[] newData = null; + try { + ImageIO.write(image, "jpg", outputStream); + newData = outputStream.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + outputStream.reset(); + } + + if (newData != null) { + currentBuffer.set(newData); + synchronized (currentBuffer) { + currentBuffer.notifyAll(); + } + } + } + + try { + long remainingTime = sleepTill - System.currentTimeMillis(); + if (remainingTime > 0) + Thread.sleep(remainingTime); + } catch (InterruptedException e) { + return; + } + } + } + + public void registerListener() { + listenerCount.incrementAndGet(); + } + + public void unregisterListener() { + listenerCount.decrementAndGet(); + } + + public void waitForData() throws InterruptedException { + synchronized (currentBuffer) { + currentBuffer.wait(); + } + } + + public byte[] getData() { + return currentBuffer.get(); + } + } + + private class HttpMultipartReplaceStream implements ChunkedInput { + private final byte[] header; + private boolean closed; + + public HttpMultipartReplaceStream(String boundary) { + this.header = ("--" + boundary + "\r\nContent-Type: image/jpeg\r\n\r\n").getBytes(); + + renderer.registerListener(); + } + + @Override + public void close() throws Exception { + if (closed) + return; + + closed = true; + renderer.unregisterListener(); + } + + @Override + public boolean hasNextChunk() throws Exception { + return !closed; + } + + @Override + public boolean isEndOfInput() throws Exception { + return closed; + } + + @Override + public Object nextChunk() throws Exception { + if (closed) + return null; + + renderer.waitForData(); + byte[] body = renderer.getData(); + + return ChannelBuffers.wrappedBuffer(header, body); + } + } + + private class HttpStreamerHandler extends SimpleChannelUpstreamHandler { + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { + HttpRequest request = (HttpRequest) e.getMessage(); + if (request.getMethod() != GET) { + sendError(ctx, METHOD_NOT_ALLOWED); + return; + } else if (!"/stream".equals(request.getUri())) { + sendError(ctx, NOT_FOUND); + return; + } + + final String boundary = "thisisourmagicboundary"; + HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); + HttpHeaders.setHeader(response, "Connection", "close"); + HttpHeaders.setHeader(response, "Content-Type", "multipart/x-mixed-replace;boundary=" + boundary); + HttpHeaders.setHeader(response, "Cache-control", "no-cache"); + HttpHeaders.setHeader(response, "Pragma", "no-cache"); + HttpHeaders.setHeader(response, "Expires", "Thu, 01 Dec 1994 16:00:00 GMT"); + + Channel ch = e.getChannel(); + + final HttpMultipartReplaceStream replaceStream = new HttpMultipartReplaceStream(boundary); + ch.getCloseFuture().addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + // Stop the stream when the channel is closed + replaceStream.close(); + } + }); + + // Write the initial line and the headers + ch.write(response); + + // Write the content + ChannelFuture writeFuture = ch.write(replaceStream); + + // Close the connection when the whole content is written out + writeFuture.addListener(ChannelFutureListener.CLOSE); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { + Channel ch = e.getChannel(); + Throwable cause = e.getCause(); + if (cause instanceof TooLongFrameException) { + sendError(ctx, BAD_REQUEST); + return; + } else if (cause instanceof ClosedChannelException) { + ch.close(); + return; + } + + cause.printStackTrace(); + if (ch.isConnected()) + sendError(ctx, INTERNAL_SERVER_ERROR); + } + + private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { + HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status); + response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8"); + response.setContent(ChannelBuffers.copiedBuffer("Failure: " + status.toString() + "\r\n", CharsetUtil.UTF_8)); + + // Close the connection as soon as the error message is sent. + ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE); + } + } + + public Streamer(int port, Component view) { + this.port = port; + this.view = view; + this.renderer = new ViewRenderer(); + } + + public void run() { + // Configure the server. + ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); + + // Set up the event pipeline factory. + bootstrap.setPipelineFactory(new ChannelPipelineFactory() { + @Override + public ChannelPipeline getPipeline() throws Exception { + // Create a default pipeline implementation. + ChannelPipeline pipeline = Channels.pipeline(); + + pipeline.addLast("decoder", new HttpRequestDecoder()); + pipeline.addLast("aggregator", new HttpChunkAggregator(65536)); + pipeline.addLast("encoder", new HttpResponseEncoder()); + pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); + pipeline.addLast("handler", new HttpStreamerHandler()); + + return pipeline; + } + }); + + // Bind and start to accept incoming connections. + bootstrap.bind(new InetSocketAddress(port)); + + // Start the renderer + new Thread(renderer, "Image renderer").start(); + } +} \ No newline at end of file diff --git a/healthdisplay/src/Test.java b/healthdisplay/src/Test.java new file mode 100644 index 0000000..2aec3a5 --- /dev/null +++ b/healthdisplay/src/Test.java @@ -0,0 +1,49 @@ +import javax.sound.midi.*; +import java.io.File; + +public class Test { + public static final int NOTE_ON = 0x90; + public static final int NOTE_OFF = 0x80; + public static final String[] NOTE_NAMES = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; + + public static void main(String[] args) throws Exception { + Sequence sequence = MidiSystem.getSequence(new File("/home/lucas/jake-avril_14th.mid")); + + int trackNumber = 0; + for (Track track : sequence.getTracks()) { + trackNumber++; + System.out.println("Track " + trackNumber + ": size = " + track.size()); + System.out.println(); + for (int i=0; i < track.size(); i++) { + MidiEvent event = track.get(i); + System.out.print("@" + event.getTick() + " "); + MidiMessage message = event.getMessage(); + if (message instanceof ShortMessage) { + ShortMessage sm = (ShortMessage) message; + System.out.print("Channel: " + sm.getChannel() + " "); + if (sm.getCommand() == NOTE_ON) { + int key = sm.getData1(); + int octave = (key / 12)-1; + int note = key % 12; + String noteName = NOTE_NAMES[note]; + int velocity = sm.getData2(); + System.out.println("Note on, " + noteName + octave + " key=" + key + " velocity: " + velocity); + } else if (sm.getCommand() == NOTE_OFF) { + int key = sm.getData1(); + int octave = (key / 12)-1; + int note = key % 12; + String noteName = NOTE_NAMES[note]; + int velocity = sm.getData2(); + System.out.println("Note off, " + noteName + octave + " key=" + key + " velocity: " + velocity); + } else { + System.out.println("Command:" + sm.getCommand()); + } + } else { + System.out.println("Other message: " + message.getClass()); + } + } + + System.out.println(); + } + } +} \ No newline at end of file