/**
*
*/
package org.goko.controller.tinyg.commons.schedule;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.goko.controller.tinyg.commons.ITinyGControllerService;
import org.goko.core.common.event.EventListener;
import org.goko.core.common.exception.GkException;
import org.goko.core.common.measure.quantity.Time;
import org.goko.core.common.measure.quantity.TimeUnit;
import org.goko.core.common.measure.units.Unit;
import org.goko.core.controller.bean.DefaultControllerValues;
import org.goko.core.controller.bean.MachineState;
import org.goko.core.controller.bean.MachineValue;
import org.goko.core.controller.event.MachineValueUpdateEvent;
/**
* A scheduler used to monitor MachineValue on a controller and trigger action when expected state is met
*
* @author Psyko
* @date 11 janv. 2017
*/
public class Scheduler<T extends ITinyGControllerService<?>> {
/** The controller service */
private T controllerService;
/** The expected machine value identifier*/
private String machineValueId;
/** The expected value */
private Object expectedMachineValue;
/** Internal - Time for timeout*/
private Timer timer;
/** The runnable to run */
private Runnable runnable;
/**
* Constructor
* @param controllerService the used controller service
*/
public Scheduler(T controllerService){
this.controllerService = controllerService;
}
/**
* Creates default runnable to send the given data
* @param data the data to send
* @return scheduler
*/
public Scheduler<T> send(String data){
runnable = new SendDataRunnable<ITinyGControllerService<?>>(controllerService, data, false);
return this;
}
/**
* Creates default runnable to send the given data with high priority
* @param data the data to send
* @return scheduler
*/
public Scheduler<T> sendImmediately(String data){
runnable = new SendDataRunnable<ITinyGControllerService<?>>(controllerService, data, true);
return this;
}
/**
* Set the given runnable as the one to run
* @param runnable the runnable to run
* @return scheduler
*/
public Scheduler<T> execute(Runnable runnable){
this.runnable = runnable;
return this;
}
/**
* Defines the expected value as the expected state
* @param state the expected state
* @return scheduler
*/
public Scheduler<T> whenState(MachineState state){
return when(DefaultControllerValues.STATE, state);
}
/**
* Defines the id and value of the expected machine value that will trigger the action
* @param id the id of the value
* @param value the expected value
* @return scheduler
*/
public Scheduler<T> when(String id, Object value){
machineValueId = id;
expectedMachineValue = value;
return this;
}
/**
* Defines a timeout
* @param delay the delay
* @param unit the unit of the delay
* @return scheduler
*/
public Scheduler<T> timeout(int delay, Unit<Time> unit){
this.timer = new Timer();
TimerTask action = new TimerTask() {
@Override
public void run() {
onTimeout();
}
};
this.timer.schedule(action, Time.valueOf(delay, unit).value(TimeUnit.MILLISECOND).longValue());
return this;
}
/**
* Timeout callback
*/
protected void onTimeout() {
System.err.println("Timeout occured");
if(timer != null){
timer.cancel();
}
controllerService.removeListener(this);
}
/**
* Starts listening for machine value change
*/
public void begin(){
try{
MachineValue<?> currentValue = controllerService.getMachineValue(machineValueId, controllerService.getMachineValueType(machineValueId));
if(currentValue != null && ObjectUtils.equals(currentValue.getValue(), expectedMachineValue)){
executeRunnable();
}else{
controllerService.addListener(this);
}
}catch(GkException e){
e.printStackTrace();
}
}
/**
* MachineValueUpdate callback
* @param evt the event
*/
@EventListener(MachineValueUpdateEvent.class)
public void onEvent(MachineValueUpdateEvent evt){
MachineValue<?> newMachineValue = evt.getTarget();
if( StringUtils.equals(evt.getTarget().getIdDescriptor(), machineValueId)
&& ObjectUtils.equals(newMachineValue.getValue(), expectedMachineValue)){
executeRunnable();
}
}
/**
* Executes the runnable
*/
private void executeRunnable() {
if(timer != null){
timer.cancel();
}
controllerService.removeListener(this);
runnable.run();
}
}