12.4 Implementing Agents in C

12.4 Implementing Agents in C++

We will explore a simplified variation of our previous example of an agent and demonstrate how it can be approached in C++. The purpose of this agent is to schedule and book vacations for the owner of the ABC Auto Repair Company. The owner has dozens of employees and therefore doesn't have time to figure out when and where to go on vacation. Furthermore, unless the owner is making a certain amount of profit, vacations are out of the question. So the owner has acquired agent software that will plan and schedule vacations at various times throughout the year if the conditions are right. As far as the owner is concerned , the primary selling feature is that the agent runs unattended. Once the agent is installed on the computer the owner doesn't have to bother with it. When the agent determines that an appropriate time for a vacation has arrived, the agent will schedule the vacation, book the hotel and transportation, and then e-mail the owner an itinerary . The only responsibility the owner has is during the setup of the agent. The owner has to specify where he would like to go and how much profit the business must make before he can go. Let's look at how this agent could be constructed . Recall from Figure 12-1 that the rational component of an agent class consists of cognitive data structures and inference methods . The cognitive data structures help to capture beliefs, propositions , notions of epistemic data, knowledge, fallacies, facts, and so on. The agent class uses inference methods to access these cognitive data structures in the process of problem solving and task performance. The standard C++ library comes with a set of container classes and algorithms that can be used to implement the CDS and the inference methods.

12.4.1 Proposition Datatypes and Belief Structures

This agent has beliefs about the performance of the owners auto repair business. The beliefs capture information about how many customers per hour , the bay utilization per day, and the total sales during the period. Furthermore, the agent knows that the owner only likes bus trips. Therefore, the agent keeps up on any available bus trips that the owner might enjoy. In a math- intensive program, the primary datatypes are integers and floating point numbers . In a graphics-intensive program, the primary datatypes are pixels, lines, colors, circles, and so on. In an agent-oriented program, the primary datatypes are propositions, rules, statements, literals, and strings. We will use the object-oriented support in C++ to build a few datatypes that are native to agent-oriented programming. Example 12.1 shows the declaration for a proposition class.

Example 12.1 Declaration of a proposition class.
 template<class C> class proposition { //... protected:    list<C> UniverseOfDiscourse;    bool TruthValue; public:    virtual bool operator()(void) = 0;    bool operator&&(proposition &X);    bool operator(proposition &X);    bool operator(bool X);    bool operator&&(bool X);    operator void*();    bool operator!(void);    bool possible(proposition &X);    bool necessary(proposition &X);    void universe(list<C> &X);    //... }; 

A proposition is a statement in which the subject is affirmed or denied by the predicate. A proposition is either true or false. A proposition can be used to capture any single belief that the agent has. Also, other information that the agent does not necessarily believe but is offered to the agent will be presented as a proposition. The proposition is a cognitive datatype. It should be just as functional in an agent-oriented program as a floating point or integer datatype in a math-oriented program. Therefore, we use the operator overloading facilities of C++ to provide some of the basic operators that are applicable to propositions. Table 12-4 shows how the operators are mapped to logic operators.

The proposition class in Example 12.1 is a scaled-down version. The goal of this class is to allow the proposition datatype to be used just as easily and naturally as any other C++ datatype. Notice that the proposition class has the declaration:

Table 12-4. How the Operators are Mapped to Logical Operators

C++ User -Defined Operators

Commonly Used Logical Operators

&&

^

v

!

~

possible

necessary

 virtual bool operator()(void) = 0; 

This is called a pure virtual method. When a class has a pure virtual method, it means the class is an abstract class and cannot be directly instantiated . This is because there is no definition for the pure virtual function in the class. The function is only declared, not defined. Abstract classes are used to define policies and blueprints for derived classes. A derived class must define any pure virtual functions that it inherits from the abstract class. Here the proposition class is used to define the minimum capability that any descendant will have. Another important thing to notice about the proposition class in Example 12.1 is it's also a template class. It contains the data member:

 list<C> UniverseOfDiscourse; 

This data member will be used to hold the universe of discourse that the proposition belongs to. In logic, the universe of discourse contains all of the legal things that may be considered during a discussion. Here, we use a list container. Since the topics under consideration in a universe of discourse can take on different types, we use a container class. We make the UniverseOfDiscourse protected instead of private so that it can be accessed by all descendants of the proposition class. The proposition class also has the capability to deal with logical necessity and possibility, the major themes in modal logic that are also useful in agent-oriented programming. Modal logic allows the agent to deal with what is possibly true or what is necessarily true. Table 12-4 lists the primary operators used for logical possibility and necessity. We provide these methods for exposition purposes only; the implementations of these methods are beyond the scope of this book. However, they are part of the proposition classes that the authors use in practice. To make the proposition class usable, we derive a new class that we name trip_announcement . The trip_announcement class is a statement about the existence of a bus trip from some point of origin to some destination. For instance: There is a bus trip from Detroit to Toledo. This makes a statement that is either true or false. If we were concerned with when this statement was true or false we might imply temporal logic. Temporal logic deals with the logic of time. Agents also employ temporal reasoning. But here all propositions refer to the current time. This statement asserts that there is currently a bus trip from Detroit to Toledo. The agent will either be able to verify this and therefore believe or reject it as a false statement. Example 12.2 shows the declaration of the trip_announcement class.

Example 12.2 Declaration of the trip_announcement class.
 class trip_announcement : public proposition<trip_announcement>{ //... protected:    string Origin;    string Destination;    stack<trip_announcement> Candidates; public:    bool operator()(void);    bool operator==(const trip_announcement &X) const;    void origin(string X);    string origin(void);    void destination(string X);    string destination(void);    bool directTrip(void);    bool validTrip(list<trip_announcement>::iterator I,                   string TempOrigin);    stack<trip_announcement> candidates(void);    friend bool operator(bool X,trip_announcement &Y);    friend bool operator&&(bool X,trip_announcement &Y);    //... }; 

Notice that the trip_announcement class inherits the proposition class. Recall that the proposition class is a template class and requires a parameter to determine its type. The declaration:

 class trip_announcement : public proposition<trip_announcement>                           {...}; 

provides the proposition class with a type. It is also important to note that the trip_announcement class defines the operator() . Therefore, trip_announcement is a concrete class as opposed to an abstract class. We may declare and use the trip_announcement proposition directly within our agent program. The trip_announcement class adds some additional data members :

 Origin Destination Candidates 

These data members are used to contain the origin and destination of a bus trip. If the bus trip requires transfers from one bus to another and multiple layovers, then the Candidates data member will contain the complete route involved. Therefore, the trip_announcement object will be a statement about a bus trip and the route involved. The trip_announcement class also defines some additional operators. These operators help to put the trip_announcement class on equal footing with built-in C++ datatypes. In addition to beliefs about trips, the agent also has beliefs about the performance of areas within the owner's business. These beliefs differ in structure but are still basically statements that will be either true or false. So, we again use the proposition class as a base class. Example 12.3 shows the declaration for the peformance_statement class.

Example 12.3 Declaration for the performance_statement class.
 class performance_statement : public proposition<performance_                               statement>{    //...    int Bays;    float Sales;    float PerHour; public:    bool operator() (void);    bool operator==(const performance_statement &X) const;    void bays(int X);    int bays(void);    float sales(void);    void sales(float X);    float perHour(void);    void perHour(float X);    friend bool operator(bool X,performance_statement &Y);    friend bool operator&&(bool X,performance_statement &Y);    //... }; 

Notice that this class also provides the template class proposition with a parameter:

 class performance_statement : public proposition<performance_                               statement> {...} 

With this declaration the proposition class is now specified for performance_statements . The performance_statement class is used to represent beliefs about how many sales, customers per hour, and bay utilization the owner's business has. Each statement corresponds to a single belief that the agent has in each area. This information is held in the data members:

 Bays Sales PerHour 

Statements such as: "Location 1 grossed $300,000 in sales, had 10 customers per hour, and had a bay utilization of 4" can be represented by the performance_statement class. So our agent class has two categories of beliefs implemented as datatypes derived from the proposition class. Figure 12-2 shows a UML class diagram for the trip_announcement class and the performance_statement class. These classes hold the structure of the agent's beliefs.

Figure 12-2. UML class diagram for the trip_announcement class and the performance_statement class.

graphics/12fig02.gif

12.4.2 The Agent Class

The classes shown in Figure 12-2 provide the foundation for the CDS of the agent, the basis for what will make the agent rational. It's the fact that the agent class is rational that distinguishes it from other types of object-oriented classes. Example 12.4 shows the declaration for the agent class.

Example 12.4 Declaration of the agent class.
 class agent{    //... private:    performance_statement Manager1;    performance_statement Manager2;    performance_statement Manager3;    trip_announcement Trip1;    trip_announcement Trip2;    trip_announcement Trip3;    list<trip_announcement> TripBeliefs;    list<performance_statement> PerformanceBeliefs; public:    agent(void);    bool determineVacationAppropriate(void);    bool scheduleVacation(void);    void updateBeliefs(void);    void setGoals(void);    void displayTravelPlan(void);    //... }; 

As with the proposition classes, the agent class is a scaled-down version of what would be used in practice. A complete listing of the declaration of the practical versions of these classes would be three or four pages. We show enough for exposition purposes. The agent class contains two list containers:

 list<trip_announcement> TripBeliefs; list<performance_statement> PerformanceBeliefs; 

The list containers are standard C++ lists. Each list is used to hold a collection of what the agent currently believes about the world. Our simple agent world is restricted to knowledge about bus trips and sales performance of his owner's business. The contents of these two containers represent the complete knowledge and belief set of the agent. If there are statements in these lists that the agent no longer believes, the agent will remove them. If the agent uncovers new statements during the course of inference, they are added to these beliefs. The agent has ongoing access to information about bus trips and the performance of the owner's business. The agent is able to update its beliefs as necessary. In addition to beliefs, the agent has objectives, which are sometimes represented as desires in the BDI (Beliefs Desires Intentions) model. The objectives support the primary directives that the agent has been given by its client. In our case the objectives will be stored in statements:

 performance_statement Manager1; performance_statement Manager2; performance_statement Manager3; trip_announcement Trip1; trip_announcement Trip2; trip_announcement Trip3; 

Keep in mind that this is an oversimplification of how objectives and directives are represented within an agent class. However, there is enough here to understand how these structures are built. The three Manager statements contain the performance goals that must be met before the owner can even consider going on vacation. The three Trip statements contain the bus trips that the owner would like to take if a vacation is earned. The beliefs together with the directives provide the basic cognitive datatypes that the agent has. The agent's inference methods combined with these cognitive datatypes form the agent's CDS (Cognitive Data Structure). The CDS forms the rational component and the distinguishing feature of an agent class. In addition to containers that hold beliefs and structures that in turn hold directives and objectives, most practical agent classes will have containers that hold the agent's intentions, commitments, or plans. The agent gives directives by his client. The agent uses its ability to inference and acts to fulfill its directives. The inferencing and actions that an agent does often result in a container that holds intentions, commitments, or plans. Our simple agent doesn't require a container to hold intentions or plans. However, it does keep track of the route that a bus trip vacation would take. This is stored in a container called Candidates . The intentions or plans would work similarly. If our agent is able to achieve its directives, it will schedule the trip and e-mail the owner the specifics. The instant our agent object is constructed, it goes to work. Example 12.5 shows an excerpt from the agent's constructor.

Example 12.5 The agent class's constructor.
 agent::agent(void) {    setGoals();    updateBeliefs();    if(determineVacationAppropriate()){       displayTravelPlan();       scheduleVacation();       cout << "Emailed Vacation Approved and Scheduled" << endl;    }    else{           cout << "Emailed Vacation Not Appropriate At this time" << endl;    } } 
12.4.2.1 The Agent Loop

Many definitions for agents include requirements of continuity and autonomy. The idea is that the agent continually performs what tasks it is assigned without the need for human intervention. The agent has the capability to interact and semi-control its environment through a feedback loop. The continuity and autonomy are often implemented as an event loop where the agent continually receives messages and events. The agent uses the messages and events to update its internal model of the world, intentions, and take action. However, autonomy and continuity are relative terms. Some agents need to function from one microsecond to the next , while other agents only need to perform their services annually. In fact, with deep space mission software, an agent may have longer than an annual cycle. Multiple years may pass before the agent performs the next task. Therefore, we don't focus on physical event loops and constantly active message queues. While these work for some agents, they are not appropriate for others. We have found the notion of a logical cycle to be the most practical. The logical cycle may or may not be implemented as an event loop. The logical cycle can be anything from nanoseconds to years. Figure 12-3 shows a simple overview of a logical agent cycle.

Figure 12-3. Simple overview of a logical agent cycle.

graphics/12fig03.gif

The universe of discourse in Figure 12-3 represents everything our agent can legitimately interact with. This might include files, information from ports, or data acquisition devices. The information will be represented as some kind of proposition or statement. Notice that there is a feedback loop from the agent's outputs back to the agent's inputs. Our agent from Example 12.4 is only needed a few times a year. Therefore, it is not appropriate to put it in an event loop that constantly runs. Our agent will simply activate itself periodically during the course of the year to execute its initiatives. Example 12.5 shows the agent's constructor. When the agent activates, it sets some goals, updates its beliefs, and then determines whether a vacation is appropriate. If the vacation is appropriate, the necessary steps are taken and the owner is e-mailed. If a vacation is not appropriate at that time, then the owner is e-mailed with that fact also.

12.4.2.2 The Agent's Inference Methods

This agent has inference capabilities implemented partially by the proposition class descendants and partially by the method. Recall that the proposition class declared the operator()=0 as a pure virtual. They force derived classes to implement the operator() . We use this operator to design a proposition so the proposition itself can determine whether it is true or not, that is, the proposition classes are self-contained. This is a fundamental tenet of object-oriented programming, namely, that a class is a self-contained encapsulation of characteristics and behaviors. Therefore, one of the primary behaviors of the proposition class and its descendants is the capability to determine whether it is true or not. The operator overloading and function objects are used to accommodate this feature. Example 12.6 shows an excerpt from the proposition class and its descendant's definitions.

 determineVacationAppropriate() 
Example 12.6 Excerpts from the definition of the proposition class and its descendants.
 template <class C> bool proposition<C>::operator&&(proposition &X) {    return((*this)() && X()); } template <class C> bool proposition<C>::operator(proposition &X) {    return((*this)()  X()); } template<class C> proposition<C>::operator void*(void) {    return((void*)(TruthValue)); } bool trip_announcement::operator() (void) {    list<trip_announcement>::iterator I;    if(directTrip()){       return(true);    }    I = UniverseOfDiscourse.begin();    if(validTrip(I,Origin)){       return(true);    }    return(false); } 

The definitions of the and the && operators for the proposition classes determine whether the proposition is true or false. Each of these operator definitions ultimately calls the operator() defined for its class. Notice in Example 12.6 the definition for . This operator is defined as:

 template <class C> bool proposition<C>::operator                                         (proposition &X) {    return((*this)()  X()); } 

It allows code to be written as:

 trip_announcement A; performance_statement B; if (A  B){    // Do Something } 

When A or B is evaluated, the operator definitions will cause the operator() to be called. Each proposition class defines behavior for the operator() . For example, the trip_announcement class defines the operator() as:

 bool trip_announcement::operator()(void) {    list<trip_announcement>::iterator I;    if(directTrip()){       return(true);    }    I = UniverseOfDiscourse.begin();    if(validTrip(I,Origin)){       return(true);    }    return(false); } 

This code will determine whether there is a trip from some designated origin to some destination. For example, if the desired type is Detroit to Columbus and the universe of discourse contains:

Detroit to Toledo

Toledo to Columbus

Then a trip_announcement object will report that the statement that there is a trip from Detroit to Columbus is true, although the universe of discourse does not contain a statement like:

Detroit to Columbus

In fact, the trip_announcement class does check to see if there is a direct route from Detroit to Columbus. If there is a direct route, then it returns true. If there is no direct route, it attempts to find an indirect route. This behavior is accomplished by:

 if(directTrip()){    return(true); } I = UniverseOfDiscourse.begin(); if(validTrip(I,Origin)){    return(true); } 

processing in the operator() for the trip_anouncement class. The directTrip() method is straightforward and simply sequentially searches through the universe to see if there is a statement that says:

Detroit to Columbus

The validTrip() method uses a DFS (Depth First Search) technique to determine if there is an indirect route. Example 12.7 contains definitions for validTrip() and directTrip() :

Example 12.7 Definitions for validTrip and directTrip() .
 bool trip_announcement::validTrip(list<trip_announcement>::                                   iterator I, string TempOrigin) {    if(I == UniverseOfDiscourse.end()){       if(Candidates.empty()){          TruthValue = false;          return(false);       }       else{              trip_announcement Temp;              Temp = Candidates.top();              I = find(UniverseOfDiscourse.begin()                       UniverseOfDiscourse.end(),Temp);              UniverseOfDiscourse.erase(I);              Candidates.pop();              I = UniverseOfDiscourse.begin();              if(I != UniverseOfDiscourse.end()){                 TempOrigin = Origin;              }              else{                 TruthValue = false;                 return(false);           }       }    }    if((*I).origin() == TempOrigin && (*I).destination() == Destination){        Candidates.push(*I);        TruthValue = true;        return(true);    }    if((*I).origin() == TempOrigin){           TempOrigin = (*I).destination();           Candidates.push(*I);    }    I++;    return(validTrip(I,TempOrigin)); } bool trip_announcement::directTrip(void) {    list<trip_announcement>::iterator I;    I = find(UniverseOfDiscourse.begin(),UniverseOfDiscourse.end(),*this);    if(I == UniverseOfDiscourse.end()){       TruthValue = false;       return(false);    }    TruthValue = true;    return(true); } 

Both the validTrip() and directTrip() methods make use of the find() algorithm from the Standard C++ library. The UniverseOfDiscourse is a container that contains the agent's beliefs and statements made to the agent. Recall that one of the first steps the agent took was to updateBeliefs() . The updateBeliefs() method is what ultimately populates the UniverseOfDiscourse container. Example 12.8 contains the definition for the updateBeliefs() method.

Example 12.8 Update beliefs.
 void agent::updateBeliefs(void) {    performance_statement TempP;    TempP.sales(203.0);    TempP.perHour(100.0);    TempP.bays(4);    PerformanceBeliefs.push_back(TempP);    trip_announcement Temp;    Temp.origin("Detroit");    Temp.destination("LA");    TripBeliefs.push_back(Temp);    Temp.origin("LA");    Temp.destination("NJ");    TripBeliefs.push_back(Temp);    Temp.origin("NJ");    Temp.destination("Windsor");    TripBeliefs.push_back(Temp); } 

In practice the beliefs will come from the agent's executing environment (i.e., files, sensors, ports, data acquisition devices, etc.). In Example 12.8 the information pushed into TripBeliefs and PerformanceBeliefs represent new statements that the agent is receiving about the available trips and the performance of one of the auto repair locations. These statements will be evaluated against the directives or initiatives that the agent has. The setGoals() method establishes what the agent's directives are. Example 12.9 shows the definition for the setGoals() method.

Example 12.9 The set goals method.
 void agent::setGoals(void) {    Manager1.perHour(15.0);    Manager1.bays(8);    Manager1.sales(123.23);    Manager2.perHour(25.34);    Manager2.bays(4);    Manager2.sales(12.33);    Manager3.perHour(34.34);    Manager3.sales(100000.12);    Manager3.bays(10);    Trip1.origin("Detroit");    Trip1.destination("Chicago");    Trip2.origin("Detroit");    Trip2.destination("NY");    Trip3.origin("Detroit");    Trip3.destination("Windsor"); } 

These goals tell the agent that the owner would like to go from either Detroit to Chicago, Detroit to New York, or Detroit to Windsor. In addition to the trips, the financial objectives are also set. In order for a vacation to be achieved, one or more of these objectives must be met. After the goals have been set and the agent updates its beliefs, the next objective is to determine from the goals and the beliefs if a vacation can be scheduled. The second component of the agent's inference methods is invoked:

 determineVacationAppropriate() 

This method will pass the UniverseOfDiscourse to each of the proposition objects. It will then use a statement of the form:

 (A v B v C) ^ (Q v R v S)  W 

which states if at least one of the statements is true from each grouping, then W is true. In the case of our agent, this means that if at least one of the sales performance goals are met and there is a bus trip that is satisfactory, then a vacation is appropriate. Example 12.10 shows the definition of the determineVacationAppropriate() method.

Example 12.10 Second inference method.
 bool agent::determineVacationAppropriate(void) {    bool TruthValue;    Manager1.universe(PerformanceBeliefs);    Manager2.universe(PerformanceBeliefs);    Manager3.universe(PerformanceBeliefs);    Trip1.universe(TripBeliefs);    Trip2.universe(TripBeliefs);    Trip3.universe(TripBeliefs);    TruthValue = ((Manager1  Manager2  Manager3) &&                  (Trip1  Trip2  Trip3));    return(TruthValue); } 

Notice that the TripBeliefs and the PerformanceBeliefs are arguments to the universe() method of the Trip and Manager objects. This is where the propositions get the UniverseOfDiscourse information. Prior to the proposition calling their operator() , their UniverseOfDiscourse is populated . In Example 12.10 the statement:

 ((Manager1  Manager2  Manager3) && (Trip1  Trip2                                          Trip3)); 

causes six propositions to be evaluated by the operator. The operator for each proposition executes the operator() for each proposition. The operator() uses the UniverseOfDiscourse to determine whether the proposition is true or false. Examples 12.6 and 12.7 show how the operator() is defined for the trip_announcement class. Keep in mind that the trip_announcement class and the performance_statement class inherit much of their functionality from the proposition class. Example 12.11 shows how the operator() is defined for the performance_statement class.

Example 12.11 The performance_statement class.
 bool performance_statement::operator()(void) {    bool Satisfactory = false;    list<performance_statement>::iterator I;    I = UniverseOfDiscourse.begin();    while(I != UniverseOfDiscourse.end() && !Satisfactory)    {       if(((*I).bays() >= Bays)  ((*I).sales() >= Sales)           ((*I).perHour() >= PerHour)){         Satisfactory = true;       }       I++;    }    return(Satisfactory); } 

The operator() for each proposition class plays a part in the inferencing capabilities of the agent class. Example 12.6 shows how the operator() is called whenever or && is evaluated for a proposition class or one of its descendants. It is the combination of the proposition classes' operator() methods and the agent 's methods that produce the inference methods for the agent class. In addition to the and && operator defined by the proposition class, the trip_announcement class and the performance_statement class define:

 friend bool operator(bool X,trip_announcement &Y); friend bool operator&&(bool X,trip_announcement &Y); 

The friend declarations allow the proposition s to be used in longer expressions. If we have:

 //... trip_announcement A,B,C; bool X; X = A  B  C; //... 

then A and B will be OR'ed together and the result will be a bool . The next part of the evaluation tries to the bool with the trip_announcement datatype:

 bool  trip_announcement 

Without the friend declararations, this would be a illegal operation. Example 12.12 shows how these friend functions are defined.

Example 12.12 Operator overloading of and && .
 bool operator(bool X,trip_announcement &Y) {    return(X  Y()); } bool operator&&(bool X,trip_announcement &Y) {    return(X && Y()); } 

Notice that the definitions of these friend functions also call the function operator() with the reference to Y() . These functions are also defined for the performance_statement class. The goal is to make the proposition classes as easy to use as the built-in datatypes. The proposition class also defines another operator that allows the proposition to be used in a natural fashion. Let's examine the code:

 //... trip_announcement   A; if(A){    //... do something } //... 

How does the compiler define Test A ? The if() statement is looking for an integral type or a bool . A is neither . We want the compiler to treat A as a statement that is either true or false. The function operator is not called under this circumstance. So we define the void* operator to give us the desired functionality. This function operator can be defined accordingly :

 template<class C> proposition<C>::operator void*(void) {    return((void*)(TruthValue)); } 

This definition allows any proposition type presented in standalone fashion to be tested for a truth value. For example, when our agent class is preparing to send the owner an e-mail containing the route. The agent needs to determine which trip is available. Example 12.13 shows another excerpt from the agent's trip processing methods.

Example 12.13 displayTravelPlan() method.
 void agent::displayTravelPlan(void) {    stack<trip_announcement> Route;    if(Trip1){       Route = Trip1.candidates();    }    if(Trip2){       Route = Trip2.candidates();    }    if(Trip3){       Route = Trip3.candidates();    }    while(!Route.empty())    {        cout << Route.top().origin() << " TO " << Route.                top().destination() << endl;       Route.pop();    } } 

Notice that Trip1 , Trip2 , and Trip3 are tested as if they were bools . The candidates() method simply returns the route discovered for the trip. The operator overloading capabilities and the template facilities support the development of reusable inference methods and the CDS. These inference methods and the CDS make the object a rational object. A C++ programmer uses the classes construct to design agents. Container objects are often used in conjunction with the built-in algorithms to implement the CDS. A class that has a CDS is rational. A rational class is an agent.

12.4.3 Simple Autonomy

Since our simple agent class does not require the traditional "agent loop " to do its processing, we need other means to activate this agent without human intervention and on a period or cyclic basis. There will be many situations where an agent that you are writing only needs to run sometimes or only under limited conditions. The UNIX/Linux environments come with the crontab facility. The crontab facility is the user interface of the UNIX cron system. The crontab utility allows you to schedule one or more programs to be executed on a cyclic or periodic basis. Crontab jobs can be scheduled to the month, week, day, hour, and minute. To use crontab a file must be created that contains the schedule for when the agent is to be activated. The file is a simple text file set up in the following form:

minute

hour

day

month

weekday

command

Where each column can take on the following values:

minute

0-59

hour

0 “23

day

1 “31

month

1 “12

weekday

1 “7 (1 = Mon., 7 = Sun)

command

can be any UNIX/Linux command as well as the name of the file that contains your agents

Once this text file is created, it is submitted to the cron system as the command:

  $crontab   NameOfCronFile  

For example, if we have a file named activate.agent that is set up as follows :

15

8

*

*

*

agent1

21

*

*

6

agent2

*

*

1

12

*

agent3

and we execute the crontab command:

  $crontab  activate.agent 

Then agent1 will activate everyday at 8:15 A.M., agent2 will activate every Saturday at 9:00 P.M. and agent3 will activate every 1st of December. The cron files can be added or deleted as necessary. Cronfiles can contain references to other cron jobs, thus allowing an agent to reschedule itself. In fact, shell scripts can be used in conjunction with the crontab utility to provide extremely flexible, dynamic, and reliable activation of agents. See the man pages for a complete description of the crontab facility.

  $man  crontab or  $man  at 

The crontab and at facilities are the simplest method to automate or regularly schedule agents that don't require constantly executing event loops. They are reliable and flexible. On the other extreme, the implementation repositories and object request brokers that we discussed in Chapter 8 can also be used to implement automatic agent activation. Standard CORBA implementations also provide event looping capabilities.



Parallel and Distributed Programming Using C++
Parallel and Distributed Programming Using C++
ISBN: 0131013769
EAN: 2147483647
Year: 2002
Pages: 133

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net