/**
* Copyright (c) 2011-2014, OpenIoT
*
* This file is part of OpenIoT.
*
* OpenIoT 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, version 3 of the License.
*
* OpenIoT 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 OpenIoT. If not, see <http://www.gnu.org/licenses/>.
*
* Contact: OpenIoT mailto: info@openiot.eu
* @author bgpearn
*/
package org.openiot.gsn.vsensor;
import org.openiot.gsn.beans.DataField;
import org.openiot.gsn.beans.DataTypes;
import org.openiot.gsn.beans.StreamElement;
import org.openiot.gsn.utils.Helpers;
import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TreeMap;
import org.apache.commons.lang.time.DateUtils;
import org.apache.log4j.Logger;
import org.joda.time.format.ISODateTimeFormat;
import java.util.TimerTask;
/**
* Extends AbstractVirtualSensor to allow scheduled output.
* Parameters:
* rate = interval between data output
* start-time = scheduled time to start output (defaults to the next whole time interval)
*
* @author bgpearn
*
*/
public abstract class AbstractScheduledVirtualSensor extends AbstractVirtualSensor{
private static final String RATE_PARAM = "rate";
private static final String START_PARAM = "start-time";
protected int clock_rate; // Scheduled interval in milliseconds
protected long startTime; // Schedule start time
private final String CURRENT_TIME = ISODateTimeFormat.dateTime().print(System.currentTimeMillis());
protected StreamElement dataItem ; //Buffer for most recent stream element
protected static final transient Logger logger = Logger
.getLogger(AbstractScheduledVirtualSensor.class);
protected Timer timer0;
/**
* Called once while initializing an instance of the virtual sensor
* Gets schedule parameters from VSD, calculates timer parameters and instantiates timer instance.
*
* @return True if the initialization is done successfully.
*/
public boolean initialize ( ){
TreeMap<String, String> params = getVirtualSensorConfiguration()
.getMainClassInitialParams();
String rate_value = params.get(RATE_PARAM);
if (rate_value == null) {
logger.warn("Parameter \"" + RATE_PARAM
+ "\" not provider in Virtual Sensor file");
return false;
}
clock_rate = Integer.parseInt(rate_value);
String start_value = params.get(START_PARAM);
if (start_value != null) { //start value set in VSD
try {
startTime = Helpers.convertTimeFromIsoToLong(start_value);
}catch (Exception e) {
logger.error("Failed to parse the start-time parameter of the remote wrapper, a sample time could be:"+(CURRENT_TIME));
throw new RuntimeException(e);
}
}
// If the scheduled start is not in the future
// then start at the next whole time interval
if (System.currentTimeMillis() >= startTime) {
startTime = System.currentTimeMillis(); // current time
// Calculate from midnight
long midnight = DateUtils.truncate(new Date(), Calendar.DATE).getTime();
long current_time = System.currentTimeMillis();
// Start
startTime = midnight + (((current_time - midnight) / clock_rate) + 1) * clock_rate;
} //otherwise use the time retrieved from the VSD
logger.warn(getVirtualSensorConfiguration().getName()+ " scheduled to start at " + new Date(startTime).toString());
// startTime is used in the virtual sensor class to start a timer
timer0 = new Timer();
// timer task is started in the sub class
return true;}
public void dataAvailable(String inputStreamName, StreamElement data) {
try {
// <TODO> if AbstractVirtualSensor.validateStreamElement() was protected then
// we could re-use the validate and compatibleStructure methods but I am
// not sure if that would be ok to do.
// super.validateStreamElement(data, true);
validateStreamElement(data, true);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return;
}
if (logger.isDebugEnabled())
logger.debug("Data received under the name: " + inputStreamName);
dataItem = data;
}
private void validateStreamElement ( StreamElement streamElement ,boolean adjust) {
if ( !compatibleStructure( streamElement, getVirtualSensorConfiguration( ).getOutputStructure( ),adjust ) ) {
StringBuilder exceptionMessage = new StringBuilder( ).append( "The streamElement produced by :" ).append( getVirtualSensorConfiguration( ).getName( ) ).append(
" Virtual Sensor is not compatible with the defined streamElement.\n" );
exceptionMessage.append( "The expected stream element structure (specified in " ).append( getVirtualSensorConfiguration( ).getFileName( ) ).append( " is [" );
for ( DataField df : getVirtualSensorConfiguration( ).getOutputStructure( ) )
exceptionMessage.append( df.getName( ) ).append( " (" ).append( DataTypes.TYPE_NAMES[ df.getDataTypeID( ) ] ).append( ") , " );
exceptionMessage.append( "] but the actual stream element received from the " + getVirtualSensorConfiguration( ).getName( ) ).append( " has the [" );
for ( int i = 0 ; i < streamElement.getFieldNames( ).length ; i++ )
exceptionMessage.append( streamElement.getFieldNames( )[ i ] ).append( "(" ).append( DataTypes.TYPE_NAMES[ streamElement.getFieldTypes( )[ i ] ] ).append( ")," );
exceptionMessage.append(" ] thus the stream element dropped !!!" );
throw new RuntimeException( exceptionMessage.toString( ) );
}
}
/**
* First checks compatibility of the data type of each output data item in the stream element with the
* defined output in the VSD file. (this check is done regardless of the value for adjust flag).
* <p>
* If the adjust flag is set to true, the method checks the newly generated stream element
* and returns true if and only if the number of data items is equal to the number of output
* data structure defined for this virtual sensor.
* If the adjust=true, then this test is not performed.
*
* @param se
* @param outputStructure
* @param adjust default is false.
* @return
*/
private static boolean compatibleStructure ( StreamElement se , DataField [] outputStructure ,boolean adjust ) {
if (!adjust && outputStructure.length != se.getFieldNames().length ) {
logger.warn( "Validation problem, the number of field doesn't match the number of output data strcture of the virtual sensor" );
return false;
}
int i =-1;
for (DataField field: outputStructure) {
Serializable value = se.getData(field.getName());
i++;
if (value==null)
continue;
if ( ( ( field.getDataTypeID() == DataTypes.BIGINT ||
field.getDataTypeID() == DataTypes.DOUBLE ||
field.getDataTypeID() == DataTypes.INTEGER||
field.getDataTypeID() == DataTypes.SMALLINT||
field.getDataTypeID() == DataTypes.TINYINT ) &&!(value instanceof Number))
||
( (field.getDataTypeID() == DataTypes.VARCHAR || field.getDataTypeID() == DataTypes.CHAR) && !(value instanceof String)) ||
( (field.getDataTypeID() == DataTypes.BINARY) && !(value instanceof byte[]))
){
logger.warn( "Validation problem for output field >" + field.getName( ) + ", The field type declared as >" + field.getType()+"< while in VSD it is defined as >"+DataTypes.TYPE_NAMES[outputStructure[ i ].getDataTypeID( )]);
return false;
}
}
return true;
}
public abstract class MyTimerTask extends TimerTask{};
public abstract void dispose();
}