/** * Squidy is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Squidy is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Squidy. If not, see <http://www.gnu.org/licenses/>. * * 2006-2009 Human-Computer Interaction Group, University of Konstanz. * <http://hci.uni-konstanz.de> * * Please contact info@squidy-lib.de or visit our website http://squidy-lib.de for * further information. */ /** * */ package org.squidy.nodes; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import java.util.Timer; import java.util.TimerTask; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; import org.squidy.manager.ProcessException; import org.squidy.manager.controls.CheckBox; import org.squidy.manager.controls.TextField; import org.squidy.manager.data.DataConstant; import org.squidy.manager.data.IDataContainer; import org.squidy.manager.data.Processor; import org.squidy.manager.data.Property; import org.squidy.manager.data.impl.DataDigital; import org.squidy.manager.data.impl.DataInertial; import org.squidy.manager.data.impl.DataKey; import org.squidy.manager.data.impl.DataPosition6D; import org.squidy.manager.model.AbstractNode; import org.squidy.nodes.keyboard.KeyStrokeMap; import jserial.JSerial; /** * <code>Shake</code>. * * <pre> * Date: Okt 12, 2009 * Time: 15:32:29 PM * </pre> * * @author Nicolas Hirrle <a * href="mailto:nihirrle@htwg-konstanz.de">nihirrle@htwg-konstanz.de</a> * Human-Computer Interaction Group University of Konstanz * @version $Id: Shake.java 68 2009-11-10 20:22:53Z hirrle $ * @since 2.0.0 * */ @SuppressWarnings("restriction") @XmlType(name = "Shake") @Processor(name = "Shake", icon = "/org/squidy/nodes/image/48x48/shake.png", types = { Processor.Type.INPUT, Processor.Type.FILTER, Processor.Type.OUTPUT }, tags = { "shake" }) public class Shake extends AbstractNode { // ################################################################################ // BEGIN OF PROPERTIES // ################################################################################ @XmlAttribute(name = "ComPort") @Property(name = "ComPort", description = "Com Port Number") @TextField private int port = 6; public int getPort() { return port; } public void setPort(int port) { this.port = port; } // @XmlAttribute(name = "ShowCube") // @Property(name = "ShowCube", description = "Show Cube for visualization of the Position of the Shake Sensor") // @CheckBox // private boolean cube = true; // // public boolean isCube() { // return cube; // } // // public void setCube(boolean cube) { // this.cube = cube; // } // ################################################################################ // END OF PROPERTIES // ################################################################################ // ################################################################################ // BEGIN OF LAUNCH // ################################################################################ private long lastMessageReceived = 0; // private RotatingCube rotatingCube; private JSerial js; private boolean stopped = true; private ShakeMessage last; private Timer timer = new Timer(); @Override public void onStart() throws ProcessException { stopped = false; new Thread() { public void run() { if (js == null) js = new JSerial(); boolean open = js.open(port); System.out.println("connect: " + open); timer.schedule(new CheckConnection(), 10000, 2000); if (open) read(); } }.start(); new Thread() { public void run() { // if (cube) // startCube(); } }.start(); } private void startCube() { stopped = true; new Thread() { public void run() { // if (rotatingCube == null) { // rotatingCube = new RotatingCube(); // rotatingCube.main(null); // } // else // rotatingCube.setVisible(true); } }.start(); } @Override public void onStop() throws ProcessException { // if (rotatingCube != null) // rotatingCube.stop();//.setVisible(false);// .destroy(); if (js != null) js.close(); } public void read() { // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } byte[] buf = new byte[256]; int read = js.readBytes(buf, 256, 0); List<String> messages = new LinkedList<String>(); while (!Thread.interrupted() && Shake.this.isProcessing()) { lastMessageReceived = System.currentTimeMillis(); // System.out.println("read bytes: " + read); String message = new String(); parseMessages(buf, read, messages, message); for (String m : messages) { StringTokenizer st = new StringTokenizer(m, ","); List<String> tokens = new LinkedList<String>(); while (st.hasMoreElements()) { String token = st.nextToken(); // System.out.println(token); tokens.add(token); } String id = tokens.get(0); if (id.equals("$NVC\r\n")) // send DataDigital to signal // GestureRecognizerShake that // recognition shall start { vibrate(); DataDigital dataDigital = new DataDigital(Shake.class, true); dataDigital.setAttribute(DataConstant.IDENTIFIER, "ShakeSK7"); publish(dataDigital); } else if (id.equals("$NVN\r\n")) // send DataDigital to signal // GestureRecognizerShake // that recognition shall // stop { vibrate(); DataDigital dataDigital = new DataDigital(Shake.class, false); dataDigital.setAttribute(DataConstant.IDENTIFIER, "ShakeSK7"); publish(dataDigital); } else if (tokens.size() >= 5 && (id.equals("$ACC") || id.equals("$RPH"))) { double x = 0; double y = 0; double z = 0; try { x = Math.round( Double.parseDouble(tokens.get(1)) * 1000. ) / 1000.; y = Math.round( Double.parseDouble(tokens.get(2)) * 1000. ) / 1000.; z = Math.round( Double.parseDouble(tokens.get(3)) * 1000. ) / 1000.; // x = Double.parseDouble(tokens.get(1)); // y = Double.parseDouble(tokens.get(2)); // z = Double.parseDouble(tokens.get(3)); // if(id.equals("$RPH")) // System.out.println("x: " + x + " y: " + y + " z: " + z); } catch (NumberFormatException e) { continue; } double messageNumber = 1; ShakeMessage sm = new ShakeMessage(id, x, y, z, messageNumber); if (last == null) { last = sm; } else { if (sm.id .equals(ShakeMessageID.ROTATE_PITCH_HITCHHIKING)) { DataPosition6D dataPosition6D = new DataPosition6D( Shake.class, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1000 * sm.x, 1000 * sm.y, 1000 * sm.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); publish(dataPosition6D); // if (cube) { // rotateCube(sm); // } last = sm; // System.out.println(sm.toString()); } else if (sm.id.equals(ShakeMessageID.ACCELERATION)) { publish(new DataInertial(Shake.class, sm.x, sm.y, sm.z, false)); } } } } messages.clear(); buf = new byte[256]; read = js.readBytes(buf, 256, 0); } } private void rotateCube(final ShakeMessage sm) { double phi = (last.x - sm.x) * 10; double theta = (sm.y - last.y) * 10; double psi = (last.z - sm.z) * 10; if (last.x > 0 && sm.x < 0) phi = (last.x - Math.abs(sm.x)) * 10; else if (last.x < 0 && sm.x > 0) phi = (Math.abs(last.x) - sm.x) * 10; // if (last.y < 0 && sm.y >0 ) // phi = (last.y - Math.abs(sm.y)) * 10; // else if (last.y > 0 && sm.y < 0 ) // phi = (Math.abs(last.y) - sm.y) * 10; if (last.z > 0.35 && sm.z < 0.05) psi = ((sm.z + 0.36) - last.z) * 10; else if (last.z < 0.05 && sm.z > 0.35) psi = ((last.z + 0.36) - sm.z) * 10; // World3D.itsBody.rotateInPlace(phi, theta, psi); // World3D.instance.repaint(); } private void parseMessages(byte[] buf, int read, List<String> messages, String message) { for (int i = 0; i < read; i++) { char c = (char) buf[i]; if (c == '$') { if (message.length() > 0) { if (!message.contains("ACK") && !message.contains("NAK")) { // System.out.println(message); messages.add(new String(message)); } message = new String(); } message += c; continue; } if (message.length() > 0) { message += c; if (c == '+' || c == '-') message += "0."; } } } private static class ShakeMessage { public ShakeMessageID id; public double x; public double y; public double z; public double messageNumber; public ShakeMessage(String id, double x, double y, double z, double messageNumber) { this(ShakeMessageID.fromValue(id), x, y, z, messageNumber); } public ShakeMessage(ShakeMessageID id, double x, double y, double z, double messageNumber) { super(); this.id = id; this.x = x; this.y = y; this.z = z; this.messageNumber = messageNumber; } @Override public String toString() { return "id=" + id + ", x=" + x + ", y=" + y + ", z=" + z + ", messageNumber=" + messageNumber; } } private enum ShakeMessageID { ACCELERATION, ROTATE_PITCH_HITCHHIKING, CAPACITIVE_SENDING; public static ShakeMessageID fromValue(String id) { if (id.equals("$ACC")) return ShakeMessageID.ACCELERATION; if (id.equals("$RPH")) return ShakeMessageID.ROTATE_PITCH_HITCHHIKING; if (id.equals("$CSA")) return ShakeMessageID.CAPACITIVE_SENDING; return null; } } private void vibrate() { String str = "vcAB"; byte[] bufwrite = str.getBytes(); js.writeBytes(bufwrite, bufwrite.length, 0); } private class CheckConnection extends TimerTask { @Override public void run() { long currentTime = System.currentTimeMillis(); if (!Shake.this.isProcessing()) { return; } if (currentTime - Shake.this.lastMessageReceived > 1000) { // Shake.this.onStop(); // Shake.this.onStart(); if (js != null) { js.close(); boolean open = js.open(port); System.out.println("connect: " + open); } // if (open) // read(); } } } // ################################################################################ // END OF LAUNCH // ################################################################################ }