/**
* Squidy Interaction Library 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 Interaction Library 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 Interaction Library. If not, see
* <http://www.gnu.org/licenses/>.
*
* 2009 Human-Computer Interaction Group, University of Konstanz.
* <http://hci.uni-konstanz.de>
*
* Please contact info@squidy-lib.de or visit our website
* <http://www.squidy-lib.de> for further information.
*/
package org.squidy.nodes.tracking;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TooManyListenersException;
import java.util.Vector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.squidy.manager.data.IData;
import org.squidy.manager.data.impl.DataButton;
import org.squidy.manager.data.impl.DataInertial;
import org.squidy.nodes.Laserpointer;
/**
*
* @author Markus Nitsche, markus.nitsche@uni-konstanz.de, University of Konstanz
*
*/
public class WirelessLaserDriverBlack extends Thread implements
SerialPortEventListener, LaserVibrate {
// Logger to log info, error, debug,... messages.
private static final Log LOG = LogFactory.getLog(WirelessLaserDriverBlack.class);
private String port;
private Laserpointer laserPointer;
private static CommPortIdentifier portId;
private static Enumeration portList;
private InputStream inputStream;
private SerialPort serialPort;
private OutputStream outputStream;
private LaserVibration laserVibration;
private static boolean outputBufferEmptyFlag = false;
private boolean isVibrating = false;
private boolean connected = false;
private boolean running = true;
private int aliveCounter = 0;
private boolean pushMode = true;
private boolean laserOn = true;
private boolean inertiaOn = false;
private double batmin = 999999;
private double batmax = -1;
//Battery status variables
private long batteryStatusUpdateInterval = 10000;
private long lastUpdateTime = 0;
private Vector<Double> batteryData = new Vector<Double>();
public boolean isInertiaOn() {
return inertiaOn;
}
public void setInertiaOn(boolean inertiaOn) {
// this.inertiaOn = false;
this.inertiaOn = inertiaOn;
updateStatus();
}
private boolean shutDown = false;
public WirelessLaserDriverBlack(Laserpointer laserPointer, String port) {
this.laserPointer = laserPointer;
this.port = port;
init();
updateStatus();
}
public void init() {
boolean portFound = false;
// parse ports and if the specified port is found, initialized the
// reader
portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
portId = (CommPortIdentifier) portList.nextElement();
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
if (portId.getName().equals(port)) {
portFound = true;
start();
break;
}
}
}
if (!portFound) {
if (LOG.isErrorEnabled()) {
LOG.error("serial port " + port + " not found.");
}
laserPointer.publishFailure(new Exception("serial port " + port + " not found."));
}
}
public void run() {
// initalize serial port
try {
serialPort = (SerialPort) portId.open("WirelessLaserDriverBlack",
2000);
} catch (PortInUseException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
laserPointer.publishFailure(e);
return;
}
try {
inputStream = serialPort.getInputStream();
} catch (IOException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
laserPointer.publishFailure(e);
return;
}
try {
serialPort.addEventListener(this);
} catch (TooManyListenersException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
laserPointer.publishFailure(e);
return;
}
// activate the DATA_AVAILABLE notifier
serialPort.notifyOnDataAvailable(true);
try {
// set port parameters
serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
laserPointer.publishFailure(e);
return;
}
initwritetoport();
initLaserPointer();
// initAlive();
// try {
// while (true) {
// // write string to port, the serialEvent will read it
// writetoport();
// Thread.sleep(1000);
// }
// } catch (InterruptedException e) {}
}
public void initwritetoport() {
// initwritetoport() assumes that the port has already been opened and
// initialized
try {
// get the outputstream
outputStream = serialPort.getOutputStream();
} catch (IOException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
laserPointer.publishFailure(e);
return;
}
try {
// activate the OUTPUT_BUFFER_EMPTY notifier
serialPort.notifyOnOutputEmpty(true);
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
laserPointer.publishFailure(e);
return;
}
connected = true;
}
public void initLaserPointer() {
isVibrating = false;
pushMode = true;
laserOn = true;
inertiaOn = laserPointer.isInertialActive();
shutDown = false;
updateStatus();
}
private synchronized void writeSerial(String str) throws IOException {
// logger.debug("write serial "+str);
if (outputStream != null && connected) {
outputStream.write(str.getBytes());
outputStream.flush();
}
}
public void serialEvent(SerialPortEvent event) {
switch (event.getEventType()) {
case SerialPortEvent.BI:
case SerialPortEvent.OE:
case SerialPortEvent.FE:
case SerialPortEvent.PE:
case SerialPortEvent.CD:
case SerialPortEvent.CTS:
case SerialPortEvent.DSR:
case SerialPortEvent.RI:
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
break;
case SerialPortEvent.DATA_AVAILABLE:
// we get here if data has been received
byte[] readBuffer = new byte[20];
if(!connected){
connected = true;
updateStatus();
return;
}
aliveCounter++;
// read data
int numBytes = 0;
try {
while (inputStream.available() > 0) {
numBytes = inputStream.read(readBuffer);
}
} catch (IOException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
laserPointer.publishFailure(e);
return;
}
String[] messageHex = new String[numBytes];
for(int i=0; i<numBytes;i++){
messageHex[i] = asciiToHex(new String(readBuffer,i,1));
}
// read button status
int butBin = Integer.parseInt(messageHex[0], 16);
// read inertia status
float raw_a = 0.0f;
float raw_b = 0.0f;
float raw_c = 0.0f;
double batteryStatus = 0.0;
if (messageHex.length >= 4) {
raw_a = (float)Integer.parseInt(messageHex[1], 16)/200.0f; // Sensor_Z, Wii_-Y
raw_b = (float)Integer.parseInt(messageHex[2], 16)/200.0f; // Sensor_X, Wii_-Z
raw_c = (float)Integer.parseInt(messageHex[3], 16)/200.0f; // Sensor_Y, Wii_X
}
// System.out.println("ECHO: " + raw_b);
if (messageHex.length >= 5) {
//TODO: float? why /255?
batteryStatus = (double)Integer.parseInt(messageHex[4], 16); ///255.0;
}
// System.out.println(messageHex[0] + " | " + messageHex[1] + " | " + messageHex[2]);
List<IData> dataContainer = new ArrayList<IData>(4);
// publish data
if(inertiaOn) {
dataContainer.add(new DataInertial(laserPointer.getClass(), raw_a, raw_b, raw_c));
}
dataContainer.add(new DataButton(Laserpointer.class, DataButton.BUTTON_3, ((butBin & 0x1) > 0)));
dataContainer.add(new DataButton(Laserpointer.class, DataButton.BUTTON_1, ((butBin & 0x2) > 0)));
dataContainer.add(new DataButton(Laserpointer.class, DataButton.BUTTON_2, ((butBin & 0x4) > 0)));
laserPointer.publish(dataContainer);
//if inertia sensor is off, no battery status is sent. Therefore, we'll keep the original value
if(batteryStatus > 0.0) {
//calculate battery status value from 0 to 1 based on hardware specs
//max: 103.0; min: 31.0; critical (vibration): 35.0 corresponds to
//max: 1.0; min: 1.0; crit: 0.05556
batteryStatus = (batteryStatus - 31.0) / 72.0;
if(batteryStatus < 0.0)
batteryStatus = 0.0;
else if(batteryStatus > 1.0)
batteryStatus = 1.0;
batteryData.add(batteryStatus);
if((System.currentTimeMillis() - lastUpdateTime) > batteryStatusUpdateInterval) {
lastUpdateTime = System.currentTimeMillis();
//calculate average of batterydata for last 10 seconds
double avg = 0.0;
for(double d : batteryData) {
avg += d;
}
avg = avg / (double) batteryData.size();
laserPointer.setBatterystatus((float) avg);
batteryData.removeAllElements();
}
}
//Change status LED color on button press
if(laserPointer.getLaserpointerMode() == Laserpointer.LP_MODE_DEFAULT) {
// if(((butBin & 0x1) > 0) || ((butBin & 0x2) > 0) || ((butBin & 0x4) > 0)) {
// if(laserPointer.getLED2color() != Laserpointer.LED_COLOR_BLUE)
// laserPointer.setLED2color(Laserpointer.LED_COLOR_BLUE);
// }
// else if(laserPointer.getLED2color() != Laserpointer.LED_COLOR_GREEN) {
// laserPointer.setLED2color(Laserpointer.LED_COLOR_GREEN);
// }
}
if(laserPointer.getLaserpointerMode() == Laserpointer.LP_MODE_DEMO) {
if((butBin & 0x1) > 0 && laserPointer.getLED5color() != laserPointer.getLED2color()) {
laserPointer.setLED2color(laserPointer.getLED5color());
updateStatus();
vibrate(true, 3000);
}
else if((butBin & 0x2) > 0 && laserPointer.getLED4color() != laserPointer.getLED2color()) {
laserPointer.setLED2color(laserPointer.getLED4color());
updateStatus();
}
else if((butBin & 0x4) > 0 && laserPointer.getLED3color() != laserPointer.getLED2color()) {
laserPointer.setLED2color(laserPointer.getLED3color());
updateStatus();
}
}
break;
}
}
public synchronized void updateLEDStatus(int led) {
if (!connected)
return;
if (led < 1 || led > 5)
return;
byte ledmsg = 0;
if(led == 1) {
ledmsg = (byte) (200 | laserPointer.getLED1color());
}
else if (led == 2) {
ledmsg = (byte) (208 | laserPointer.getLED2color());
}
else if (led == 3) {
ledmsg = (byte) (216 | laserPointer.getLED3color());
}
else if (led == 4) {
ledmsg = (byte) (224 | laserPointer.getLED4color());
}
else if (led == 5) {
ledmsg = (byte) (232 | laserPointer.getLED5color());
}
try {
if (outputStream != null) {
outputStream.write(ledmsg);
outputStream.flush();
}
}
catch (IOException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
laserPointer.publishFailure(e);
}
}
public synchronized void updateStatus() {
if (!connected)
return;
try {
inertiaOn = laserPointer.isInertialActive();
byte led1 = (byte) (200 | laserPointer.getLED1color());
byte led2 = (byte) (208 | laserPointer.getLED2color());
byte led3 = (byte) (216 | laserPointer.getLED3color());
byte led4 = (byte) (224 | laserPointer.getLED4color());
byte led5 = (byte) (232 | laserPointer.getLED5color());
//Push, Intertia, Shutdown
byte push = (byte) (pushMode?1:0);
byte inertia = (byte) (inertiaOn?1:0);
byte shutdown = (byte) (shutDown?1:0);
byte mode1 = (byte)((shutdown * 1) | (inertia * 2) | (push * 4));
//Laser, Vibration
byte laser = (byte)(laserOn?1:0);
byte vibrate = (byte)(isVibrating?1:0);
byte mode2 = (byte) ((vibrate*1) | (laser * 2) | 128);
if (outputStream != null) {
outputStream.write(mode1);
outputStream.write(mode2);
outputStream.write(led1);
outputStream.write(led2);
outputStream.write(led3);
outputStream.write(led4);
outputStream.write(led5);
outputStream.flush();
}
}
catch (IOException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
laserPointer.publishFailure(e);
}
}
private String asciiToHex(String ascii){
StringBuilder hex = new StringBuilder();
for (int i=0; i < ascii.length(); i++) {
hex.append(Integer.toHexString(ascii.charAt(i)));
}
return hex.toString();
}
public void vibrate(boolean vibrate, int duration) {
if (laserVibration != null) {
laserVibration.cancel();
}
isVibrating = vibrate;
updateStatus();
if (duration > 0) {
laserVibration = new LaserVibration(this, duration);
}
}
public void close() {
if(laserPointer.getShutdownOnStop()) {
shutDown = true;
updateStatus();
}
running = false;
try {
inputStream.close();
outputStream.close();
}
catch (Exception e) {
e.printStackTrace();
}
if (laserVibration != null) {
laserVibration.cancel();
}
serialPort.close();
}
}