/*
* (C) Copyright 2005 Arnaud Bailly (arnaud.oqube@gmail.com),
* Yves Roos (yroos@lifl.fr) and others.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rationals.ioautomata.testing;
import fr.lifl.utils.Barrier;
import rationals.State;
import rationals.Transition;
import rationals.ioautomata.*;
import rationals.ioautomata.IOTransition.IOLetter;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
/**
* Provide an implementation of IOStateMachine which is controlled by an
* IOAutomaton.
* <p>
* Silent transitions in the IOAutomaton represents non deterministic internal
* computations. They take a certain time quantum to be performed. This
* implementation is fair, mono threaded and non-deterministic if the underlying
* IOAutomaton is non deterministic:
* <ul>
* <li>Probability of firing several transitions from a state or set of state is
* uniform</li>
* <li>Only one letter is input or output at each step</li>
* </ul>
* This class is an instance of Runnable so that it can run in its own thread.
* Basically, method {@see #run()}sits in a loop waiting for input events and
* generating output events according to current state of the IOAutomaton. If an
* input event cannot be consumed, the machine is stopped and its error status
* is set to true.
* <p>
* Input-enabledness behavior can be turned on/off using {@see
* #setInputEnabled(boolean)}. This implementation is synchronous: the input and
* output methods are blocking.
*
* @author nono
* @version $Id: SynchIOAutomatonSMAdapter.java 13 2007-06-01 16:11:03Z oqube $
*/
public class SynchIOAutomatonSMAdapter implements IOStateMachine, Runnable {
/* the automaton which is run */
private IOAutomaton<IOTransition,IOTransitionBuilder> auto;
/* the synchronization test */
private IOSynchronization synch = new IOSynchronization();
/* current state of automaton */
private Set<State> state;
/* error status */
private boolean error;
/* stop flag */
private boolean stop;
private Random rand = new Random();
private final Barrier barrier = new Barrier(2);
private Object in;
private Object out;
/* the input-enabledness of this machine */
private boolean inputEnabled;
/* timeout for synchronization */
private long timeout = 100;
private boolean ready;
private MessageAdapter internalHandler = new MessageAdapter(Function.VOID,
this);
/**
* Create an IOSM with given IOAutomaton as implementation.
*
* @param auto
* a non-null IOAutomaton instance.
*/
public SynchIOAutomatonSMAdapter(IOAutomaton<IOTransition,IOTransitionBuilder> auto) {
this.auto = auto;
this.auto.setBuilder(new IOTransitionBuilder());
this.state = auto.getStateFactory().stateSet(auto.initials());
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#input(java.lang.Object)
*/
public void input(Object o) {
barrier.await();
synchronized (this) {
boolean ok = doInput(o);
/* check input is OK, else error */
if (!ok && !inputEnabled) {
error = true;
stop = true;
}
ready = true;
/* wake up sm */
this.notify();
}
}
/**
* @param o
* @return
*/
private boolean doInput(Object o) {
boolean ok = false;
for (Transition tr : auto.delta(state)) {
IOTransition.IOLetter lt = (IOLetter) ((IOTransition) tr).label();
if (IOSynchronization.inputMatchesLetter(o, lt)) {
doTransition(tr.label());
ok = true;
}
}
return ok;
}
private Object doOutput() {
Set<Transition<Object>> trs = auto.delta(state);
selectOutputs(trs);
if (trs.size() == 0)
return null;
int r = rand.nextInt(trs.size());
IOTransition tr = null;
for (Iterator it = trs.iterator(); r >= 0; r--)
tr = (IOTransition) it.next();
doTransition(tr.label());
return ((IOTransition.IOLetter) tr.label()).label;
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#isInputEnabled()
*/
public boolean isInputEnabled() {
return inputEnabled;
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#output()
*/
public Object output() {
barrier.await();
synchronized (this) {
Object ret = doOutput();
if (ret == null) {
error = true;
stop = true;
}
ready = true;
this.notify();
return ret;
}
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#output(java.lang.Object[], int,
* int)
*/
public int output(Object[] o, int start, int len) {
Object ret = output();
if (ret == null)
return 0;
else {
o[start] = ret;
return 1;
}
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#availableOutput()
*/
public int availableOutput() {
return out != null ? 1 : 0;
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
public void run() {
_run: while (!stop) {
/* get all fireable transitions in current state */
Set trs = auto.delta(state);
if (trs.size() == 0)
break _run;
int r = rand.nextInt(trs.size());
IOTransition tr = null;
for (Iterator it = trs.iterator(); r >= 0; r--)
tr = (IOTransition) it.next();
/*
* if transition is internal, then execute, else, wait for
* communication
*/
if (tr.getType() == IOAlphabetType.INTERNAL) {
doTransition(tr.label());
internalHandler.handle(tr.label());
} else {
ready = false;
barrier.await();
synchronized (this) {
try {
while (!ready)
this.wait(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/* break barrier */
synchronized (barrier) {
barrier.close();
barrier.notifyAll();
}
}
/**
* @param object
*/
private void doTransition(Object object) {
state = auto.step(state, object);
}
/**
* Remove all input transitions from this set of transitions
*
* @param trs
*/
private void selectOutputs(Set trs) {
for (Iterator i = trs.iterator(); i.hasNext();)
if (((IOTransition) i.next()).getType() != IOAlphabetType.OUTPUT)
i.remove();
}
public boolean isError() {
return error;
}
public void setInputEnabled(boolean inputEnabled) {
this.inputEnabled = inputEnabled;
}
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#reset()
*/
public void reset() {
this.state = auto.initials();
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#stop()
*/
public void stop() {
this.stop = true;
}
/*
* (non-Javadoc)
*
* @see
* rationals.ioautomata.IOStateMachine#setInternalHandler(rationals.ioautomata
* .Function)
*/
public void setInternalHandler(Function hdl) {
this.internalHandler = new MessageAdapter(hdl, this);
}
}