00001 package edu.stanford.hci.r3.pen.streaming;
00002
00003 import java.io.IOException;
00004 import java.io.InputStream;
00005 import java.util.ArrayList;
00006 import java.util.Enumeration;
00007 import java.util.List;
00008 import java.util.TooManyListenersException;
00009
00010 import javax.comm.CommPortIdentifier;
00011 import javax.comm.PortInUseException;
00012 import javax.comm.SerialPort;
00013 import javax.comm.SerialPortEvent;
00014 import javax.comm.SerialPortEventListener;
00015 import javax.comm.UnsupportedCommOperationException;
00016
00017 import edu.stanford.hci.r3.pen.PenSample;
00018 import edu.stanford.hci.r3.pen.streaming.listeners.PenListener;
00019 import edu.stanford.hci.r3.util.DebugUtils;
00020 import edu.stanford.hci.r3.util.communications.COMPort;
00021
00040 public class PenStreamingConnection implements SerialPortEventListener {
00041
00042 private static enum StreamingField {
00043 FORCE, HEADER, X, X_FRACTION, Y, Y_FRACTION
00044 }
00045
00046 private static final boolean DEBUG = false;
00047
00048 public static final COMPort DEFAULT_PORT = COMPort.COM5;
00049
00050
00051 private static final byte ID_PEN_UP = 0x01;
00052
00053
00054 private static final byte ID_SIMPLE_COORD = 0x00;
00055
00059 private static PenStreamingConnection instance = null;
00060
00061
00062 private static final byte LENGTH_PEN_UP = 0x00;
00063
00064
00065 private static final byte LENGTH_SIMPLE_COORD = 0x0B;
00066
00067 private static CommPortIdentifier portID;
00068
00072 public static PenStreamingConnection getInstance() {
00073 return getInstance(null);
00074 }
00075
00080 @SuppressWarnings("unchecked")
00081 public static PenStreamingConnection getInstance(COMPort port) {
00082 if (instance != null) {
00083 return instance;
00084 }
00085
00086
00087
00088 if (port == null) {
00089 port = DEFAULT_PORT;
00090 }
00091
00092 StringBuilder msg = new StringBuilder();
00093 msg.append("PenStreamingConnection: Looking for " + port + ". Found {");
00094
00095 final Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
00096 while (portList.hasMoreElements()) {
00097 portID = (CommPortIdentifier) portList.nextElement();
00098 if (portID.getPortType() == CommPortIdentifier.PORT_SERIAL) {
00099 String nameOfDiscoveredPort = portID.getName();
00100 if (nameOfDiscoveredPort.equals(port.toString())) {
00101 msg.append(" " + nameOfDiscoveredPort + " }");
00102 instance = new PenStreamingConnection();
00103 DebugUtils.println(msg.toString());
00104 System.out.flush();
00105 return instance;
00106 } else {
00107 msg.append(" " + nameOfDiscoveredPort);
00108 }
00109 }
00110 }
00111 msg.append(" }\n");
00112 msg.append("Port " + port + " not found.");
00113 DebugUtils.println(msg.toString());
00114 System.out.flush();
00115 System.err.println("Is JavaCOMM not installed?");
00116 System.err.println("Is your Bluetooth Dongle unplugged?");
00117 System.err.println("Are connecting to the correct COM port, named ANOTO STREAMING?");
00118 return null;
00119 }
00120
00121 private byte bCurrent;
00122
00123 private byte bLast;
00124
00125 private byte bLastLast;
00126
00127 private int force = 0;
00128
00129 private InputStream inputStream;
00130
00131 private PenSample lastSample;
00132
00133
00134 private List<PenListener> listeners = new ArrayList<PenListener>();
00135
00136 private StreamingField nextUp = StreamingField.HEADER;
00137
00138 private int numBytesCoord;
00139
00140 private boolean penIsUp = true;
00141
00142 private SerialPort serialPort;
00143
00144 private long timestamp;
00145
00146 private int x = 0;
00147
00148 private int xFraction = 0;
00149
00150 private int y = 0;
00151
00152 private int yFraction = 0;
00153
00157 private PenStreamingConnection() {
00158 try {
00159 serialPort = (SerialPort) portID.open("StreamingPen", 2000);
00160 } catch (PortInUseException e) {
00161 e.printStackTrace();
00162 }
00163
00164 try {
00165
00166 inputStream = serialPort.getInputStream();
00167 } catch (IOException e) {
00168 e.printStackTrace();
00169 }
00170
00171 try {
00172 serialPort.addEventListener(this);
00173 } catch (TooManyListenersException e) {
00174 e.printStackTrace();
00175 }
00176
00177 serialPort.notifyOnDataAvailable(true);
00178
00179 try {
00180 serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
00181 SerialPort.PARITY_NONE);
00182 } catch (UnsupportedCommOperationException e) {
00183 e.printStackTrace();
00184 }
00185 }
00186
00193 public void addPenListener(PenListener pl) {
00194 if (DEBUG) {
00195 System.out.println("Adding a listener...");
00196 }
00197 listeners.add(pl);
00198 }
00199
00203 public void exit() {
00204
00205 try {
00206 DebugUtils.println("Closing the connection to the Streaming Pen.");
00207 inputStream.close();
00208 serialPort.close();
00209 instance = null;
00210 } catch (IOException e) {
00211 e.printStackTrace();
00212 }
00213
00214 }
00215
00221 private void handleByte(byte streamedByte) {
00222
00223
00224 bLastLast = bLast;
00225 bLast = bCurrent;
00226 bCurrent = streamedByte;
00227
00228
00229
00230
00231 if (nextUp == StreamingField.HEADER) {
00232 if (bCurrent == LENGTH_SIMPLE_COORD && bLast == 0x00 && bLastLast == ID_SIMPLE_COORD) {
00233
00234
00235
00236 nextUp = StreamingField.X;
00237 numBytesCoord = 0;
00238 } else if (bCurrent == LENGTH_PEN_UP && bLast == 0x00 && bLastLast == ID_PEN_UP) {
00239
00240
00241 double lastX = 0;
00242 double lastY = 0;
00243 if (lastSample != null) {
00244 lastX = lastSample.x;
00245 lastY = lastSample.y;
00246 }
00247
00248 penIsUp = true;
00249 for (PenListener pl : listeners) {
00250
00251
00252
00253 final PenSample penSample = new PenSample(lastX, lastY, 0, timestamp, true);
00254
00255
00256
00257
00258
00259 pl.penUp(penSample);
00260 }
00261 }
00262 } else if (nextUp == StreamingField.X) {
00263 numBytesCoord++;
00264 x = x << 8;
00265 x = x | (bCurrent & 0xFF);
00266
00267 if (numBytesCoord == 4) {
00268
00269 nextUp = StreamingField.Y;
00270 numBytesCoord = 0;
00271 }
00272 } else if (nextUp == StreamingField.Y) {
00273 numBytesCoord++;
00274 y = y << 8;
00275 y = y | (bCurrent & 0xFF);
00276
00277 if (numBytesCoord == 4) {
00278
00279 nextUp = StreamingField.X_FRACTION;
00280 numBytesCoord = 0;
00281 }
00282 } else if (nextUp == StreamingField.X_FRACTION) {
00283
00284 xFraction = (bCurrent >> 5) & 0x7;
00285 nextUp = StreamingField.Y_FRACTION;
00286 } else if (nextUp == StreamingField.Y_FRACTION) {
00287
00288 yFraction = (bCurrent >> 5) & 0x7;
00289 nextUp = StreamingField.FORCE;
00290 } else if (nextUp == StreamingField.FORCE) {
00291
00292
00293
00294 force = 126 - (bCurrent & 0xFF) * 2;
00295 if (force < 0) {
00296 force = 0;
00297 }
00298
00299
00300
00301
00302
00303
00304 timestamp = System.currentTimeMillis();
00305
00306
00307 if (DEBUG) {
00308 System.out.println("(" + (x + xFraction * 0.125) + ", " + (y + yFraction * 0.125) + ")"
00309 + " f: " + force + " t: " + timestamp);
00310 System.out.flush();
00311 }
00312
00313 final PenSample penSample = new PenSample(x + (xFraction * 0.125), y + (yFraction * 0.125),
00314 force, timestamp, false);
00315
00316 if (penIsUp) {
00317 penIsUp = false;
00318 for (PenListener pl : listeners) {
00319
00320
00321
00322
00323
00324
00325 pl.penDown(penSample);
00326 }
00327 } else {
00328
00329
00330 for (PenListener pl : listeners) {
00331
00332
00333
00334
00335
00336
00337 pl.sample(penSample);
00338 }
00339 }
00340
00341
00342 lastSample = penSample;
00343
00344
00345 x = 0;
00346 y = 0;
00347 xFraction = 0;
00348 yFraction = 0;
00349 force = 0;
00350
00351
00352 nextUp = StreamingField.HEADER;
00353 }
00354 }
00355
00361 public void serialEvent(SerialPortEvent event) {
00362 switch (event.getEventType()) {
00363
00364 case SerialPortEvent.BI:
00365
00366 case SerialPortEvent.OE:
00367
00368 case SerialPortEvent.FE:
00369
00370 case SerialPortEvent.PE:
00371
00372 case SerialPortEvent.CD:
00373
00374 case SerialPortEvent.CTS:
00375
00376 case SerialPortEvent.DSR:
00377
00378 case SerialPortEvent.RI:
00379
00380 case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
00381 break;
00382
00383 case SerialPortEvent.DATA_AVAILABLE:
00384 byte[] readBuffer = new byte[20];
00385 try {
00386 while (inputStream.available() > 0) {
00387 int numBytes = inputStream.read(readBuffer);
00388
00389
00390 for (int i = 0; i < numBytes; i++) {
00391 handleByte(readBuffer[i]);
00392 }
00393 }
00394 } catch (IOException e) {
00395 }
00396
00397 break;
00398 }
00399 }
00400 }