package org.myrobotlab.service;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.myrobotlab.framework.Service;
import org.myrobotlab.framework.ServiceType;
import org.myrobotlab.logging.Level;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.logging.Logging;
import org.myrobotlab.logging.LoggingFactory;
import org.myrobotlab.service.data.SensorData;
import org.myrobotlab.service.interfaces.DeviceController;
import org.myrobotlab.service.interfaces.RangeListener;
import org.myrobotlab.service.interfaces.SensorControl;
import org.myrobotlab.service.interfaces.SensorController;
import org.slf4j.Logger;
/**
*
* UltrasonicSensor - This will read data from an ultrasonic sensor module
* connected to an android.
*
*/
public class UltrasonicSensor extends Service implements RangeListener, SensorControl {
private static final long serialVersionUID = 1L;
public final static Logger log = LoggerFactory.getLogger(UltrasonicSensor.class);
public final Set<String> types = new HashSet<String>(Arrays.asList("SR04"));
private int pings;
private Integer trigPin = null;
private Integer echoPin = null;
private String type = "SR04";
private Integer lastRaw;
private Integer lastRange;
// for blocking asynchronous data
private boolean isBlocking = false;
transient private BlockingQueue<Integer> data = new LinkedBlockingQueue<Integer>();
private transient SensorController controller;
String controllerName;
public UltrasonicSensor(String n) {
super(n);
}
// ---- part of interfaces begin -----
// Uber good - .. although this is "chained" versus star routing
// Star routing would be routing from the Arduino directly to the Listener
// The "chained" version takes 2 thread contexts :( .. but it has the
// benefit
// of the "publishRange" method being affected by the Sensor service e.g.
// change units, sample rate, etc
// FIXME - NOT SERVICE .. possibly name or interface but not service
public void addRangeListener(Service service) {
addListener("publishRange", service.getName(), "onRange");
}
public void attach(SensorController controller, int trigPin, int echoPin) throws Exception {
this.controller = controller;
this.trigPin = trigPin;
this.echoPin = echoPin;
controller.deviceAttach(this, trigPin, echoPin);
}
// FIXME - should be MicroController Interface ..
public SensorController getController() {
return controller;
}
public int getEchoPin() {
return echoPin;
}
public int getTriggerPin() {
return trigPin;
}
@Override
public void onRange(Long range) {
log.info(String.format("RANGE: %d", range));
}
/* FIXME !!! IMPORTANT PUT IN INTERFACE & REMOVE SELF FROM ARDUINO !!! */
public Integer publishRange(Integer duration) {
++pings;
lastRange = duration / 58;
log.info("publishRange {}", lastRange);
return lastRange;
}
public int range() {
return range(10);
}
public Integer range(int timeout) {
Integer ret = null;
try {
data.clear();
startRanging(timeout);
// sendMsg(GET_VERSION);
ret = data.poll(timeout, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Logging.logError(e);
}
data.clear(); // double tap
return ret;// controller.pulseIn(trigPin, echoPin, timeout);
}
public boolean setType(String type) {
if (types.contains(type)) {
this.type = type;
return true;
}
return false;
}
// ---- part of interfaces end -----
public void startRanging() {
startRanging(10); // 10000 uS = 10 ms
}
public void startRanging(int timeoutMS) {
// controller.sensorPollingStart(getName(), timeoutMS); ?? FIXME - add
// timeoutMs as part of attach config or
// setting in a command method
controller.sensorActivate(this, timeoutMS);
}
public void stopRanging() {
controller.sensorDeactivate(this);
}
// probably should do this in a util class
public static int byteArrayToInt(int[] b) {
return b[3] & 0xFF | (b[2] & 0xFF) << 8 | (b[1] & 0xFF) << 16 | (b[0] & 0xFF) << 24;
}
public static void main(String[] args) {
LoggingFactory.getInstance().configure();
LoggingFactory.getInstance().setLevel(Level.INFO);
try {
// Runtime.start("gui", "GUIService");
/*
* int [] config = new int[]{1,2}; int [] payload = new
* int[config.length + 2]; payload = Arrays.copyOfRange(config, 0,
* 2);
*/
Runtime.start("srf05", "UltrasonicSensor");
Runtime.start("python", "Python");
Runtime.start("gui", "GUIService");
/*
* srf05.attach("COM9", 7);
*
* Runtime.start("webgui", "WebGui");
*
* Arduino arduino = srf05.getController(); arduino.digitalWrite(13,
* 1); arduino.digitalWrite(13, 0); arduino.digitalWrite(13, 1);
* arduino.digitalWrite(13, 0); arduino.digitalWrite(13, 1); Integer
* version = arduino.getVersion(); log.info("version {}", version);
* version = arduino.getVersion(); log.info("version {}", version);
* version = arduino.getVersion(); log.info("version {}", version);
*
* srf05.startRanging();
*
* srf05.stopRanging();
*
* int x = srf05.range();
*/
log.info("here");
} catch (Exception e) {
Logging.logError(e);
}
}
/**
* This static method returns all the details of the class without it having
* to be constructed. It has description, categories, dependencies, and peer
* definitions.
*
* @return ServiceType - returns all the data
*
*/
static public ServiceType getMetaData() {
ServiceType meta = new ServiceType(UltrasonicSensor.class.getCanonicalName());
meta.addDescription("Ranging sensor");
meta.addCategory("sensor");
return meta;
}
public int getPings() {
return pings;
}
public String getType() {
return type;
}
/**
* interface from SensorController
* we expect a int[] of a count of (units)
* until echo was heard - we will convert it
* to a preferered units here
*/
@Override
public void onSensorData(SensorData event) {
// FIXME - convert to appropriate range
// inches/meters/other kubits?
int[] rawData = (int[])event.getData();
++pings;
lastRaw = byteArrayToInt(rawData);
if (isBlocking) {
try {
data.put(lastRaw);
} catch (InterruptedException e) {
Logging.logError(e);
}
}
invoke("publishRange", lastRaw);
}
// FIXME should be done in "default" interface or abstract class :P
@Override
public boolean isAttached() {
return controller != null;
}
@Override
public void setController(DeviceController controller) {
this.controller = (SensorController)controller;
}
@Override
public void activate(Object... conf) {
// TODO Auto-generated method stub
}
@Override
public void deactivate() {
// TODO Auto-generated method stub
}
@Override
public void attach(SensorController controller, Object... conf) {
// TODO Auto-generated method stub
}
}