From 1c2b26268043da54eff2bd106188d8feab563d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Ple=C3=9F?= Date: Mon, 27 Oct 2014 22:04:11 +0100 Subject: [PATCH 1/8] added Snmp client thread (untested) --- .../.idea/copyright/profiles_settings.xml | 4 +- healthdisplay/.idea/misc.xml | 2 +- .../src/main/java/de/psychose/Main.java | 1 + .../src/main/java/de/psychose/MainForm.java | 4 ++ .../main/java/de/psychose/SnmpStatClient.java | 46 +++++++++++++++++-- 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/healthdisplay/.idea/copyright/profiles_settings.xml b/healthdisplay/.idea/copyright/profiles_settings.xml index 3572571..e7bedf3 100644 --- a/healthdisplay/.idea/copyright/profiles_settings.xml +++ b/healthdisplay/.idea/copyright/profiles_settings.xml @@ -1,5 +1,3 @@ - - - + \ No newline at end of file diff --git a/healthdisplay/.idea/misc.xml b/healthdisplay/.idea/misc.xml index 8a80acb..4c4c1de 100644 --- a/healthdisplay/.idea/misc.xml +++ b/healthdisplay/.idea/misc.xml @@ -10,7 +10,7 @@ - + diff --git a/healthdisplay/src/main/java/de/psychose/Main.java b/healthdisplay/src/main/java/de/psychose/Main.java index 0a14f32..b5a43b6 100644 --- a/healthdisplay/src/main/java/de/psychose/Main.java +++ b/healthdisplay/src/main/java/de/psychose/Main.java @@ -41,6 +41,7 @@ public class Main { @Override public void windowClosing(WindowEvent e) { chaOSCclient.stopReceiver(); + snmp.stopRunning(); super.windowClosing(e); } }); diff --git a/healthdisplay/src/main/java/de/psychose/MainForm.java b/healthdisplay/src/main/java/de/psychose/MainForm.java index 24bc772..6f46532 100644 --- a/healthdisplay/src/main/java/de/psychose/MainForm.java +++ b/healthdisplay/src/main/java/de/psychose/MainForm.java @@ -63,6 +63,7 @@ public class MainForm { } }); snmpTimer.setRepeats(true); + snmpStatClient.start(); if(showErrors) { @@ -76,6 +77,8 @@ public class MainForm { pulse3.hide(); statDisplay.hide(); } + + } @@ -146,4 +149,5 @@ public class MainForm { + } diff --git a/healthdisplay/src/main/java/de/psychose/SnmpStatClient.java b/healthdisplay/src/main/java/de/psychose/SnmpStatClient.java index ee84098..a76ba09 100644 --- a/healthdisplay/src/main/java/de/psychose/SnmpStatClient.java +++ b/healthdisplay/src/main/java/de/psychose/SnmpStatClient.java @@ -21,19 +21,22 @@ import java.util.List; * @author: lucas * @date: 18.04.14 12:10 */ -public class SnmpStatClient { +public class SnmpStatClient extends Thread { + private final int napTime = 5000; public static final String OID_COUNTER = "1.3.6.1.2.1.2.2.1.10"; private HashMap lastPorts = new HashMap<>(); private HashMap sumPorts = new HashMap<>(); private Snmp snmp; private CommunityTarget communityTarget; + private long currentTrafficSum = 0; + private Boolean doRun = true; private CommunityTarget getCommunityTarget(String host) { CommunityTarget communityTarget = new CommunityTarget(); communityTarget.setCommunity(new OctetString("public")); communityTarget.setVersion(SnmpConstants.version2c); communityTarget.setAddress(new UdpAddress(host)); - communityTarget.setTimeout(100); + communityTarget.setTimeout(300); return communityTarget; } @@ -46,15 +49,44 @@ public class SnmpStatClient { this.snmp = new Snmp(transportMapping); } catch (IOException e) { + e.printStackTrace(); System.out.println("error: cannot get traffic from snmp target"); } } - public long getTrafficSum() { + public void stopRunning() { + doRun = false; + } + @Override + public void run() { if (snmp == null || this.communityTarget == null) { System.out.println("snmp error"); - return 0; + doRun = false; + } + + while (doRun && !Thread.interrupted()) { + long sleepTill = System.currentTimeMillis() + napTime; + + getSNMPValues(); + + try { + long remainingTime = sleepTill - System.currentTimeMillis(); + if (remainingTime > 0) + Thread.sleep(remainingTime); + } catch (InterruptedException e) { + return; + } + + } + + } + + private void getSNMPValues() { + if (snmp == null || this.communityTarget == null) { + System.out.println("snmp error"); + doRun = false; + return; } long sum = 0; @@ -87,7 +119,11 @@ public class SnmpStatClient { sum += port; } - return sum; + currentTrafficSum = sum; + } + + public long getTrafficSum() { + return currentTrafficSum; } } From 065006acdbf9c9271116fe60c2404076c41ba7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Ple=C3=9F?= Date: Tue, 28 Oct 2014 19:52:32 +0100 Subject: [PATCH 2/8] changed ehealth temperature to float output --- ehealth_sketch/ehealth_eno/ehealth_eno.ino | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ehealth_sketch/ehealth_eno/ehealth_eno.ino b/ehealth_sketch/ehealth_eno/ehealth_eno.ino index 839c917..48b01e8 100644 --- a/ehealth_sketch/ehealth_eno/ehealth_eno.ino +++ b/ehealth_sketch/ehealth_eno/ehealth_eno.ino @@ -59,14 +59,14 @@ float getTemperature(void) void loop() { zaehler++; - int airFlow = analogRead(A1); - int emg = analogRead(0); - int temp = getTemperature(); - Serial.print(airFlow); +// int airFlow = analogRead(A1); +// int emg = analogRead(0); +// int temp = getTemperature(); + Serial.print(analogRead(A1)); Serial.print(";"); - Serial.print(emg); + Serial.print(analogRead(0)); Serial.print(";"); - Serial.println(temp); + Serial.println(getTemperature()); delay(100); if(zaehler >= 10) { From e9830ca27602161c4fa24a8ed91c0bfc75522c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Ple=C3=9F?= Date: Wed, 19 Nov 2014 03:12:08 +0100 Subject: [PATCH 3/8] removed snmp and status functions added hearts and breath display added extra control window made the main window for external display w/o borders --- healthdisplay/.idea/uiDesigner.xml | 12 +- healthdisplay/healthdisplay.iml | 9 +- healthdisplay/pom.xml | 21 +- .../src/main/java/de/psychose/ActorData.java | 103 +++++++ .../main/java/de/psychose/ActorDisplay.java | 151 +++++------ .../src/main/java/de/psychose/ActorHeart.form | 18 ++ .../src/main/java/de/psychose/ActorHeart.java | 67 +++++ .../main/java/de/psychose/ChaOSCclient.java | 7 +- .../main/java/de/psychose/ControlForm.form | 43 +++ .../main/java/de/psychose/ControlForm.java | 54 ++++ .../src/main/java/de/psychose/Main.java | 164 +++++++++-- .../src/main/java/de/psychose/MainForm.form | 74 ++--- .../src/main/java/de/psychose/MainForm.java | 132 +-------- .../main/java/de/psychose/PulseControl.form | 2 - .../main/java/de/psychose/PulseControl.java | 7 +- .../main/java/de/psychose/SnmpStatClient.java | 129 --------- .../main/java/de/psychose/StatsDisplay.form | 112 -------- .../main/java/de/psychose/StatsDisplay.java | 37 --- .../src/main/java/de/psychose/Streamer.java | 256 ------------------ .../src/main/java/de/psychose/Test.java | 94 ------- .../resources/de/psychose/heart1_klein.jpg | Bin 0 -> 43236 bytes .../de/psychose/heart1_klein_inv.jpg | Bin 0 -> 36803 bytes .../resources/de/psychose/heart2_klein.jpg | Bin 0 -> 52630 bytes .../de/psychose/heart2_klein_inv.jpg | Bin 0 -> 45007 bytes 24 files changed, 568 insertions(+), 924 deletions(-) create mode 100644 healthdisplay/src/main/java/de/psychose/ActorData.java create mode 100644 healthdisplay/src/main/java/de/psychose/ActorHeart.form create mode 100644 healthdisplay/src/main/java/de/psychose/ActorHeart.java create mode 100644 healthdisplay/src/main/java/de/psychose/ControlForm.form create mode 100644 healthdisplay/src/main/java/de/psychose/ControlForm.java delete mode 100644 healthdisplay/src/main/java/de/psychose/SnmpStatClient.java delete mode 100644 healthdisplay/src/main/java/de/psychose/StatsDisplay.form delete mode 100644 healthdisplay/src/main/java/de/psychose/StatsDisplay.java delete mode 100644 healthdisplay/src/main/java/de/psychose/Streamer.java delete mode 100644 healthdisplay/src/main/java/de/psychose/Test.java create mode 100644 healthdisplay/src/main/resources/de/psychose/heart1_klein.jpg create mode 100644 healthdisplay/src/main/resources/de/psychose/heart1_klein_inv.jpg create mode 100644 healthdisplay/src/main/resources/de/psychose/heart2_klein.jpg create mode 100644 healthdisplay/src/main/resources/de/psychose/heart2_klein_inv.jpg diff --git a/healthdisplay/.idea/uiDesigner.xml b/healthdisplay/.idea/uiDesigner.xml index c47fa82..709fd23 100644 --- a/healthdisplay/.idea/uiDesigner.xml +++ b/healthdisplay/.idea/uiDesigner.xml @@ -119,16 +119,18 @@ - - - + + + + + + - - + \ No newline at end of file diff --git a/healthdisplay/healthdisplay.iml b/healthdisplay/healthdisplay.iml index d987de2..efc4c55 100644 --- a/healthdisplay/healthdisplay.iml +++ b/healthdisplay/healthdisplay.iml @@ -5,20 +5,19 @@ + + + - - - - - + \ No newline at end of file diff --git a/healthdisplay/pom.xml b/healthdisplay/pom.xml index cd5c36f..8fc0fba 100644 --- a/healthdisplay/pom.xml +++ b/healthdisplay/pom.xml @@ -15,21 +15,22 @@ 0.2 - - org.jboss.netty - netty - 3.2.7.Final - + + + + + com.intellij forms_rt 7.0.3 + compile - - org.snmp4j - snmp4j - 1.9.1f - + + + + + diff --git a/healthdisplay/src/main/java/de/psychose/ActorData.java b/healthdisplay/src/main/java/de/psychose/ActorData.java new file mode 100644 index 0000000..db0374b --- /dev/null +++ b/healthdisplay/src/main/java/de/psychose/ActorData.java @@ -0,0 +1,103 @@ +package de.psychose; + +/** + * @author: lucas + * @date: 17.11.14 21:07 + */ +public class ActorData { + + private PulseData pulseData = new PulseData(); + private int airflow; + private int ekg; + private int emg; + private float temperature; + private boolean tommyHeartbeat; + + private long timestampPulse = 0; + private long timestampTommyPulse = 0; + private long timestampEkg = 0; + private long timestampEmg = 0; + private long timestampTemperature = 0; + private long timestampBreath = 0; + + // TODO: hier die timestamps setzen wann letztes mal geändert, + // dann kann ich in ActorDisplay im Timer einfach prüfen ob differenz > timeout, dann rot setzen + + + public void setTimestampPulse() { + this.timestampPulse = System.currentTimeMillis(); + } + public PulseData getPulseData() { + return pulseData; + } + + public void setPulseData(PulseData pulseData) { + this.pulseData = pulseData; + this.timestampPulse = System.currentTimeMillis(); + } + + public int getAirflow() { + return airflow; + } + + public void setAirflow(int airflow) { + this.airflow = airflow; + this.timestampBreath = System.currentTimeMillis(); + } + + public int getEkg() { + return ekg; + } + + public void setEkg(int ekg) { + this.ekg = ekg; + this.timestampEkg = System.currentTimeMillis(); + } + + public int getEmg() { + return emg; + } + + public void setEmg(int emg) { + this.emg = emg; + this.timestampEmg = System.currentTimeMillis(); + } + + public float getTemperature() { + return temperature; + } + + public void setTemperature(float temperature) { + this.temperature = temperature; + this.timestampTemperature = System.currentTimeMillis(); + } + + public boolean getTommyHeartbeat() { + return tommyHeartbeat; + } + + public void setTommyHeartbeat(boolean tommyHeartbeat) { + this.tommyHeartbeat = tommyHeartbeat; + this.timestampTommyPulse = System.currentTimeMillis(); + } + + public long getTimestampPulse() { + return timestampPulse; + } + + public long getTimestampEkg() { + return timestampEkg; + } + + public long getTimestampEmg() { + return timestampEmg; + } + + public long getTimestampTemperature() { + return timestampTemperature; + } + + public long getTimestampBreath() { + return timestampBreath; + } +} diff --git a/healthdisplay/src/main/java/de/psychose/ActorDisplay.java b/healthdisplay/src/main/java/de/psychose/ActorDisplay.java index e2f0531..a6c9313 100644 --- a/healthdisplay/src/main/java/de/psychose/ActorDisplay.java +++ b/healthdisplay/src/main/java/de/psychose/ActorDisplay.java @@ -3,6 +3,7 @@ package de.psychose; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; +import java.text.DecimalFormat; /** * @author: lucas @@ -14,7 +15,6 @@ public class ActorDisplay { private final static Color offColor = Color.RED; private final static String offText = "no data"; - private JPanel actorPanel; private JLabel lblCaption; private JLabel lblHeartbeat; private JLabel lblPulse; @@ -23,54 +23,32 @@ public class ActorDisplay { private JLabel lblEmg; private JLabel lblTemperature; private JLabel lblBreath; + private JPanel actorPanel; + private ActorData actorData; + private boolean showErrors = false; + private DecimalFormat df = new DecimalFormat("#.0"); - 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 counterBreath = 0; + //TODO: die einzelnen Setter wegmachen, dafür eine setData() bauen die die daten en bloc nimmt + // die darin enthaltenen timestamps dann für rotfärbung nehmen - private int timeout = 20; // 20 * 100ms + public void setActorData(ActorData actorData) { + this.actorData = actorData; + } public void setCaption(String caption) { lblCaption.setText(caption); } - public void setBreath(String breath) { - lblBreath.setText(breath); - counterBreath = 0; - } + public void update() { + lblBreath.setText(String.valueOf(actorData.getAirflow())); - public void setTemperature(String temperature) { - lblTemperature.setText(temperature); - counterTemperature = 0; - } + lblTemperature.setText(df.format(actorData.getTemperature())); + lblEkg.setText(String.valueOf(actorData.getEkg())); + lblPulse.setText(actorData.getPulseData().getHeartbeat() == 0 ? "systole" : "diastole"); + lblEmg.setText(String.valueOf(actorData.getEmg())); + lblOxy.setText(String.valueOf(actorData.getPulseData().getOxygen())); + lblHeartbeat.setText(String.valueOf(actorData.getPulseData().getPulse())); - 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() { @@ -78,62 +56,65 @@ public class ActorDisplay { @Override public void actionPerformed(ActionEvent e) { - if (++counterTemperature > timeout) { - lblTemperature.setForeground(offColor); - lblTemperature.setText(offText); - } else { - lblTemperature.setForeground(onColor); - } + if (actorData == null) + return; - if (++counterPulse > timeout) { - lblPulse.setForeground(offColor); - lblPulse.setText(offText); - } else { - lblPulse.setForeground(onColor); - } + update(); - if (++counterOxy > timeout) { - lblOxy.setForeground(offColor); - lblOxy.setText(offText); - } else { - lblOxy.setForeground(onColor); - } + if (showErrors) { - if (++counterEkg > timeout) { - lblEkg.setForeground(offColor); - lblEkg.setText(offText); - } else { - lblEkg.setForeground(onColor); - } + long timeout = System.currentTimeMillis() - 1000; - if (++counterEmg > timeout) { - lblEmg.setForeground(offColor); - lblEmg.setText(offText); - } else { - lblEmg.setForeground(onColor); - } + if (actorData.getTimestampTemperature() < timeout) { + lblTemperature.setForeground(offColor); + lblTemperature.setText(offText); + } else { + lblTemperature.setForeground(onColor); + } - if (++counterHeartbeat > timeout) { - lblHeartbeat.setForeground(offColor); - lblHeartbeat.setText(offText); - } else { - lblHeartbeat.setForeground(onColor); - } + if (actorData.getTimestampPulse() < timeout) { + lblPulse.setForeground(offColor); + lblPulse.setText(offText); + lblOxy.setForeground(offColor); + lblOxy.setText(offText); + lblHeartbeat.setForeground(offColor); + lblHeartbeat.setText(offText); + } else { + lblPulse.setForeground(onColor); + lblOxy.setForeground(onColor); + lblHeartbeat.setForeground(onColor); + } - if (++counterBreath > timeout) { - lblBreath.setForeground(offColor); - lblBreath.setText(offText); - } else { - lblBreath.setForeground(onColor); + if (actorData.getTimestampEkg() < timeout) { + lblEkg.setForeground(offColor); + lblEkg.setText(offText); + } else { + lblEkg.setForeground(onColor); + } + + if (actorData.getTimestampEmg() < timeout) { + lblEmg.setForeground(offColor); + lblEmg.setText(offText); + } else { + lblEmg.setForeground(onColor); + } + + if (actorData.getTimestampBreath() < timeout) { + lblBreath.setForeground(offColor); + lblBreath.setText(offText); + } else { + lblBreath.setForeground(onColor); + } } } }); - this.timer.setRepeats(true); + + timer.setRepeats(true); + timer.start(); } - public void startErrorTimer() { - this.timer.start(); + public void setShowErrors(boolean showErrors) { + this.showErrors = showErrors; } - } diff --git a/healthdisplay/src/main/java/de/psychose/ActorHeart.form b/healthdisplay/src/main/java/de/psychose/ActorHeart.form new file mode 100644 index 0000000..86b3aa2 --- /dev/null +++ b/healthdisplay/src/main/java/de/psychose/ActorHeart.form @@ -0,0 +1,18 @@ + +
+ + + + + + + + + + + + + + + +
diff --git a/healthdisplay/src/main/java/de/psychose/ActorHeart.java b/healthdisplay/src/main/java/de/psychose/ActorHeart.java new file mode 100644 index 0000000..2dfeef4 --- /dev/null +++ b/healthdisplay/src/main/java/de/psychose/ActorHeart.java @@ -0,0 +1,67 @@ +package de.psychose; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.image.BufferedImage; +import java.io.IOException; + +/** + * @author: lucas + * @date: 15.11.14 21:36 + */ +public class ActorHeart { + private JPanel heartPanel; + private ActorData actorData1; + private ActorData actorData2; + private ActorData actorData3; + private ImagePanel imagePanel; + private Timer timer; + + public ActorHeart() { + imagePanel = new ImagePanel("/de/psychose/heart1_klein_inv.jpg", "/de/psychose/heart2_klein_inv.jpg"); + heartPanel.add(imagePanel); + + timer = new Timer(100, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if(actorData1 != null && actorData2 != null && actorData3 != null) { + imagePanel.repaint(); + } + } + }); + timer.setRepeats(true); + timer.start(); + } + + public void setActorData(final ActorData actorData1, final ActorData actorData2, final ActorData actorData3) { + this.actorData1 = actorData1; + this.actorData2 = actorData2; + this.actorData3 = actorData3; + } + + private class ImagePanel extends JPanel { + private BufferedImage image1; + private BufferedImage image2; + + public ImagePanel(String imageA, String imageB) { + try { + image1 = ImageIO.read(getClass().getResourceAsStream(imageA)); + image2 = ImageIO.read(getClass().getResourceAsStream(imageB)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + if(actorData1 != null && actorData2 != null && actorData3 != null) { + g.drawImage(ActorHeart.this.actorData1.getTommyHeartbeat() ? image1 : image2, 0, 0, null, null); + g.drawImage(ActorHeart.this.actorData2.getTommyHeartbeat() ? image1 : image2, 263, 0, null, null); + g.drawImage(ActorHeart.this.actorData3.getTommyHeartbeat() ? image1 : image2, 526, 0, null, null); + } + } + } +} diff --git a/healthdisplay/src/main/java/de/psychose/ChaOSCclient.java b/healthdisplay/src/main/java/de/psychose/ChaOSCclient.java index 9674dfa..a795850 100644 --- a/healthdisplay/src/main/java/de/psychose/ChaOSCclient.java +++ b/healthdisplay/src/main/java/de/psychose/ChaOSCclient.java @@ -23,7 +23,12 @@ public class ChaOSCclient { public ChaOSCclient(String host, int port) throws UnknownHostException, SocketException { portOut = new OSCPortOut(InetAddress.getByName(host), port); - portIn = new OSCPortIn(OSC_CLIENT_PORT); + try { + portIn = new OSCPortIn(OSC_CLIENT_PORT); + } catch (SocketException se) { + System.out.println("Port " + OSC_CLIENT_PORT + " already in use. Trying " + OSC_CLIENT_PORT + 1); + portIn = new OSCPortIn(OSC_CLIENT_PORT + 1); + } } public void addListener(String address, OSCListener listener) { diff --git a/healthdisplay/src/main/java/de/psychose/ControlForm.form b/healthdisplay/src/main/java/de/psychose/ControlForm.form new file mode 100644 index 0000000..1a0130d --- /dev/null +++ b/healthdisplay/src/main/java/de/psychose/ControlForm.form @@ -0,0 +1,43 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/healthdisplay/src/main/java/de/psychose/ControlForm.java b/healthdisplay/src/main/java/de/psychose/ControlForm.java new file mode 100644 index 0000000..4bec000 --- /dev/null +++ b/healthdisplay/src/main/java/de/psychose/ControlForm.java @@ -0,0 +1,54 @@ +package de.psychose; + +import javax.swing.*; +import java.util.Observable; +import java.util.Observer; + +/** + * @author: lucas + * @date: 15.11.14 22:23 + */ +public class ControlForm { + private PulseControl pulse1; + private PulseControl pulse2; + private PulseControl pulse3; + private JPanel mainPanel; + private ActorDisplay actor1; + private ActorDisplay actor2; + private ActorDisplay actor3; + + private final ChaOSCclient osCclient; + + public JPanel getMainPanel() { + return mainPanel; + } + + + public ControlForm(ChaOSCclient chaOSCclient, final ActorData actorData1, final ActorData actorData2, final ActorData actorData3) { + this.osCclient = chaOSCclient; + + addActor("merle", pulse1, actor1, actorData1); + addActor("uwe", pulse2, actor2, actorData2); + addActor("bjoern", pulse3, actor3, actorData3); + + actor1.setShowErrors(true); + actor2.setShowErrors(true); + actor3.setShowErrors(true); + + } + + private void addActor(final String actor, PulseControl pulse, ActorDisplay display, ActorData actorData) { + pulse.addObserver(new Observer() { + @Override + public void update(Observable o, Object arg) { + if(arg instanceof PulseData) { + final PulseData data = (PulseData)arg; + osCclient.sendPulse(actor, data.getHeartbeat(), data.getPulse(), data.getOxygen()); + } + } + }); + display.setCaption(actor); + display.setActorData(actorData); + } + +} diff --git a/healthdisplay/src/main/java/de/psychose/Main.java b/healthdisplay/src/main/java/de/psychose/Main.java index b5a43b6..3bcfa59 100644 --- a/healthdisplay/src/main/java/de/psychose/Main.java +++ b/healthdisplay/src/main/java/de/psychose/Main.java @@ -1,60 +1,192 @@ package de.psychose; +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: 25.04.14 00:23 */ public class Main { + private ChaOSCclient chaOSCclient; + private ControlForm controlForm; + private MainForm mainForm; + + private int totalMessageCount = 0; + private int messagesTempCounter = 0; + + private long totalTraffic = 0; + private long lastTraffic = 0; + + private final ActorData actorData1 = new ActorData(); + private final ActorData actorData2 = new ActorData(); + private final ActorData actorData3 = new ActorData(); public static void main(String[] args) { + new Main(); + } - final boolean showErrors = args.length > 0; - - try - { - //UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() ); - UIManager.setLookAndFeel( "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" ); - } - catch ( Exception e ) - { + public Main() { + try { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); + } catch (Exception e) { e.printStackTrace(); } try { - final ChaOSCclient chaOSCclient = new ChaOSCclient("chaosc", 7110); - final SnmpStatClient snmp = new SnmpStatClient("switch/161"); - final MainForm mainForm = new MainForm(showErrors, chaOSCclient, snmp); - final JFrame frame = new JFrame("MainForm"); + this.chaOSCclient = new ChaOSCclient("chaosc", 7110); + this.controlForm = new ControlForm(chaOSCclient, actorData1, actorData2, actorData3); + + final JFrame cframe = new JFrame("HD Control"); + cframe.setContentPane(controlForm.getMainPanel()); + cframe.setResizable(false); + cframe.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + cframe.pack(); + + + this.mainForm = new MainForm(actorData1, actorData2, actorData3); + final JFrame frame = new JFrame("HD Main"); frame.setContentPane(mainForm.getMainPanel()); frame.setResizable(false); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); +// frame.setExtendedState(JFrame.MAXIMIZED_BOTH); + frame.setUndecorated(true); frame.pack(); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { chaOSCclient.stopReceiver(); - snmp.stopRunning(); +// snmp.stopRunning(); super.windowClosing(e); } }); - frame.setVisible(true); - new Streamer(8888, mainForm.getMainPanel()).run(); + addActor("merle", actorData1); + addActor("uwe", actorData2); + addActor("bjoern", actorData3); + + cframe.setVisible(true); + frame.setVisible(true); chaOSCclient.startReceiver(); } catch (UnknownHostException | SocketException e) { e.printStackTrace(); } + } + private void addActor(final String actor, final ActorData actorData) { + + chaOSCclient.addListener("/" + actor.toLowerCase() + "/heartbeat", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + if (message.getArguments().length == 3) { + totalMessageCount++; + + if (message.getArguments()[1] instanceof Integer) { + int pulse = (int) (message.getArguments()[1]); + + if (pulse > 60) { // try to skip the invalid pulserate from device + + // set the heartrate + actorData.getPulseData().setPulse((int) (message.getArguments()[1])); + + // set the beat ( 0 or 1 ) + if (message.getArguments()[0] instanceof Integer) { + actorData.getPulseData().setHeartbeat((int) (message.getArguments()[0])); + } + + //TODO: remove this, its for testing without tommy only + actorData.setTommyHeartbeat(((int) message.getArguments()[0]) == 1); + + // set the oxy level + if (message.getArguments()[2] instanceof Integer) { + actorData.getPulseData().setOxygen((int) (message.getArguments()[2])); + } + + actorData.setTimestampPulse(); + } + } + } + } + }); + + chaOSCclient.addListener("/" + actor.toLowerCase() + "/ekg", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + if (message.getArguments().length == 1) { + totalMessageCount++; + + if (message.getArguments()[0] instanceof Integer) { + actorData.setEkg((int) (message.getArguments()[0])); + } + } + } + }); + + chaOSCclient.addListener("/" + actor.toLowerCase() + "/emg", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + if (message.getArguments().length == 1) { + totalMessageCount++; + + if (message.getArguments()[0] instanceof Integer) { + actorData.setEmg((int) (message.getArguments()[0])); + } + } + } + }); + + chaOSCclient.addListener("/" + actor.toLowerCase() + "/temperatur", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + if (message.getArguments().length == 1) { + totalMessageCount++; + + if (message.getArguments()[0] instanceof Float) { + actorData.setTemperature((float) (message.getArguments()[0])); + } + } + } + }); + + chaOSCclient.addListener("/" + actor.toLowerCase() + "/airFlow", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + if (message.getArguments().length == 1) { + totalMessageCount++; + + if (message.getArguments()[0] instanceof Integer) { + actorData.setAirflow((int) (message.getArguments()[0])); + } + } + } + }); + + chaOSCclient.addListener("/" + actor.toLowerCase() + "/tommypuls", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + if (message.getArguments().length == 1) { + totalMessageCount++; + + if (message.getArguments()[0] instanceof Integer) { + actorData.setTommyHeartbeat((boolean) (message.getArguments()[0])); + } + //TODO: evtl muss das oben hier noch anders + } + } + }); + + } } diff --git a/healthdisplay/src/main/java/de/psychose/MainForm.form b/healthdisplay/src/main/java/de/psychose/MainForm.form index 7a2b026..75ac05b 100644 --- a/healthdisplay/src/main/java/de/psychose/MainForm.form +++ b/healthdisplay/src/main/java/de/psychose/MainForm.form @@ -1,74 +1,52 @@
- - + + - + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + + + + + + + + + + + + + + diff --git a/healthdisplay/src/main/java/de/psychose/MainForm.java b/healthdisplay/src/main/java/de/psychose/MainForm.java index 6f46532..9178b32 100644 --- a/healthdisplay/src/main/java/de/psychose/MainForm.java +++ b/healthdisplay/src/main/java/de/psychose/MainForm.java @@ -1,152 +1,48 @@ package de.psychose; -import com.illposed.osc.OSCListener; -import com.illposed.osc.OSCMessage; - import javax.swing.*; import java.awt.event.ActionEvent; -import java.util.Date; -import java.util.Observable; -import java.util.Observer; +import java.text.DecimalFormat; /** * @author: lucas * @date: 14.04.14 21:43 */ public class MainForm { - private final ChaOSCclient osCclient; private JPanel mainPanel; + private ActorHeart heart1; private ActorDisplay actor1; private ActorDisplay actor2; private ActorDisplay actor3; - private StatsDisplay statDisplay; - private PulseControl pulse1; - private PulseControl pulse2; - private PulseControl pulse3; - - private int totalMessageCount = 0; - private int messagesTempCounter = 0; - - private long totalTraffic = 0; - private long lastTraffic = 0; + private JLabel breath; + private final DecimalFormat df = new DecimalFormat("#.0"); public JPanel getMainPanel() { return mainPanel; } - public MainForm(final boolean showErrors, final ChaOSCclient chaOSCclient, final SnmpStatClient snmpStatClient) { - this.osCclient = chaOSCclient; + public MainForm(final ActorData actorData1, final ActorData actorData2, final ActorData actorData3) { - addActor("merle", "Körper 1", actor1, pulse1); - addActor("uwe", "Körper 2", actor2, pulse2); - addActor("bjoern", "Körper 3", actor3, pulse3); + actor1.setCaption("Körper 1"); + actor2.setCaption("Körper 2"); + actor3.setCaption("Körper 3"); + actor1.setActorData(actorData1); + actor2.setActorData(actorData2); + actor3.setActorData(actorData3); + heart1.setActorData(actorData1, actorData2, actorData3); - - final Timer timer = new Timer(1000, new AbstractAction() { + final Timer timer = new Timer(100, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { - statDisplay.setMessagesPerSec(String.valueOf(totalMessageCount - messagesTempCounter)); - statDisplay.setMessageCount(String.valueOf(totalMessageCount)); - messagesTempCounter = totalMessageCount; + breath.setText(String.valueOf(actorData1.getAirflow())); } }); timer.setRepeats(true); timer.start(); - final Timer snmpTimer = new Timer(5000, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - totalTraffic = snmpStatClient.getTrafficSum(); // in kB - statDisplay.setTotalTraffic(String.valueOf(totalTraffic)); - statDisplay.setBandwidth(String.valueOf((totalTraffic - lastTraffic) / 5)); - lastTraffic = totalTraffic; - } - }); - snmpTimer.setRepeats(true); - snmpStatClient.start(); - - - if(showErrors) { - actor1.startErrorTimer(); - actor2.startErrorTimer(); - actor3.startErrorTimer(); - snmpTimer.start(); - } else { - pulse1.hide(); - pulse2.hide(); - pulse3.hide(); - statDisplay.hide(); - } - - } - private void addActor(final String actor, final String label, final ActorDisplay actorDisplay, final PulseControl pulse) { - actorDisplay.setCaption(label); - osCclient.addListener("/" + actor.toLowerCase() + "/heartbeat", new OSCListener() { - @Override - public void acceptMessage(Date time, OSCMessage message) { - if (message.getArguments().length == 3) { - totalMessageCount++; - actorDisplay.setHeartbeat(message.getArguments()[0].toString().equals("0") ? "Systole" : "Diastole"); - 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) { - totalMessageCount++; - 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) { - totalMessageCount++; - 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) { - totalMessageCount++; - actorDisplay.setTemperature(message.getArguments()[0].toString()); - } - } - }); - - osCclient.addListener("/" + actor.toLowerCase() + "/airFlow", new OSCListener() { - @Override - public void acceptMessage(Date time, OSCMessage message) { - if (message.getArguments().length == 1) { - totalMessageCount++; - actorDisplay.setBreath(message.getArguments()[0].toString()); - } - } - }); - - pulse.addObserver(new Observer() { - @Override - public void update(Observable o, Object arg) { - if(arg instanceof PulseData) { - final PulseData data = (PulseData)arg; - osCclient.sendPulse(actor, data.getHeartbeat(), data.getPulse(), data.getOxygen()); - } - } - }); - } - diff --git a/healthdisplay/src/main/java/de/psychose/PulseControl.form b/healthdisplay/src/main/java/de/psychose/PulseControl.form index 9e77e02..a27987b 100644 --- a/healthdisplay/src/main/java/de/psychose/PulseControl.form +++ b/healthdisplay/src/main/java/de/psychose/PulseControl.form @@ -6,7 +6,6 @@ - @@ -16,7 +15,6 @@ - diff --git a/healthdisplay/src/main/java/de/psychose/PulseControl.java b/healthdisplay/src/main/java/de/psychose/PulseControl.java index 915518e..191d0a2 100644 --- a/healthdisplay/src/main/java/de/psychose/PulseControl.java +++ b/healthdisplay/src/main/java/de/psychose/PulseControl.java @@ -35,7 +35,7 @@ public class PulseControl extends Observable { int pulse = pulseWobbleCenter - PULSE_WOBBLE_WIDTH / 2 + random.nextInt(PULSE_WOBBLE_WIDTH); if(pulse < 60) pulse = 60; - if(pulse > 180) pulse = 180; + if(pulse > 230) pulse = 230; final PulseData data = new PulseData(heartbeat, pulse, 95 + random.nextInt(4)); setChanged(); @@ -51,7 +51,6 @@ public class PulseControl extends Observable { enableCheckBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { - System.out.println("item state changed"); JCheckBox checkBox = (JCheckBox)e.getSource(); if(checkBox.isSelected()) { if(!timer.isRunning()) { @@ -69,8 +68,4 @@ public class PulseControl extends Observable { }); } - public void hide() { - this.pulsePanel.setVisible(false); - } - } diff --git a/healthdisplay/src/main/java/de/psychose/SnmpStatClient.java b/healthdisplay/src/main/java/de/psychose/SnmpStatClient.java deleted file mode 100644 index a76ba09..0000000 --- a/healthdisplay/src/main/java/de/psychose/SnmpStatClient.java +++ /dev/null @@ -1,129 +0,0 @@ -package de.psychose; - -import org.snmp4j.CommunityTarget; -import org.snmp4j.Snmp; -import org.snmp4j.TransportMapping; -import org.snmp4j.mp.SnmpConstants; -import org.snmp4j.smi.OID; -import org.snmp4j.smi.OctetString; -import org.snmp4j.smi.UdpAddress; -import org.snmp4j.smi.VariableBinding; -import org.snmp4j.transport.DefaultUdpTransportMapping; -import org.snmp4j.util.DefaultPDUFactory; -import org.snmp4j.util.TreeEvent; -import org.snmp4j.util.TreeUtils; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; - -/** - * @author: lucas - * @date: 18.04.14 12:10 - */ -public class SnmpStatClient extends Thread { - private final int napTime = 5000; - public static final String OID_COUNTER = "1.3.6.1.2.1.2.2.1.10"; - private HashMap lastPorts = new HashMap<>(); - private HashMap sumPorts = new HashMap<>(); - private Snmp snmp; - private CommunityTarget communityTarget; - private long currentTrafficSum = 0; - private Boolean doRun = true; - - private CommunityTarget getCommunityTarget(String host) { - CommunityTarget communityTarget = new CommunityTarget(); - communityTarget.setCommunity(new OctetString("public")); - communityTarget.setVersion(SnmpConstants.version2c); - communityTarget.setAddress(new UdpAddress(host)); - communityTarget.setTimeout(300); - return communityTarget; - } - - public SnmpStatClient(String host) { - try { - final TransportMapping transportMapping = new DefaultUdpTransportMapping(); - transportMapping.listen(); - - this.communityTarget = getCommunityTarget(host); - this.snmp = new Snmp(transportMapping); - - } catch (IOException e) { - e.printStackTrace(); - System.out.println("error: cannot get traffic from snmp target"); - } - } - - public void stopRunning() { - doRun = false; - } - - @Override - public void run() { - if (snmp == null || this.communityTarget == null) { - System.out.println("snmp error"); - doRun = false; - } - - while (doRun && !Thread.interrupted()) { - long sleepTill = System.currentTimeMillis() + napTime; - - getSNMPValues(); - - try { - long remainingTime = sleepTill - System.currentTimeMillis(); - if (remainingTime > 0) - Thread.sleep(remainingTime); - } catch (InterruptedException e) { - return; - } - - } - - } - - private void getSNMPValues() { - if (snmp == null || this.communityTarget == null) { - System.out.println("snmp error"); - doRun = false; - return; - } - - long sum = 0; - - try { - - final TreeUtils treeUtils = new TreeUtils(snmp, new DefaultPDUFactory()); - final List treeEventList = treeUtils.getSubtree(this.communityTarget, new OID(OID_COUNTER)); - - for (TreeEvent treeEvent : treeEventList) { - if (treeEvent.getStatus() == TreeEvent.STATUS_OK) { - for (VariableBinding binding : treeEvent.getVariableBindings()) { - int oid = binding.getOid().last(); - long value = binding.getVariable().toLong() / 1024; // convert bytes down to kilobytes - long lastValue = 0; - if (lastPorts.containsKey(oid)) lastValue = lastPorts.get(oid); - long diff = value - lastValue; - - if (diff > 0) { - sumPorts.put(oid, lastValue + diff); - } - } - } - } - } catch (IllegalArgumentException e) { - System.out.println("error: could not resolve address from snmp target"); - } - - for (long port : sumPorts.values()) { - sum += port; - } - - currentTrafficSum = sum; - } - - public long getTrafficSum() { - return currentTrafficSum; - } - -} diff --git a/healthdisplay/src/main/java/de/psychose/StatsDisplay.form b/healthdisplay/src/main/java/de/psychose/StatsDisplay.form deleted file mode 100644 index 0ea389a..0000000 --- a/healthdisplay/src/main/java/de/psychose/StatsDisplay.form +++ /dev/null @@ -1,112 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/healthdisplay/src/main/java/de/psychose/StatsDisplay.java b/healthdisplay/src/main/java/de/psychose/StatsDisplay.java deleted file mode 100644 index d0a002d..0000000 --- a/healthdisplay/src/main/java/de/psychose/StatsDisplay.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.psychose; - -import javax.swing.*; - -/** - * @author: lucas - * @date: 18.04.14 02:19 - */ -public class StatsDisplay { - private JLabel lblCaption; - private JLabel lblMessageCount; - private JLabel lblMessagesPerSec; - private JPanel statPanel; - private JLabel lblTraffic; - private JLabel lblBandwidth; - - public void setMessageCount(String count) { - lblMessageCount.setText(count); - } - - public void setMessagesPerSec(String messagesPerSec) { - lblMessagesPerSec.setText(messagesPerSec); - } - - public void setTotalTraffic(String totalTraffic) { - lblTraffic.setText(totalTraffic); - } - - public void setBandwidth(String bandwidth) { - lblBandwidth.setText(bandwidth); - } - - public void hide() { - this.statPanel.setVisible(false); - } - -} diff --git a/healthdisplay/src/main/java/de/psychose/Streamer.java b/healthdisplay/src/main/java/de/psychose/Streamer.java deleted file mode 100644 index deea16a..0000000 --- a/healthdisplay/src/main/java/de/psychose/Streamer.java +++ /dev/null @@ -1,256 +0,0 @@ -package de.psychose; - -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/main/java/de/psychose/Test.java b/healthdisplay/src/main/java/de/psychose/Test.java deleted file mode 100644 index 6a1e54c..0000000 --- a/healthdisplay/src/main/java/de/psychose/Test.java +++ /dev/null @@ -1,94 +0,0 @@ -package de.psychose; - -import javax.sound.midi.*; -import java.io.File; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashSet; - -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#", "H"}; - - private static void displayInterfaceInformation(NetworkInterface netint) throws SocketException { - System.out.printf("Display name: %s%n", netint.getDisplayName()); - System.out.printf("Name: %s%n", netint.getName()); - Enumeration inetAddresses = netint.getInetAddresses(); - - for (InetAddress inetAddress : Collections.list(inetAddresses)) { - System.out.printf("InetAddress: %s%n", inetAddress); - } - System.out.printf("%n"); - } - - public static void main(String[] args) throws Exception { - - SnmpStatClient snmpStatClient = new SnmpStatClient("switch/161"); - - System.out.println(snmpStatClient.getTrafficSum() / 1024 / 1024 + "MB"); - - - if(true) return; - - Enumeration nets = NetworkInterface.getNetworkInterfaces(); - for (NetworkInterface netint : Collections.list(nets)) { - displayInterfaceInformation(netint); - } - - - if(true) return; - - Sequence sequence = MidiSystem.getSequence(new File("/home/lucas/jake-avril_14th.mid")); - - HashSet notes = new HashSet(); - - 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); - - notes.add(noteName + octave); - - } 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(); - - - } - - - System.out.println(notes); - } -} \ No newline at end of file diff --git a/healthdisplay/src/main/resources/de/psychose/heart1_klein.jpg b/healthdisplay/src/main/resources/de/psychose/heart1_klein.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b6d2260e705df7988c07c0391e0627f5e7382ea3 GIT binary patch literal 43236 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 Dg10<@ literal 0 HcmV?d00001 diff --git a/healthdisplay/src/main/resources/de/psychose/heart1_klein_inv.jpg b/healthdisplay/src/main/resources/de/psychose/heart1_klein_inv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..842f27e7a40dc4f682a441e5ac3011313ffe65ff GIT binary patch literal 36803 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

%RZX&)r}XEW(a*nqeS!)nIy1)X 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

JQ)UzxS0!CUk?^G;;Fnz7>k1t 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 literal 0 HcmV?d00001 diff --git a/healthdisplay/src/main/resources/de/psychose/heart2_klein.jpg b/healthdisplay/src/main/resources/de/psychose/heart2_klein.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0c0604843036bbe1bc9595998aa3024d2b0ef924 GIT binary patch literal 52630 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 Gx&H%jkuQw^ literal 0 HcmV?d00001 diff --git a/healthdisplay/src/main/resources/de/psychose/heart2_klein_inv.jpg b/healthdisplay/src/main/resources/de/psychose/heart2_klein_inv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4d1d6d522815c28e01c845a2dc5b56acd995c75e GIT binary patch literal 45007 zcmeFZ2UJwcwl=z;$r%I$$w_kPCUui@lq?w~Hj;Bj1VNCTgJc8*iIOvdkT?L~ zYMH)C*2~%w08~@}HUI!H0CXfG0E9>(A^rfQI{@lU8UW0YD1N7HkXU|UkP#R#fQSJc zL~ucZ+@y&S=_k*v0hC{|@re5*0*NT`M(fR8UQtDz7QzMP0`mY69x$H}7%T)9q=oPZ z@$m}r3jhFWEC8S*Bv5!DV4j@6(_kK+9HhTWzq$f|9OS=CK|;>~{Z6wZoH=sG^+dgX z`bgK!xVey!K`5wb=opw-*a$!^0f3AI0wJS-P*G73j}OTY!3R(XQHkill4!RyOws9` zi6KwpUScpvRlXz99Q(@1YvvM&iA8#wjQkGMU1km%O** zuAaVuxdq(vk(ITLtDC!rr=b>TY@d=4Z$tf>V({l6jUlkO-E-J37uBol7 zZ)j}l=Fw(u7@wG&nx2`Rn_pg8U0dJS{Jgb&aCmfla{BG;`}uXd5Ek)QvwpYi z-`YipXcsaH3J3-Lx?M=fo`?V>-3 zA6qk;a)RrwcW zcu0(!ly`sw;X&*YOhT@gR;e>SA^YsPU#ZG7{mb~1F2KzSsi6&hecQ0rxY6YYuSP?- zqbhDG5?96=IzQM+>QO)sR1`Un)CCK~Pv41Z8MK-HMpzMN8M=qm^t9%yziOKSuy`OA zWB%xMS%!~&jSg1>K8~>ZylkZ-1{pg+aDzuT&s%T&yJiwt#!p^||d!?a0HG>I)hyQ=7``np18KUYv1nv-R< zV#%8*kuDPP+jZ@~E)rFK`lPT*rl;#_as_y;GWu`=Hro{)8l3iJs{-RruK{w{);EcB z-ORJ6FNN0U4ST|f)z(>L0jeMeCsF6@o>r_MS{5y7dk1x6`V*N8bw@#?t`|GQrKJuh zzRS2ZmWml`=lCvG$hjrLsOrkM0?=I_kX!*lwb<;o-YK`fSCuj`$bF!UTrCv;>HI)0 zds!JDUzY0QZpX^;;cKz&?4wWLb-K8V9)2+;dPa#cN$KMv3YMT$3%%es&)+h(4%$PO zWRrWfu1_SP9mar>Z|=Bbl;qpuF+Nfy>sG>9Y+;v(n?vcY)gxb2`cbXj=WZU_ju*~t zd{%`nbO+Z7hQjZA64d;%wzydOZFSU(_v5K}H#|1E-kM109?(z4~`$325t73VT7qUK{&z62LKQo2H;vbAB;keqT_iEm`HvIHEM^bygN7Ci#61zkFv$--rFZ%MGn(>$WcS55b2@Dyl zix;Ykt8aCjUqY{d9m@a~o2F;fG*!{QEf2Q^mETf$igVazk^gL3!hZuIU;c73tgH!b zY>vyLqSvau{WoONE2R>rcc(n?4@IajuhA@R{+1gCIb&>I?$|!vsm+m;@e?P$0<*v~8pv zmE3TE<&VJXQO6q;W2C98@@iYKsjuQaZG9b%M^^$_Cl69;7_1M9^CZB}1^9&JrL@|m z%k;@jfG~S;cxPAi3P|#?(@FN> fr)g$mb#Q3ndmexQjDmE@`3-6F?PzNa8M#;!U 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 literal 0 HcmV?d00001 From e116bb2447d7019adbd7102d0b26da093e898122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Ple=C3=9F?= Date: Wed, 19 Nov 2014 03:14:48 +0100 Subject: [PATCH 4/8] added EKG code, since i changed the protocol --- mod_ekg_psychose/gcc/MOD-EKG_DemoSoft.c | 462 ++++ mod_ekg_psychose/gcc/Makefile | 91 + mod_ekg_psychose/gcc/make.sh | 2 + .../iar/Backup of MOD-EKG_DemoSoft.ewd | 571 +++++ .../iar/Backup of MOD-EKG_DemoSoft.ewp | 1744 +++++++++++++ mod_ekg_psychose/iar/MOD-EKG_DemoSoft.c | 436 ++++ mod_ekg_psychose/iar/MOD-EKG_DemoSoft.dep | 128 + mod_ekg_psychose/iar/MOD-EKG_DemoSoft.ewd | 819 +++++++ mod_ekg_psychose/iar/MOD-EKG_DemoSoft.ewp | 2161 +++++++++++++++++ mod_ekg_psychose/iar/MOD-EKG_DemoSoft.eww | 10 + mod_ekg_psychose/iar/mul.s43 | 110 + mod_ekg_psychose/iar/path.txt | 0 .../settings/MOD-EKG_DemoSoft.Debug.cspy.bat | 24 + .../iar/settings/MOD-EKG_DemoSoft.cspy.bat | 34 + .../iar/settings/MOD-EKG_DemoSoft.dbgdt | 89 + .../iar/settings/MOD-EKG_DemoSoft.dni | 94 + .../iar/settings/MOD-EKG_DemoSoft.wsdt | 77 + .../iar/settings/MOD-EKG_DemoSoft.wspos | 2 + 18 files changed, 6854 insertions(+) create mode 100644 mod_ekg_psychose/gcc/MOD-EKG_DemoSoft.c create mode 100644 mod_ekg_psychose/gcc/Makefile create mode 100755 mod_ekg_psychose/gcc/make.sh create mode 100644 mod_ekg_psychose/iar/Backup of MOD-EKG_DemoSoft.ewd create mode 100644 mod_ekg_psychose/iar/Backup of MOD-EKG_DemoSoft.ewp create mode 100644 mod_ekg_psychose/iar/MOD-EKG_DemoSoft.c create mode 100644 mod_ekg_psychose/iar/MOD-EKG_DemoSoft.dep create mode 100644 mod_ekg_psychose/iar/MOD-EKG_DemoSoft.ewd create mode 100644 mod_ekg_psychose/iar/MOD-EKG_DemoSoft.ewp create mode 100644 mod_ekg_psychose/iar/MOD-EKG_DemoSoft.eww create mode 100644 mod_ekg_psychose/iar/mul.s43 create mode 100644 mod_ekg_psychose/iar/path.txt create mode 100644 mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.Debug.cspy.bat create mode 100644 mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.cspy.bat create mode 100644 mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.dbgdt create mode 100644 mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.dni create mode 100644 mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.wsdt create mode 100644 mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.wspos diff --git a/mod_ekg_psychose/gcc/MOD-EKG_DemoSoft.c b/mod_ekg_psychose/gcc/MOD-EKG_DemoSoft.c new file mode 100644 index 0000000..ed8deac --- /dev/null +++ b/mod_ekg_psychose/gcc/MOD-EKG_DemoSoft.c @@ -0,0 +1,462 @@ +//***************************************************************************** +// MSP430FG439-Heart Rate Monitor Demo +// +// Description; Uses one Instrumentation Amplifier INA321 and the three +// internal opamps of the MSP430FG439 +// +// Murugavel Raju +// Texas Instruments, Inc +// October 2004 +// Edited by: M Morales, November 2008 +// * Updated to non-depracated intrinsic functions +// * Changed spacing for legibility +// Edited by: +// Penko T. Bozhkov - Olimex LTD, 05.10.2012 +// * RTC capcitors changed according to Olimex's crystall requirements +// * Olimex LCD definitions are added and heart rate is visualized at 2 places on LCD +// Built with IAR Embedded Workbench Version: 4.21 +//***************************************************************************** +//***************************************************************************** +// THIS PROGRAM IS PROVIDED "AS IS". TI MAKES NO WARRANTIES OR +// REPRESENTATIONS, EITHER EXPRESS, IMPLIED OR STATUTORY, +// INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR +// COMPLETENESS OF RESPONSES, RESULTS AND LACK OF NEGLIGENCE. +// TI DISCLAIMS ANY WARRANTY OF TITLE, QUIET ENJOYMENT, QUIET +// POSSESSION, AND NON-INFRINGEMENT OF ANY THIRD PARTY +// INTELLECTUAL PROPERTY RIGHTS WITH REGARD TO THE PROGRAM OR +// YOUR USE OF THE PROGRAM. +// +// IN NO EVENT SHALL TI BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +// CONSEQUENTIAL OR INDIRECT DAMAGES, HOWEVER CAUSED, ON ANY +// THEORY OF LIABILITY AND WHETHER OR NOT TI HAS BEEN ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGES, ARISING IN ANY WAY OUT +// OF THIS AGREEMENT, THE PROGRAM, OR YOUR USE OF THE PROGRAM. +// EXCLUDED DAMAGES INCLUDE, BUT ARE NOT LIMITED TO, COST OF +// REMOVAL OR REINSTALLATION, COMPUTER TIME, LABOR COSTS, LOSS +// OF GOODWILL, LOSS OF PROFITS, LOSS OF SAVINGS, OR LOSS OF +// USE OR INTERRUPTION OF BUSINESS. IN NO EVENT WILL TI'S +// AGGREGATE LIABILITY UNDER THIS AGREEMENT OR ARISING OUT OF +// YOUR USE OF THE PROGRAM EXCEED FIVE HUNDRED DOLLARS +// (U.S.$500). +// +// Unless otherwise stated, the Program written and copyrighted +// by Texas Instruments is distributed as "freeware". You may, +// only under TI's copyright in the Program, use and modify the +// Program without any charge or restriction. You may +// distribute to third parties, provided that you transfer a +// copy of this license to the third party and the third party +// agrees to these terms by its first use of the Program. You +// must reproduce the copyright notice and any other legend of +// ownership on each copy or partial copy, of the Program. +// +// You acknowledge and agree that the Program contains +// copyrighted material, trade secrets and other TI proprietary +// information and is protected by copyright laws, +// international copyright treaties, and trade secret laws, as +// well as other intellectual property laws. To protect TI's +// rights in the Program, you agree not to decompile, reverse +// engineer, disassemble or otherwise translate any object code +// versions of the Program to a human-readable form. You agree +// that in no event will you alter, remove or destroy any +// copyright notice included in the Program. TI reserves all +// rights not specifically granted under this license. Except +// as specifically provided herein, nothing in this agreement +// shall be construed as conferring by implication, estoppel, +// or otherwise, upon you, any license or other right under any +// TI patents, copyrights or trade secrets. +// +// You may not use the Program in non-TI devices. +//***************************************************************************** +#include "msp430fg439.h" +#include "math.h" +//defines +#define PB_2_0 (1 << 0) // Push Button on P2.0 +#define PB_2_1 (1 << 1) // Push Button on P2.1 + +// variables declaration +static char beats; +int i=0, first_detection=0; +long result = 0; +int Datain, Dataout, Dataout_pulse, pulseperiod, counter, heartrate; +int heartrate_buffer[] = {0,0,0,0,0,0,0,0,0,0}; +int heartrate_cursor = 0, heartrate_mean; + +// Lowpass FIR filter coefficients for 17 taps to filter > 30Hz +static const int coeffslp[9] = { + 5225, 5175, 7255, 9453, 11595, 13507, 15016, 15983, 16315 }; +// Highpass FIR filter coefficients for 17 taps to filter < 2Hz +static const int coeffshp[9] = { + -763, -1267, -1091, -1867, -1969, -2507, -2619, -2911, 29908 }; + +// ******************************************* +// Definitions related to Olimex LCD Digits!!!! +// ******************************************* +// Definitions for Olimex LCD digits 10 and 11 +#define a 0x10 +#define b 0x01 +#define c 0x04 +#define d 0x08 +#define e 0x40 +#define f 0x20 +#define g 0x02 +#define h 0x80 +// Character generator definition for display digits 10 and 11 +const char char_gen_10_11[] = { + a+b+c+d+e+f, // 0 Displays "0" + b+c, // 1 Displays "1" + a+b+d+e+g, // 2 Displays "2" + a+b+c+d+g, // 3 Displays "3" + b+c+f+g, // 4 Displays "4" + a+c+d+f+g, // 5 Displays "5" + a+c+d+e+f+g, // 6 Displays "6" + a+b+c, // 7 Displays "7" + a+b+c+d+e+f+g, // 8 Displays "8" + a+b+c+d+f+g, // 9 Displays "9" +}; +// undefines +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h + +// Definitions for Olimex LCD digits 8 and 9 +#define a 0x01 +#define b 0x02 +#define c 0x04 +#define d 0x80 +#define e 0x40 +#define f 0x10 +#define g 0x20 +#define h 0x08 +// Character generator definition for display digits 8 and 9 +const char char_gen_8_9[] = { + a+b+c+d+e+f, // 0 Displays "0" + b+c, // 1 Displays "1" + a+b+d+e+g, // 2 Displays "2" + a+b+c+d+g, // 3 Displays "3" + b+c+f+g, // 4 Displays "4" + a+c+d+f+g, // 5 Displays "5" + a+c+d+e+f+g, // 6 Displays "6" + a+b+c, // 7 Displays "7" + a+b+c+d+e+f+g, // 8 Displays "8" + a+b+c+d+f+g, // 9 Displays "9" +}; +// undefines +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h + +// Definitions for Olimex LCD digits 1 to 7. Here each digit definition require 2 bytes +#define a 0x0080 +#define b 0x0040 +#define c 0x0020 +#define d 0x0010 +#define e 0x2000 +#define f 0x4000 +#define g 0x0402 +#define h 0x1000 +// Character generator definition for display digits 1 to 7 +const int char_gen_1_7[] = { + a+b+c+d+e+f, // 0 Displays "0" + b+c, // 1 Displays "1" + a+b+d+e+g, // 2 Displays "2" + a+b+c+d+g, // 3 Displays "3" + b+c+f+g, // 4 Displays "4" + a+c+d+f+g, // 5 Displays "5" + a+c+d+e+f+g, // 6 Displays "6" + a+b+c, // 7 Displays "7" + a+b+c+d+e+f+g, // 8 Displays "8" + a+b+c+d+f+g, // 9 Displays "9" +}; +// undefines +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h + + + +// function prototypes +void Init(void); // Initializes device for the application +void ClearLCD(void); // Clears the LCD memory +int filterlp(int); // 17 tap lowpass FIR filter +int filterhp(int); // 17 tap highpass FIR filter +long mul16(register int x, register int y); // 16-bit signed multiplication +int itobcd(int i); // 16-bit hex to bcd conversion + +// main function +int main(void) +{ + Init(); // Initialize device for the application + LCDMEM[7] = 0x80; // Turn on LCD's Olimex row!!! + +/* + // For debug purpose only! + for(unsigned char j=0;j<10;j++){ + LCDMEM[2] = char_gen_10_11[j]; // LCD -> Digit 11 + LCDMEM[3] = char_gen_10_11[j]; // LCD -> Digit 10 + LCDMEM[4] = char_gen_8_9[j]; // LCD -> Digit 9 + LCDMEM[5] = char_gen_8_9[j]; // LCD -> Digit 8 + + LCDMEM[7] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 7 High Byte + LCDMEM[6] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 7 Low Byte + LCDMEM[9] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 6 High Byte + LCDMEM[8] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 6 Low Byte + LCDMEM[11] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 5 High Byte + LCDMEM[10] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 5 Low Byte + //LCDMEM[13] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 4 High Byte + //LCDMEM[12] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 4 Low Byte + //LCDMEM[15] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 3 High Byte + //LCDMEM[14] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 3 Low Byte + //LCDMEM[17] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 2 High Byte + //LCDMEM[16] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 2 Low Byte + } +*/ + + while(1) + { + __bis_SR_register(LPM0_bits); // Enter LPM0 needed for UART TX completion + __no_operation(); + + Dataout = filterlp(Datain); // Lowpass FIR filter for filtering out 60Hz + Dataout_pulse = filterhp(Dataout)-128; // Highpass FIR filter to filter muscle artifacts + Dataout = Dataout >> 6; // Scale Dataout to use scope program + if(Dataout > 255) Dataout = 255; // Set boundary 255 max + if(Dataout < 0) Dataout = 0; // Set boundary 0 min + //DAC12_0DAT = Dataout; // For scope display + + + if( 0 ) { + TXBUF0 = Dataout; // Transmit via UART0 for Scope display + } else { + // send the data as ascii values + TXBUF0 = (Dataout / 100) + 48; // hundreds + while (!(IFG1 & UTXIFG0)); // wait for transmission + TXBUF0 = ((Dataout / 10) % 10 ) + 48; // tens + while (!(IFG1 & UTXIFG0)); + TXBUF0 = ((Dataout / 1) % 10 ) + 48; // ones + while (!(IFG1 & UTXIFG0)); + TXBUF0 = 32; // send a blank + while (!(IFG1 & UTXIFG0)); + TXBUF0 = ((heartrate & 0xf00) >> 8) + 48; + while (!(IFG1 & UTXIFG0)); + TXBUF0 = ((heartrate & 0xf0) >> 4) + 48; + while (!(IFG1 & UTXIFG0)); + TXBUF0 = (heartrate & 0x0f) + 48; + while (!(IFG1 & UTXIFG0)); + TXBUF0 = 10; // send a \n + } + + + counter++; // Debounce counter + pulseperiod++; // Pulse period counter + if (Dataout_pulse > 100) // Check if above threshold (48) + { + LCDMEM[1] = 0xF0; // Heart beat detected enable "<^>" on LCD + counter = 0; // Reset debounce counter + } + if (counter == 128) // Allow 128 sample debounce time + { + LCDMEM[1] = 0x00; // Disable "<^>" on LCD for blinking effect + beats++; + if (beats == 3) + { + beats = 0; + + //heartrate_buffer[heartrate_cursor] = 50720/pulseperiod; + //heartrate_cursor++; + //heartrate_cursor = heartrate_cursor % 10; + + //int ct; + //for(ct = 0; ct < 10; ct++) { +// heartrate_mean += heartrate_buffer[ct]; + //} + //heartrate = itobcd(heartrate_mean / 10); + + // heartrate = itobcd(30720/pulseperiod); // Calculate beat to beat heart rate per min + //heartrate = itobcd(92160/pulseperiod); // Calculate 3 beat average heart rate per min + heartrate = itobcd(50720/pulseperiod); // Calculate 3 beat average heart rate per min + //heartrate = (92160/pulseperiod); // Calculate 3 beat average heart rate per min + pulseperiod = 0; // Reset pulse period for next measurement + + + + ///* + LCDMEM[2] = char_gen_10_11[heartrate & 0x0f]; // Display current heart rate units -> LCD Digit 11 + LCDMEM[3] = char_gen_10_11[(heartrate & 0xf0) >> 4]; // tens -> LCD Digit 10 + LCDMEM[4] = char_gen_8_9[(heartrate & 0xf00) >> 8]; // hundreds -> LCD Digit 9 + + LCDMEM[7] = ((char)(char_gen_1_7[heartrate & 0x0f]>>8)); // LCD -> Digit 7 High Byte + LCDMEM[6] = ((char)(char_gen_1_7[heartrate & 0x0f]&0x00FF)); // LCD -> Digit 7 Low Byte + LCDMEM[9] = ((char)(char_gen_1_7[((heartrate & 0xf0) >> 4)]>>8)); // LCD -> Digit 6 High Byte + LCDMEM[8] = ((char)(char_gen_1_7[((heartrate & 0xf0) >> 4)]&0x00FF)); // LCD -> Digit 6 Low Byte + LCDMEM[11] = ((char)(char_gen_1_7[((heartrate & 0xf00) >> 8)]>>8)); // LCD -> Digit 5 High Byte + LCDMEM[10] = ((char)(char_gen_1_7[((heartrate & 0xf00) >> 8)]&0x00FF)); // LCD -> Digit 5 Low Byte + //*/ + + } + } + } +}//main + +// Initialization function +void Init( void ) +{ + FLL_CTL0 |= XCAP10PF; // Set load capacitance for xtal + WDTCTL = WDTPW | WDTHOLD; // Disable the Watchdog + while ( LFOF & FLL_CTL0); // wait for watch crystal to stabilize + SCFQCTL = 63; // 32 x 32768 x 2 = 2.097152MHz + BTCTL = BT_fLCD_DIV128; // Set LCD frame freq = ACLK/128 +// Initialize and enable LCD peripheral + ClearLCD(); // Clear LCD memory + LCDCTL = LCDSG0_3 + LCD4MUX + LCDON ; // 4mux LCD, segs0-23 enabled +// Initialize and enable GPIO ports + P1OUT = 0x00 + BIT3; // Clear P1OUT register, INA turned ON + P1DIR = 0x3f; // Unused pins as outputs, Comparator pins as inputs + P2OUT = 0x00; // Clear P2OUT register + P2DIR = 0xff; // Unused pins as outputs + P2DIR = ~(PB_2_0+PB_2_1); // P2.0 and P2.1 push buttons + P2IES = 0x00; // Interrupt edge low to high transition + P2IFG = 0x00; // Clear pending P2 interrupts + P2IE = PB_2_0 | PB_2_1; // Enable intterupts for push buttons + P3OUT = 0x00; // Clear P3OUT register + P3DIR = 0x0f; // Unused pins as outputs except P3.<4-7> -> For the new LCD's received at ~04.10.2012 this must be inputs!! + P4OUT = 0x00; // Clear P4OUT register + P4DIR = 0xff; // Unused pins as outputs + P5OUT = 0x00; // Clear P5OUT register + P5DIR = 0xff; // Unused pins as outputs + P5SEL = 0xfc; // Set Rxx and COM pins for LCD + P6OUT = 0x00; // Clear P6OUT register + P6SEL = 0xff; // P6 = Analog +// Initialize and enable UART + P2SEL|=BIT4; // P2.4 = TXD + UCTL0 |= SWRST; // UART SWRST = 1 + ME1 |= UTXE0; // Enable UART0 TXD + UCTL0 |= CHAR; // 8-bit char, SWRST=1 + UTCTL0 |= SSEL1; // UCLK = SMCLK + UBR00 = 18; // 115200 from 2.097152MHz + UBR10 = 0; + UMCTL0 = 0x2c; // Modulation = 0.2044 + UCTL0 &= ~SWRST; // UART SWRST = 0, enable UART + IFG1 &= ~UTXIFG0; +// Initialize and enable ADC12 + ADC12CTL0 = ADC12ON + SHT0_4 + REFON + REF2_5V; + // ADC12 ON, Reference = 2.5V for DAC0 + ADC12CTL1 = SHP + SHS_1 + CONSEQ_2; // Use sampling timer, TA1 trigger + ADC12MCTL0 = INCH_1 + SREF_1; // Vref, channel = 1 = OA0 Out + ADC12IE = BIT0; // Enable interrupt for ADC12 MEM0 + ADC12CTL0 |= ENC; // Enable conversions +// Initialize and enable Timer_A + TACTL = TASSEL0 + MC_1 + TACLR; // ACLK, Clear TAR, Up Mode + TACCTL1 = OUTMOD_2; // Set / Reset + TACCR0 = 63; // 512 samples per second + TACCR1 = 15; // +// Initialize and enable DAC12x + DAC12_0CTL = DAC12OPS + DAC12CALON + DAC12IR + DAC12AMP_2 + DAC12ENC;// DAC0 enable + DAC12_1CTL = DAC12CALON + DAC12IR + DAC12AMP_2 + DAC12ENC; // DAC1 enable + DAC12_1DAT = 0x099A; // Offset level = 1.5V for op amp bias +// Initialize and enable opamps + OA0CTL0 = OAP_1 + OAPM_1 + OAADC1; // OA0 enable power mode 1, OA0- = P6.0, 0A0+ = P6.2, OA0O = P6.1 + OA0CTL1 = OARRIP; // General purpose mode, no Rail-to-Rail inputs + OA1CTL0 = OAP_3 + OAPM_1 + OAADC1; // OA1 enable power mode 1, OA1- = P6.4, OA1+ = DAC1, OA1O = P6.3 + OA1CTL1 = OARRIP; // General purpose mode, no Rail-to-Rail inputs + OA2CTL0 = OAP_3 + OAPM_1 + OAADC1; // OA2 enable power mode 1, OA2+ = DAC1, OA2O = P6.5, Select inputs, power mode + OA2CTL1 = OAFC_1 + OARRIP; // Unit gain Mode, no Rail-to-Rail inputs + __enable_interrupt(); // Enable global Interrupts +} //init + +void ClearLCD(void) +{ + int i; // + for( i = 0; i < 20; i++){ // Clear LCDMEM + LCDMEM[i] = 0; // + } +}//clear LCD + +int itobcd(int i) // Convert hex word to BCD. +{ + int bcd = 0; // + char j = 0; // + + while (i > 9) // + { + bcd |= ((i % 10) << j); // + i /= 10; // + j += 4; + } // + return (bcd | (i << j)); // Return converted value +}// itobcd(i) + + +int filterlp(int sample) // Lowpass FIR filter for EKG +{ + static int buflp[32]; // Reserve 32 loactions for circular buffering + static int offsetlp = 0; + long z; + int i; + + buflp[offsetlp] = sample; + z = mul16(coeffslp[8], buflp[(offsetlp - 8) & 0x1F]); + + __no_operation(); + + for (i = 0; i < 8; i++){ + z += mul16(coeffslp[i], buflp[(offsetlp - i) & 0x1F] + buflp[(offsetlp - 16 + i) & 0x1F]); + } + + offsetlp = (offsetlp + 1) & 0x1F; + return z >> 15; // Return filter output +}// int filter + +int filterhp(int samplehp) // Highpass FIR filter for hear rate +{ + static int bufhp[32]; // Reserve 32 loactions for circular buffering + static int offsethp = 0; + long z; + int i; + + bufhp[offsethp] = samplehp; + z = mul16(coeffshp[8], bufhp[(offsethp - 8) & 0x1F]); + + for (i = 0; i < 8; i++){ + z += mul16(coeffshp[i], bufhp[(offsethp - i) & 0x1F] + bufhp[(offsethp - 16 + i) & 0x1F]); + } + + offsethp = (offsethp + 1) & 0x1F; + return z >> 15; // Return filter output +}// int filterhp + +long mul16(register int x, register int y) +{ + return ((long)x) * y; +} + + +#pragma vector = PORT2_VECTOR +__interrupt void Port2ISR (void) +{ + P2IFG = 0; +}//Push buttons unused + +#pragma vector = ADC_VECTOR // ADC12 ISR +__interrupt void ADC12ISR (void) +{ + Datain = ADC12MEM0; // Store converted value in Datain + __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0 on return +}// ADC12ISR + + diff --git a/mod_ekg_psychose/gcc/Makefile b/mod_ekg_psychose/gcc/Makefile new file mode 100644 index 0000000..08dfffa --- /dev/null +++ b/mod_ekg_psychose/gcc/Makefile @@ -0,0 +1,91 @@ +# +# Makefile for msp430 +# +# 'make' builds everything +# 'make clean' deletes everything except source files and Makefile +# You need to set TARGET, MCU and SOURCES for your project. +# TARGET is the name of the executable file to be produced +# $(TARGET).elf $(TARGET).hex and $(TARGET).txt nad $(TARGET).map are all generated. +# The TXT file is used for BSL loading, the ELF can be used for JTAG use +# +TARGET = mod-pulse-ekg +MCU = msp430fg439 + +# List all the source files here +# eg if you have a source file foo.c then list it here +SOURCES = MOD-EKG_DemoSoft.c +# Include are located in the Include directory +INCLUDES = -IInclude + +# Add or subtract whatever MSPGCC flags you want. There are plenty more +####################################################################################### +CFLAGS = -mmcu=$(MCU) -g -Os -Wall -Wunused $(INCLUDES) +ASFLAGS = -mmcu=$(MCU) -x assembler-with-cpp -Wa,-gstabs +LDFLAGS = -mmcu=$(MCU) -Wl,-Map=$(TARGET).map -lm -lfp -pipe +######################################################################################## +CC = msp430-gcc +LD = msp430-ld +AR = msp430-ar +AS = msp430-gcc +GASP = msp430-gasp +NM = msp430-nm +OBJCOPY = msp430-objcopy +RANLIB = msp430-ranlib +STRIP = msp430-strip +SIZE = msp430-size +READELF = msp430-readelf +MAKETXT = srec_cat +CP = cp -p +RM = rm -f +MV = mv +######################################################################################## +# the file which will include dependencies + +DEPEND = $(SOURCES:.c=.d) + +# all the object files +OBJECTS = $(SOURCES:.c=.o) + +#all: $(TARGET).elf $(TARGET).hex $(TARGET).txt +all: $(TARGET).elf $(TARGET).hex +$(TARGET).elf: $(OBJECTS) + echo "Linking $@" + $(CC) $(OBJECTS) $(LDFLAGS) $(LIBS) -o $@ + echo + echo ">>>> Size of Firmware <<<<" + $(SIZE) $(TARGET).elf + echo + +%.hex: %.elf + $(OBJCOPY) -O ihex $< $@ + +#%.txt: %.hex +# $(MAKETXT) -O $@ -TITXT $< -I +# unix2dos $(TARGET).txt +# The above line is required for the DOS based TI BSL tool to be able to read the txt file generated from linux/unix systems. + +%.o: %.c + echo "Compiling $<" + $(CC) -c $(CFLAGS) -o $@ $< + +# rule for making assembler source listing, to see the code +%.lst: %.c + $(CC) -c $(ASFLAGS) -Wa,-anlhd $< > $@ + +# include the dependencies unless we're going to clean, then forget about them. +ifneq ($(MAKECMDGOALS), clean) +-include $(DEPEND) +endif +# dependencies file +# includes also considered, since some of these are our own +# (otherwise use -MM instead of -M) +%.d: %.c + echo "Generating dependencies $@ from $<" + $(CC) -M ${CFLAGS} $< >$@ +.SILENT: +.PHONY: clean +clean: + -$(RM) $(OBJECTS) + -$(RM) $(TARGET).* + -$(RM) $(SOURCES:.c=.lst) + -$(RM) $(DEPEND) diff --git a/mod_ekg_psychose/gcc/make.sh b/mod_ekg_psychose/gcc/make.sh new file mode 100755 index 0000000..736a772 --- /dev/null +++ b/mod_ekg_psychose/gcc/make.sh @@ -0,0 +1,2 @@ +msp430-gcc -I/usr/msp430/include -Wall mod_ -mmcu=msp430fg439 -o mod-ekg-pyschose -L /usr/msp430/lib/ldscripts/msp430fg439 -lm -lfp -pipe + diff --git a/mod_ekg_psychose/iar/Backup of MOD-EKG_DemoSoft.ewd b/mod_ekg_psychose/iar/Backup of MOD-EKG_DemoSoft.ewd new file mode 100644 index 0000000..a46677a --- /dev/null +++ b/mod_ekg_psychose/iar/Backup of MOD-EKG_DemoSoft.ewd @@ -0,0 +1,571 @@ + + + + 2 + + Debug + + MSP430 + + 1 + + C-SPY + 4 + + 23 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 430FET + 1 + + 15 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SIM430 + 1 + + 3 + 1 + 1 + + + + + + + + $TOOLKIT_DIR$\plugins\Lcd\lcd.ewplugin + 1 + + + $TOOLKIT_DIR$\plugins\rtos\embOS\embOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\PowerPac\PowerPacRTOS.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-KA-CSpy.ewplugin + 0 + + + $EW_DIR$\common\plugins\CodeCoverage\CodeCoverage.ENU.ewplugin + 1 + + + $EW_DIR$\common\plugins\Orti\Orti.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\Profiling\Profiling.ENU.ewplugin + 1 + + + $EW_DIR$\common\plugins\Stack\Stack.ENU.ewplugin + 1 + + + $EW_DIR$\common\plugins\SymList\SymList.ENU.ewplugin + 1 + + + + + Release + + MSP430 + + 0 + + C-SPY + 4 + + 23 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 430FET + 1 + + 15 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SIM430 + 1 + + 3 + 1 + 0 + + + + + + + + $TOOLKIT_DIR$\plugins\Lcd\lcd.ewplugin + 1 + + + $TOOLKIT_DIR$\plugins\rtos\embOS\embOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\PowerPac\PowerPacRTOS.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-KA-CSpy.ewplugin + 0 + + + $EW_DIR$\common\plugins\CodeCoverage\CodeCoverage.ENU.ewplugin + 1 + + + $EW_DIR$\common\plugins\Orti\Orti.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\Profiling\Profiling.ENU.ewplugin + 1 + + + $EW_DIR$\common\plugins\Stack\Stack.ENU.ewplugin + 1 + + + $EW_DIR$\common\plugins\SymList\SymList.ENU.ewplugin + 1 + + + + + + diff --git a/mod_ekg_psychose/iar/Backup of MOD-EKG_DemoSoft.ewp b/mod_ekg_psychose/iar/Backup of MOD-EKG_DemoSoft.ewp new file mode 100644 index 0000000..36b8bcb --- /dev/null +++ b/mod_ekg_psychose/iar/Backup of MOD-EKG_DemoSoft.ewp @@ -0,0 +1,1744 @@ + + + + 2 + + Debug + + MSP430 + + 1 + + General + 7 + + 26 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICC430 + 4 + + 28 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A430 + 4 + + 13 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + XLINK + 4 + + 22 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XAR + 4 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + + Release + + MSP430 + + 0 + + General + 7 + + 26 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICC430 + 4 + + 28 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A430 + 4 + + 13 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + XLINK + 4 + + 22 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XAR + 4 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + + $PROJ_DIR$\MOD-EKG_DemoSoft.c + + + $PROJ_DIR$\mul.s43 + + + + diff --git a/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.c b/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.c new file mode 100644 index 0000000..6db8d34 --- /dev/null +++ b/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.c @@ -0,0 +1,436 @@ +//***************************************************************************** +// MSP430FG439-Heart Rate Monitor Demo +// +// Description; Uses one Instrumentation Amplifier INA321 and the three +// internal opamps of the MSP430FG439 +// +// Murugavel Raju +// Texas Instruments, Inc +// October 2004 +// Edited by: M Morales, November 2008 +// * Updated to non-depracated intrinsic functions +// * Changed spacing for legibility +// Edited by: +// Penko T. Bozhkov - Olimex LTD, 05.10.2012 +// * RTC capcitors changed according to Olimex's crystall requirements +// * Olimex LCD definitions are added and heart rate is visualized at 2 places on LCD +// Built with IAR Embedded Workbench Version: 4.21 +//***************************************************************************** +//***************************************************************************** +// THIS PROGRAM IS PROVIDED "AS IS". TI MAKES NO WARRANTIES OR +// REPRESENTATIONS, EITHER EXPRESS, IMPLIED OR STATUTORY, +// INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR +// COMPLETENESS OF RESPONSES, RESULTS AND LACK OF NEGLIGENCE. +// TI DISCLAIMS ANY WARRANTY OF TITLE, QUIET ENJOYMENT, QUIET +// POSSESSION, AND NON-INFRINGEMENT OF ANY THIRD PARTY +// INTELLECTUAL PROPERTY RIGHTS WITH REGARD TO THE PROGRAM OR +// YOUR USE OF THE PROGRAM. +// +// IN NO EVENT SHALL TI BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +// CONSEQUENTIAL OR INDIRECT DAMAGES, HOWEVER CAUSED, ON ANY +// THEORY OF LIABILITY AND WHETHER OR NOT TI HAS BEEN ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGES, ARISING IN ANY WAY OUT +// OF THIS AGREEMENT, THE PROGRAM, OR YOUR USE OF THE PROGRAM. +// EXCLUDED DAMAGES INCLUDE, BUT ARE NOT LIMITED TO, COST OF +// REMOVAL OR REINSTALLATION, COMPUTER TIME, LABOR COSTS, LOSS +// OF GOODWILL, LOSS OF PROFITS, LOSS OF SAVINGS, OR LOSS OF +// USE OR INTERRUPTION OF BUSINESS. IN NO EVENT WILL TI'S +// AGGREGATE LIABILITY UNDER THIS AGREEMENT OR ARISING OUT OF +// YOUR USE OF THE PROGRAM EXCEED FIVE HUNDRED DOLLARS +// (U.S.$500). +// +// Unless otherwise stated, the Program written and copyrighted +// by Texas Instruments is distributed as "freeware". You may, +// only under TI's copyright in the Program, use and modify the +// Program without any charge or restriction. You may +// distribute to third parties, provided that you transfer a +// copy of this license to the third party and the third party +// agrees to these terms by its first use of the Program. You +// must reproduce the copyright notice and any other legend of +// ownership on each copy or partial copy, of the Program. +// +// You acknowledge and agree that the Program contains +// copyrighted material, trade secrets and other TI proprietary +// information and is protected by copyright laws, +// international copyright treaties, and trade secret laws, as +// well as other intellectual property laws. To protect TI's +// rights in the Program, you agree not to decompile, reverse +// engineer, disassemble or otherwise translate any object code +// versions of the Program to a human-readable form. You agree +// that in no event will you alter, remove or destroy any +// copyright notice included in the Program. TI reserves all +// rights not specifically granted under this license. Except +// as specifically provided herein, nothing in this agreement +// shall be construed as conferring by implication, estoppel, +// or otherwise, upon you, any license or other right under any +// TI patents, copyrights or trade secrets. +// +// You may not use the Program in non-TI devices. +//***************************************************************************** +#include +#include "math.h" +//defines +#define PB_2_0 (1 << 0) // Push Button on P2.0 +#define PB_2_1 (1 << 1) // Push Button on P2.1 + +// variables declaration +static char beats; +int i=0, first_detection=0; +long result = 0; +int Datain, Dataout, Dataout_pulse, pulseperiod, counter, heartrate; +int Heart_Rate_Buffer[] = {0,0,0,0}; +// Lowpass FIR filter coefficients for 17 taps to filter > 30Hz +static const int coeffslp[9] = { + 5225, 5175, 7255, 9453, 11595, 13507, 15016, 15983, 16315 }; +// Highpass FIR filter coefficients for 17 taps to filter < 2Hz +static const int coeffshp[9] = { + -763, -1267, -1091, -1867, -1969, -2507, -2619, -2911, 29908 }; + +// ******************************************* +// Definitions related to Olimex LCD Digits!!!! +// ******************************************* +// Definitions for Olimex LCD digits 10 and 11 +#define a 0x10 +#define b 0x01 +#define c 0x04 +#define d 0x08 +#define e 0x40 +#define f 0x20 +#define g 0x02 +#define h 0x80 +// Character generator definition for display digits 10 and 11 +const char char_gen_10_11[] = { + a+b+c+d+e+f, // 0 Displays "0" + b+c, // 1 Displays "1" + a+b+d+e+g, // 2 Displays "2" + a+b+c+d+g, // 3 Displays "3" + b+c+f+g, // 4 Displays "4" + a+c+d+f+g, // 5 Displays "5" + a+c+d+e+f+g, // 6 Displays "6" + a+b+c, // 7 Displays "7" + a+b+c+d+e+f+g, // 8 Displays "8" + a+b+c+d+f+g, // 9 Displays "9" +}; +// undefines +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h + +// Definitions for Olimex LCD digits 8 and 9 +#define a 0x01 +#define b 0x02 +#define c 0x04 +#define d 0x80 +#define e 0x40 +#define f 0x10 +#define g 0x20 +#define h 0x08 +// Character generator definition for display digits 8 and 9 +const char char_gen_8_9[] = { + a+b+c+d+e+f, // 0 Displays "0" + b+c, // 1 Displays "1" + a+b+d+e+g, // 2 Displays "2" + a+b+c+d+g, // 3 Displays "3" + b+c+f+g, // 4 Displays "4" + a+c+d+f+g, // 5 Displays "5" + a+c+d+e+f+g, // 6 Displays "6" + a+b+c, // 7 Displays "7" + a+b+c+d+e+f+g, // 8 Displays "8" + a+b+c+d+f+g, // 9 Displays "9" +}; +// undefines +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h + +// Definitions for Olimex LCD digits 1 to 7. Here each digit definition require 2 bytes +#define a 0x0080 +#define b 0x0040 +#define c 0x0020 +#define d 0x0010 +#define e 0x2000 +#define f 0x4000 +#define g 0x0402 +#define h 0x1000 +// Character generator definition for display digits 1 to 7 +const int char_gen_1_7[] = { + a+b+c+d+e+f, // 0 Displays "0" + b+c, // 1 Displays "1" + a+b+d+e+g, // 2 Displays "2" + a+b+c+d+g, // 3 Displays "3" + b+c+f+g, // 4 Displays "4" + a+c+d+f+g, // 5 Displays "5" + a+c+d+e+f+g, // 6 Displays "6" + a+b+c, // 7 Displays "7" + a+b+c+d+e+f+g, // 8 Displays "8" + a+b+c+d+f+g, // 9 Displays "9" +}; +// undefines +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h + + + +// function prototypes +void Init(void); // Initializes device for the application +void ClearLCD(void); // Clears the LCD memory +int filterlp(int); // 17 tap lowpass FIR filter +int filterhp(int); // 17 tap highpass FIR filter +long mul16(register int x, register int y); // 16-bit signed multiplication +int itobcd(int i); // 16-bit hex to bcd conversion +// main function +void main(void) +{ + Init(); // Initialize device for the application + LCDMEM[7] = 0x80; // Turn on LCD's Olimex row!!! + +/* + // For debug purpose only! + for(unsigned char j=0;j<10;j++){ + LCDMEM[2] = char_gen_10_11[j]; // LCD -> Digit 11 + LCDMEM[3] = char_gen_10_11[j]; // LCD -> Digit 10 + LCDMEM[4] = char_gen_8_9[j]; // LCD -> Digit 9 + LCDMEM[5] = char_gen_8_9[j]; // LCD -> Digit 8 + + LCDMEM[7] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 7 High Byte + LCDMEM[6] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 7 Low Byte + LCDMEM[9] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 6 High Byte + LCDMEM[8] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 6 Low Byte + LCDMEM[11] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 5 High Byte + LCDMEM[10] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 5 Low Byte + //LCDMEM[13] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 4 High Byte + //LCDMEM[12] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 4 Low Byte + //LCDMEM[15] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 3 High Byte + //LCDMEM[14] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 3 Low Byte + //LCDMEM[17] = ((char)(char_gen_1_7[j]>>8)); // LCD -> Digit 2 High Byte + //LCDMEM[16] = ((char)(char_gen_1_7[j]&0x00FF)); // LCD -> Digit 2 Low Byte + } +*/ + + while(1) + { + __bis_SR_register(LPM0_bits); // Enter LPM0 needed for UART TX completion + __no_operation(); + + Dataout = filterlp(Datain); // Lowpass FIR filter for filtering out 60Hz + Dataout_pulse = filterhp(Dataout)-128; // Highpass FIR filter to filter muscle artifacts + Dataout = Dataout >> 6; // Scale Dataout to use scope program + if(Dataout > 255) Dataout = 255; // Set boundary 255 max + if(Dataout < 0) Dataout = 0; // Set boundary 0 min + //DAC12_0DAT = Dataout; // For scope display + //TXBUF0 = Dataout; // Transmit via UART0 for Scope display + + // send the data as ascii values + TXBUF0 = (Dataout / 100) + 48; // hundreds + while (!(IFG1 & UTXIFG0)); // wait for transmission + TXBUF0 = ((Dataout / 10) % 10 ) + 48; // tens + while (!(IFG1 & UTXIFG0)); + TXBUF0 = ((Dataout / 1) % 10 ) + 48; // ones + while (!(IFG1 & UTXIFG0)); + TXBUF0 = 32; // send a blank + while (!(IFG1 & UTXIFG0)); + TXBUF0 = ((heartrate & 0xf00) >> 8) + 48; + while (!(IFG1 & UTXIFG0)); + TXBUF0 = ((heartrate & 0xf0) >> 4) + 48; + while (!(IFG1 & UTXIFG0)); + TXBUF0 = (heartrate & 0x0f) + 48; + while (!(IFG1 & UTXIFG0)); + TXBUF0 = 10; // send a \n + + + counter++; // Debounce counter + pulseperiod++; // Pulse period counter + if (Dataout_pulse > 110) // Check if above threshold (48) + { + LCDMEM[1] = 0xF0; // Heart beat detected enable "<^>" on LCD + counter = 0; // Reset debounce counter + } + if (counter == 128) // Allow 128 sample debounce time + { + LCDMEM[1] = 0x00; // Disable "<^>" on LCD for blinking effect + beats++; + if (beats == 3) + { + beats = 0; + // heartrate = itobcd(30720/pulseperiod); // Calculate beat to beat heart rate per min + //heartrate = itobcd(92160/pulseperiod); // Calculate 3 beat average heart rate per min + heartrate = itobcd(50720/pulseperiod); // Calculate 3 beat average heart rate per min + //heartrate = (92160/pulseperiod); // Calculate 3 beat average heart rate per min + pulseperiod = 0; // Reset pulse period for next measurement + + ///* + LCDMEM[2] = char_gen_10_11[heartrate & 0x0f]; // Display current heart rate units -> LCD Digit 11 + LCDMEM[3] = char_gen_10_11[(heartrate & 0xf0) >> 4]; // tens -> LCD Digit 10 + LCDMEM[4] = char_gen_8_9[(heartrate & 0xf00) >> 8]; // hundreds -> LCD Digit 9 + + LCDMEM[7] = ((char)(char_gen_1_7[heartrate & 0x0f]>>8)); // LCD -> Digit 7 High Byte + LCDMEM[6] = ((char)(char_gen_1_7[heartrate & 0x0f]&0x00FF)); // LCD -> Digit 7 Low Byte + LCDMEM[9] = ((char)(char_gen_1_7[((heartrate & 0xf0) >> 4)]>>8)); // LCD -> Digit 6 High Byte + LCDMEM[8] = ((char)(char_gen_1_7[((heartrate & 0xf0) >> 4)]&0x00FF)); // LCD -> Digit 6 Low Byte + LCDMEM[11] = ((char)(char_gen_1_7[((heartrate & 0xf00) >> 8)]>>8)); // LCD -> Digit 5 High Byte + LCDMEM[10] = ((char)(char_gen_1_7[((heartrate & 0xf00) >> 8)]&0x00FF)); // LCD -> Digit 5 Low Byte + //*/ + + } + } + } +}//main + +// Initialization function +void Init( void ) +{ + FLL_CTL0 |= XCAP10PF; // Set load capacitance for xtal + WDTCTL = WDTPW | WDTHOLD; // Disable the Watchdog + while ( LFOF & FLL_CTL0); // wait for watch crystal to stabilize + SCFQCTL = 63; // 32 x 32768 x 2 = 2.097152MHz + BTCTL = BT_fLCD_DIV128; // Set LCD frame freq = ACLK/128 +// Initialize and enable LCD peripheral + ClearLCD(); // Clear LCD memory + LCDCTL = LCDSG0_3 + LCD4MUX + LCDON ; // 4mux LCD, segs0-23 enabled +// Initialize and enable GPIO ports + P1OUT = 0x00 + BIT3; // Clear P1OUT register, INA turned ON + P1DIR = 0x3f; // Unused pins as outputs, Comparator pins as inputs + P2OUT = 0x00; // Clear P2OUT register + P2DIR = 0xff; // Unused pins as outputs + P2DIR = ~(PB_2_0+PB_2_1); // P2.0 and P2.1 push buttons + P2IES = 0x00; // Interrupt edge low to high transition + P2IFG = 0x00; // Clear pending P2 interrupts + P2IE = PB_2_0 | PB_2_1; // Enable intterupts for push buttons + P3OUT = 0x00; // Clear P3OUT register + P3DIR = 0x0f; // Unused pins as outputs except P3.<4-7> -> For the new LCD's received at ~04.10.2012 this must be inputs!! + P4OUT = 0x00; // Clear P4OUT register + P4DIR = 0xff; // Unused pins as outputs + P5OUT = 0x00; // Clear P5OUT register + P5DIR = 0xff; // Unused pins as outputs + P5SEL = 0xfc; // Set Rxx and COM pins for LCD + P6OUT = 0x00; // Clear P6OUT register + P6SEL = 0xff; // P6 = Analog +// Initialize and enable UART + P2SEL|=BIT4; // P2.4 = TXD + UCTL0 |= SWRST; // UART SWRST = 1 + ME1 |= UTXE0; // Enable UART0 TXD + UCTL0 |= CHAR; // 8-bit char, SWRST=1 + UTCTL0 |= SSEL1; // UCLK = SMCLK + UBR00 = 18; // 115200 from 2.097152MHz + UBR10 = 0; + UMCTL0 = 0x2c; // Modulation = 0.2044 + UCTL0 &= ~SWRST; // UART SWRST = 0, enable UART + IFG1 &= ~UTXIFG0; +// Initialize and enable ADC12 + ADC12CTL0 = ADC12ON + SHT0_4 + REFON + REF2_5V; + // ADC12 ON, Reference = 2.5V for DAC0 + ADC12CTL1 = SHP + SHS_1 + CONSEQ_2; // Use sampling timer, TA1 trigger + ADC12MCTL0 = INCH_1 + SREF_1; // Vref, channel = 1 = OA0 Out + ADC12IE = BIT0; // Enable interrupt for ADC12 MEM0 + ADC12CTL0 |= ENC; // Enable conversions +// Initialize and enable Timer_A + TACTL = TASSEL0 + MC_1 + TACLR; // ACLK, Clear TAR, Up Mode + TACCTL1 = OUTMOD_2; // Set / Reset + TACCR0 = 63; // 512 samples per second + TACCR1 = 15; // +// Initialize and enable DAC12x + DAC12_0CTL = DAC12OPS + DAC12CALON + DAC12IR + DAC12AMP_2 + DAC12ENC;// DAC0 enable + DAC12_1CTL = DAC12CALON + DAC12IR + DAC12AMP_2 + DAC12ENC; // DAC1 enable + DAC12_1DAT = 0x099A; // Offset level = 1.5V for op amp bias +// Initialize and enable opamps + OA0CTL0 = OAP_1 + OAPM_1 + OAADC1; // OA0 enable power mode 1, OA0- = P6.0, 0A0+ = P6.2, OA0O = P6.1 + OA0CTL1 = OARRIP; // General purpose mode, no Rail-to-Rail inputs + OA1CTL0 = OAP_3 + OAPM_1 + OAADC1; // OA1 enable power mode 1, OA1- = P6.4, OA1+ = DAC1, OA1O = P6.3 + OA1CTL1 = OARRIP; // General purpose mode, no Rail-to-Rail inputs + OA2CTL0 = OAP_3 + OAPM_1 + OAADC1; // OA2 enable power mode 1, OA2+ = DAC1, OA2O = P6.5, Select inputs, power mode + OA2CTL1 = OAFC_1 + OARRIP; // Unit gain Mode, no Rail-to-Rail inputs + __enable_interrupt(); // Enable global Interrupts +} //init + +void ClearLCD(void) +{ + int i; // + for( i = 0; i < 20; i++){ // Clear LCDMEM + LCDMEM[i] = 0; // + } +}//clear LCD + +int itobcd(int i) // Convert hex word to BCD. +{ + int bcd = 0; // + char j = 0; // + + while (i > 9) // + { + bcd |= ((i % 10) << j); // + i /= 10; // + j += 4; + } // + return (bcd | (i << j)); // Return converted value +}// itobcd(i) + + +int filterlp(int sample) // Lowpass FIR filter for EKG +{ + static int buflp[32]; // Reserve 32 loactions for circular buffering + static int offsetlp = 0; + long z; + int i; + + buflp[offsetlp] = sample; + z = mul16(coeffslp[8], buflp[(offsetlp - 8) & 0x1F]); + + __no_operation(); + + for (i = 0; i < 8; i++){ + z += mul16(coeffslp[i], buflp[(offsetlp - i) & 0x1F] + buflp[(offsetlp - 16 + i) & 0x1F]); + } + + offsetlp = (offsetlp + 1) & 0x1F; + return z >> 15; // Return filter output +}// int filter + +int filterhp(int samplehp) // Highpass FIR filter for hear rate +{ + static int bufhp[32]; // Reserve 32 loactions for circular buffering + static int offsethp = 0; + long z; + int i; + + bufhp[offsethp] = samplehp; + z = mul16(coeffshp[8], bufhp[(offsethp - 8) & 0x1F]); + + for (i = 0; i < 8; i++){ + z += mul16(coeffshp[i], bufhp[(offsethp - i) & 0x1F] + bufhp[(offsethp - 16 + i) & 0x1F]); + } + + offsethp = (offsethp + 1) & 0x1F; + return z >> 15; // Return filter output +}// int filterhp + +#pragma vector = PORT2_VECTOR +__interrupt void Port2ISR (void) +{ + P2IFG = 0; +}//Push buttons unused + +#pragma vector = ADC_VECTOR // ADC12 ISR +__interrupt void ADC12ISR (void) +{ + Datain = ADC12MEM0; // Store converted value in Datain + __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0 on return +}// ADC12ISR + + diff --git a/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.dep b/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.dep new file mode 100644 index 0000000..7466b10 --- /dev/null +++ b/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.dep @@ -0,0 +1,128 @@ + + + + 2 + 475830264 + + Debug + + $PROJ_DIR$\MOD-EKG_DemoSoft.c + $TOOLKIT_DIR$\inc\msp430xg43x.h + $PROJ_DIR$\Debug\Exe\MOD-EKG_DemoSoft.d43 + $PROJ_DIR$\Debug\Obj\MOD-EKG_DemoSoft.pbd + $PROJ_DIR$\mul.s43 + $TOOLKIT_DIR$\inc\intrinsics.h + $PROJ_DIR$\Debug\Obj\MOD-EKG_DemoSoft.pbi + $TOOLKIT_DIR$\lib\dlib\dl430fn.h + $PROJ_DIR$\Debug\Obj\MOD-EKG_DemoSoft.r43 + $TOOLKIT_DIR$\lib\dlib\dl430fn.r43 + $TOOLKIT_DIR$\inc\in430.h + $PROJ_DIR$\Debug\Exe\MOD-EKG_DemoSoft.ulp + $PROJ_DIR$\Debug\Obj\mul.r43 + $TOOLKIT_DIR$\inc\dlib\c\ymath.h + $TOOLKIT_DIR$\inc\dlib\c\math.h + $TOOLKIT_DIR$\inc\dlib\c\ycheck.h + $TOOLKIT_DIR$\inc\dlib\c\DLib_Defaults.h + $TOOLKIT_DIR$\inc\dlib\c\xencoding_limits.h + $TOOLKIT_DIR$\inc\dlib\c\yvals.h + $TOOLKIT_DIR$\inc\dlib\c\DLib_Threads.h + $TOOLKIT_DIR$\inc\dlib\c\DLib_Product.h + $TOOLKIT_DIR$\inc\dlib\c\xtgmath.h + $TOOLKIT_DIR$\config\linker\lnk430fg439.xcl + $PROJ_DIR$\Debug\Exe\MOD-EKG_DemoSoft.a43 + $PROJ_DIR$\Debug\Exe\MOD-EKG_DemoSoft.hex + $PROJ_DIR$\Debug\Exe\ekg.hex + + + $PROJ_DIR$\MOD-EKG_DemoSoft.c + + + BICOMP + 6 + + + ICC430 + 8 + + + + + BICOMP + 1 10 5 14 15 13 18 16 7 20 17 19 21 + + + ICC430 + 1 10 5 14 15 13 18 16 7 20 17 19 21 + + + + + $PROJ_DIR$\Debug\Exe\MOD-EKG_DemoSoft.d43 + + + XLINK + 22 8 12 9 + + + + + $PROJ_DIR$\mul.s43 + + + A430 + 12 + + + + + $PROJ_DIR$\Debug\Exe\MOD-EKG_DemoSoft.a43 + + + XLINK + 22 8 12 9 + + + + + $PROJ_DIR$\Debug\Exe\MOD-EKG_DemoSoft.hex + + + XLINK + 22 8 12 9 + + + + + [ROOT_NODE] + + + XLINK + 25 + + + ULP430 + 11 + + + + + $PROJ_DIR$\Debug\Exe\ekg.hex + + + XLINK + 22 8 12 9 + + + + + + Release + + + [MULTI_TOOL] + XLINK + + + + + diff --git a/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.ewd b/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.ewd new file mode 100644 index 0000000..8e50d56 --- /dev/null +++ b/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.ewd @@ -0,0 +1,819 @@ + + + + 2 + + Debug + + MSP430 + + 1 + + C-SPY + 4 + + 27 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 430FET + 1 + + 27 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SIM430 + 1 + + 4 + 1 + 1 + + + + + + + + + + + $TOOLKIT_DIR$\plugins\Lcd\lcd.ewplugin + 1 + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxTinyPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\embOS\embOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\OpenRTOS\OpenRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\PowerPac\PowerPacRTOS.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SafeRTOS\SafeRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\TI-RTOS\tirtosplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-286-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-III\uCOS-III-KA-CSpy.ewplugin + 0 + + + $EW_DIR$\common\plugins\CodeCoverage\CodeCoverage.ENU.ewplugin + 1 + + + $EW_DIR$\common\plugins\Orti\Orti.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\SymList\SymList.ENU.ewplugin + 1 + + + $EW_DIR$\common\plugins\uCProbe\uCProbePlugin.ENU.ewplugin + 0 + + + + + Release + + MSP430 + + 0 + + C-SPY + 4 + + 27 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 430FET + 1 + + 27 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SIM430 + 1 + + 4 + 1 + 0 + + + + + + + + + + + $TOOLKIT_DIR$\plugins\Lcd\lcd.ewplugin + 1 + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxTinyPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\embOS\embOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\OpenRTOS\OpenRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\PowerPac\PowerPacRTOS.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SafeRTOS\SafeRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\TI-RTOS\tirtosplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-286-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-III\uCOS-III-KA-CSpy.ewplugin + 0 + + + $EW_DIR$\common\plugins\CodeCoverage\CodeCoverage.ENU.ewplugin + 1 + + + $EW_DIR$\common\plugins\Orti\Orti.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\SymList\SymList.ENU.ewplugin + 1 + + + $EW_DIR$\common\plugins\uCProbe\uCProbePlugin.ENU.ewplugin + 0 + + + + + + diff --git a/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.ewp b/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.ewp new file mode 100644 index 0000000..774bc6b --- /dev/null +++ b/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.ewp @@ -0,0 +1,2161 @@ + + + + 2 + + Debug + + MSP430 + + 1 + + General + 14 + + 32 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICC430 + 4 + + 37 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A430 + 5 + + 14 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + XLINK + 4 + + 27 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XAR + 4 + + 0 + 1 + 1 + + + + + + + ULP430 + 1 + + 1 + 1 + 1 + + + + + + + + + BILINK + 0 + + + + + Release + + MSP430 + + 0 + + General + 14 + + 32 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICC430 + 4 + + 37 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A430 + 5 + + 14 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + XLINK + 4 + + 27 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XAR + 4 + + 0 + 1 + 0 + + + + + + + ULP430 + 1 + + 1 + 1 + 0 + + + + + + + + + BILINK + 0 + + + + + $PROJ_DIR$\MOD-EKG_DemoSoft.c + + + $PROJ_DIR$\mul.s43 + + + + diff --git a/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.eww b/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.eww new file mode 100644 index 0000000..a488007 --- /dev/null +++ b/mod_ekg_psychose/iar/MOD-EKG_DemoSoft.eww @@ -0,0 +1,10 @@ + + + + + $WS_DIR$\MOD-EKG_DemoSoft.ewp + + + + + diff --git a/mod_ekg_psychose/iar/mul.s43 b/mod_ekg_psychose/iar/mul.s43 new file mode 100644 index 0000000..38de4bf --- /dev/null +++ b/mod_ekg_psychose/iar/mul.s43 @@ -0,0 +1,110 @@ +// +//16x16=>32 multiply +//long mul16(register int x, register int y) +// +// Edited by: M Morales, November 2008 +// * Updated calling conventions in support of IAR compiler >= 4.x +//***************************************************************************** +// THIS PROGRAM IS PROVIDED "AS IS". TI MAKES NO WARRANTIES OR +// REPRESENTATIONS, EITHER EXPRESS, IMPLIED OR STATUTORY, +// INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR +// COMPLETENESS OF RESPONSES, RESULTS AND LACK OF NEGLIGENCE. +// TI DISCLAIMS ANY WARRANTY OF TITLE, QUIET ENJOYMENT, QUIET +// POSSESSION, AND NON-INFRINGEMENT OF ANY THIRD PARTY +// INTELLECTUAL PROPERTY RIGHTS WITH REGARD TO THE PROGRAM OR +// YOUR USE OF THE PROGRAM. +// +// IN NO EVENT SHALL TI BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +// CONSEQUENTIAL OR INDIRECT DAMAGES, HOWEVER CAUSED, ON ANY +// THEORY OF LIABILITY AND WHETHER OR NOT TI HAS BEEN ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGES, ARISING IN ANY WAY OUT +// OF THIS AGREEMENT, THE PROGRAM, OR YOUR USE OF THE PROGRAM. +// EXCLUDED DAMAGES INCLUDE, BUT ARE NOT LIMITED TO, COST OF +// REMOVAL OR REINSTALLATION, COMPUTER TIME, LABOR COSTS, LOSS +// OF GOODWILL, LOSS OF PROFITS, LOSS OF SAVINGS, OR LOSS OF +// USE OR INTERRUPTION OF BUSINESS. IN NO EVENT WILL TI'S +// AGGREGATE LIABILITY UNDER THIS AGREEMENT OR ARISING OUT OF +// YOUR USE OF THE PROGRAM EXCEED FIVE HUNDRED DOLLARS +// (U.S.$500). +// +// Unless otherwise stated, the Program written and copyrighted +// by Texas Instruments is distributed as "freeware". You may, +// only under TI's copyright in the Program, use and modify the +// Program without any charge or restriction. You may +// distribute to third parties, provided that you transfer a +// copy of this license to the third party and the third party +// agrees to these terms by its first use of the Program. You +// must reproduce the copyright notice and any other legend of +// ownership on each copy or partial copy, of the Program. +// +// You acknowledge and agree that the Program contains +// copyrighted material, trade secrets and other TI proprietary +// information and is protected by copyright laws, +// international copyright treaties, and trade secret laws, as +// well as other intellectual property laws. To protect TI's +// rights in the Program, you agree not to decompile, reverse +// engineer, disassemble or otherwise translate any object code +// versions of the Program to a human-readable form. You agree +// that in no event will you alter, remove or destroy any +// copyright notice included in the Program. TI reserves all +// rights not specifically granted under this license. Except +// as specifically provided herein, nothing in this agreement +// shall be construed as conferring by implication, estoppel, +// or otherwise, upon you, any license or other right under any +// TI patents, copyrights or trade secrets. +// +// You may not use the Program in non-TI devices. +//***************************************************************************** + public mul16 + + RSEG CODE +mul16 + +#define x1 r9 +#define z0 r14 +#define z1 r15 +#define x r12 +#define y r13 + + push r9 + + clr z0 + mov z0,z1 + mov z0,x1 + tst x + jge xbooth_2 + mov #-1,x1 + jmp xbooth_2 + +xbooth_6 + add x,z1 + addc x1,z0 +xbooth_1 + rla x + rlc x1 +xbooth_2 + rra y + jc xbooth_5 + jne xbooth_1 + jmp xbooth_4 + +xbooth_5 + sub x,z1 + subc x1,z0 +xbooth_3 + rla x + rlc x1 + rra y + jnc xbooth_6 + cmp #0FFFFh,y + jne xbooth_3 + +xbooth_4 + mov z1,r12 + mov z0,r13 + + pop r9 + + ret + end diff --git a/mod_ekg_psychose/iar/path.txt b/mod_ekg_psychose/iar/path.txt new file mode 100644 index 0000000..e69de29 diff --git a/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.Debug.cspy.bat b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.Debug.cspy.bat new file mode 100644 index 0000000..bdbe693 --- /dev/null +++ b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.Debug.cspy.bat @@ -0,0 +1,24 @@ +@REM This batch file has been generated by the IAR Embedded Workbench +@REM C-SPY Debugger, as an aid to preparing a command line for running +@REM the cspybat command line utility using the appropriate settings. +@REM +@REM Note that this file is generated every time a new debug session +@REM is initialized, so you may want to move or rename the file before +@REM making changes. +@REM +@REM You can launch cspybat by typing the name of this batch file followed +@REM by the name of the debug file (usually an ELF/DWARF or UBROF file). +@REM +@REM Read about available command line parameters in the C-SPY Debugging +@REM Guide. Hints about additional command line parameters that may be +@REM useful in specific cases: +@REM --download_only Downloads a code image without starting a debug +@REM session afterwards. +@REM --silent Omits the sign-on message. +@REM --timeout Limits the maximum allowed execution time. +@REM + + +"C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.0_2\common\bin\cspybat" "C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.0_2\430\bin\430proc.dll" "C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.0_2\430\bin\430fet.dll" %1 --plugin "C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.0_2\430\bin\430bat.dll" --backend -B "-p" "C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.0_2\430\config\debugger\MSP430FG439.ddf" "--iv_base" "0xFFE0" "-d" "fet" "--erase_main_and_info" "--derivative" "MSP430FG43x_F43x" "--protocol" "4wire" "--eem" "EMEX_LOW" "--port" "Automatic" "--connection" "olimex" "--settlingtime=0" "--msp430_dll" "msp430.dll" "--vccDefault" "3.3" "--jtag_speed" "medium" "--memtype" "F" + + diff --git a/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.cspy.bat b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.cspy.bat new file mode 100644 index 0000000..e68ae66 --- /dev/null +++ b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.cspy.bat @@ -0,0 +1,34 @@ +@REM This bat file has been generated by the IAR Embeddded Workbench +@REM C-SPY interactive debugger,as an aid to preparing a command +@REM line for running the cspybat command line utility with the +@REM appropriate settings. +@REM +@REM After making some adjustments to this file, you can launch cspybat +@REM by typing the name of this file followed by the name of the debug +@REM file (usually an ubrof file). Note that this file is generated +@REM every time a new debug session is initialized, so you may want to +@REM move or rename the file before making changes. +@REM +@REM Note: some command line arguments cannot be properly generated +@REM by this process. Specifically, the plugin which is responsible +@REM for the Terminal I/O window (and other C runtime functionality) +@REM comes in a special version for cspybat, and the name of that +@REM plugin dll is not known when generating this file. It resides in +@REM the $TOOLKIT_DIR$\bin folder and is usually called XXXbat.dll or +@REM XXXlibsupportbat.dll, where XXX is the name of the corresponding +@REM tool chain. Replace the '' parameter +@REM below with the appropriate file name. Other plugins loaded by +@REM C-SPY are usually not needed by, or will not work in, cspybat +@REM but they are listed at the end of this file for reference. + + +"C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\common\bin\cspybat" "C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\430\bin\430proc.dll" "C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\430\bin\430fet.dll" %1 --plugin "C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\430\bin\" --backend -B "-p" "C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\430\config\MSP430FG439.ddf" "--iv_base" "0xFFE0" "-d" "fet" "--erase_main_and_info" "--derivative" "MSP430FG439" "--vccvoltage=3.3" "--protocol" "4wire" "--eem" "EMEX_LOW" "--connection" "olimex" "--settlingtime=0" + + +@REM Loaded plugins: +@REM C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\430\bin\430libsupport.dll +@REM C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\430\plugins\lcd\lcd.dll +@REM C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\common\plugins\CodeCoverage\CodeCoverage.dll +@REM C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\common\plugins\Profiling\Profiling.dll +@REM C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\common\plugins\stack\stack.dll +@REM C:\Program Files\IAR Systems\IAR Embedded Workbench for MSP, v4.21 Evaluation version\common\plugins\SymList\SymList.dll diff --git a/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.dbgdt b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.dbgdt new file mode 100644 index 0000000..ad1fd54 --- /dev/null +++ b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.dbgdt @@ -0,0 +1,89 @@ + + + + + + + + + 201221 + + + + + + 2091524461 + + + + + + + 174272727 + + + + + + 100Disassembly_I05002011 + + + + + + + + + TabID-32631-32423 + Debug Log + Debug-Log + + + + TabID-32108-32433 + Build + Build + + + + + 0 + + + TabID-10611-32426 + Workspace + Workspace + + + MOD-EKG_DemoSoft + + + + 0 + + + TabID-21360-32430 + Disassembly + Disassembly + + + + + 0 + + + + + + TextEditor$WS_DIR$\MOD-EKG_DemoSoft.c00000249115951159500100000010000001 + + + + + + + iaridepm.enu1debuggergui.enu1430fet1-2-2562248-2-2250175156250211353156250681159-2-2562248-2-2250175156250211353156250681159-2-21731602-2-216041751002500211353156250211353 + + + + diff --git a/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.dni b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.dni new file mode 100644 index 0000000..82319a6 --- /dev/null +++ b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.dni @@ -0,0 +1,94 @@ +[Interrupts] +Enabled=1 +[MemoryMap] +Enabled=0 +Base=0 +UseAuto=0 +TypeViolation=1 +UnspecRange=1 +ActionState=1 +[TraceHelper] +Enabled=0 +ShowSource=1 +[DebugChecksum] +Checksum=-1742591943 +[State Storage] +Control Register=5814 +[Sequencer] +Control Register=0 +NextState0=0 +NextState1=0 +[Action Register] +Break=0 +State Storage=0 +[DisAssemblyWindow] +NumStates=_ 1 +State 1=_ 1 +[InstructionProfiling] +Enabled=_ 0 +[CodeCoverage] +Enabled=_ 0 +[Profiling] +Enabled=0 +[StackPlugin] +Enabled=1 +OverflowWarningsEnabled=1 +WarningThreshold=90 +SpWarningsEnabled=1 +WarnHow=0 +UseTrigger=1 +TriggerName=main +LimitSize=0 +ByteLimit=50 +[Stack] +FillEnabled=0 +OverflowWarningsEnabled=1 +WarningThreshold=90 +SpWarningsEnabled=1 +WarnLogOnly=1 +UseTrigger=1 +TriggerName=main +LimitSize=0 +ByteLimit=50 +[EEM State Storage] +Buffer=BAAAAAAADAAAAAAAAAAAAAAA +[EEM Sequencer] +Buffer=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +[NumberOfSequencerBp] +Number=4 +[Breakpoints] +Count=0 +[FET] +Clock mode=46 +Extended Clock mode=65535 +Secure Password= +Extended Clock Control Enable=1 +Advanced Extended Clock Control=0 +Emulation mode=0 +Free running=0 +Shutting Down=3 +[Memory Dump] +Start address= +Lenghth= +Address info=0 +Format=0 +Dump registers=0 +PC=0 +SP=0 +SR=0 +all registers=0 +File name= +[Log file] +LoggingEnabled=_ 0 +LogFile=_ "" +Category=_ 0 +[TermIOLog] +LoggingEnabled=_ 0 +LogFile=_ "" +[Aliases] +Count=0 +SuppressDialog=0 +[CallStack] +ShowArgs=0 +[Disassembly] +MixedMode=1 diff --git a/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.wsdt b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.wsdt new file mode 100644 index 0000000..5dc5a36 --- /dev/null +++ b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.wsdt @@ -0,0 +1,77 @@ + + + + + + MOD-EKG_DemoSoft/Debug + + + + + + + + + 318272727 + + + + + + + 2091524461 + + + + 201221 + + 44062754 + + + + + + + TabID-5007-30075 + Workspace + Workspace + + + MOD-EKG_DemoSoftMOD-EKG_DemoSoft/OutputMOD-EKG_DemoSoft/Output/MOD-EKG_DemoSoft.d43 + + + + 0 + + + TabID-616-32204 + Build + Build + + + + TabID-32338-32224 + Debug Log + Debug-Log + + + TabID-552-6144Find in FilesFind-in-Files + + 0 + + + + + + TextEditor$WS_DIR$\MOD-EKG_DemoSoft.c00000199119051190500100000010000001 + + + + + + + iaridepm.enu1-2-2630392-2-2250175156250211353246250763285-2-21531602-2-216041551002500187198156250211353 + + + + diff --git a/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.wspos b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.wspos new file mode 100644 index 0000000..2c8cfad --- /dev/null +++ b/mod_ekg_psychose/iar/settings/MOD-EKG_DemoSoft.wspos @@ -0,0 +1,2 @@ +[MainWindow] +WindowPlacement=_ 25 25 1225 657 3 From 33b8ee0ec78af94c750850d16697c86401e9fe9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Ple=C3=9F?= Date: Wed, 19 Nov 2014 03:18:26 +0100 Subject: [PATCH 5/8] changed temperature from int to float, since the arduino is sending now floats, fixed the typo --- sensors2osc/sensors2osc/ehealth2osc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sensors2osc/sensors2osc/ehealth2osc.py b/sensors2osc/sensors2osc/ehealth2osc.py index e65fc54..ff1bb6f 100644 --- a/sensors2osc/sensors2osc/ehealth2osc.py +++ b/sensors2osc/sensors2osc/ehealth2osc.py @@ -79,14 +79,14 @@ def main(): try: - temp = int(temp) + temp = float(temp) except ValueError, msg: logger.exception(msg) continue try: - osc_message = OSCMessage("/%s/temperatur" % actor) - osc_message.appendTypedArg(temp, "i") + osc_message = OSCMessage("/%s/temperature" % actor) + osc_message.appendTypedArg(temp, "f") platform.osc_sock.sendto(osc_message.encode_osc(), platform.remote) except socket.error, msg: logger.exception(msg) From 27914e6a1931b35d074037c99e41f230da515e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Ple=C3=9F?= Date: Wed, 19 Nov 2014 03:21:14 +0100 Subject: [PATCH 6/8] and fixed the temperature typo everywhere else (hopefully) --- dump_grabber/dump_grabber/main.py | 2 -- healthdisplay/src/main/java/de/psychose/Main.java | 2 +- sensors2osc/sensors2osc/main.py | 2 +- sensors2osc/sensors2osc/sensorTest.py | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/dump_grabber/dump_grabber/main.py b/dump_grabber/dump_grabber/main.py index 5dd5951..8b6aa9e 100644 --- a/dump_grabber/dump_grabber/main.py +++ b/dump_grabber/dump_grabber/main.py @@ -174,8 +174,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, except AttributeError: pass else: - if text == "temperatur": - text += "e" if actor == "merle": self.add_text(0, "%s = %s" % ( text, ", ".join([str(i) for i in args]))) diff --git a/healthdisplay/src/main/java/de/psychose/Main.java b/healthdisplay/src/main/java/de/psychose/Main.java index 3bcfa59..aad4233 100644 --- a/healthdisplay/src/main/java/de/psychose/Main.java +++ b/healthdisplay/src/main/java/de/psychose/Main.java @@ -148,7 +148,7 @@ public class Main { } }); - chaOSCclient.addListener("/" + actor.toLowerCase() + "/temperatur", new OSCListener() { + chaOSCclient.addListener("/" + actor.toLowerCase() + "/temperature", new OSCListener() { @Override public void acceptMessage(Date time, OSCMessage message) { if (message.getArguments().length == 1) { diff --git a/sensors2osc/sensors2osc/main.py b/sensors2osc/sensors2osc/main.py index 01a0606..e726c14 100644 --- a/sensors2osc/sensors2osc/main.py +++ b/sensors2osc/sensors2osc/main.py @@ -73,7 +73,7 @@ class EHealth2OSC(Forwarder): 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 = OSCMessage("/%s/temperature" % self.actor) osc_message.appendTypedArg(temp, "i") osc_sock.sendall(osc_message.encode_osc()) diff --git a/sensors2osc/sensors2osc/sensorTest.py b/sensors2osc/sensors2osc/sensorTest.py index 79ab7b7..5602db4 100644 --- a/sensors2osc/sensors2osc/sensorTest.py +++ b/sensors2osc/sensors2osc/sensorTest.py @@ -75,7 +75,7 @@ class EHealth2OSC(Forwarder): 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 = OSCMessage("/%s/temperature" % self.actor) osc_message.appendTypedArg(temp, "i") osc_sock.sendall(osc_message.encode_osc()) From 9c0c7a0fdcf6710b7831116ef19bafd9ef0197bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Ple=C3=9F?= Date: Thu, 20 Nov 2014 15:35:46 +0100 Subject: [PATCH 7/8] added fensterkram teil 1 --- fensterkram/.devilspie/vlc.ds | 1 + fensterkram/positions.sh | 23 +++++++++++++++++++++++ fensterkram/vlc_webcam.sh | 7 +++++++ 3 files changed, 31 insertions(+) create mode 100644 fensterkram/.devilspie/vlc.ds create mode 100755 fensterkram/positions.sh create mode 100755 fensterkram/vlc_webcam.sh diff --git a/fensterkram/.devilspie/vlc.ds b/fensterkram/.devilspie/vlc.ds new file mode 100644 index 0000000..7f329ee --- /dev/null +++ b/fensterkram/.devilspie/vlc.ds @@ -0,0 +1 @@ +(if (contains (window_name) "VLC media player") (undecorate)) diff --git a/fensterkram/positions.sh b/fensterkram/positions.sh new file mode 100755 index 0000000..b34d458 --- /dev/null +++ b/fensterkram/positions.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# vlc mit Kamera +STR="Universal Pictures" +wmctrl -r $STR -e 0,1600,0,640,480 +wmctrl -r $STR -b add,sticky,above + +# vlc mit annes texter +STR="http://walter" +wmctrl -r $STR -e 0,2240,0,640,480 +wmctrl -r $STR -b add,sticky,above + +# datenstroeme +STR="http://devimages" +wmctrl -r $STR -e 0,2880,0,640,480 +wmctrl -r $STR -b add,sticky,above + +#das HealthDisplay +wmctrl -r "HD Main" -e 0,2570,480,950,570 +wmctrl -r "HD Main" -b add,sticky,above + +killall devilspie +devilspie & diff --git a/fensterkram/vlc_webcam.sh b/fensterkram/vlc_webcam.sh new file mode 100755 index 0000000..42ed975 --- /dev/null +++ b/fensterkram/vlc_webcam.sh @@ -0,0 +1,7 @@ +#!/bin/bash + + + +vlc --no-audio --video-on-top --no-video-title-show --no-osd \ + --repeat --qt-notification 0 --no-video-deco --autoscale \ + ../streaming/Serenity\ -\ HD\ DVD\ Trailer.mp4 From ac890076b444a882ec7e5f248c4be8ad8224fbb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Ple=C3=9F?= Date: Fri, 21 Nov 2014 03:50:27 +0100 Subject: [PATCH 8/8] cleaned up the code changed GUI layout to respect current desktop layout for SDI capture added temperature offset controls --- healthdisplay/.idea/uiDesigner.xml | 3 + healthdisplay/README | 5 + .../src/main/java/de/psychose/ActorData.java | 93 ++++++++-- .../main/java/de/psychose/ActorDisplay.form | 2 +- .../main/java/de/psychose/ActorDisplay.java | 118 ++++--------- .../src/main/java/de/psychose/ActorHeart.form | 2 +- .../src/main/java/de/psychose/ActorHeart.java | 42 ++--- .../main/java/de/psychose/ChaOSCclient.java | 14 +- .../main/java/de/psychose/ControlForm.form | 21 ++- .../main/java/de/psychose/ControlForm.java | 54 +++--- .../src/main/java/de/psychose/Main.java | 167 ++++++------------ .../src/main/java/de/psychose/MainForm.form | 23 ++- .../src/main/java/de/psychose/MainForm.java | 42 +++-- .../main/java/de/psychose/PulseControl.form | 21 ++- .../main/java/de/psychose/PulseControl.java | 47 ++--- .../src/main/java/de/psychose/PulseData.java | 8 +- .../java/de/psychose/TemperatureControl.form | 35 ++++ .../java/de/psychose/TemperatureControl.java | 34 ++++ 18 files changed, 383 insertions(+), 348 deletions(-) create mode 100644 healthdisplay/src/main/java/de/psychose/TemperatureControl.form create mode 100644 healthdisplay/src/main/java/de/psychose/TemperatureControl.java diff --git a/healthdisplay/.idea/uiDesigner.xml b/healthdisplay/.idea/uiDesigner.xml index 709fd23..aff8117 100644 --- a/healthdisplay/.idea/uiDesigner.xml +++ b/healthdisplay/.idea/uiDesigner.xml @@ -131,6 +131,9 @@ + + + \ No newline at end of file diff --git a/healthdisplay/README b/healthdisplay/README index 4a3b245..f965e40 100644 --- a/healthdisplay/README +++ b/healthdisplay/README @@ -1,5 +1,10 @@ +you need jdk 1.7 or newer and maven2 installed compile with mvn clean compile ideauidesigner:javac2 assembly:single + +run with + +java -jar psychose-X.X-jar-with-dependencies.jar diff --git a/healthdisplay/src/main/java/de/psychose/ActorData.java b/healthdisplay/src/main/java/de/psychose/ActorData.java index db0374b..3f921cd 100644 --- a/healthdisplay/src/main/java/de/psychose/ActorData.java +++ b/healthdisplay/src/main/java/de/psychose/ActorData.java @@ -6,34 +6,63 @@ package de.psychose; */ public class ActorData { + private String actor = ""; + private String caption = ""; private PulseData pulseData = new PulseData(); private int airflow; private int ekg; private int emg; - private float temperature; + private double temperature; + private double temperatureOffset; private boolean tommyHeartbeat; private long timestampPulse = 0; - private long timestampTommyPulse = 0; + private long timestampHeartbeat = 0; + private long timestampOxygen = 0; + private long timestampTommyHeartbeat = 0; private long timestampEkg = 0; private long timestampEmg = 0; private long timestampTemperature = 0; private long timestampBreath = 0; - // TODO: hier die timestamps setzen wann letztes mal geändert, - // dann kann ich in ActorDisplay im Timer einfach prüfen ob differenz > timeout, dann rot setzen - - - public void setTimestampPulse() { - this.timestampPulse = System.currentTimeMillis(); - } - public PulseData getPulseData() { - return pulseData; + public ActorData(String actor, String caption) { + this.actor = actor; + this.caption = caption; } - public void setPulseData(PulseData pulseData) { - this.pulseData = pulseData; - this.timestampPulse = System.currentTimeMillis(); + public String getActor() { + return actor; + } + + public String getCaption() { + return caption; + } + + public int getOxygen() { + return pulseData.getOxygen(); + } + + public void setOxygen(int oxygen) { + timestampOxygen = System.currentTimeMillis(); + pulseData.setOxygen(oxygen); + } + + public boolean getHeartbeat() { + return pulseData.getHeartbeat(); + } + + public void setHeartbeat(boolean heartbeat) { + timestampHeartbeat = System.currentTimeMillis(); + pulseData.setHeartbeat(heartbeat); + } + + public int getPulse() { + return pulseData.getPulse(); + } + + public void setPulse(int pulse) { + timestampPulse = System.currentTimeMillis(); + pulseData.setPulse(pulse); } public int getAirflow() { @@ -63,11 +92,11 @@ public class ActorData { this.timestampEmg = System.currentTimeMillis(); } - public float getTemperature() { + public double getTemperature() { return temperature; } - public void setTemperature(float temperature) { + public void setTemperature(double temperature) { this.temperature = temperature; this.timestampTemperature = System.currentTimeMillis(); } @@ -78,7 +107,15 @@ public class ActorData { public void setTommyHeartbeat(boolean tommyHeartbeat) { this.tommyHeartbeat = tommyHeartbeat; - this.timestampTommyPulse = System.currentTimeMillis(); + this.timestampTommyHeartbeat = System.currentTimeMillis(); + } + + public double getTemperatureOffset() { + return temperatureOffset; + } + + public void setTemperatureOffset(double temperatureOffset) { + this.temperatureOffset = temperatureOffset; } public long getTimestampPulse() { @@ -100,4 +137,26 @@ public class ActorData { public long getTimestampBreath() { return timestampBreath; } + + @Override + public String toString() { + return "ActorData{" + + "actor='" + actor + '\'' + + ", caption='" + caption + '\'' + + ", airflow=" + airflow + + ", ekg=" + ekg + + ", emg=" + emg + + ", temperature=" + temperature + + ", temperatureOffset=" + temperatureOffset + + ", tommyHeartbeat=" + tommyHeartbeat + + ", timestampPulse=" + timestampPulse + + ", timestampHeartbeat=" + timestampHeartbeat + + ", timestampOxygen=" + timestampOxygen + + ", timestampTommyHeartbeat=" + timestampTommyHeartbeat + + ", timestampEkg=" + timestampEkg + + ", timestampEmg=" + timestampEmg + + ", timestampTemperature=" + timestampTemperature + + ", timestampBreath=" + timestampBreath + + '}'; + } } diff --git a/healthdisplay/src/main/java/de/psychose/ActorDisplay.form b/healthdisplay/src/main/java/de/psychose/ActorDisplay.form index d72dc0a..3bb806e 100644 --- a/healthdisplay/src/main/java/de/psychose/ActorDisplay.form +++ b/healthdisplay/src/main/java/de/psychose/ActorDisplay.form @@ -1,6 +1,6 @@

- + diff --git a/healthdisplay/src/main/java/de/psychose/ActorDisplay.java b/healthdisplay/src/main/java/de/psychose/ActorDisplay.java index a6c9313..d6383cf 100644 --- a/healthdisplay/src/main/java/de/psychose/ActorDisplay.java +++ b/healthdisplay/src/main/java/de/psychose/ActorDisplay.java @@ -2,7 +2,6 @@ package de.psychose; import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; import java.text.DecimalFormat; /** @@ -10,11 +9,7 @@ import java.text.DecimalFormat; * @date: 14.04.14 21:44 */ public class ActorDisplay { - private final Timer timer; - private final static Color onColor = Color.WHITE; - private final static Color offColor = Color.RED; - private final static String offText = "no data"; - + private static final long TIMEOUT_MILLISECONDS = 2000; private JLabel lblCaption; private JLabel lblHeartbeat; private JLabel lblPulse; @@ -23,98 +18,49 @@ public class ActorDisplay { private JLabel lblEmg; private JLabel lblTemperature; private JLabel lblBreath; - private JPanel actorPanel; + private JPanel mainPanel; private ActorData actorData; private boolean showErrors = false; private DecimalFormat df = new DecimalFormat("#.0"); - //TODO: die einzelnen Setter wegmachen, dafür eine setData() bauen die die daten en bloc nimmt - // die darin enthaltenen timestamps dann für rotfärbung nehmen - - public void setActorData(ActorData actorData) { - this.actorData = actorData; - } - - public void setCaption(String caption) { - lblCaption.setText(caption); - } - public void update() { + if (actorData == null) { + return; + } + lblBreath.setText(String.valueOf(actorData.getAirflow())); - - lblTemperature.setText(df.format(actorData.getTemperature())); + lblTemperature.setText(df.format(actorData.getTemperature() + actorData.getTemperatureOffset())); lblEkg.setText(String.valueOf(actorData.getEkg())); - lblPulse.setText(actorData.getPulseData().getHeartbeat() == 0 ? "systole" : "diastole"); + lblPulse.setText(actorData.getHeartbeat() ? "systole" : "diastole"); lblEmg.setText(String.valueOf(actorData.getEmg())); - lblOxy.setText(String.valueOf(actorData.getPulseData().getOxygen())); - lblHeartbeat.setText(String.valueOf(actorData.getPulseData().getPulse())); + lblOxy.setText(String.valueOf(actorData.getOxygen())); + lblHeartbeat.setText(String.valueOf(actorData.getPulse())); + if (showErrors) { + checkTimeout(lblTemperature, actorData.getTimestampTemperature()); + checkTimeout(lblPulse, actorData.getTimestampPulse()); + checkTimeout(lblOxy, actorData.getTimestampPulse()); + checkTimeout(lblHeartbeat, actorData.getTimestampPulse()); + checkTimeout(lblEkg, actorData.getTimestampEkg()); + checkTimeout(lblEmg, actorData.getTimestampEmg()); + checkTimeout(lblBreath, actorData.getTimestampBreath()); + } } - public ActorDisplay() { - this.timer = new Timer(100, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - - if (actorData == null) - return; - - update(); - - if (showErrors) { - - long timeout = System.currentTimeMillis() - 1000; - - if (actorData.getTimestampTemperature() < timeout) { - lblTemperature.setForeground(offColor); - lblTemperature.setText(offText); - } else { - lblTemperature.setForeground(onColor); - } - - if (actorData.getTimestampPulse() < timeout) { - lblPulse.setForeground(offColor); - lblPulse.setText(offText); - lblOxy.setForeground(offColor); - lblOxy.setText(offText); - lblHeartbeat.setForeground(offColor); - lblHeartbeat.setText(offText); - } else { - lblPulse.setForeground(onColor); - lblOxy.setForeground(onColor); - lblHeartbeat.setForeground(onColor); - } - - if (actorData.getTimestampEkg() < timeout) { - lblEkg.setForeground(offColor); - lblEkg.setText(offText); - } else { - lblEkg.setForeground(onColor); - } - - if (actorData.getTimestampEmg() < timeout) { - lblEmg.setForeground(offColor); - lblEmg.setText(offText); - } else { - lblEmg.setForeground(onColor); - } - - if (actorData.getTimestampBreath() < timeout) { - lblBreath.setForeground(offColor); - lblBreath.setText(offText); - } else { - lblBreath.setForeground(onColor); - } - } - } - }); - - timer.setRepeats(true); - timer.start(); - } - - public void setShowErrors(boolean showErrors) { + public void init(ActorData actorData, final boolean showErrors) { + this.actorData = actorData; + lblCaption.setText(actorData.getCaption()); this.showErrors = showErrors; } + + private void checkTimeout(final JLabel label, final long time) { + if (time < System.currentTimeMillis() - TIMEOUT_MILLISECONDS) { + label.setText("no data"); + label.setForeground(Color.red); + } else { + label.setForeground(Color.white); + } + } + } diff --git a/healthdisplay/src/main/java/de/psychose/ActorHeart.form b/healthdisplay/src/main/java/de/psychose/ActorHeart.form index 86b3aa2..ef11e05 100644 --- a/healthdisplay/src/main/java/de/psychose/ActorHeart.form +++ b/healthdisplay/src/main/java/de/psychose/ActorHeart.form @@ -1,6 +1,6 @@ - + diff --git a/healthdisplay/src/main/java/de/psychose/ActorHeart.java b/healthdisplay/src/main/java/de/psychose/ActorHeart.java index 2dfeef4..f1ece30 100644 --- a/healthdisplay/src/main/java/de/psychose/ActorHeart.java +++ b/healthdisplay/src/main/java/de/psychose/ActorHeart.java @@ -3,7 +3,6 @@ package de.psychose; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; import java.io.IOException; @@ -12,33 +11,21 @@ import java.io.IOException; * @date: 15.11.14 21:36 */ public class ActorHeart { - private JPanel heartPanel; - private ActorData actorData1; - private ActorData actorData2; - private ActorData actorData3; + private JPanel mainPanel; + private ActorData[] actorDatas; private ImagePanel imagePanel; - private Timer timer; public ActorHeart() { imagePanel = new ImagePanel("/de/psychose/heart1_klein_inv.jpg", "/de/psychose/heart2_klein_inv.jpg"); - heartPanel.add(imagePanel); - - timer = new Timer(100, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - if(actorData1 != null && actorData2 != null && actorData3 != null) { - imagePanel.repaint(); - } - } - }); - timer.setRepeats(true); - timer.start(); + mainPanel.add(imagePanel); } - public void setActorData(final ActorData actorData1, final ActorData actorData2, final ActorData actorData3) { - this.actorData1 = actorData1; - this.actorData2 = actorData2; - this.actorData3 = actorData3; + public void update() { + imagePanel.repaint(); + } + + public void setActorDatas(final ActorData[] actorDatas) { + this.actorDatas = actorDatas; } private class ImagePanel extends JPanel { @@ -57,10 +44,13 @@ public class ActorHeart { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); - if(actorData1 != null && actorData2 != null && actorData3 != null) { - g.drawImage(ActorHeart.this.actorData1.getTommyHeartbeat() ? image1 : image2, 0, 0, null, null); - g.drawImage(ActorHeart.this.actorData2.getTommyHeartbeat() ? image1 : image2, 263, 0, null, null); - g.drawImage(ActorHeart.this.actorData3.getTommyHeartbeat() ? image1 : image2, 526, 0, null, null); + + if (actorDatas != null) { + for (int i = 0; i < actorDatas.length; i++) { + if (actorDatas[i] != null) { + g.drawImage(actorDatas[i].getTommyHeartbeat() ? image1 : image2, 263 * i, 0, null, null); + } + } } } } diff --git a/healthdisplay/src/main/java/de/psychose/ChaOSCclient.java b/healthdisplay/src/main/java/de/psychose/ChaOSCclient.java index a795850..9670aac 100644 --- a/healthdisplay/src/main/java/de/psychose/ChaOSCclient.java +++ b/healthdisplay/src/main/java/de/psychose/ChaOSCclient.java @@ -45,21 +45,21 @@ public class ChaOSCclient { return changeChaoscSubscription(true); } - public void sendPulse(String actor, int heartbeat, int pulse, int oxygen) { - + public void sendMessage(final String address, Object... args) { try { - OSCMessage subscribeMessage = new OSCMessage("/" + actor + "/heartbeat"); - subscribeMessage.addArgument(heartbeat); - subscribeMessage.addArgument(pulse); - subscribeMessage.addArgument(oxygen); + OSCMessage subscribeMessage = new OSCMessage(address); + + for(Object param: args) { + subscribeMessage.addArgument(param); + } portOut.send(subscribeMessage); } catch (IOException e) { System.out.println("could not send pulse OSC Message"); e.printStackTrace(); } - } + } private boolean changeChaoscSubscription(boolean subscribe) { try { diff --git a/healthdisplay/src/main/java/de/psychose/ControlForm.form b/healthdisplay/src/main/java/de/psychose/ControlForm.form index 1a0130d..865d822 100644 --- a/healthdisplay/src/main/java/de/psychose/ControlForm.form +++ b/healthdisplay/src/main/java/de/psychose/ControlForm.form @@ -1,6 +1,6 @@ - + @@ -25,15 +25,30 @@ - + - + + + + + + + + + + + + + + + + diff --git a/healthdisplay/src/main/java/de/psychose/ControlForm.java b/healthdisplay/src/main/java/de/psychose/ControlForm.java index 4bec000..537a458 100644 --- a/healthdisplay/src/main/java/de/psychose/ControlForm.java +++ b/healthdisplay/src/main/java/de/psychose/ControlForm.java @@ -8,47 +8,61 @@ import java.util.Observer; * @author: lucas * @date: 15.11.14 22:23 */ -public class ControlForm { +public class ControlForm extends JFrame { private PulseControl pulse1; private PulseControl pulse2; private PulseControl pulse3; - private JPanel mainPanel; + private JPanel rootPanel; private ActorDisplay actor1; private ActorDisplay actor2; private ActorDisplay actor3; + private TemperatureControl temp1; + private TemperatureControl temp2; + private TemperatureControl temp3; private final ChaOSCclient osCclient; - public JPanel getMainPanel() { - return mainPanel; - } - - - public ControlForm(ChaOSCclient chaOSCclient, final ActorData actorData1, final ActorData actorData2, final ActorData actorData3) { + public ControlForm(ChaOSCclient chaOSCclient, final ActorData[] actorData) { + super("HD Control"); this.osCclient = chaOSCclient; - addActor("merle", pulse1, actor1, actorData1); - addActor("uwe", pulse2, actor2, actorData2); - addActor("bjoern", pulse3, actor3, actorData3); + setContentPane(rootPanel); + setResizable(false); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - actor1.setShowErrors(true); - actor2.setShowErrors(true); - actor3.setShowErrors(true); + addActor(pulse1, actor1, temp1, actorData[0]); + addActor(pulse2, actor2, temp2, actorData[1]); + addActor(pulse3, actor3, temp3, actorData[2]); + pack(); + setVisible(true); } - private void addActor(final String actor, PulseControl pulse, ActorDisplay display, ActorData actorData) { + private void addActor(final PulseControl pulse, final ActorDisplay display, final TemperatureControl temp, final ActorData actorData) { pulse.addObserver(new Observer() { @Override public void update(Observable o, Object arg) { - if(arg instanceof PulseData) { - final PulseData data = (PulseData)arg; - osCclient.sendPulse(actor, data.getHeartbeat(), data.getPulse(), data.getOxygen()); + if (arg instanceof PulseData) { + final PulseData data = (PulseData) arg; + osCclient.sendMessage("/" + actorData.getActor().toLowerCase() + "/heartbeat", data.getHeartbeat(), + data.getPulse(), data.getOxygen()); + + // TODO: delete this line, bc tommy will send the real events + osCclient.sendMessage("/" + actorData.getActor().toLowerCase() + "/tommyheartbeat"); } } }); - display.setCaption(actor); - display.setActorData(actorData); + + temp.addObserver(new Observer() { + @Override + public void update(Observable o, Object arg) { + if (arg instanceof Double) { + actorData.setTemperatureOffset((double)arg); + } + } + }); + + display.init(actorData, true); } } diff --git a/healthdisplay/src/main/java/de/psychose/Main.java b/healthdisplay/src/main/java/de/psychose/Main.java index aad4233..b993c40 100644 --- a/healthdisplay/src/main/java/de/psychose/Main.java +++ b/healthdisplay/src/main/java/de/psychose/Main.java @@ -4,8 +4,6 @@ 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; @@ -15,26 +13,17 @@ import java.util.Date; * @date: 25.04.14 00:23 */ public class Main { - private ChaOSCclient chaOSCclient; - private ControlForm controlForm; - private MainForm mainForm; - - private int totalMessageCount = 0; - private int messagesTempCounter = 0; - - private long totalTraffic = 0; - private long lastTraffic = 0; - - private final ActorData actorData1 = new ActorData(); - private final ActorData actorData2 = new ActorData(); - private final ActorData actorData3 = new ActorData(); - public static void main(String[] args) { new Main(); } public Main() { + final ActorData[] actorDatas = new ActorData[3]; + actorDatas[0] = new ActorData("merle", "Körper 1"); + actorDatas[1] = new ActorData("uwe", "Körper 2"); + actorDatas[2] = new ActorData("bjoern", "Körper 3"); + try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); } catch (Exception e) { @@ -42,151 +31,99 @@ public class Main { } try { - this.chaOSCclient = new ChaOSCclient("chaosc", 7110); - this.controlForm = new ControlForm(chaOSCclient, actorData1, actorData2, actorData3); + final ChaOSCclient chaOSCclient = new ChaOSCclient("chaosc", 7110); - final JFrame cframe = new JFrame("HD Control"); - cframe.setContentPane(controlForm.getMainPanel()); - cframe.setResizable(false); - cframe.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - cframe.pack(); - - - this.mainForm = new MainForm(actorData1, actorData2, actorData3); - final JFrame frame = new JFrame("HD Main"); - frame.setContentPane(mainForm.getMainPanel()); - frame.setResizable(false); - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); -// frame.setExtendedState(JFrame.MAXIMIZED_BOTH); - frame.setUndecorated(true); - frame.pack(); - - frame.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - chaOSCclient.stopReceiver(); -// snmp.stopRunning(); - super.windowClosing(e); - } - }); - - - addActor("merle", actorData1); - addActor("uwe", actorData2); - addActor("bjoern", actorData3); - - cframe.setVisible(true); - frame.setVisible(true); + for(int i = 0; i < actorDatas.length; i++) { + addActorOSCListeners(chaOSCclient, actorDatas[i]); + } chaOSCclient.startReceiver(); + new ControlForm(chaOSCclient, actorDatas); + new MainForm(actorDatas); + + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + chaOSCclient.stopReceiver(); + } + })); + } catch (UnknownHostException | SocketException e) { e.printStackTrace(); } - } - private void addActor(final String actor, final ActorData actorData) { + private static void addActorOSCListeners(final ChaOSCclient chaOSCclient, final ActorData actorData) { - chaOSCclient.addListener("/" + actor.toLowerCase() + "/heartbeat", new OSCListener() { + chaOSCclient.addListener("/" + actorData.getActor().toLowerCase() + "/heartbeat", new OSCListener() { @Override public void acceptMessage(Date time, OSCMessage message) { if (message.getArguments().length == 3) { - totalMessageCount++; + // set the beat ( 0 or 1 ) + if (message.getArguments()[0] instanceof Integer) { + actorData.setHeartbeat( (int)(message.getArguments()[0]) == 1); + } + + // set the heartrate if (message.getArguments()[1] instanceof Integer) { - int pulse = (int) (message.getArguments()[1]); - - if (pulse > 60) { // try to skip the invalid pulserate from device - - // set the heartrate - actorData.getPulseData().setPulse((int) (message.getArguments()[1])); - - // set the beat ( 0 or 1 ) - if (message.getArguments()[0] instanceof Integer) { - actorData.getPulseData().setHeartbeat((int) (message.getArguments()[0])); - } - - //TODO: remove this, its for testing without tommy only - actorData.setTommyHeartbeat(((int) message.getArguments()[0]) == 1); - - // set the oxy level - if (message.getArguments()[2] instanceof Integer) { - actorData.getPulseData().setOxygen((int) (message.getArguments()[2])); - } - - actorData.setTimestampPulse(); + final int pulse = (int) (message.getArguments()[1]); + if (pulse > 60) { // try to skip the invalid pulse rate from device + actorData.setPulse(pulse); } } - } - } - }); - chaOSCclient.addListener("/" + actor.toLowerCase() + "/ekg", new OSCListener() { - @Override - public void acceptMessage(Date time, OSCMessage message) { - if (message.getArguments().length == 1) { - totalMessageCount++; - - if (message.getArguments()[0] instanceof Integer) { - actorData.setEkg((int) (message.getArguments()[0])); + // set the oxy level + if (message.getArguments()[2] instanceof Integer) { + actorData.setOxygen((int) (message.getArguments()[2])); } } } }); - chaOSCclient.addListener("/" + actor.toLowerCase() + "/emg", new OSCListener() { + chaOSCclient.addListener("/" + actorData.getActor().toLowerCase() + "/ekg", new OSCListener() { @Override public void acceptMessage(Date time, OSCMessage message) { - if (message.getArguments().length == 1) { - totalMessageCount++; - - if (message.getArguments()[0] instanceof Integer) { - actorData.setEmg((int) (message.getArguments()[0])); - } + if (message.getArguments().length == 1 && message.getArguments()[0] instanceof Integer) { + actorData.setEkg((int) (message.getArguments()[0])); } } }); - chaOSCclient.addListener("/" + actor.toLowerCase() + "/temperature", new OSCListener() { + chaOSCclient.addListener("/" + actorData.getActor().toLowerCase() + "/emg", new OSCListener() { @Override public void acceptMessage(Date time, OSCMessage message) { - if (message.getArguments().length == 1) { - totalMessageCount++; - - if (message.getArguments()[0] instanceof Float) { - actorData.setTemperature((float) (message.getArguments()[0])); - } + if (message.getArguments().length == 1 && message.getArguments()[0] instanceof Integer) { + actorData.setEmg((int) (message.getArguments()[0])); } } }); - chaOSCclient.addListener("/" + actor.toLowerCase() + "/airFlow", new OSCListener() { + chaOSCclient.addListener("/" + actorData.getActor().toLowerCase() + "/temperature", new OSCListener() { @Override public void acceptMessage(Date time, OSCMessage message) { - if (message.getArguments().length == 1) { - totalMessageCount++; - - if (message.getArguments()[0] instanceof Integer) { - actorData.setAirflow((int) (message.getArguments()[0])); - } + if (message.getArguments().length == 1 && message.getArguments()[0] instanceof Float) { + actorData.setTemperature((float) (message.getArguments()[0])); } } }); - chaOSCclient.addListener("/" + actor.toLowerCase() + "/tommypuls", new OSCListener() { + chaOSCclient.addListener("/" + actorData.getActor().toLowerCase() + "/airFlow", new OSCListener() { @Override public void acceptMessage(Date time, OSCMessage message) { - if (message.getArguments().length == 1) { - totalMessageCount++; - - if (message.getArguments()[0] instanceof Integer) { - actorData.setTommyHeartbeat((boolean) (message.getArguments()[0])); - } - //TODO: evtl muss das oben hier noch anders + if (message.getArguments().length == 1 && message.getArguments()[0] instanceof Integer) { + actorData.setAirflow((int) (message.getArguments()[0])); } } }); + //TODO: evtl muss das oben hier noch anders + chaOSCclient.addListener("/" + actorData.getActor().toLowerCase() + "/tommyheartbeat", new OSCListener() { + @Override + public void acceptMessage(Date time, OSCMessage message) { + actorData.setTommyHeartbeat(!actorData.getTommyHeartbeat()); + } + }); } } diff --git a/healthdisplay/src/main/java/de/psychose/MainForm.form b/healthdisplay/src/main/java/de/psychose/MainForm.form index 75ac05b..126e114 100644 --- a/healthdisplay/src/main/java/de/psychose/MainForm.form +++ b/healthdisplay/src/main/java/de/psychose/MainForm.form @@ -1,16 +1,16 @@ - + - + - - + + @@ -19,9 +19,9 @@ - + - + @@ -36,17 +36,22 @@ - + - + - + + + + + + diff --git a/healthdisplay/src/main/java/de/psychose/MainForm.java b/healthdisplay/src/main/java/de/psychose/MainForm.java index 9178b32..d2eccf5 100644 --- a/healthdisplay/src/main/java/de/psychose/MainForm.java +++ b/healthdisplay/src/main/java/de/psychose/MainForm.java @@ -2,44 +2,48 @@ package de.psychose; import javax.swing.*; import java.awt.event.ActionEvent; -import java.text.DecimalFormat; /** * @author: lucas * @date: 14.04.14 21:43 */ -public class MainForm { +public class MainForm extends JFrame { private JPanel mainPanel; - private ActorHeart heart1; + private ActorHeart heart; private ActorDisplay actor1; private ActorDisplay actor2; private ActorDisplay actor3; private JLabel breath; - private final DecimalFormat df = new DecimalFormat("#.0"); - public JPanel getMainPanel() { - return mainPanel; - } + public MainForm(final ActorData[] actorDatas) { + super("HD Main"); + setContentPane(mainPanel); + setResizable(false); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setUndecorated(true); - public MainForm(final ActorData actorData1, final ActorData actorData2, final ActorData actorData3) { + actor1.init(actorDatas[0], false); + actor2.init(actorDatas[1], false); + actor3.init(actorDatas[2], false); + heart.setActorDatas(actorDatas); - actor1.setCaption("Körper 1"); - actor2.setCaption("Körper 2"); - actor3.setCaption("Körper 3"); - actor1.setActorData(actorData1); - actor2.setActorData(actorData2); - actor3.setActorData(actorData3); - heart1.setActorData(actorData1, actorData2, actorData3); - - final Timer timer = new Timer(100, new AbstractAction() { + // this is now our main timer to update all and everything gui related + final Timer timer = new Timer(50, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { - breath.setText(String.valueOf(actorData1.getAirflow())); + // update the breath display + breath.setText(String.valueOf(actorDatas[0].getAirflow())); + + actor1.update(); + actor2.update(); + actor3.update(); + heart.update(); } }); - timer.setRepeats(true); timer.start(); + pack(); + setVisible(true); } diff --git a/healthdisplay/src/main/java/de/psychose/PulseControl.form b/healthdisplay/src/main/java/de/psychose/PulseControl.form index a27987b..1386e2c 100644 --- a/healthdisplay/src/main/java/de/psychose/PulseControl.form +++ b/healthdisplay/src/main/java/de/psychose/PulseControl.form @@ -1,9 +1,9 @@
- + - + @@ -12,29 +12,34 @@ - + + - + - + + - + - + - + + + + diff --git a/healthdisplay/src/main/java/de/psychose/PulseControl.java b/healthdisplay/src/main/java/de/psychose/PulseControl.java index 191d0a2..13a92aa 100644 --- a/healthdisplay/src/main/java/de/psychose/PulseControl.java +++ b/healthdisplay/src/main/java/de/psychose/PulseControl.java @@ -14,58 +14,41 @@ import java.util.Random; public class PulseControl extends Observable { private final int PULSE_WOBBLE_WIDTH = 10; private JCheckBox enableCheckBox; - private JSpinner spinner1; - private JPanel pulsePanel; - private Timer timer; - private Random random = new Random(); - private int heartbeat = 0; - + private JSpinner spinner; + private JPanel mainPanel; + private final Timer timer; + private final Random random = new Random(); + private boolean heartbeat = false; public PulseControl() { - enableCheckBox.setFocusable(false); - spinner1.setFocusable(false); - spinner1.setValue(110); + spinner.setValue(110); - timer = new Timer(100, new AbstractAction() { + timer = new Timer(500, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { - heartbeat = (heartbeat+1) % 2; + heartbeat = !heartbeat; - final int pulseWobbleCenter = (int)spinner1.getValue(); - int pulse = pulseWobbleCenter - PULSE_WOBBLE_WIDTH / 2 + random.nextInt(PULSE_WOBBLE_WIDTH); + int pulse = (int) spinner.getValue() - PULSE_WOBBLE_WIDTH / 2 + random.nextInt(PULSE_WOBBLE_WIDTH); if(pulse < 60) pulse = 60; if(pulse > 230) pulse = 230; - final PulseData data = new PulseData(heartbeat, pulse, 95 + random.nextInt(4)); setChanged(); - notifyObservers(data); + notifyObservers(new PulseData(heartbeat, pulse, 95 + random.nextInt(4))); - final int delay = 60000 / pulse; - timer.setDelay(delay); + timer.setDelay(60000 / pulse); } }); - timer.setRepeats(true); - enableCheckBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { - JCheckBox checkBox = (JCheckBox)e.getSource(); - if(checkBox.isSelected()) { - if(!timer.isRunning()) { - System.out.println("starting pulsecontrol " + this); - timer.start(); - } - } else { - if(timer.isRunning()) { - System.out.println("stopping pulsecontrol " + this); - timer.stop(); - } + if (enableCheckBox.isSelected() && !timer.isRunning()) { + timer.start(); + } else if (timer.isRunning()) { + timer.stop(); } - } }); } - } diff --git a/healthdisplay/src/main/java/de/psychose/PulseData.java b/healthdisplay/src/main/java/de/psychose/PulseData.java index 7d23497..5198362 100644 --- a/healthdisplay/src/main/java/de/psychose/PulseData.java +++ b/healthdisplay/src/main/java/de/psychose/PulseData.java @@ -6,7 +6,7 @@ package de.psychose; */ public class PulseData { - private int heartbeat; + private boolean heartbeat; private int pulse; private int oxygen; @@ -14,17 +14,17 @@ public class PulseData { } - public PulseData(int heartbeat, int pulse, int oxygen) { + public PulseData(boolean heartbeat, int pulse, int oxygen) { this.heartbeat = heartbeat; this.pulse = pulse; this.oxygen = oxygen; } - public int getHeartbeat() { + public boolean getHeartbeat() { return heartbeat; } - public void setHeartbeat(int heartbeat) { + public void setHeartbeat(boolean heartbeat) { this.heartbeat = heartbeat; } diff --git a/healthdisplay/src/main/java/de/psychose/TemperatureControl.form b/healthdisplay/src/main/java/de/psychose/TemperatureControl.form new file mode 100644 index 0000000..34215ed --- /dev/null +++ b/healthdisplay/src/main/java/de/psychose/TemperatureControl.form @@ -0,0 +1,35 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/healthdisplay/src/main/java/de/psychose/TemperatureControl.java b/healthdisplay/src/main/java/de/psychose/TemperatureControl.java new file mode 100644 index 0000000..4a1fd35 --- /dev/null +++ b/healthdisplay/src/main/java/de/psychose/TemperatureControl.java @@ -0,0 +1,34 @@ +package de.psychose; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.util.Observable; + +/** + * @author: lucas + * @date: 20.11.14 23:11 + */ +public class TemperatureControl extends Observable { + private static final double MIN_OFFSET = -20; + private static final double MAX_OFFSET = 20; + private static final double INCREMENT = 0.1; + private JCheckBox enableCheckBox; + private JSpinner spinner1; + private JPanel mainPanel; + + public TemperatureControl() { + spinner1.setModel(new SpinnerNumberModel(0, MIN_OFFSET, MAX_OFFSET, INCREMENT)); + + final ChangeListener changeListener = new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + setChanged(); + notifyObservers(enableCheckBox.isSelected() ? spinner1.getValue() : 0.0); + } + }; + + spinner1.addChangeListener(changeListener); + enableCheckBox.addChangeListener(changeListener); + } +}