Anatomy of a Script

Anatomy of a Script

This file is last updated Thu Sep 2 16:08:21 EDT 1999

The Basics
Default Imports
The Functions The Script Creates/Overrides
Things Available For You To Use

Finally, I am writing this (much needed) document ...

  • The Basics: a script, when created in GPGP2/fsmcc/, and after make namehere.script, then by default, a Java source file will be created in IHome/modules/agent/agent/coordinate/

  • The name of the Java file is the Script: field in your script. This Java file defines a class that extends the base InheritsFrom: class. Normally it's just FSM.

    Some of the fields in the script is fairly intuitive: since we are talking about a FSM, so naturally we'd have the States, StartState, FinalStates, Rules. Variables, InititateBy, Events, and Functions are the supporting fields, and they are discussed later in this page.

  • By default, your new class imports the following packages:
    package agent.coordinate;
    import java.util.*;
    import java.io.*;
    import taems.*;
    import taems.parser.*;
    import utilities.*;
    import agent.base.*;
    import agent.simplest.Log;
    import agent.mass.State;
    import agent.mass.ProblemSolver;
    

  • In your new class, the scripting tool adds/overrides the following functions to the base FSM class, namely:

    • base constructor, without any parameter public ()

    • another constructor, public (agent.simplest.Log, agent.mass.State)

    • static public Object inititate(FSMEvent, agent.simplest.Log, agent.mass.State)
      static public Object inititate(KQMLMessage, agent.simplest.Log, agent.mass.State)

      When an unknown msg or unknown event is received at the coordination bean, the bean will attemp to call this static function and check if it returns a non-null value. The returned object should be an object of yourFSM type.

      In your script, the InitiateBy: field specify pairs such as
      event:MyEventType -> MyEventFunction, or
      msg:MyMsgType -> MyMsgFunction

      This is the place that processes that information. Basically, if an unknown MyEventType event or MyMsgType msg is received, the constructor (Log, State) will be called to instantiate a newfsm object. Then, the function newfsm.MyFunction will be called.

      MyEventFunction has to be void MyEventFunction(FSMEvent).
      MyMsgFunction has to be void MyMsgFunction(KQMLMessage).

    • public void getSharedVariables(Hashtable)
      public void putSharedVariables(Hashtable)

      These two updates all Variables: objects in each FSM pulse.

    • public void deliverEvents(Vector)

      public void deliverMessages(Vector)

      These two functions dictate what types of FSMEvent/KQMLMessage this FSM receives. Each works as a filter, looking at the Vector and constructing a subset.

      There are two places in your script that contains info for : deliverEvents().

      First, the Events: slot specifies pairs such as
      event:MyEventType -> MyEvtCondition

      This information is used here: basically, if MyEventType FSMEvent appears in the Vector, you check if MyCondition(FSMEvent) is true. If so, this FSMEvent is delivered to this FSM. Clearly, the MyEvtCondition function must be
      boolean MyEvtCondition(FSMEvent) A second, but simpler case, it that when you use Event("MyEventType") as the precondition of a transition rule (don't forget the "", and don't leave any space in the expression. In that case, if MyEventType is not already defined in Event: slot, any FSMEvent of MyEventType in the Vector will be delivered to this FSM. Notice in this case you don't have a MyCondition function, which may be an unexpected effect. Personally, I don't think you should use Event("MyEventType") as the precondition without defining MyEventType in the Event: slot of your script.

      For KQMLMessages, deliverMessages() only looks at the Events: slot of your script and check for this:
      msg:MyMsgType -> MyMsgCondition.

      Clearly, MyMsgCondition has to be
      boolean MyMsgCondition(KQMLMessage)

    • public boolean final_state()
      public Vector enabledTransitions()
      public void fireTransition(String)

      These functions handle the Rules: part of your script. In my opinion, each and every rule should be in this format:
      StateA | MyRuleCondition(...) -> MyAction(...) | StateB.

      There are features such as AND/OR combinations in the rule firing condition part, but for clarity you would be better off just group the combinations in a separate function called MyRuleCondition(...). The parameters in () must be exactly the same as if it is a Java function call (be careful you need to put "" in your Strings), and don't leave any space in the (...) part. The same for MyAction(...). Here MyRuleCondition(...) must be a function that returns boolean.

    • Plus, all the Java code your wrote in your script after the Functions: keyword. You can write any Java code there, and use all the inherited data members and functions, as well as defining new class members.

  • Things you can use in your Script:

    • If you use Event("MyEventType") as a rule firing condition, you can use the currentevent variable to access the FSMEvent you just found. currentevent is already defined in FSM.java. The boolean Event(String) is defined in FSM.java which find the first FSMEvent of that String type, store it in currentevent, and returns true.

      Similarly, if you use Message("myMsgType"), you can use the currentmsg variable to access the KQMLMessage you just found.

    • There are these two functions that are often used when constructing the rule firing conditions:

      KQMLMessage MessageReceived(String msgtype)
      FSMEvent EventReceived(String evttype)

      Unlike Event(String), these return the objects instead of boolean values, so you have to define some script-wide local Variables to store the results. Another important thing is that MessageReceived and EventReceived removes the found object from the queue, (so they couldn't be processed twice), but Event(String) or Message does not remove the event/msg from the queue (may lead to the same event processed twice). Personally I'd avoid using Event(String) Message(String) unless you are sure the event/msg won't cause later trouble.

      Since MessageReceived is used so often, you can use MessageReceived(String msgtype) as the rule firing condition. This is the only exception that the rule firing condition does not have to be a boolean expression. The return KQMLMessage is stored in a variable named reply_to, which is defined in FSM.java. How convenient! :)

    • In your InitiateBy: slot, you can use the noFunction as the MyXXXFunction part, if you don't need to perform any additional action when the triggering FSMEvent/KQMLMessage is found.

    • In your Events: slot, you can use the noCondition as the MyCondition part if no additional condition is needed. The most popular condition, checkFsmID is also defined, which simply checks the FsmID of the FSMEvent to see if it matches the ID of this FSM.

    • To send an FSMEvent from the FSM, use FSMsendEvent(FSMEvent) or startFireEvent(FSMEvent); to send a KQMLMessage to other agent(s), use FSMsendMsg(KQMLMessage sndmsg) (the old FSMsendMsg(KQMLMessage sndmsg, KQMLMessage replyto) has been removed from the base FSM.java. If you still need that function, just copy this function to your script (or inherit from a FSM subclass that have this function) --
        public void FSMsendMessage(KQMLMessage send, KQMLMessage rply_to)  {
          // First grab the FSMId from reply_to
          String replyID;
          if (rply_to != null)
            replyID = rply_to.getField("reply-with");
          else
            replyID = new String("-1");
        
          log.log("ReplyID = "+replyID,1);
          send.addField("deliver-to-fsm", replyID);
          send.addField("reply-with", Integer.toString(ID));
        
          sendMessages.addElement(send);
        }
      


This page is maintained by Ping Xuan. Notify me if the content here isn't up-to-date.