Using UTC instead of LOCAL time.
Former-commit-id: 21831d97d7f616313e0e90bf8ae69e04dea57685
This commit is contained in:
parent
4d9589dc9a
commit
5036ec90e9
6 changed files with 461 additions and 382 deletions
|
|
@ -1,116 +1,117 @@
|
|||
package se.koc.hal;
|
||||
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.DBUpgradeHandler;
|
||||
import zutil.db.handler.PropertiesSQLResult;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class HalContext {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
// Constants
|
||||
private static final String PROPERTY_DB_VERSION = "db_version";
|
||||
|
||||
private static final String CONF_FILE = "hal.conf";
|
||||
private static final String DB_FILE = "hal.db";
|
||||
private static final String DEFAULT_DB_FILE = "hal-default.db";
|
||||
|
||||
// Variables
|
||||
private static DBConnection db;
|
||||
|
||||
private static Properties defaultFileConf;
|
||||
private static Properties fileConf;
|
||||
private static Properties dbConf;
|
||||
|
||||
|
||||
static {
|
||||
defaultFileConf = new Properties();
|
||||
defaultFileConf.setProperty("http_port", ""+8080);
|
||||
defaultFileConf.setProperty("sync_port", ""+6666);
|
||||
}
|
||||
|
||||
|
||||
public static void initialize(){
|
||||
try {
|
||||
// Read conf
|
||||
fileConf = new Properties(defaultFileConf);
|
||||
FileReader in = new FileReader(CONF_FILE);
|
||||
fileConf.load(in);
|
||||
in.close();
|
||||
|
||||
// Init DB
|
||||
File dbFile = FileUtil.find(DB_FILE);
|
||||
if(dbFile == null){
|
||||
logger.info("Creating new DB...");
|
||||
FileUtil.copy(dbFile, FileUtil.find(DEFAULT_DB_FILE));
|
||||
}
|
||||
db = new DBConnection(DBConnection.DBMS.SQLite, DB_FILE);
|
||||
|
||||
// Read DB conf
|
||||
dbConf = db.exec("SELECT * FROM conf", new PropertiesSQLResult());
|
||||
|
||||
// Upgrade DB needed?
|
||||
DBConnection referenceDB = new DBConnection(DBConnection.DBMS.SQLite, DEFAULT_DB_FILE);
|
||||
Properties defaultDBConf =
|
||||
referenceDB.exec("SELECT * FROM conf", new PropertiesSQLResult());
|
||||
// Check DB version
|
||||
logger.fine("DB version: "+ dbConf.getProperty(PROPERTY_DB_VERSION));
|
||||
if(dbConf.getProperty(PROPERTY_DB_VERSION) == null ||
|
||||
defaultDBConf.getProperty(PROPERTY_DB_VERSION).compareTo(dbConf.getProperty(PROPERTY_DB_VERSION)) > 0) {
|
||||
logger.info("Starting DB upgrade...");
|
||||
File backupDB = FileUtil.getNextFile(dbFile);
|
||||
logger.fine("Backing up DB to: "+ backupDB);
|
||||
FileUtil.copy(dbFile, backupDB);
|
||||
|
||||
logger.fine(String.format("Upgrading DB (from: v%s, to: v%s)...",
|
||||
dbConf.getProperty(PROPERTY_DB_VERSION),
|
||||
defaultDBConf.getProperty(PROPERTY_DB_VERSION)));
|
||||
DBUpgradeHandler handler = new DBUpgradeHandler(referenceDB);
|
||||
handler.setTargetDB(db);
|
||||
//handler.setForcedDBUpgrade(true);
|
||||
handler.upgrade();
|
||||
|
||||
logger.info("DB upgrade done");
|
||||
dbConf.setProperty(PROPERTY_DB_VERSION, defaultDBConf.getProperty(PROPERTY_DB_VERSION));
|
||||
storeProperties();
|
||||
}
|
||||
referenceDB.close();
|
||||
} catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getStringProperty(String key){
|
||||
String value = fileConf.getProperty(key);
|
||||
if(value == null)
|
||||
value = dbConf.getProperty(key);
|
||||
return value;
|
||||
}
|
||||
public static int getIntegerProperty(String key){
|
||||
return Integer.parseInt(getStringProperty(key));
|
||||
}
|
||||
public synchronized static void storeProperties() throws SQLException {
|
||||
logger.fine("Saving conf to DB...");
|
||||
PreparedStatement stmt = db.getPreparedStatement("REPLACE INTO conf (key, value) VALUES (?, ?)");
|
||||
for(Object key : dbConf.keySet()){
|
||||
stmt.setObject(1, key);
|
||||
stmt.setObject(2, dbConf.get(key));
|
||||
stmt.addBatch();
|
||||
}
|
||||
DBConnection.execBatch(stmt);
|
||||
}
|
||||
|
||||
public static DBConnection getDB(){
|
||||
return db;
|
||||
}
|
||||
|
||||
}
|
||||
package se.koc.hal;
|
||||
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.DBUpgradeHandler;
|
||||
import zutil.db.handler.PropertiesSQLResult;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class HalContext {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
// Constants
|
||||
private static final String PROPERTY_DB_VERSION = "db_version";
|
||||
|
||||
private static final String CONF_FILE = "hal.conf";
|
||||
private static final String DB_FILE = "hal.db";
|
||||
private static final String DEFAULT_DB_FILE = "hal-default.db";
|
||||
|
||||
// Variables
|
||||
private static DBConnection db;
|
||||
|
||||
private static Properties defaultFileConf;
|
||||
private static Properties fileConf;
|
||||
private static Properties dbConf;
|
||||
|
||||
|
||||
static {
|
||||
defaultFileConf = new Properties();
|
||||
defaultFileConf.setProperty("http_port", ""+8080);
|
||||
defaultFileConf.setProperty("sync_port", ""+6666);
|
||||
}
|
||||
|
||||
|
||||
public static void initialize(){
|
||||
try {
|
||||
// Read conf
|
||||
fileConf = new Properties(defaultFileConf);
|
||||
FileReader in = new FileReader(CONF_FILE);
|
||||
fileConf.load(in);
|
||||
in.close();
|
||||
|
||||
// Init DB
|
||||
File dbFile = FileUtil.find(DB_FILE);
|
||||
if(dbFile == null){
|
||||
logger.info("Creating new DB...");
|
||||
FileUtil.copy(dbFile, FileUtil.find(DEFAULT_DB_FILE));
|
||||
}
|
||||
db = new DBConnection(DBConnection.DBMS.SQLite, DB_FILE);
|
||||
|
||||
// Read DB conf
|
||||
dbConf = db.exec("SELECT * FROM conf", new PropertiesSQLResult());
|
||||
|
||||
// Upgrade DB needed?
|
||||
DBConnection referenceDB = new DBConnection(DBConnection.DBMS.SQLite, DEFAULT_DB_FILE);
|
||||
Properties defaultDBConf =
|
||||
referenceDB.exec("SELECT * FROM conf", new PropertiesSQLResult());
|
||||
// Check DB version
|
||||
logger.fine("DB version: "+ dbConf.getProperty(PROPERTY_DB_VERSION));
|
||||
int defaultDBVersion = Integer.parseInt(defaultDBConf.getProperty(PROPERTY_DB_VERSION));
|
||||
int dbVersion = Integer.parseInt(dbConf.getProperty(PROPERTY_DB_VERSION));
|
||||
if(dbConf.getProperty(PROPERTY_DB_VERSION) == null || defaultDBVersion > dbVersion ) {
|
||||
logger.info("Starting DB upgrade...");
|
||||
File backupDB = FileUtil.getNextFile(dbFile);
|
||||
logger.fine("Backing up DB to: "+ backupDB);
|
||||
FileUtil.copy(dbFile, backupDB);
|
||||
|
||||
logger.fine(String.format("Upgrading DB (from: v%s, to: v%s)...",
|
||||
dbConf.getProperty(PROPERTY_DB_VERSION),
|
||||
defaultDBConf.getProperty(PROPERTY_DB_VERSION)));
|
||||
DBUpgradeHandler handler = new DBUpgradeHandler(referenceDB);
|
||||
handler.setTargetDB(db);
|
||||
//handler.setForcedDBUpgrade(true);
|
||||
handler.upgrade();
|
||||
|
||||
logger.info("DB upgrade done");
|
||||
dbConf.setProperty(PROPERTY_DB_VERSION, defaultDBConf.getProperty(PROPERTY_DB_VERSION));
|
||||
storeProperties();
|
||||
}
|
||||
referenceDB.close();
|
||||
} catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getStringProperty(String key){
|
||||
String value = fileConf.getProperty(key);
|
||||
if(value == null)
|
||||
value = dbConf.getProperty(key);
|
||||
return value;
|
||||
}
|
||||
public static int getIntegerProperty(String key){
|
||||
return Integer.parseInt(getStringProperty(key));
|
||||
}
|
||||
public synchronized static void storeProperties() throws SQLException {
|
||||
logger.fine("Saving conf to DB...");
|
||||
PreparedStatement stmt = db.getPreparedStatement("REPLACE INTO conf (key, value) VALUES (?, ?)");
|
||||
for(Object key : dbConf.keySet()){
|
||||
stmt.setObject(1, key);
|
||||
stmt.setObject(2, dbConf.get(key));
|
||||
stmt.addBatch();
|
||||
}
|
||||
DBConnection.execBatch(stmt);
|
||||
}
|
||||
|
||||
public static DBConnection getDB(){
|
||||
return db;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,11 +45,11 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
|||
logger.fine("The sensor is of type: " + sensor.getType());
|
||||
if(sensor.getType().equals("PowerMeter")){
|
||||
logger.fine("aggregating raw data to five minute periods");
|
||||
aggregateRawData(sensor.getId(), TimeUtility.FIVE_MINUTES_IN_MS, 5, sensor.getAggregationMethod());
|
||||
aggregateRawData(sensor, TimeUtility.FIVE_MINUTES_IN_MS, 5);
|
||||
logger.fine("aggregating five minute periods into hour periods");
|
||||
aggrigateAggregatedData(sensor.getId(), TimeUtility.FIVE_MINUTES_IN_MS, TimeUtility.HOUR_IN_MS, 12, sensor.getAggregationMethod());
|
||||
aggrigateAggregatedData(sensor, TimeUtility.FIVE_MINUTES_IN_MS, TimeUtility.HOUR_IN_MS);
|
||||
logger.fine("aggregating one hour periods into one day periods");
|
||||
aggrigateAggregatedData(sensor.getId(), TimeUtility.HOUR_IN_MS, TimeUtility.DAY_IN_MS, 24, sensor.getAggregationMethod());
|
||||
aggrigateAggregatedData(sensor, TimeUtility.HOUR_IN_MS, TimeUtility.DAY_IN_MS);
|
||||
}else{
|
||||
logger.fine("The sensor type is not supported by the aggregation daemon. Ignoring");
|
||||
}
|
||||
|
|
@ -60,7 +60,9 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
|||
* @param sensorId The sensor for to aggregate data
|
||||
* @param toPeriodSizeInMs The period length in ms to aggregate to
|
||||
*/
|
||||
private void aggregateRawData(long sensorId, long toPeriodSizeInMs, int expectedSampleCount, AggregationMethod aggrMethod){
|
||||
private void aggregateRawData(HalSensor sensor, long toPeriodSizeInMs, int expectedSampleCount){
|
||||
long sensorId = sensor.getId();
|
||||
AggregationMethod aggrMethod = sensor.getAggregationMethod();
|
||||
DBConnection db = HalContext.getDB();
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
|
|
@ -73,8 +75,8 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
|||
Long maxTimestampFoundForSensor = DBConnection.exec(stmt, new SimpleSQLResult<Long>());
|
||||
if(maxTimestampFoundForSensor == null)
|
||||
maxTimestampFoundForSensor = 0l;
|
||||
long currentPeriodStartTimestamp = TimeUtility.getTimestampPeriodStart(toPeriodSizeInMs, System.currentTimeMillis());
|
||||
logger.fine("Calculating periods... (from:"+ maxTimestampFoundForSensor +", to:"+ currentPeriodStartTimestamp +")");
|
||||
long currentPeriodStartTimestamp = TimeUtility.getTimestampPeriodStart_UTC(toPeriodSizeInMs, System.currentTimeMillis());
|
||||
logger.fine("Calculating periods... (from:"+ maxTimestampFoundForSensor +", to:"+ currentPeriodStartTimestamp +") with expected sample count: " + expectedSampleCount);
|
||||
stmt = db.getPreparedStatement("SELECT *, 1 AS confidence, timestamp AS timestamp_start FROM sensor_data_raw"
|
||||
+" WHERE sensor_id == ?"
|
||||
+ " AND ? < timestamp"
|
||||
|
|
@ -95,7 +97,10 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
|||
* @param fromPeriodSizeInMs The period length in ms to aggregate from
|
||||
* @param toPeriodSizeInMs The period length in ms to aggregate to
|
||||
*/
|
||||
private void aggrigateAggregatedData(long sensorId, long fromPeriodSizeInMs, long toPeriodSizeInMs, int expectedSampleCount, AggregationMethod aggrMethod){
|
||||
private void aggrigateAggregatedData(HalSensor sensor, long fromPeriodSizeInMs, long toPeriodSizeInMs){
|
||||
long sensorId = sensor.getId();
|
||||
AggregationMethod aggrMethod = sensor.getAggregationMethod();
|
||||
int expectedSampleCount = (int)Math.ceil((double)toPeriodSizeInMs / (double)fromPeriodSizeInMs);
|
||||
DBConnection db = HalContext.getDB();
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
|
|
@ -108,8 +113,8 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
|||
Long maxTimestampFoundForSensor = DBConnection.exec(stmt, new SimpleSQLResult<Long>());
|
||||
if(maxTimestampFoundForSensor == null)
|
||||
maxTimestampFoundForSensor = 0l;
|
||||
long currentPeriodStartTimestamp = TimeUtility.getTimestampPeriodStart(toPeriodSizeInMs, System.currentTimeMillis());
|
||||
logger.fine("Calculating periods... (from:"+ maxTimestampFoundForSensor +", to:"+ currentPeriodStartTimestamp +")");
|
||||
long currentPeriodStartTimestamp = TimeUtility.getTimestampPeriodStart_UTC(toPeriodSizeInMs, System.currentTimeMillis());
|
||||
logger.fine("Calculating periods... (from:"+ maxTimestampFoundForSensor +", to:"+ currentPeriodStartTimestamp +") with expected sample count: " + expectedSampleCount);
|
||||
|
||||
stmt = db.getPreparedStatement("SELECT * FROM sensor_data_aggr"
|
||||
+" WHERE sensor_id == ?"
|
||||
|
|
@ -160,7 +165,7 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
|||
throw new IllegalArgumentException("found entry for aggregation for the wrong sensorId (expecting: "+sensorId+", but was: "+result.getInt("sensor_id")+")");
|
||||
}
|
||||
long timestamp = result.getLong("timestamp_start");
|
||||
long periodTimestamp = TimeUtility.getTimestampPeriodStart(this.aggrTimeInMs, timestamp);
|
||||
long periodTimestamp = TimeUtility.getTimestampPeriodStart_UTC(this.aggrTimeInMs, timestamp);
|
||||
if(currentPeriodTimestamp != 0 && periodTimestamp != currentPeriodTimestamp){
|
||||
float aggrConfidence = confidenceSum / (float)this.expectedSampleCount;
|
||||
float data = -1;
|
||||
|
|
|
|||
|
|
@ -42,15 +42,14 @@ public class DataDeletionDaemon extends TimerTask implements HalDaemon {
|
|||
|
||||
public void cleanupSensor(HalSensor sensor) {
|
||||
logger.fine("The sensor is of type: " + sensor.getType());
|
||||
if(sensor.getType().equals("PowerMeter")){
|
||||
//if(sensor.isInternal()){ //TODO
|
||||
cleanupInternalSensorData(sensor.getId(), TimeUtility.HOUR_IN_MS, TimeUtility.FIVE_MINUTES_IN_MS, TimeUtility.DAY_IN_MS);
|
||||
cleanupInternalSensorData(sensor.getId(), TimeUtility.DAY_IN_MS, TimeUtility.HOUR_IN_MS, TimeUtility.WEEK_IN_MS);
|
||||
//}else{ //TODO
|
||||
//cleanupExternalSensorData(sensor.getId(), TimeUtility.FIVE_MINUTES_IN_MS, TimeUtility.DAY_IN_MS);
|
||||
//cleanupExternalSensorData(sensor.getId(), TimeUtility.DAY_IN_MS, TimeUtility.WEEK_IN_MS);
|
||||
//}
|
||||
clearPeriodsOfWrongLenght(sensor.getId(), TimeUtility.FIVE_MINUTES_IN_MS, TimeUtility.HOUR_IN_MS, TimeUtility.WEEK_IN_MS);
|
||||
if(sensor.getType().equals("PowerMeter")){ //TODO: use instanceof instead
|
||||
if(sensor.getUser().isExternal()){
|
||||
cleanupExternalSensorData(sensor.getId(), TimeUtility.FIVE_MINUTES_IN_MS, TimeUtility.DAY_IN_MS);
|
||||
cleanupExternalSensorData(sensor.getId(), TimeUtility.DAY_IN_MS, TimeUtility.WEEK_IN_MS);
|
||||
}else{
|
||||
cleanupInternalSensorData(sensor.getId(), TimeUtility.HOUR_IN_MS, TimeUtility.FIVE_MINUTES_IN_MS, TimeUtility.DAY_IN_MS);
|
||||
cleanupInternalSensorData(sensor.getId(), TimeUtility.DAY_IN_MS, TimeUtility.HOUR_IN_MS, TimeUtility.WEEK_IN_MS);
|
||||
}
|
||||
}else{
|
||||
logger.fine("The sensor type is not supported by the cleanup deamon. Ignoring");
|
||||
}
|
||||
|
|
@ -69,7 +68,6 @@ public class DataDeletionDaemon extends TimerTask implements HalDaemon {
|
|||
try {
|
||||
Long maxDBTimestamp = null;
|
||||
|
||||
// delete too old 5 minute periods that already have been aggregated into hours
|
||||
stmt = db.getPreparedStatement("SELECT MAX(timestamp_end) FROM sensor_data_aggr"
|
||||
+" WHERE sensor_id == ? AND timestamp_end-timestamp_start == ?");
|
||||
stmt.setLong(1, sensorId);
|
||||
|
|
@ -117,32 +115,6 @@ public class DataDeletionDaemon extends TimerTask implements HalDaemon {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will delete all aggregated entries for a sensor id where the period length is not expected
|
||||
* @param sensorId
|
||||
* @param expectedPeriodLengths
|
||||
*/
|
||||
private void clearPeriodsOfWrongLenght(long sensorId, long... expectedPeriodLengths){
|
||||
DBConnection db = HalContext.getDB();
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
StringBuilder querry = new StringBuilder("SELECT * FROM sensor_data_aggr WHERE sensor_id == ?");
|
||||
for(int i = 0; i < expectedPeriodLengths.length; ++i){
|
||||
querry.append(" AND timestamp_end-timestamp_start != ?");
|
||||
}
|
||||
stmt = db.getPreparedStatement(querry.toString());
|
||||
for(int i = 0; i < expectedPeriodLengths.length; ++i){
|
||||
stmt.setLong(i+1, expectedPeriodLengths[i]);
|
||||
}
|
||||
long deletedRows = DBConnection.exec(stmt, new AggregateDataDeleter(sensorId));
|
||||
if(deletedRows > 0){
|
||||
logger.severe("removed aggregated data with an unknown period length. Is the database corrupt?");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
}
|
||||
|
||||
private class AggregateDataDeleter implements SQLResultHandler<Long>{
|
||||
private long sensorId = -1;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,140 +1,151 @@
|
|||
package se.koc.hal.plugin.localsensor;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
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.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 ImpulseTracker implements Runnable {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
private static final int IMPULSE_REPORT_TIMEOUT = 60000; //one minute
|
||||
private long nanoSecondsSleep = IMPULSE_REPORT_TIMEOUT * 1000000L;
|
||||
private Integer impulseCount = 0;
|
||||
private ExecutorService executorPool;
|
||||
private final DBConnection db;
|
||||
private final int sensorId;
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
new ImpulseTracker(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param sensorId The ID of this sensor. Will be written to the DB
|
||||
* @throws Exception
|
||||
*/
|
||||
public ImpulseTracker(int sensorId) throws Exception{
|
||||
|
||||
this.sensorId = sensorId;
|
||||
|
||||
// create gpio controller
|
||||
final GpioController gpio = GpioFactory.getInstance();
|
||||
|
||||
// provision gpio pin #02 as an input pin with its internal pull up resistor enabled
|
||||
final GpioPinDigitalInput irLightSensor = gpio.provisionDigitalInputPin(RaspiPin.GPIO_02, 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 - (IMPULSE_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+", "+ImpulseTracker.this.sensorId+", "+data+")");
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -10,91 +10,109 @@ public class TimeUtility {
|
|||
public static final long DAY_IN_MS = HOUR_IN_MS * 24;
|
||||
public static final long WEEK_IN_MS = DAY_IN_MS * 7;
|
||||
|
||||
public static long getTimestampPeriodStart_UTC(long periodLengthInMs, long timestamp) throws NumberFormatException{
|
||||
if(periodLengthInMs < 0 || timestamp < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
|
||||
return timestamp - (timestamp % periodLengthInMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timstamp for the given timestamp floored with the period length. The result should point to the beginning of the timestamps period.
|
||||
* @param periodLengthInMs The periods length to floor the timestamp with
|
||||
* @param timestamp The timestamp to floor.
|
||||
* @return
|
||||
*/
|
||||
public static long getTimestampPeriodStart(long periodLengthInMs, long timestamp){
|
||||
if(periodLengthInMs < DAY_IN_MS){ //simple math if the period is less than a day long
|
||||
return timestamp - (timestamp % periodLengthInMs);
|
||||
}else{
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(timestamp);
|
||||
boolean clear = false;
|
||||
int days = millisecondsToDays(periodLengthInMs);
|
||||
if(days > 0){
|
||||
int currentDay = cal.get(Calendar.DAY_OF_YEAR);
|
||||
cal.set(Calendar.DAY_OF_YEAR, (currentDay/days)*days);
|
||||
clear = true;
|
||||
}
|
||||
int hours = millisecondsToHourOfDay(periodLengthInMs);
|
||||
if(hours > 0){
|
||||
int currentHour = cal.get(Calendar.HOUR_OF_DAY);
|
||||
cal.set(Calendar.HOUR_OF_DAY, (currentHour/hours)*hours);
|
||||
clear = true;
|
||||
}else if(clear){
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
}
|
||||
int minutes = millisecondsToMinuteOfHour(periodLengthInMs);
|
||||
if(minutes > 0){
|
||||
int currentMinute = cal.get(Calendar.MINUTE);
|
||||
cal.set(Calendar.MINUTE, (currentMinute/minutes)*minutes);
|
||||
clear = true;
|
||||
}else if(clear){
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
}
|
||||
int seconds = millisecondsToSecondOfMinute(periodLengthInMs);
|
||||
if(seconds > 0){
|
||||
int currentSecond = cal.get(Calendar.SECOND);
|
||||
cal.set(Calendar.SECOND, (currentSecond/seconds)*seconds);
|
||||
clear = true;
|
||||
}else if(clear){
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
}
|
||||
int milliseconds = millisecondsToMillisecondInSecond(periodLengthInMs);
|
||||
if(milliseconds > 0){
|
||||
int currentMillisecond = cal.get(Calendar.MILLISECOND);
|
||||
cal.set(Calendar.MILLISECOND, (currentMillisecond/milliseconds)*milliseconds);
|
||||
}else if(clear){
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
}
|
||||
return cal.getTimeInMillis();
|
||||
public static long getTimestampPeriodStart_LOCAL(long periodLengthInMs, long timestamp) throws NumberFormatException{
|
||||
if(periodLengthInMs < 0 || timestamp < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(timestamp);
|
||||
boolean clear = false;
|
||||
int days = getDaysFromTimestamp(periodLengthInMs);
|
||||
if(days > 0){
|
||||
int currentDay = cal.get(Calendar.DAY_OF_YEAR);
|
||||
cal.set(Calendar.DAY_OF_YEAR, (currentDay/days)*days);
|
||||
clear = true;
|
||||
}
|
||||
int hours = getHourOfDayFromTimestamp(periodLengthInMs);
|
||||
if(hours > 0){
|
||||
int currentHour = cal.get(Calendar.HOUR_OF_DAY);
|
||||
cal.set(Calendar.HOUR_OF_DAY, (currentHour/hours)*hours);
|
||||
clear = true;
|
||||
}else if(clear){
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
}
|
||||
int minutes = getMinuteOfHourFromTimestamp(periodLengthInMs);
|
||||
if(minutes > 0){
|
||||
int currentMinute = cal.get(Calendar.MINUTE);
|
||||
cal.set(Calendar.MINUTE, (currentMinute/minutes)*minutes);
|
||||
clear = true;
|
||||
}else if(clear){
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
}
|
||||
int seconds = getSecondOfMinuteFromTimestamp(periodLengthInMs);
|
||||
if(seconds > 0){
|
||||
int currentSecond = cal.get(Calendar.SECOND);
|
||||
cal.set(Calendar.SECOND, (currentSecond/seconds)*seconds);
|
||||
clear = true;
|
||||
}else if(clear){
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
}
|
||||
int milliseconds = getMillisecondInSecondFromTimestamp(periodLengthInMs);
|
||||
if(milliseconds > 0){
|
||||
int currentMillisecond = cal.get(Calendar.MILLISECOND);
|
||||
cal.set(Calendar.MILLISECOND, (currentMillisecond/milliseconds)*milliseconds);
|
||||
}else if(clear){
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
}
|
||||
return cal.getTimeInMillis();
|
||||
}
|
||||
|
||||
public static int millisecondsToMillisecondInSecond(long ms){
|
||||
public static int getMillisecondInSecondFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
return (int) (ms % SECOND_IN_MS);
|
||||
}
|
||||
|
||||
public static int millisecondsToSecondOfMinute(long ms){
|
||||
public static int getSecondOfMinuteFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
return (int) ((ms % MINUTES_IN_MS) / SECOND_IN_MS);
|
||||
}
|
||||
|
||||
public static int millisecondsToMinuteOfHour(long ms){
|
||||
public static int getMinuteOfHourFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
return (int) ((ms % HOUR_IN_MS) / MINUTES_IN_MS);
|
||||
}
|
||||
|
||||
public static int millisecondsToHourOfDay(long ms){
|
||||
public static int getHourOfDayFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
return (int) ((ms % DAY_IN_MS) / HOUR_IN_MS);
|
||||
}
|
||||
|
||||
public static int millisecondsToDays(long ms){
|
||||
public static int getDaysFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
return (int) (ms / DAY_IN_MS);
|
||||
}
|
||||
|
||||
public static String msToString(long ms){
|
||||
public static String msToString(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
String retval = "";
|
||||
int days = millisecondsToDays(ms);
|
||||
int days = getDaysFromTimestamp(ms);
|
||||
retval += days + "days+";
|
||||
int hours = millisecondsToHourOfDay(ms);
|
||||
int hours = getHourOfDayFromTimestamp(ms);
|
||||
retval += (hours<10?"0"+hours:hours);
|
||||
int minutes = millisecondsToMinuteOfHour(ms);
|
||||
int minutes = getMinuteOfHourFromTimestamp(ms);
|
||||
retval += ":" + (minutes<10?"0"+minutes:minutes);
|
||||
int seconds = millisecondsToSecondOfMinute(ms);
|
||||
int seconds = getSecondOfMinuteFromTimestamp(ms);
|
||||
retval += ":" + (seconds<10?"0"+seconds:seconds);
|
||||
int milliseconds = millisecondsToMillisecondInSecond(ms);
|
||||
int milliseconds = getMillisecondInSecondFromTimestamp(ms);
|
||||
retval += "." + (milliseconds<100?"0"+(milliseconds<10?"0"+milliseconds:milliseconds):milliseconds);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
120
test/se/koc/hal/util/TimeUtilityTest.java
Executable file → Normal file
120
test/se/koc/hal/util/TimeUtilityTest.java
Executable file → Normal file
|
|
@ -9,19 +9,20 @@ import org.junit.Test;
|
|||
import se.koc.hal.util.TimeUtility;
|
||||
|
||||
public class TimeUtilityTest {
|
||||
private long currentTime;
|
||||
private Calendar referenceCalendar;
|
||||
private long currentTime_UTC;
|
||||
private Calendar referenceCalendar_LOCAL;
|
||||
|
||||
@Before
|
||||
public void setup(){
|
||||
currentTime = System.currentTimeMillis();
|
||||
referenceCalendar = Calendar.getInstance();
|
||||
referenceCalendar.setTimeInMillis(currentTime);
|
||||
currentTime_UTC = System.currentTimeMillis();
|
||||
referenceCalendar_LOCAL = Calendar.getInstance();
|
||||
referenceCalendar_LOCAL.setTimeInMillis(currentTime_UTC);
|
||||
}
|
||||
|
||||
// Test flooring LOCAL time to the closes day
|
||||
@Test
|
||||
public void testDayStartForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart(TimeUtility.DAY_IN_MS, currentTime);
|
||||
public void testDayStart_LOCAL_ForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart_LOCAL(TimeUtility.DAY_IN_MS, currentTime_UTC);
|
||||
Calendar testCalendar = Calendar.getInstance();
|
||||
testCalendar.setTimeInMillis(thisPeriodStartedAt);
|
||||
|
||||
|
|
@ -29,48 +30,106 @@ public class TimeUtilityTest {
|
|||
assertEquals("second is wrong", 0, testCalendar.get(Calendar.SECOND));
|
||||
assertEquals("minute is wrong", 0, testCalendar.get(Calendar.MINUTE));
|
||||
assertEquals("hour is wrong", 0, testCalendar.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals("day is wrong", referenceCalendar.get(Calendar.DAY_OF_YEAR), testCalendar.get(Calendar.DAY_OF_YEAR));
|
||||
assertEquals("day is wrong", referenceCalendar_LOCAL.get(Calendar.DAY_OF_YEAR), testCalendar.get(Calendar.DAY_OF_YEAR));
|
||||
}
|
||||
|
||||
// Test flooring LOCAL time to the closes hour
|
||||
@Test
|
||||
public void testHourStartForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart(TimeUtility.HOUR_IN_MS, currentTime);
|
||||
public void testHourStart_LOCAL_ForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart_LOCAL(TimeUtility.HOUR_IN_MS, currentTime_UTC);
|
||||
Calendar testCalendar = Calendar.getInstance();
|
||||
testCalendar.setTimeInMillis(thisPeriodStartedAt);
|
||||
|
||||
assertEquals("millisecond is wrong", 0, testCalendar.get(Calendar.MILLISECOND));
|
||||
assertEquals("second is wrong", 0, testCalendar.get(Calendar.SECOND));
|
||||
assertEquals("minute is wrong", 0, testCalendar.get(Calendar.MINUTE));
|
||||
assertEquals("hour is wrong", referenceCalendar.get(Calendar.HOUR_OF_DAY), testCalendar.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals("day is wrong", referenceCalendar.get(Calendar.DAY_OF_YEAR), testCalendar.get(Calendar.DAY_OF_YEAR));
|
||||
assertEquals("hour is wrong", referenceCalendar_LOCAL.get(Calendar.HOUR_OF_DAY), testCalendar.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals("day is wrong", referenceCalendar_LOCAL.get(Calendar.DAY_OF_YEAR), testCalendar.get(Calendar.DAY_OF_YEAR));
|
||||
}
|
||||
|
||||
// Test flooring LOCAL time to the closes minute
|
||||
@Test
|
||||
public void testMinuteStartForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart(TimeUtility.MINUTES_IN_MS, currentTime);
|
||||
public void testMinuteStart_LOCAL_ForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart_LOCAL(TimeUtility.MINUTES_IN_MS, currentTime_UTC);
|
||||
Calendar testCalendar = Calendar.getInstance();
|
||||
testCalendar.setTimeInMillis(thisPeriodStartedAt);
|
||||
|
||||
assertEquals("millisecond is wrong", 0, testCalendar.get(Calendar.MILLISECOND));
|
||||
assertEquals("second is wrong", 0, testCalendar.get(Calendar.SECOND));
|
||||
assertEquals("minute is wrong", referenceCalendar.get(Calendar.MINUTE), testCalendar.get(Calendar.MINUTE));
|
||||
assertEquals("hour is wrong", referenceCalendar.get(Calendar.HOUR_OF_DAY), testCalendar.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals("day is wrong", referenceCalendar.get(Calendar.DAY_OF_YEAR), testCalendar.get(Calendar.DAY_OF_YEAR));
|
||||
assertEquals("minute is wrong", referenceCalendar_LOCAL.get(Calendar.MINUTE), testCalendar.get(Calendar.MINUTE));
|
||||
assertEquals("hour is wrong", referenceCalendar_LOCAL.get(Calendar.HOUR_OF_DAY), testCalendar.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals("day is wrong", referenceCalendar_LOCAL.get(Calendar.DAY_OF_YEAR), testCalendar.get(Calendar.DAY_OF_YEAR));
|
||||
}
|
||||
|
||||
// Test flooring LOCAL time to the closes second
|
||||
@Test
|
||||
public void testSecondStartForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart(TimeUtility.SECOND_IN_MS, currentTime);
|
||||
public void testSecondStart_LOCAL_ForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart_LOCAL(TimeUtility.SECOND_IN_MS, currentTime_UTC);
|
||||
Calendar testCalendar = Calendar.getInstance();
|
||||
testCalendar.setTimeInMillis(thisPeriodStartedAt);
|
||||
|
||||
assertEquals("millisecond is wrong", 0, testCalendar.get(Calendar.MILLISECOND));
|
||||
assertEquals("second is wrong", referenceCalendar.get(Calendar.SECOND), testCalendar.get(Calendar.SECOND));
|
||||
assertEquals("minute is wrong", referenceCalendar.get(Calendar.MINUTE), testCalendar.get(Calendar.MINUTE));
|
||||
assertEquals("hour is wrong", referenceCalendar.get(Calendar.HOUR_OF_DAY), testCalendar.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals("day is wrong", referenceCalendar.get(Calendar.DAY_OF_YEAR), testCalendar.get(Calendar.DAY_OF_YEAR));
|
||||
assertEquals("second is wrong", referenceCalendar_LOCAL.get(Calendar.SECOND), testCalendar.get(Calendar.SECOND));
|
||||
assertEquals("minute is wrong", referenceCalendar_LOCAL.get(Calendar.MINUTE), testCalendar.get(Calendar.MINUTE));
|
||||
assertEquals("hour is wrong", referenceCalendar_LOCAL.get(Calendar.HOUR_OF_DAY), testCalendar.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals("day is wrong", referenceCalendar_LOCAL.get(Calendar.DAY_OF_YEAR), testCalendar.get(Calendar.DAY_OF_YEAR));
|
||||
}
|
||||
|
||||
// Test flooring UTC time to the closes day
|
||||
@Test
|
||||
public void testDayStart_UTC_ForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart_UTC(TimeUtility.DAY_IN_MS, currentTime_UTC);
|
||||
|
||||
assertEquals("millisecond is wrong", 0, TimeUtility.getMillisecondInSecondFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("second is wrong", 0, TimeUtility.getSecondOfMinuteFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("minute is wrong", 0, TimeUtility.getMinuteOfHourFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("hour is wrong", 0, TimeUtility.getHourOfDayFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("day is wrong", TimeUtility.getDaysFromTimestamp(currentTime_UTC), TimeUtility.getDaysFromTimestamp(thisPeriodStartedAt));
|
||||
}
|
||||
|
||||
// Test flooring UTC time to the closes hour
|
||||
@Test
|
||||
public void testHourStart_UTC_ForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart_UTC(TimeUtility.HOUR_IN_MS, currentTime_UTC);
|
||||
Calendar testCalendar = Calendar.getInstance();
|
||||
testCalendar.setTimeInMillis(thisPeriodStartedAt);
|
||||
|
||||
assertEquals("millisecond is wrong", 0, TimeUtility.getMillisecondInSecondFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("second is wrong", 0, TimeUtility.getSecondOfMinuteFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("minute is wrong", 0, TimeUtility.getMinuteOfHourFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("hour is wrong", TimeUtility.getHourOfDayFromTimestamp(currentTime_UTC), TimeUtility.getHourOfDayFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("day is wrong", TimeUtility.getDaysFromTimestamp(currentTime_UTC), TimeUtility.getDaysFromTimestamp(thisPeriodStartedAt));
|
||||
}
|
||||
|
||||
// Test flooring UTC time to the closes minute
|
||||
@Test
|
||||
public void testMinuteStart_UTC_ForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart_UTC(TimeUtility.MINUTES_IN_MS, currentTime_UTC);
|
||||
Calendar testCalendar = Calendar.getInstance();
|
||||
testCalendar.setTimeInMillis(thisPeriodStartedAt);
|
||||
|
||||
assertEquals("millisecond is wrong", 0, TimeUtility.getMillisecondInSecondFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("second is wrong", 0, TimeUtility.getSecondOfMinuteFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("minute is wrong", TimeUtility.getMinuteOfHourFromTimestamp(currentTime_UTC), TimeUtility.getMinuteOfHourFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("hour is wrong", TimeUtility.getHourOfDayFromTimestamp(currentTime_UTC), TimeUtility.getHourOfDayFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("day is wrong", TimeUtility.getDaysFromTimestamp(currentTime_UTC), TimeUtility.getDaysFromTimestamp(thisPeriodStartedAt));
|
||||
}
|
||||
|
||||
// Test flooring UTC time to the closes second
|
||||
@Test
|
||||
public void testSecondStart_UTC_ForCurrentTime(){
|
||||
long thisPeriodStartedAt = TimeUtility.getTimestampPeriodStart_UTC(TimeUtility.SECOND_IN_MS, currentTime_UTC);
|
||||
Calendar testCalendar = Calendar.getInstance();
|
||||
testCalendar.setTimeInMillis(thisPeriodStartedAt);
|
||||
|
||||
assertEquals("millisecond is wrong", 0, TimeUtility.getMillisecondInSecondFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("second is wrong", TimeUtility.getSecondOfMinuteFromTimestamp(currentTime_UTC), TimeUtility.getSecondOfMinuteFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("minute is wrong", TimeUtility.getMinuteOfHourFromTimestamp(currentTime_UTC), TimeUtility.getMinuteOfHourFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("hour is wrong", TimeUtility.getHourOfDayFromTimestamp(currentTime_UTC), TimeUtility.getHourOfDayFromTimestamp(thisPeriodStartedAt));
|
||||
assertEquals("day is wrong", TimeUtility.getDaysFromTimestamp(currentTime_UTC), TimeUtility.getDaysFromTimestamp(thisPeriodStartedAt));
|
||||
}
|
||||
|
||||
// Test printing converting milliseconds to text
|
||||
@Test
|
||||
public void testMsToString(){
|
||||
//low values
|
||||
|
|
@ -82,7 +141,7 @@ public class TimeUtilityTest {
|
|||
assertEquals("0days+01:00:00.000", TimeUtility.msToString(TimeUtility.HOUR_IN_MS));
|
||||
assertEquals("1days+00:00:00.000", TimeUtility.msToString(TimeUtility.DAY_IN_MS));
|
||||
assertEquals("7days+00:00:00.000", TimeUtility.msToString(TimeUtility.WEEK_IN_MS));
|
||||
|
||||
|
||||
//high values
|
||||
assertEquals("0days+00:00:00.999", TimeUtility.msToString(999));
|
||||
assertEquals("0days+00:00:59.000", TimeUtility.msToString(TimeUtility.SECOND_IN_MS*59));
|
||||
|
|
@ -90,9 +149,22 @@ public class TimeUtilityTest {
|
|||
assertEquals("0days+23:00:00.000", TimeUtility.msToString(TimeUtility.HOUR_IN_MS*23));
|
||||
assertEquals("369days+00:00:00.000", TimeUtility.msToString(TimeUtility.DAY_IN_MS*369));
|
||||
|
||||
//high overflow values
|
||||
assertEquals("0days+00:00:01.999", TimeUtility.msToString(1999));
|
||||
assertEquals("0days+00:02:39.000", TimeUtility.msToString(TimeUtility.SECOND_IN_MS*159));
|
||||
assertEquals("0days+02:39:00.000", TimeUtility.msToString(TimeUtility.MINUTES_IN_MS*159));
|
||||
assertEquals("5days+03:00:00.000", TimeUtility.msToString(TimeUtility.HOUR_IN_MS*123));
|
||||
|
||||
//combinations
|
||||
long ms = (TimeUtility.DAY_IN_MS*999) + (TimeUtility.HOUR_IN_MS*23) + (TimeUtility.MINUTES_IN_MS*59) + (TimeUtility.SECOND_IN_MS*59) + 999;
|
||||
assertEquals("999days+23:59:59.999", TimeUtility.msToString(ms));
|
||||
}
|
||||
|
||||
// Test printing converting milliseconds to text for a negative time
|
||||
@Test(expected=NumberFormatException.class)
|
||||
public void testMsToStringForNegativeArgument(){
|
||||
//low values
|
||||
TimeUtility.msToString(-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue