A Project Accessory Class (PAC) is a Java class with methods which can be invoked by an agent to modify or interact with the agent's environment. PACs are used to transfer information from a user into the agent's mental model, to display information from the agent's mental model, and for executing actions.
PACs may come from a variety of sources: PACs can be written by the developer, may be obtained from commercial off-the-shelf packages, or may be freeware downloaded from the Internet. Although you can write PACs entirely in Java, there are situations where Java alone does not meet the needs of an application. You can use the Java Native Interface (JNI) to write non-Java methods to handle those situations when an action cannot be implemented in Java; C/C++ functions that conform to the JNI can be used to implement private actions. You are free to create new methods and/or integrate existing code (Java, C or C++) into a PAC.
PACs are assembled into an agent's Project Accessory Class (PAC) library, which contains all of the domain-specific code the agent requires for operation in its domain.
Sometimes PACs are used to implement a control panel for an agent. We describe a BuyerSeller example where a PAC is used as the control panel for the Buyer agent; it allows the user to choose a product and amount to be purchased, and it displays the buyer's inventory and account information. Several questions to consider when designing a control panel are discussed in the section entitled Control Panel Design.
AgentBuilder provides a simple built-in PAC for input, the InputDialog, which can be easily customized and launched from an agent, and which can be used to get input of any simple data type from the user. This is adequate in many situations, such as getting string or integer values from the user. Complex data types with more than a single field, however, will require the developer to create a PAC which allows input of the fields of the complex data type, and which performs any domain-specific testing of the inputs. Figure 82 shows the built-in Input Dialog, customized for use in a travel agent application.
Figure 82. Input Dialog
AgentBuilder provides a simple built-in PAC for output, the Output Dialog , which will display a message from the agent. Figure 83 shows the built-in Output Dialog.
Figure 83. Output Dialog
AgentBuilder also provides a built-in console which displays program output and error output, and allows the user to freeze the display and save output to a file. For many projects, this built-in console will be adequate for monitoring the agent and displaying results to the user. Figure 84 shows the built-in console.
Figure 84. The Console
Other projects, however, will require the developer to create a PAC to display the agent's output in a domain-specific manner. In the BuyerSeller example PACs are used for the display of the Buyer agent's and Seller agent's output. Although it would be possible to display their outputs in the built-in consoles, the use of custom PACs allows the different quantities of interest (e.g., account balance) to be displayed in separate text areas for easier viewing.
PACs are also used to modify an agent's environment, perform domain-specific processing, and facilitate information retrieval outside of an agent's mental model. PACs can be used to access a file and return a piece of information, or make a database query and return the result to the agent's mental model. An agent may use a PAC to perform data analysis, e.g., a PAC could analyze a volume of data using a neural network and report the results back to the agent for use in the agent's reasoning. An agent's mental model is based on symbolic reasoning; using PACs, they can also perform various types of sub-symbolic reasoning when that is appropriate to the problem domain.
For example, an agent designed for a loan approval program could have several PACs for input, output, and processing. An input PAC could be used to extract all the data for the next loan applicant from a file containing loan applications, or perhaps from a graphical interface used by the applicant. The input PAC would provide the agent's mental model with all the parameters of interest in the loan application domain (applicant's name, social security number, income, etc.). The agent could perform some preliminary processing such as immediately rejecting the application if the income level is below some threshold, for example. This could trigger the display of another interface (another PAC, or another method on the input PAC) to notify the applicant of the decision or ask for confirmation of the income level.
The agent could have a PAC that interacts with a database of credit histories. The agent would supply the PAC with the applicant's parameters and the PAC would perform a database query at one of the credit history services and return a rating to the agent. Or the agent could supply a neural network PAC with the applicant's parameters and receive a rating of the applicant. Perhaps both methods would be used and the results would be further analyzed by the agent, using reasoning developed from the experience of human loan-approval officers. Finally, the agent would use a PAC to display its recommendation and its justifications to a human loan officer or directly to the applicant.
For another example, an agent could be used to control a piece of machinery in an industrial plant. Such an agent might have PACs to measure physical quantities such as temperature or RPM, and other PACs to perform physical activities such as moving an actuator or halting the machine. This type of agent would probably also have a PAC for a control panel to allow a human user to set parameters in the agent or override the agent's normal processing. Java PACs used to control physical devices might be provided by the manufacturer of the device, or the developer may need to create a Java PAC to encapsulate a C-language device driver provided by the manufacturer.
Not all private actions are suitable for running on the agent's execution thread, i.e., as part of the sequence of instructions within a computational process. Actions running on the same thread as the agent must execute relatively quickly, because the agent is blocked until the private action finishes execution. To prevent blocking, it may be necessary to run a private action on its own thread. Running actions on their own threads allows long-running private actions to execute without interfering with the basic agent cycle. For example, control panels require execution on a separate thread because of their long-running nature.
PACs are able to communicate information back to the agent in two ways. For non-threaded actions, information is returned to the agent via a return value. For threaded actions, information can only be returned to the agent in a message, such as a KQML message. For example, a control panel can send messages back to an agent as the user enters information.
It is possible, but not generally recommended, for a non-threaded action to return results to an agent via a message. It is also possible for an agent to send a message to a PAC, instead of invoking a method on the PAC. Thus it's possible to use messages for all agent/PAC communication, but this is not generally recommended. It's much more efficient to use method invocations and return values for communication between an agent and a non-threaded PAC, and use method invocations to start a threaded PAC and messages only for returning results from a threaded PAC.
There are no restrictions on the types used for communication of data between an agent and a PAC, except that messages cannot contain primitive values. Any Java object type or primitive type may be passed from the agent to a PAC method in a method invocation. For a non-threaded action any Java object type or primitive type may be returned as the return value of a method by a PAC to an agent. For a threaded action, which must return a value to the agent via a message, any Java object type may be returned to the agent as the content of a message.
There are two AgentBuilder interfaces that may be implemented by PACs; both are optional and only need to be used in situations where a particular functionality is desired.
The AgentBuilderDestroyablePac interface defines the API that will be used by the agent engine when the engine is destroyed via the built-in action ShutdownEngine or via a signal from the console. This interface defines a destroy method which takes no arguments. Before the engine is destroyed it will invoke the destroy method on all PAC objects which declare they implement the AgentBuilderDestroyablePac interface.
If a PAC does not implement the AgentBuilderDestroyablePac interface and the PAC is descended from java.awt.Window, then the java.awt.Window dispose method will be invoked on the PAC object. This will dispose of PAC control panels when the engine is destroyed. If you want a PAC interface object to remain visible after the engine is destroyed, the PAC must implement the AgentBuilderDestroyablePac interface and use an empty destroy method.
The AgentBuilderRestorablePac defines the API that will be used by the agent engine when saving/restoring a PAC object. The format of the string used to represent a PAC can be chosen by the developer. The methods are:
void fromPacString(String pacString)
To create a saveable/restorable PAC these methods must be implemented and the PAC must have a constructor which takes no arguments. The save/restore functionality is currently not available. More information will be provided when this functionality becomes available.
We now look at some of the issues in designing a control panel for an agent. Questions to consider when building a control panel for an agent include:
(1) What information will be displayed to the user?
(2) What inputs will the user be allowed to make?
(3) What information will be stored in the control panel?
(4) What information will be stored in the mental model of the agent?
(5) What information will flow from the agent to the control panel?
(6) What information will flow from the control panel to the agent?
Questions (1) and (2) are standard interface design issues and will not be discussed here. Questions (3) through (6) are discussed in turn below.
What information will be stored in the control panel?
Ideally you should store all interface information (e.g., button color) in the control panel, and you should store all domain information in the mental state of the agent. This is only a guideline, however, and many practical systems will not follow this guideline completely. For example, in some projects it may be convenient to perform some basic processing of inputs, such as range-checking of numerical inputs, in the control panel. This requires that some domain information (e.g., the limits of the acceptable range) be stored in the control panel. In other projects it may be more appropriate to do all processing of inputs in the agent's mental model.
What information will be stored in the mental model of the agent?
All domain information should be stored in the mental model of the agent so the information can be used in reasoning. In some situations it may be necessary to store some of the domain information both in the agent's mental model and in the control panel. This may be necessary in order to reduce the amount of information exchanged between the agent and the control panel or for efficient display of the information. Note that this can lead to problems if the information in the agent's mental model and the information in the control panel get out-of-sync.
What information will flow from the agent to the control panel?
In general, information will be transmitted from the agent to the control panel via method calls on the control panel object. In the HelloWorld example the agent sends a String to the control panel by invoking HelloWorldFrame::print(String) , which appends the string to the text area in the frame.
Any Java type or user-defined type can be used as the argument of a method call on the control panel object. In the BuyerSeller example the buyer agent sends the accepted PriceQuote object to its control panel for display to the user. The PriceQuote class is a PAC defined by the user; it contains information important to the agents in that domain.
The control panel class will need a method which can be called by the agent for every distinct information type or activity. Assume, for the sake of an example, that the HelloWorld agent needs to display integer values in addition to the "HelloWorld!" strings. A second print method could be added to the HelloWorldFrame class, perhaps print(int) or print(Integer) . This new print method would be something like:
public void print( Integer i
)
_textArea.append( "Current
integer = " + i.toString() );
The methods invoked by the agent are not restricted to displaying values; if the agent needs to modify the internal state or behavior of the control panel, a method could be added to the control panel class to modify its state. For example, the agent could hide the control panel and make it reappear at a later time, perhaps in response to some event detected by the agent. To hide the window a hide() method could be added to the control panel class, something like:
What information will flow from the control panel to the agent?
Information will be transmitted from the control panel to the agent via KQML messages. Information can be transmitted via KQML performatives, e.g., `tell' or `achieve'; via other built-in KQML fields such as Reply-With or In-Reply-To; or contained in the Content field of the KQML message. The Content field can contain any Java object; the agent receiving the message can invoke methods on the content object to extract information.
The choice of KQML field(s) to use when transmitting information depends on the complexity of the information and the overall design of the system. A simple system might allow all information from the control panel to be transmitted via the KQML performatives without any need for the Content field. Other systems will require message strings to be sent in the Content field, and more-complex systems might require PACs to be sent as the message content.
Whatever mechanism is used it is imperative that the sender and receiver agree on the message formats, otherwise the messages will not have the desired effects. If the control panel sends a message with the string "quit" as the content object, but the agent expects the string "Quit", the message will not trigger the desired behavior in the agent. Similarly, if the control panel sends a message with Performative = `tell' but the agent expects a message with Performative = `achieve', the message will have no effect. It is the responsibility of the system designer to ensure that the control panel and the agent agree on message formats.
Information flow from the control panel will usually be triggered by a user event (e.g., a button push), so messages will usually be sent from within callback methods. In the HelloWorld example, all messages are sent from the HelloWorldFrame::actionPerformed method, which is registered as the callback method for events in the HelloWorldFrame . This example is simple enough that only a single callback method is needed and the two button-push events can be differentiated by action commands (in this example, button labels are used). Complex control panels may require several callback methods or even separate callback classes, but their construction and operation will be similar to the actionPerformed method in this example.
This section shows the step-by-step development of a simple control panel for the HelloWorld agent. Steps (1) through (4) describe the code that is needed in the PAC, HelloWorldFrame.java. Steps (5) through (8) describe the rules in the RADL file HelloWorld.radl that interact with the PAC (in this simple example, all rules interact with the PAC). Development of the system will not always proceed from (1) through (8) in that order; it may be better to build the rules first and later develop the PACs, or build the rule and PACs together, incrementally.
This will require at least one argument, a PacCommSystem, which will be supplied by the agent. Some control panels may require other arguments as well. For example, a control panel constructor may also use a String argument to get the agent's name for the frame title. The constructor in HelloWorldFrame.java is the simplest possible constructor:
public HelloWorldFrame( PacCommSystem
pacCommSystem )
_communicationSystem =
pacCommSystem;
This constructor stores the PacCommSystem object into the data member _communicationSystem , so it will be available for use later. It would be possible to build the control panel in the constructor, but that's not done here. Building the control panel can be time consuming, so we wait and do it on the separate thread used for displaying the interface.
(2) Write the run() method, define the control panel.
Any class intended for use as a control panel should implement the Runnable interface so that it can be run on a separate thread, hence the need for a run() method. Writing the code for the control panel is a standard Java programming task, involving all the usual thrills and frustrations of GUI development. The HelloWorldFrame features a very simple interface with a text area to display output from the agent, and two buttons to send messages to the agent. As mentioned in step (1), it's usually better to build the control panel in the run() method than in the constructor because building the control panel can be slow and may cause the engine cycle to be delayed if it's done on the main execution thread. All code in the run() method can execute on a separate thread so the engine cycle is not delayed.
(3) Write an event handler method(s) to handle the events generated by the control panel.
This may require an actionPerformed method, and/or itemStateChanged method, and/or some internal classes or separate callback classes. The event handler code will depend on the type of components used in the interface, but will usually be similar to the code in HelloWorldFrame::actionPerformed . Events generated by the control panel will cause messages to be sent to the agent. The information required by the agent may be contained in the message attributes (e.g., performative) or in the content object contained in the message.
The control panel's event handler code in the Java file must be in agreement with the message handling rules in the RADL file. As mentioned in the previous section, the same message protocol must be used by the sender (the control panel) and receiver (the agent), otherwise the message will not be interpreted correctly.
In the current example, when the control panel was built in the run() method, the action command "Print" was associated with a button-push of the printButton object, in the statement:
printButton.setActionCommand(
PRINT_COMMAND );
Then the HelloWorldFrame object itself was registered as the callback object for the button-push, in the statement:
printButton.addActionListener(
this );
After the user pushes the print button the sequence of events leading to a print request from the control panel to the agent are as follows:
KqmlMessage message = new KqmlMessage();
The callback method tests the action command and determines that the current invocation is a print request, so the callback method sets the performative to "achieve" and the content to the string "Say Hello".
message.setPerformative("achieve");
message.setContent("Say
Hello");
_communicationSystem.sendKqmlMessageToAgent(message);
The agent in this system will have a rule which recognizes an inbound message with Performative = "achieve" and the string "Say Hello" for the message content. The message will cause the agent to assemble a string with "HelloWorld!" and its belief about the current time, which it will then transmit to the control panel via the Print private action.
(4) Write the method(s) to display output from the agent.
The type of output to display will be determined by the type of data being output from the agent's mental model. In this example the agent will use the PAC to print a string in a text area, so the method will be named print and will take a String argument. The choice of method name and argument list is up to the designer of the system, but must agree with the data types in the agent's mental model. Here the print method appends the string sent in by the agent to the text area in the control panel, which displays the string to the user.
public void print( String stringFromAgent
)
_textArea.append( stringFromAgent
+ "\n" );
In a more complex control panel there may be several print methods for different types of data output from the agent.
(5) Write a rule to initialize the control panel object.
Initialization of a control panel can be performed at agent start-up or during execution as the result of the firing of a rule. The main difficulty in constructing a control panel is the initialization of a PacCommSystem object with the agent's Internet address and the control panel name. In the HelloWorld example the PacCommSystem object is constructed as an argument to the HelloWorldFrame constructor. This occurs in the first rule in HelloWorld.radl in Figure 85.
Figure 85. Build HelloWorldFrame Rule
The Build and Launch HelloWorldFrame rule gets activated by an Agent belief with instance name startupTime , which is automatically created by the agent engine when the RADL file contains a startupTime belief in the initial agency beliefs section. When this rule fires a PacCommSystem object gets constructed from the agent's AgentInfo object (which contains the Internet address and related information) and a name assigned to the PAC by the agent (here, " HelloWorld:PAC "). The new PacCommSystem object then becomes an argument to the HelloWorldFrame constructor, which creates a PAC instance. The new HelloWorldFrame instance is given the instance name myHelloWorldFrame and is asserted into the agent's mental model.
The name stored in the PacCommSystem by the agent is used for communication between the PAC and the agent. Messages sent from the PAC to the agent will have the sender name set to the name in the PacCommSystem , and that is the value the agent will look for when expecting a message from the PAC. The name stored into the PacCommSystem does not need to be the same as the instance name of the PAC, although the same name can be used for both. The PAC instance name is used when testing for the existence of the PAC instance, e.g., ( BIND myHelloWorldFrame ). The name stored into the PacCommSystem is used when testing an incoming message to see whether it was sent by the PAC, e.g., ( ?message.sender EQUALS "HelloWorld:PAC" ).
(6) Write rules to handle the messages sent by the control panel.
Using the protocol developed when designing the event handler for the PAC, write rules that accept the messages sent by the PAC. The name of the PAC was stored into the PacCommSystem in the Build HelloWorldFrame rule, and that same PAC name will be used as the Sender for all messages sent by the PAC. The Print Greeting rule tests the Sender field of the message to ensure that a received message came from the PAC. As mentioned in step (3) above, a print request message will have performative = "achieve" and the message content will be the string "Say Hello", which is exactly what is tested here. See the code segment in Figure 86.
Figure 86. Print Greeting Rule
This rule then gets the current time from a built-in currentTime belief that is always present in the agent's mental model, and concatenates the time string onto its greeting and invokes the Print action. The Print action will invoke the print(String) method on the myHelloWorldFrame object, which will display the string from the agent in the text area on the control panel.
This section shows the step-by-step development of a control panel for the buyer agent in the Buyer/Seller example. This example is considerably more complicated than the HelloWorld agent example; you may wish to read through Chapter 9 in the User's Guide before reading this section.
The buyer agent uses a control panel to allow the user to select the product and quantity that the buyer agent will attempt to buy from store agents, and to display the buyer's inventory and account balance. The seller agents use display panels to display their inventories and their account balances. Both the buyer's and seller's interfaces also display information printed by the agents to indicate which messages have been received and what actions have been taken. Figure 87 and Figure 88 show these control panels.
Figure 87. BuyerFrame PAC Buyer
Figure 88. SellerFrame PACs
In addition to the control panel PACs, the buyer and seller agents also use PACs to represent entities in the problem domain and to store and transfer information. In this example a PriceQuote PAC is used to record product name, quantity, store name, and the quoted price. A PriceQuote object is sent from the BuyerFrame control panel to the buyer agent in a KQML message and then forwarded to each store agent known to the buyer agent. The store agents fill in the PriceQuote objects with their store names and quoted amounts and return the completed PriceQuote objects to the buyer agent. The buyer agent uses the information in the PriceQuote objects to determine the best deal and invokes methods on the BuyerFrame control panel with the PriceQuote objects as arguments to display the quotes to the user. The construction of data-storage PACs such as PriceQuote will not be discussed in this section. Here we'll assume they've already been built in the Object Modeling Tool and are available for use when designing the control panel and building the rule base.
Steps (1) through (6) describe the code that is needed in the PAC, BuyerFrame.java. Steps (7) through (12) describe the rules in the RADL file buyer.radl that interact with the BuyerFrame PAC. As was mentioned in the previous example, development of the system will not always proceed from (1) through (12) in that order. The ideal order of construction is: data-storage PACs, then the interface PACs, then the rules. It will usually be necessary to iterate through the process several times, adding or modifying PACs, adjusting the rules, etc.
(1) Decide what data must be stored in the control panel object.
This will depend on the type of information that will be displayed to the user and what kinds of input will be allowed. For the BuyerFrame PAC, the user will be allowed to specify a product from a list of known products, so a java.awt. Choice will be used. To specify the quantity to shop for, the user will be allowed to type a number into a java.awt. TextField, or accept a default quantity. The _messageTextArea , _inventoryTextArea , and _accountTextField are java.awt components that will display information to the user. The _pacCommSystem is a built-in AgentBuilder component that enables communication between a PAC object and its parent agent; this must be supplied by the parent agent. The _buyerName string must also be supplied by the parent agent. Finally, two hashtables provide easy lookups of data-storage PAC objects that are used by the BuyerFrame . Figure 89 shows the data members of the BuyerFrame PAC.
Figure 89. Java Code Segment
(2) Define a constructor for the control panel PAC.
This will require at least one argument, a PacCommSystem which will be supplied by the agent. The constructor in BuyerFrame.java stores data into the object and initializes its hashtables. Figure 90 shows the constructor for the BuyerFrame PAC.
Figure 90. Java Code Segment
It would be possible to build the interface in the constructor, but that's not done here. Building the interface can be time consuming, so it's best to wait and do it in the run() method. This allows us to build the interface on the separate thread used for displaying the interface.
Note, however, that we need to initialize several of the components here in the constructor because it's possible that they'll be used before the initialization of the interface is complete. Although long-running actions such as building an interface should usually be done on a separate thread, during initial development it may be easier to build the interface in the constructor. Then, when everything is working well, move the bulk of the interface initialization code to the run() method. Finally, determine which components could possibly be accessed before the interface is complete, and either adjust the rules or move the initialization of some components back into the constructor.
(3) Write the run() method, build the interface.
Any class intended for use as a control panel should implement the runnable interface so that it can be run on a separate thread, hence the need for a run() method. Writing the code for the control panel is a standard Java programming task involving all the usual thrills and frustrations of GUI development. The BuyerFrame features several java.awt components for input and output. As mentioned in step (2), it's usually better to build the control panel in the run() method than in the constructor because building the control panel can be slow and may cause the engine cycle to be delayed if it's done on the main execution thread. All code in the run() method can execute on a separate thread, so the engine cycle is not delayed.
(4) Write any methods that are needed to initialize the interface PAC with data from the agent.
In some situations all data can be transferred via the PAC constructor arguments, but in other situations extra methods may be needed. BuyerFrame has an addProduct(Product) method which the buyer agent invokes once for every Product object (a data-storage PAC defined for this project) in the initial objects section of the RADL file. These Product objects are used to populate the choice box in the interface; the product choices offered to the user are exactly the same as those known to the buyer agent.
(5) Write an event handler method(s) to handle the events generated by the control panel.
This may require an actionPerformed method, and/or itemStateChanged method, and/or some internal classes or separate callback classes. The event handler code will depend on the type of components used in the interface, but will always be similar to the code in BuyerFrame::actionPerformed . Events generated by the control panel may cause changes in the interface and/or messages to be sent to the agent. The information required by the agent may be contained in the message attributes (e.g., performative) or in the content object contained in the message.
The actionPerformed method for the BuyerFrame PAC will handle the events generated when the user clicks on the Shop! or Quit buttons. Events generated from other activities, such as selecting a product or typing a number for the quantity, will not be handled, but those other activities will change the state of the _productChoice or _quantityTextField . Then when the Shop! button event is handled the latest values for product and quantity will be extracted from the interface components.
The control panel's event handler code in the Java file must be in agreement with the message handling rules in the RADL file. As mentioned in the previous section, the same message protocol must be used by the sender (the control panel) and receiver (the agent), otherwise the message will not be interpreted correctly.
In the current example, when the control panel was built in the run() method, the Shop! button label was associated with a button-push of the shopButton object in the statement:
shopButton.setActionCommand(SHOP_BUTTON_LABEL);
Any string could be used for this command string, as long as the comparison done in actionPerformed tests for the same string. In this example the button labels are each used only once so the labels can also be used as command strings to distinguish between buttons. In an interface containing several buttons with the same label, unique command strings will need to be defined for each button.
Next, the BuyerFrame object itself was registered as the callback object for the button-push in the statement:
shopButton.addActionListener(this);
After the user pushes the Shop! button the following will occur:
KqmlMessage message = new KqmlMessage();
PriceQuote priceQuote = new
PriceQuote(productName,
message.setPerformative("forward");
message.setContent(priceQuote);
_pacCommSystem.sendKqmlMessageToAgent(message);
The agent in this system will have a rule which recognizes an inbound message with Performative = "forward" and a PriceQuote object for the message content. The message will cause the agent to forward the message on to all known store agents and assert a belief about its current goal (purchase the specified quantity of the specified product).
(6) Write the method(s) to display output from the agent.
The type of output to display will be determined by the type of data being output from the agent's mental model. In this example the agent will use the interface PAC to print status strings in a text area, print the buyer's current inventory in another text area, and print the buyer's current account balance in a text field.
The printing of status strings will be done by the methods displayMessage(String) and displayPriceQuote(PriceQuote) . Both of these methods will write output to the same text area, but different methods are used so that the extraction of values from the price quote doesn't need to be done in the buyer agent's rules. It's much easier to use the displayPriceQuote method to extract the store name, product name, and quoted price, then format and print them. This simplifies the work of the buyer agent and leaves the formatting details to the Java code in the BuyerFrame PAC.
The displayInventory(InventoryRecord) method updates a list of InventoryRecord objects (another data-storage PAC defined for this project) then prints the list to the inventory text area. The displayAccount(float) method updates the display of the account balance in the text field at the bottom of the interface.
Steps (7) through (12) describe some of the rules that interact with the BuyerFrame PAC.
(7) Write a rule to initialize the control panel object.
Initialization of a control panel can be performed at agent start-up, or during execution as the result of the firing of a rule. The main difficulty in constructing a control panel is the initialization of a PacCommSystem object with the agent's Internet address and the control panel name. In this example the PacCommSystem object is constructed as an argument to the BuyerFrame constructor. This occurs in the Build BuyerFrame rule in buyer.radl shown in Figure 91.
Figure 91. Build BuyerFrame Rule
This rule will be activated by an Agent belief with instance name "SELF", which is automatically created by the agent engine when the RADL file contains a SELF belief in the initial agency beliefs section. A PacCommSystem object gets constructed from the agent's AgentInfo object (which contains Internet address and related information) and the PAC's name, which is in the temporary variable ?pacName . BuyerFrame requires two arguments in its constructor: a PacCommSystem object and a String with the buyer agent's name.
(8) Write the rule(s) to transfer initial data from the agent to the interface .
In this example the buyer agent's mental model will be initialized with several Product objects; the control panel must be initialized with the same objects. The Add Product rule will be activated once for each Product object in the agent's mental model, and will cause the control panel to add the product to the product choice box that is displayed to the user. See the code segment in Figure 92.
Figure 92. Add Product Rule
(9) Write a rule to display the control panel.
After the initial data has been loaded into the control panel object, the interface can be displayed. The Start Control Panel rule invokes the run() method on the buyerFrame object, which will display the interface. Construction of the interface will occur on a separate thread, so the agent's main thread will not be delayed. Note that in many applications there will not be a need for a separate rule to add initial data (e.g., the Add Product rule) and so the interface display rule can be combined into the interface initialization rule. In this example the separate step is required because of the varying number. Figure 93 shows the BuyerFrame interface after it has been initialized. The code is shown in Figure 94.
Figure 93. BuyerFrame PAC
Figure 94. Start Control Panel Rule
(10) Write rules to handle the messages sent by the control panel.
Using the protocol developed when designing the event handler for the PAC, write rules that accept the messages sent by the PAC. The name of the PAC was stored into the PacCommSystem in the Build BuyerFrame rule, and that same PAC name will be used as the sender name for all messages sent by the PAC. The following rule tests the Sender field of the message to ensure that a received message came from the PAC, the performative is "forward", and the content is a PriceQuote object. This rule then stores the message temporarily into the agent's mental model and initializes a Purchase object to represent the current situation. Another rule will forward this newly-received message on to the store agents. Figure 95 shows the rule which handles messages sent by the control panel.
Figure 95. Receive Message from Control Panel Rule
(11) Write rules to generate output to the control panel.
Sometimes output to the control panel will be triggered by messages received from other agents or by combinations of values in the mental model of the agent. Private actions can be used to display output to the user in response to events or situations detected by the agent. Figure 96 shows the BuyerFrame interface after a message has been received from the control panel and forwarded to the store agent.
Figure 96. BuyerFrame PAC
Figure 97 shows the rule which handles messages from store agents. This rule will be activated by a message containing a completed PriceQuote object. The user will be notified that the message has been received and the contents of the PriceQuote will be displayed.
Figure 97. Receive Message from Store Agent Rule
Figure 98 shows the BuyerFrame interface after a PriceQuote message has been received from one of the store agents.
Figure 98. BuyerFrame PAC