hal/src/se/koc/hal/plugin/localsensor/RPiImpulseCountSensor.java
Daniel Collin 5036ec90e9 Using UTC instead of LOCAL time.
Former-commit-id: 21831d97d7f616313e0e90bf8ae69e04dea57685
2015-12-18 10:00:27 +01:00

151 lines
5.5 KiB
Java
Executable file

package se.koc.hal.plugin.localsensor;
import java.sql.SQLException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import zutil.db.DBConnection;
import zutil.log.LogUtil;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalInput;
import com.pi4j.io.gpio.Pin;
import com.pi4j.io.gpio.PinPullResistance;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;
import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
import com.pi4j.io.gpio.event.GpioPinListenerDigital;
public class RPiImpulseCountSensor implements Runnable {
private static final Logger logger = LogUtil.getLogger();
private static final int REPORT_TIMEOUT = 60_000; //one minute
private long nanoSecondsSleep = REPORT_TIMEOUT * 1_000_000L;
private volatile Integer impulseCount = 0;
private ExecutorService executorPool;
private final DBConnection db;
private final int sensorId;
public static void main(String args[]) throws Exception {
new RPiImpulseCountSensor(2, RaspiPin.GPIO_02);
}
/**
* Constructor
* @param sensorId The ID of this sensor. Will be written to the DB
* @throws Exception
*/
public RPiImpulseCountSensor(int sensorId, Pin pin) throws Exception{
this.sensorId = sensorId;
// create gpio controller
GpioController gpio = null;
try{
gpio = GpioFactory.getInstance();
}catch(IllegalArgumentException e){
logger.log(Level.SEVERE, "", e);
throw e;
}catch(UnsatisfiedLinkError e){
logger.log(Level.SEVERE, "", e);
throw e;
}
// provision gpio pin as an input pin with its internal pull up resistor enabled
final GpioPinDigitalInput irLightSensor = gpio.provisionDigitalInputPin(pin, PinPullResistance.PULL_UP);
// create and register gpio pin listener. May require the program to be run as sudo if the GPIO pin has not been exported
irLightSensor.addListener(new GpioPinListenerDigital() {
@Override
public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
if(event.getState() == PinState.LOW){ //low = light went on
//System.out.println("IR LED turned ON");
synchronized(impulseCount){
impulseCount++;
}
}
}
});
// setup a thread pool for executing database jobs
this.executorPool = Executors.newCachedThreadPool();
// Connect to the database
logger.info("Connecting to db...");
db = new DBConnection(DBConnection.DBMS.SQLite, "hal.db");
//start a daemon thread to save the impulse count every minute
Thread thread = new Thread(this);
thread.setDaemon(false);
thread.start();
}
/**
* This loop will try to save the current time and the number of impulses seen every [IMPULSE_REPORT_TIMEOUT] milliseconds.
* Every iteration the actual loop time will be evaluated and used to calculate the time for the next loop.
*/
@Override
public void run() {
long startTime = System.nanoTime();
synchronized(impulseCount){
impulseCount = 0; //reset the impulse count
}
while(true) {
sleepNano(nanoSecondsSleep); //sleep for some time. This variable will be modified every loop to compensate for the loop time spent.
int count = -1;
synchronized(impulseCount){
count = impulseCount;
impulseCount = 0;
}
save(System.currentTimeMillis(), count); //save the impulse count
long estimatedNanoTimeSpent = System.nanoTime() - startTime; //this is where the loop ends
startTime = System.nanoTime(); //this is where the loop starts from now on
if(estimatedNanoTimeSpent > 0){ //if no overflow
long nanoSecondsTooMany = estimatedNanoTimeSpent - (REPORT_TIMEOUT*1000000L);
//System.out.println("the look took ~" + estimatedNanoTimeSpent + "ns. That is " + nanoSecondsTooMany/1000000L + "ms off");
nanoSecondsSleep -= nanoSecondsTooMany / 3; //divide by constant to take into account varaiations im loop time
}
}
}
/**
* Sleep for [ns] nanoseconds
* @param ns
*/
private void sleepNano(long ns){
//System.out.println("will go to sleep for " + ns + "ns");
try{
Thread.sleep(ns/1000000L, (int)(ns%1000000L));
}catch(InterruptedException e){
//ignore
}
}
/**
* Saves the data to the database.
* This method should block the caller as short time as possible.
* Try to make the time spent in the method the same for every call (low variation).
*
* @param timestamp_end
* @param data
*/
private void save(final long timestamp_end, final int data){
//offload the timed loop by not doing the db interaction in this thread.
executorPool.execute(new Runnable(){
@Override
public void run() {
try {
db.exec("INSERT INTO sensor_data_raw(timestamp, sensor_id, data) VALUES("+timestamp_end+", "+RPiImpulseCountSensor.this.sensorId+", "+data+")");
} catch (SQLException e) {
e.printStackTrace();
}
}
});
}
}