package josx.robotics; /** * An activity that operates in coordination with other activities. * Only one activity can run at a time. * When an activity of a higher priority wants to run any activities * of a lower priority will be suppressed. Furthermore, if there is * already an activity of the same or lower priority running, that * activity will be stopped. *
* This is essentially an alternative way of implementing a Behavior or * Subsumption architecture. It is totally distinct from the Behavior * and Arbitrator classes and does not use either. *
* It works like this: *
* Example: *
* /** * * When sensor one is pressed, run the motors in some pattern. * */ * class ControlMotors extends Activity implements SensorListener * { * public ControlMotors() * { * Sensor.S1.addSensorListener(this); * } * * /** * * Called when the sensor state changes (in some thread other than this one). * */ * public void stateChanged(Sensor s, int old, int nu) * { * if (old > nu) * return; * * iWantToRun(); * } * * /** * * Encapsulates the actual activity we want to perform. * * * * @exception StopException if we are forcibly stopped. * */ * protected void action() throws StopException * { * boolean finished = false; * * // Sit here until we are finished or we are forcibly halted. * while (!finished) * { * try * { * // spin * Motor.C.forward(); * Motor.A.backward(); * * // Wait for 0.25 secs, may throw InterruptedException * pause(250); * * // Forward * Motor.A.forward(); * * // We are finished * finished = true; * } catch (InterruptedException ie) * { * // pause() was interrupted. Re-start from the beginning * } * } * } * } * ** @author Paul Andrews */ public abstract class Activity extends ActivityBase { private static final StopException stopException = new StopException(); private static Activity runnable; /******* Methods that CAN be overridden in a derived class *******/ /** * Encapsulates the actual activity we want to perform. * Returns when complete or when stopped. * * @exception StopException when some other activity has stopped this one. */ protected abstract void action() throws StopException; /** * Reset the runnable activity. Normal activities should not * override this. Background activities can in order to request that * they become runnable. */ protected void resetRunnable() { if (runnable == this) runnable = null; } /******* Methods that CAN'T be overridden in a derived class *******/ /** * Call this if you want this activity to run. */ protected final void iWantToRun() { synchronized(monitor) { // Only one activity can actually run. if (runnable == null || this.getPriority() >= runnable.getPriority()) { // Wake up the thread represented by 'this'. // That is not necessarily the thread that is actually // executing iWantToRun(); runnable = this; this.interrupt(); // Tell everyone else to reset. monitor.notifyAll(); } // Otherwise, that's just too bad. } } /** * Thread entry point. Never returns. */ public final void run() { synchronized (monitor) { while (true) { try { // Call this here so that background activities can make // themselves runnable. resetRunnable(); // Wait until we become runnable try { pause(); } catch (InterruptedException ie) {} // Perform action. action(); // Execution complete. Could be other things waiting to go, // so reset them. They won't get to run until we release // the monitor in pause(); monitor.notifyAll(); } catch (StopException e) { // Execution stopped forcibly } } } } /** * Wait at most 'time' milliseconds. Returning * without an exception means continue the activity where it left off. * * @exception InterruptedException if we were made runnable whilst * we were running. This might indicate, for example, that a sensor * was pressed while we were still reacting to an earlier press. * Typically, an activity would want to restart execution of the * action() method from the beginning if that happened. * @exception StopException if we should stop executing altogether. */ protected final void pause(long time) throws InterruptedException, StopException { monitor.wait(time); if (runnable != this) throw stopException; } /** * Wait until we've either been made runnable or someone else has. * * @exception InterruptedException if we should restart execution of the * action() method from the beginning. * @exception StopException if we should stop executing altogether. */ protected final void pause() throws InterruptedException, StopException { monitor.wait(); if (runnable != this) throw stopException; } }