Copyright 2011,
Ralph Rönnquist.
GORITE execution is designed to make use of a single thread to progress all intentions for all performers, and there are two ways to make this happen:
It's a bad idea to mix the two methods. Really bad. Obviously.
However, the top level goal approach may be used together with percept handling execution. The key is then to ensure that the top level goal does not terminate, or conversely, the execution of percepts will only continue until the top level goal execution terminates. Thus, if the application has a well defined life span (such as for instance a closed-loop simulation), then the top level goal approach may be used even if the life span includes percept handling executions.
Generally, percept handling execution is designed by means of using Perceptors for Performers. Each Perceptor defines a potential of receiving percepts, which become goal executions on a selected todo group. In practice, the application, on a separate thread, calls a Perceptor's perceive method to cause an associated percept handling with its particular perception data.
A Perceptor has three attributes:
new BDIGoal( "handle percept" )That is, the percept handling means to look up and use a plan for "handle percept" as performer capability.
The following is an illustration of the second execution approach, and the use of a Perceptor, with line numbers added for the sake of reference:
1: Performer performer = new Performer( "ralph" ); 2: performer.addCapability( new FriendResponding() ); 3: Perceptor ear = new Perceptor( performer ); 4: 5: new Thread( Executor.getDefaultExecutor() ).start(); 6: 7: BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) ); 8: while ( ( line = in.readLine() ) != null ) ear.perceive( line ); 9: System.exit( 0 );
At lines 1 and 2, a performer is defined and given a FriendResponding capability. At line 3, the performer is associated with a Perceptor using the default attributes. At line 5, a separate thread is created for running the performers todo group(s). At line 7, a BufferedReader is tied to the keyboard input, and at line 8, the program begins repeatedly reading lines of input and use them to cause percepts.
The FriendResponding capability should include plans for the "handle percept" goal, and these would access the "percept" data element, which holds the input line. The following is an example of such a capability:
public class FriendResponding extends Capability {
public Relation friends = new Relation( "friends", String.class );
public FriendResponding() {
addGoal( new Plan( "handle percept" ) {
public Query context(Data d) {
return friends.get( d.getValue( "percept" ) );
}
public States execute(Data d) {
String who = d.getValue( "percept" );
System.out.println( "hi friend: " + who );
return PASSED;
}
} );
addGoal( new Goal( "handle percept" ) {
public States execute(Data d) {
String who = d.getValue( "percept" );
System.out.println( "hi stranger: " + who );
friends.add( who );
return PASSED;
}
} );
}
}
The key to using a single execution thread for executing all performers lies in the notion of changing focus, which means to regularly stop progressing the on-going goal execution, and shift to another. The Executor includes a changeFocus() method, which simply tells when it is time to change focus. The execution machinery will then stop its progress of the current goal execution and shift to another execution.
The "steps" attribute of the Executor tells how many "steps" a goal execution may progress before a change of focus should occur. For simplicitly, a "step" is understood as a call to the changeFocus() method, which the execution machinery does in between sub goals. This is not a totally well-defined notion, but it suffices to ensure a repeatable execution.