Programming the message handler

The agent message handler is programmed by defining a QuLog action procedure with system declared type3:

act handle_message(!message,!agent_handle)
Here message is a programmer defined type specific to the agent application. The message type, and any other user defined types it uses, defines the inter-agent communication ontology of the application.

The TeleoR agent system code that is executed as the outer loop code of the message handling thread calls the application specific handle_message QuLog procedure whenever it receives a ground message of type message. All other messages are discarded unless the programmer has also defined the action handle_invalid_message of system declared type:

act handle_invalid_message(!string, !agent_handle)
This takes a string representation of the non-message as argument.

In this application it is easy to see that the only messages are ground messages of type message and so there are no invalid messages. Hence we do not need to define handle_invalid_message. However, for purposes of illustration, we assume there might be other agents, perhaps written in a different language, that also send messages to the robotic agents. It could also be argued that when developing the application code it might be good defensive programming to assume invalid messages might be received by an agent.

In the example below this action simply displays a message to standard output. If these were real robots with the agent running on the robot then there would be no standard output and so, instead, we could send a message to a monitoring process for dispay or logging.

We start with the message type declaration.

def message ::= count(int) | stopped() | stopped_dir(dir)

The first constructor is for updating the collection count and the second and third are for conflict avoidance.

We also want to send these messages to the simulation so the messages can be displayed and to do that we add the following type declaration.

def display_info_t ::= display_info(message)

We now give the two system declared, programmer defined message handling actions.

handle_message(count(Count),_) ~>
other_collected := Count
handle_message(stopped(),_) ~>
forget([stopped_received()]);
remember_for([stopped_received()], 4)
handle_message(stopped_dir(Dir),_) ~>
forget([stopped_received()]);
remember_for([stopped_dir_received(Dir)], 4)

handle_invalid_message(Message,Agent) ~>
write_list(["Invalid message received: ",Message,nl_])

Two of the above rules update the belief store and so we declare appropriate dynamic relations below. Global variables are used and so are also declared below.

int other_collected := 0
int collected := 0

dyn stopped_received()
dyn stopped_dir_sent(dir)
dyn stopped_dir_received(dir)

Each action for handle_message forwards the received message wrapped with display_info to the Python env process which displays them. The first rule updates the count for the other robot. The next two remember a fact for 4 seconds. The addition and then removal of these facts cause the agent to pick different TeleoR rules in an attempt to avoid conflict. The last rule deals with invalid message messages from other agents - the other agent sees the message sent to it from this agent as invalid.