/*
* See COPYING in top-level directory.
*/
package com.monkygames.wox.health.io;
// === java imports === //
import java.util.Calendar;
import java.util.Formatter;
import java.util.Vector;
// === motej imports === //
import motej.*;
import motej.event.*;
import motej.demos.common.SimpleMoteFinder;
import motej.request.ReportModeRequest;
// === motejx imports === //
import motejx.extensions.balanceboard.*;
// === wox imports === //
import com.monkygames.wox.health.data.Weight;
/**
* Handles communicating with the wii balance board.
* @version 1.0
*/
public class Scale implements BalanceBoardListener{
//private SimpleMoteFinder finder;
private BalanceBoardFinder finder;
private Mote mote;
private BalanceBoard balanceBoard;
private float[] samples;
private int samplePointer;
/**
* Used for calculating the tare.
**/
private float[] tares;
private int tarePointer;
/**
* True when the scale has measured someone.
**/
private boolean isMeasured;
/**
* True when the scale should start measuring.
**/
private boolean doMeasure;
/**
* Used for gettnig the tare weight.
**/
private boolean doTare;
/**
* True when tare is ready.
**/
private boolean isTared;
/**
* Used for calibrating the scale.
**/
private float tare;
/**
* True if tare should be used.
**/
private boolean tareEnabled;
private static final int SAMPLE_SIZE = 1000;
private Vector <ScaleInterface> scaleInterfaceV;
private boolean balanceBoardFound = false;
private String batteryLevel;
// ============= Constructors ============== //
public Scale(){
finder = new BalanceBoardFinder();
samples = new float[SAMPLE_SIZE];
samplePointer = 0;
tares = new float[100];
tarePointer = 0;
scaleInterfaceV = new Vector<ScaleInterface>();
}
// ============= Public Methods ============== //
/**
* Prepares the scale to weigh a person.
* @return true if a wii balance board has been found and false otherwise.
**/
public boolean initScale(){
try{
System.out.println("[Scale:initScale]");
//mote = finder.findMote();
mote = finder.findBalanceBoard(45*1000); // 45 seconds
System.out.println("[Scale:initScale] mote found = "+mote);
if(!initMote()){
balanceBoardFound = false;
return false;
}
}catch(Exception e){ return false;}
balanceBoardFound = true;
return true;
}
/**
* Calibrates the scale.
**/
public void calibrate(){
if(balanceBoard != null){
//balanceBoard.doCalibration();
}
}
/**
* Sets the tare to be used and returns the tare value.
* @return the tare value to be used.
**/
public float getTare(){
tarePointer = 0;
doTare = true;
isTared = false;
// need to wait for it to complete
while(!isTared){
try{
Thread.sleep(1000);
}catch(Exception e){ return 0;}
}
tareEnabled = true;
// calculate weight
return calcTare();
}
/**
* Removes tare from weight calculation.
**/
public void setNoTare(){
tareEnabled = false;
}
/**
* A Blocking method that returns the weight.
* @return the weight that was calculated and null on error.
**/
public Weight getWeight(){
Weight weight = new Weight();
samplePointer = 0;
doMeasure = true;
doTare = false;
isMeasured = false;
// need to wait for it to complete
while(!isMeasured){
try{
Thread.sleep(1000);
}catch(Exception e){ return null;}
}
// calculate weight
weight.weightInkg = calcWeight();
weight.date = Calendar.getInstance().getTimeInMillis();
return weight;
}
/**
* Terminates the scale connection.
**/
public void shutdown(){
try{
finder.shutdown();
isMeasured = true;
}catch(Exception e){}
}
/**
* Adds a scale interface to get scale reports.
* @param scaleInterface the interface to add.
**/
public synchronized void addScaleInterface(ScaleInterface scaleInterface){
scaleInterfaceV.add(scaleInterface);
}
/**
* Removes an interface.
* @param scaleInterface the interface to be removed.
**/
public synchronized void removeScaleInterface(ScaleInterface scaleInterface){
scaleInterfaceV.remove(scaleInterface);
}
/**
* True if the balance board has been found and false if not found.
* @return true if found and false otherwise.
**/
public boolean balanceBoardFound(){
return balanceBoardFound;
}
/**
* Returns the string of the battery level.
* @return the battery level.
**/
public String getBatteryLevel(){
return batteryLevel;
}
// ============= Protected Methods ============== //
// ============= Private Methods ============== //
/**
* Handles initializing the balance board.
* @return true if initializations is successful and false if the balance board wasn't found or other bluetooth errors.
**/
private boolean initMote(){
System.out.println("[Scale: initMote]");
mote.setPlayerLeds(new boolean[]{true,false,false,false});
// get status report
statusReport();
// get calibration report
calReport();
// check the extension
return getBalanceBoardExt();
}
/**
* Returns true if this mote is a balance board.
* @return true if mote is a balance board and false otherwise.
**/
private boolean getBalanceBoardExt(){
System.out.println("[Scale:getBalanceBoardExt]");
Extension ext = mote.getExtension();
if(ext == null){
shutdown();
return false;
}
if(ext instanceof BalanceBoard){
balanceBoard = (BalanceBoard)ext;
//balanceBoard.doCalibration();
BalanceBoardCalibrationData data = balanceBoard.getCalibrationData();
balanceBoard.addBalanceBoardListener(this);
mote.setReportMode(ReportModeRequest.DATA_REPORT_0x32);
return true;
}
shutdown();
return false;
}
/**
* Waits for the status report to be generated.
**/
private void statusReport(){
// get status report
while(mote.getStatusInformationReport() == null){
try{
Thread.sleep(1000);
}catch(Exception e){}
}
StatusInformationReport report = mote.getStatusInformationReport();
// get battery life and set it!
Formatter formatter = new Formatter();
batteryLevel = formatter.format("%d", report.getBatteryLevel()/(byte)0xD0*100)+"%";
}
/**
* Waits for the calibration report.
**/
private void calReport(){
try{
Thread.sleep(1000);
}catch(Exception e){}
while(mote.getCalibrationDataReport() == null){
try{
Thread.sleep(1000);
}catch(Exception e){}
}
CalibrationDataReport calReport = mote.getCalibrationDataReport();
}
/**
* Takes the median of all samples collected.
* @see SAMPLE_SIZE
* @return the median weight.
**/
private float calcWeight(){
/*
float total = 0f;
for(int i = 0; i < samples.length; i++){
total += samples[i];
}
return total/samples.length;
*/
return samples[samples.length/2];
}
/**
* Takes the median of all tares collected.
**/
private float calcTare(){
tare = tares[tares.length/2];
return tare;
}
// ============= Implemented Methods ============== //
public synchronized void balanceBoardChanged(BalanceBoardEvent evt){
float val1 = evt.getBottomLeftInterpolated();
float val2 = evt.getBottomRightInterpolated();
float val3 = evt.getTopLeftInterpolated();
float val4 = evt.getTopRightInterpolated();
float total = val1+val2+val3+val4;
if(doTare){
if(tarePointer == tares.length -1){
isTared = true;
doTare = false;
}else{
System.out.println("total = "+total);
if(tarePointer == 0){
tares[0] = total;
}else{
if(tares[tarePointer-1] > total){
tares[tarePointer] = tares[tarePointer-1];
tares[tarePointer-1] = total;
}else{
tares[tarePointer] = total;
}
}
tarePointer++;
}
}
if(tareEnabled){
total = total - tare;
}
// update all interfaces
for(int i = 0; i < scaleInterfaceV.size(); i++){
scaleInterfaceV.elementAt(i).weightUpdate(total);
}
if(doMeasure){
if(samplePointer == SAMPLE_SIZE -1){
// reset vars
isMeasured = true;
doMeasure = false;
// sort samples as you insert them
// note, sorting from smallest to largest where position 0 is smallest and
// position n-1 is largest value
}else{
if(samplePointer == 0){
samples[0] = total;
}else{
if(samples[samplePointer-1] > total){
samples[samplePointer] = samples[samplePointer-1];
samples[samplePointer-1] = total;
}else{
samples[samplePointer] = total;
}
}
}
samplePointer++;
}
}
public void extensionConnected(ExtensionEvent evt){
System.out.println("[Scale:extensionConnected] isconnected="+evt.isExtensionConnected());
}
public void extensionDisconnected(ExtensionEvent evt){
}
// ============= Extended Methods ============== //
// ============= Internal Classes ============== //
// ============= Static Methods ============== //
}
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* End:
*
* vim: ts=8 sts=4 sw=4 noexpandtab
*/