7. Managing ongoing activities

Some work is a finite sequence: set a light, observe the result, return. Other work must keep responding while a goal is active, continuously correct a changing state or protect shared updates. ASTRA provides different mechanisms for these jobs.

Choose the execution model

Requirement

Mechanism

Completion

Perform a finite strategy

Ordinary plan and subgoals

The plan body finishes

React only while pursuing a particular goal

Encapsulated goal with inner plans

Its goal condition holds or it explicitly ends

Repeatedly select an action from current state

Teleo-reactive function

A plan or the function deactivates it

Perform work at intervals

Timer events and ordinary plans

Cancel the timer when no longer needed

An encapsulated goal groups an activity’s body and relevant reactions. For example, reactions to device faults may belong inside a commissioning goal, rather than remaining active for the whole application. Its inner plans share the goal’s intention and stop being relevant when the goal ends. Ending the goal can also stop an unfinished inner plan, so put necessary cleanup where it will actually run.

A teleo-reactive function repeatedly selects the first condition/action rule that applies. Write achieved-state cases before corrective cases. An action may be repeated on every cycle while its condition holds, so it must be safe to repeat and quick to execute. An agent can run at most one TR function at a time.

See continuous selection in a small example

This example uses a counter rather than device I/O so that the repeated execution is visible:

agent ActivityCounter {
    module Console console;
    module System system;
    types counting { formula count(int); }
    initial count(0);

    plan +!main(list args) {
        activate countTo(3);
        wait(count(3), 2000);
        console.println("target reached");
        system.exit();
    }

    function countTo(int Target) {
        count(Target) -> deactivate
        count(int Current) -> {
            -count(Current);
            +count(Current + 1)
        }
    }
}

The function advances the count until its first rule applies. activate starts it and allows the main plan to continue to its wait; activation does not itself wait for the function to finish.

For the light controller, a persistent policy could repeatedly compare the desired state with device state and perform a corrective action. Ordinary event plans are often simpler when a sensor already reports changes. Choose continuous selection only when it improves how the policy is expressed.

Protect concurrent updates

Several intentions can read the same belief before either updates it. A read/remove/add sequence can then lose an update. A named synchronized block protects that sequence when every competing path uses the same lock:

synchronized(counter) {
    query(total(int Current));
    -total(Current);
    +total(Current + 1);
}

Locks belong to one agent. They do not protect another agent’s beliefs or a shared Java service. Coordinate shared environment resources at their own boundary or through a protocol.

An atomic plan gives its intention scheduling priority over other intentions. It is broader than a named critical section and does not provide rollback. Keep it short and avoid waits that require another intention to run. Perception and event-handling phases still occur between its statements.

Make lifetimes explicit

Stop timers and continuous control when an activity ends. Keep critical sections short. Avoid a synchronized wait whose completion requires another intention to acquire the same lock. Give encapsulated goals clear completion conditions so that an activity cannot remain active accidentally.

Next: Reusing agent behaviour.

Further Details