In reading through you code, it's obvious that you had a lot of trial
and error. What it looks like you tried to do was make HitWall act as a
Cancel class as well. The problem is that there are two instances of
HitWall and your flag variables aren't static so the flags aren't shared
between instances. In what looks like a second round of fix-ups, you
start calling suppress and action directly. This violates the behavior
paradigm that allows only Arbitrator to call these methods. Whether or
not there is any practical problem with calling them, I don't really know.
What I've done is add the Cancel class. This cleanly allows suppress to
do the correct thing. Note that action and suppress in Cancel do
nothing; that's OK because the only desired effect of Cancel running is
suppressing HitWall. I also moved the movement methods into their own
class and made them static. This allows them to be used anywhere and
makes the code cleaner. Both Cancel and HitWall listen to the touch
sensor. I'm assuming that the listener thread is atomic for notifying
the listening classes (hopefully that's a good assumption). I've also
explicitly initialized the sensor to be a touch sensor; it doesn't seem
to make a difference but it's good practice. Note that in the suppress
methods, the motors are explicitly stopped. This probably isn't
necessary since the suppressing behavior is quite likely to immediately
turn the motors on and this stop may cause the robot to stutter
unnecessarily.
I've tried running this and it seems to do the right thing. So, again
good luck.
wjr
Gary Stoneman wrote:
Wow thanks for the insight. Its all very interesting to see how this
all works. I currently have a solutions that seems to work adequately
(there may well be some small lapses where things don't happen as they
should, but it seems to function correctly). The solution was to add
flags as you say, but just use them internally in the hitWall class.
Using a thread for the hitWall we simply introduce a alreadyExecuted
flag (called hit in the code) this is true if we are already executing
the routine, if this is the case then we simply suppress the current
behaviour and reset the flag. This of course means that the line of
execution returns to the goForward class, which will most likely force
the robot to strike an obstacle again and trigger another hitWall
routine.
The only thing to improve on now would be instead of returning to the
goForward after we suppress a hitWall (where suppress is triggered by
another touch) we should instead rerun the hitWall immediately. Whilst
I endeavour to correct this the current solution will work for likely
99% of the scenarios the robot will encounter.
I'll post the code again in case you want to have a look at it, its
pretty straight forward (and quite messy as I have left lots of
commented out code in). I'll have a look at your suggestions and see
if I can come out with a working program.
Thanks
Gary
package MyRobot;
import josx.platform.rcx.*;
import josx.robotics.*;
public class GSBot {
public static void main(String[] args) {
Sensor.S1.setTypeAndMode (1, 0x20); // set to touch sensor
Sensor.S1.activate();
Behavior hit = new HitWall();
Behavior can = new Cancel();
Behavior move = new GoForward();
Behavior[] behaviorArray = {move, hit, can};
Arbitrator arbitrator = new Arbitrator(behaviorArray);
arbitrator.start();
}
}
class Cancel implements Behavior, SensorListener {
boolean hitFlg = false;
Cancel() {
Sensor.S1.addSensorListener(this);
}
public boolean takeControl() {
if (hitFlg) {
hitFlg = false;
return true;
}
return false;
}
public void action() {
}
public void suppress() {
}
public void stateChanged(Sensor bumper, int oldValue, int newValue) {
if (newValue == 1) hitFlg = true;
}
}
class HitWall implements Behavior, SensorListener {
Thread runaThread;
boolean hitFlg = false;
HitWall() {
Sensor.S1.addSensorListener(this);
}
public boolean takeControl() {
return hitFlg;
}
public void suppress() {
runaThread.interrupt();
Move.stop();
}
public void action() {
runaThread = Thread.currentThread();
try {
hitFlg = false;
//Here is were we respond to the sensor
Move.turnRight();
Thread.sleep(1183);
Move.moveForward();
Thread.sleep(1800);
Move.turnRight();
Thread.sleep(1183);
} catch (Exception e) {
}
}
//runaThread = null;
public void stateChanged(Sensor bumper, int oldValue, int newValue) {
if (newValue == 1) hitFlg = true;
}
}
class GoForward implements Behavior {
Thread runThread;
public boolean takeControl() {
return true;
}
public void suppress() {
runThread.interrupt();
Motor.A.stop();
Motor.C.stop();
}
public void action() {
runThread = Thread.currentThread();
try {
Move.moveForward();
Thread.sleep(1800);
Move.moveForward();
Thread.sleep(1800);
Move.moveBackward();
Thread.sleep(1800);
} catch(Exception w) {
//Down to here, then we respond
}
}
}
class Move {
public static void moveForward() {
Motor.A.forward();
Motor.C.forward();
}
public static void moveBackward() {
Motor.A.backward();
Motor.C.backward();
}
public static void turnRight() {
Motor.A.forward();
Motor.C.backward();
}
public static void turnLeft() {
Motor.A.backward();
Motor.C.forward();
}
public static void stop() {
Motor.A.stop();
Motor.C.stop();
}
}