MiceOnABeam Product Documentation

Modified: 4/26/2013 6:31 PM
Recently changed articles You can subscribe to this wiki article using an RSS feed reader.

Code Generation

The major goals when designing the code generation for MiceOnABeam was to ensure that the generated code is:

  • Easily readable and understandable;
  • Debuggable by the User within their virtual world environment;

This was done to some extent at the expense of a more optimum generated code, but the trade-off is considered well worth it.

There are several options that can be set that affect the code generation, including options for debug tracing, and section comments. Please see Code Generation Options for full details.

Note: The following information is provided so that advanced Users can gain an understanding of the code generation architecture. However it is not necessary for Users to become familiar with this material in order to use MiceOnABeam.

Layout

The general layout of the generated code consists of the following sections, each discussed below.

  1. Header
  2. Run-Time Preferences
  3. Custom Globals
  4. System Globals
  5. State History Globals
  6. State Variables & Functions
  7. Utility Functions
    • Trace Functions
    • Event Data
    • TERMINATE_SCRIPT
    • SET_STATE
  8. State Entry/Exit Functions
  9. User-Defined States
    • Setup
    • Entry Action
    • Event Processing for Composite States
      • Incoming Continuing or Group Transitions
      • Choice Points
      • Junction Points
      • History Points
      • Initial Point
      • Outgoing Continuing Transitions
  10. Default State
    • State Entry
    • Event Handlers

Header

The Header section consists of a banner detailing the script name and it's user-settable version number (set through the menu item Set Version... within the Script menu of the program). This version number can be obtained programatically within the script through the global string variable ScriptVersion.

This is followed by the description for the script that may be set through menu item Edit Comment... within the Script menu of the program.

//***********************************************************************
// MiceOnABeam AUTO-GENERATED LSL SCRIPT - Version: 1.0.4
// Meta-structure of generated code Copyright © 2010-2013 MiceOnABeam Software. All rights reserved
//
// SCRIPT: ScriptABC; VERSION: 1.0.0
// ScriptABC provides many userful functions for the user. This is its
// description.
//***********************************************************************
string ScriptVersion = "1.0.0";

Run-Time Preferences

To facilitate the debugging of MiceOnABeam-generated scripts by the User within their virtual world environment, various execution traces can be enabled in the model (via Code Generation Options within Options... of the Tools menu of the program) which will instrument the generated code such that it will output various text messages as the script executes. For more details on debugging please see Script Debugging.

The Run-Time Preferences section includes the specific execution traces that have been enabled and allows the User to enable or disable individual traces while within their virtual world script editor, saving having to go back to MiceOnABeam to regenerate the code. State, event and transition trace variables are only included if their corresponding trace options have been enabled in the Code Generation Options.

//**********************************
// RUN-TIME PREFERENCES
// You may set any trace variable within this section to FALSE
integer Trace = TRUE; // Enables/Disables all execution tracing
integer TraceStateEntry = TRUE; // Enables/Disables state entry tracing
integer TraceStateExit = TRUE; // Enables/Disables state exit tracing
integer TraceEvents = TRUE; // Enables/Disables event tracing
integer TraceTransitions = TRUE; // Enables/Disables transition tracing
integer TraceTimings = TRUE; // Enables/Disables performance analysis output
//**********************************

Custom Globals

Custom Globals consist of a sequence of LSL variable and function declaration statements that have been manually entered by the User into the MiceOnABeam model (via the menu item Custom Globals... within the Script menu of the program). These declarations are global to the generated code and can be accessed from within any model components that provide for user-entered code.

The Custom Globals declarations are simply inserted into the generated code by MiceOnABeam within it's own section as shown in the following example.

//**********************************
// CUSTOM GLOBALS
//**********************************
vector colorRed= <1.0, 0.0, 0.0>;
vector colorBlue= <0.0, 0.0, 1.0>;
vector colorOrange= <1.0, 0.5, 0.0>;
vector colorWhite = <1.0, 1.0, 1.0>;
vector colorBlack = <0.0, 0.0, 0.0>;
vector colorDarkGreen = <0.0, 0.4, 0.0>;
vector colorGreen = <0.0, 1.0, 0.0>;
vector colorCyan= <0.0, 1.0, 1.0>;
vector colorPink= <1.0, 0.0, 0.5>;
vector colorPurple= <0.6, 0.0, 0.6>;

System Globals

Several global variables are used in the generated code as follows:

  • CurrentState: This variable tracks the current active state of the script model. It is used primarily in the generated event handler code to determine which state the model is in when an event is received. Each state is represented by it's internal integer key.
  • DoEntryAction: This is used to determine whether the Entry Action should be executed upon entering a State. For example, if a model transitions from a substate to it's enclosing Composite State's border, then as the Composite State is already active and is not being re-entered from a modeling point of view, this variable is set FALSE so that the state's Entry Action is not executed.
     
  • GoInside: This variable is used in the processing of a Composite State. When transitioning to a Composite State it determines whether the transition is continuing to the inside decomposition of the state to handle an Incoming Continuing, Group, or Initial Transition, or is leaving the state and so we need to process an Outgoing Continuing Transition.
     
  • NextEvent: This variable is used to select the transition to process. It represents the currently fired transition's Event, but it's value is actually an internal key to the name of the transition.

    There are several internal events that the generated code can also assign to this variable, including:
    • %%init_event: The event assigned to the Initial Transition.
       
    • %%shallowhistory_event: The event triggering the processing of a Shallow History Point.
       
    • %%deephistory_event: The event triggering the processing of a Deep History Point.
       
    • %%Choice_xxx: The event triggering the processing of a Choice Point named xxx.
       
    • %%defaulthistory_event: This event is assigned in the model to the Default History transition for a Shallow/Deep History Point, however it is not currently used in the generated code.
       
    • %%completion_event: This event is assigned in the model to a transition which responds to the completion of a Composite State in response to a transition to a Final State within the Composite State's decomposition. However it is not currently used in the generated code.
       
    • %%undefined: This is a placeholder event for a transition in the model which has not yet had an event assigned or is a Continuing Transition and so cannot have an event assigned.
       
  • EventData: This variable is used to maintain a copy of the data passed by the arguments of the most recently received Event. This allows the data to be easily accessed from within any level of the design model. Various utility functions are provided to access this data which are covered in the next section.
// SYSTEM GLOBALS
integer CurrentState = 0;
integer DoEntryAction = TRUE;
integer GoInside = TRUE;
integer NextEvent = 1; // %%init_event
list EventData = [];

State History Globals

For each Composite State the generated code keeps track of the last active substate within it's decomposition, which is called it's Last State. It does this by creating a special global variable for each Composite State including the implicit top-level state of the script. These variables are used in the processing of the Shallow History and Deep History features.

Each such variable is assigned the name of a Composite State scoped to the top-level state and appended with "_LastState". The default value assigned is the internal key value for the name of the Initial Point, InitialPoint.

For a script named ScriptABC which contains the Composite State S1, which itself contains the Simple State SubS1, the following State History global variables would be declared:

// STATE HISTORY GLOBALS
string ScriptABC_LastState = key1; // InitialPoint
string S1_ScriptABC_LastState = key2; // InitialPoint

The xxx_LastState State History global variables are only created and set if the Deep History or Shall History features are used.

State Variables & Functions

State Variables and Functions can be declared within the scope of a state and are only accessible from within that state and within all it's contained substates, and so on down to the leaf states of the nested state hierarchy. Within the generated code all such variables and functions are actually declared as globals to the entire script. Proper access control is ensured by re-naming the variables and functions to reflect the scope in which they were defined within the MiceOnABeam model.

For example, the new name for a state variable or function defined in substate S, is constructed by taking it's original name and adding to it the name of each higher Composite State that state S is contained in, right up to and including the implicit top-level state (which takes on the name of the script itself).

The delimeter separating the parts of any scoped name used in the generated code is the underscore "_".  However within comment statements in the generated code, as well as in the user interface in MiceOnABeam, the double-colon, "::", delimiter is used.

Suppose the variable listenHandle has been defined at the top-level of a script named ScriptABC and the function CalcSlope has been defined within a substate S1. The variable and function will be renamed and declared as follows:

// STATE VARIABLES & FUNCTIONS
// ScriptABC:
integer listenHandle_ScriptABC;
// S1::ScriptABC:
float CalcSlope_S1_ScriptABC(float x1, float x2, float y1, float y2)
{  
    return ((y2 - y1) / (x2 - x1));
}

Naturally Users can continue to use the declared variable and function names when referencing them within user-included code, as they will be automatically updated with their properly scoped names in the generated code.

Utility Functions

Several global functions are defined in this section. These can be used by both generated code and user code.

Trace Functions

These functions are used by the generated code when the User has enabled the various run-time traces (Code Generation Options). They can also be directly called by the User in order to conveniently output the value of a particular variable of interest. Please see Script Debugging for further details on variable tracing. Aside from MSGOUT, if the generated code does not include any calls to a particular trace function, then it is not included.

// UTILITY FUNCTIONS
// TRACE FUNCTIONS
MSGOUT(string x) {if (Trace) {llOwnerSay(x);}}
TRACE_INT(string name, integer x) {MSGOUT("TRACE>> "+name+": "+(string)x);}
TRACE_FLT(string name, float x) {MSGOUT("TRACE>> "+name+": "+(string)x);}
TRACE_STR(string name, string x) {MSGOUT("TRACE>> "+name+": "+x);}
TRACE_KEY(string name, key x) {MSGOUT("TRACE>> "+name+": "+(string)x);}
TRACE_VEC(string name, vector x) {MSGOUT("TRACE>> "+name+": "+(string)x);}
TRACE_ROT(string name, rotation x) {MSGOUT("TRACE>> "+name+": "+(string)x);}
TRACE_LST(string name, list x) {MSGOUT("TRACE>> "+name+": "+llList2CSV(x));}
Event Data

Every time an event is received by a script, the code generator includes a statement to save each of the arguments passed as an element of the global list type variable EventData. For example, the following statement is generated when a listen event occurs.

    listen(integer channel, string name, key id, string message)
    {
        EventData = ["%%channel",channel,"%%name",name,"%%id",id,
        "%%message",message];
        ...
    }

Then using the appropriate function below, any one of the event arguments can be accessed anywhere in User code that has been entered into the model. For example, to obtain the value of the id parameter above, one would use: EVENT_KEY("id").

  • EVENT_ARG(string name): Returns the index of the event argument referenced by the string parameter name.
  • EVENT_INT(string name): Returns the integer value of the event argument referenced by the string parameter name.
  • EVENT_FLT(string name): Returns the float value of the event argument referenced by the string parameter name.
  • EVENT_VEC(string name): Returns the vector value of the event argument referenced by the string parameter name.
  • EVENT_ROT(string name): Returns the rotation value of the event argument referenced by the string parameter name.
  • EVENT_KEY(string name): Returns the key value of the event argument referenced by the string parameter name.
  • EVENT_STR(string name): Returns the string value of the event argument referenced by the string parameter name.
  • EVENT_LST(string name): Returns the list value of the event argument referenced by the string parameter name. The value returned is a list corresponding to the list argument, however as the list's elements have been converted to strings, they must be extracted using llList2String and explicitly cast to the correct type.

If the generated code does not include any calls to a particular EVENT_XXX function, then it is not included.

// EVENT DATA
integer EVENT_ARG(string name) {integer x=llListFindList(EventData,["%%"+name]); if(x>=0) return x+1; else return 0x7FFFFFFF;}
integer EVENT_INT(string name) {return llList2Integer(EventData,EVENT_ARG(name));}
float EVENT_FLT(string name) {return llList2Float(EventData,EVENT_ARG(name));}
vector EVENT_VEC(string name) {return llList2Vector(EventData,EVENT_ARG(name));}
rotation EVENT_ROT(string name) {return llList2Rot(EventData,EVENT_ARG(name));}
key EVENT_KEY(string name) {return llList2Key(EventData,EVENT_ARG(name));}
string EVENT_STR(string name) {return llList2String(EventData,EVENT_ARG(name));}
list EVENT_LST(string name) {return llParseStringKeepNulls(llList2String(EventData,EVENT_ARG(name)),["::"],[]);}

For additional information on when these functions should be used, please see Event Data.

Terminate Script

This function is used to terminate the script in two scenarios. First, whenever the model transitions to a Terminate Point, the code generator adds a call to TERMINATE_SCRIPT after including the transition's action code.

Secondly, in the situation where the script at Run-Time is at a point where it is missing information on where to proceed, the script is terminated by a call to TERMINATE_SCRIPT and a message is output indicating why the script has terminated.

If the generated code does not include any calls to TERMINATE_SCRIPT, then the function is not included.

// TERMINATE SCRIPT
TERMINATE_SCRIPT(string x)
{
    if (llStringLength(x)!=0) {MSGOUT(x);}
    NextEvent = 0; // %%null_event
    llSetScriptState(llGetScriptName(),FALSE);
    llSleep(0.1);
}
Set State

This function is called to set the values of key system global variables whenever a Simple State (Leaf State) is entered.

// SET STATE
SET_STATE(integer stateNum)
{
    CurrentState = stateNum;
    DoEntryAction = TRUE;
    GoInside = TRUE;
    NextEvent = 0; // %%null_event
}

State Entry/Exit Functions

Any state may define an Entry Action which is a sequence of user-entered LSL statements that is executed whenever the state is entered, (i.e., becomes an active state). For each such Entry Action, a parameter-less global function is created and assigned the name of it's state, scoped to the top-level state and appended with "_state_entry".

For example take the script named ScriptABC which contains the Composite State Robot, which itself contains a Simple State Awake. If both these states have Entry Actions defined, the following global State Entry Functions would be declared:

// STATE-ENTRY/EXIT FUNCTIONS
Robot_ScriptABC_state_entry()
{
    llOwnerSay("Ready");
}
Awake_Robot_ScriptABC_state_entry()
{
    listenHandle_ScriptABC = llListen(0,"",llGetOwner(),"");
}

Any state may also define an Exit Action which is a sequence of user-entered LSL statements that is executed whenever the state is exited, (i.e., becomes an inactive state). For each such Exit Action a parameter-less global function is created and assigned the name of it's state, scoped to the top-level state and appended with "_state_exit".

For example take the script named ScriptABC which contains the Composite State Robot, which itself contains a Simple State Awake. If both these states have Exit Actions defined, the following global State Exit Functions would be declared:

Robot_ScriptABC_state_exit()
{
    llOwnerSay("I hear and obey");
}
Awake_Robot_ScriptABC_state_exit()
{
    llListenRemove(listenHandle_ScriptABC);
}

User-Defined States

Each and every state created in a MiceOnABeam model maps to a unique function in the generated code. The scoped name of the function generated for a state is constructed from the prefix "state_" followed by the state's name in the model, appended with the name of each higher Composite State that the state is contained in, right up to and including the implicit top-level state (which takes on the name of the script itself).

The code generated for a user-defined Composite State is divided into the following sections:

  • Setup
  • Entry Action
  • Event Processing for Composite States
    • Incoming Continuing or Group Transitions
    • Choice Points
    • Junction Points
    • History Points
    • Initial Point
    • Outgoing Continuing Transitions

Each of these sections are now described with reference to the pseudo-code of the overall structure shown below. Note that the pseudo-code is for illustrative purposes only as not every section will be present in the function generated for a specific state. In particular, the code for a Simple State will consist of a call to the system function Set_State and optionally a call to it's Entry Action function.

//**********************************
// STATE: S::ModelABC
//**********************************
state_S_ModelABC()
{
    ModelABC_LastState = aState;
    CurrentState = aState;
    if (DoEntryAction) { S_ModelABC_state_entry();}
    DoEntryAction = TRUE;

    // Event Processing for Composite States
    while (NextEvent != 0) // %%null_event
    {
        if (GoInside)
        {
            // Incoming Continuing or Group Transitions
            if (NextEvent == Transition_nnn)
            {
                // Transition nnn
                NextEvent = 1; // %%init_event
                state_SomeSubState_S_ModelABC();
            }

            // Check for Choice Points
            else if (NextEvent == Choice_nnn)
            {
                // Transition: ChoiceTransition 1
                if (GuardCondition1)
                {
                    NextEvent = Transition_nnn;
                }
                // Transition: ChoiceTransition 2
                else if (GuardCondition2)
                {
                    NextEvent = Transition_nnn;
                }
                else
                {
                    // If reached this point, no guard evaluated TRUE
                    // Terminate Script
                    TERMINATE_SCRIPT("Script terminated! No guard evaluates
                    TRUE for Choice Point: nnn in state: S_ModelABC");
                }
            }
            // Check for Junction Points
            if (NextEvent == Junction_nnn) // Junction_nnn
            {
                // Transition: JunctionTransition
                NextEvent = Destination_nnn;
                state_State_nnn();
            }
            // Check for shallow history event
            else if (NextEvent == %%shallowhistory_event)
            {
                NextEvent = %%init_event;
                // Transition to LastState.
                // NextEvent is set to %%init_event
            }
            // Check for deep history event
            else if (NextEvent == %deephistory_event)
            {
                // Transition to LastState.
                // NextEvent remains %%deephistory_event
            }
            //Initial Point
            else
            {
                // Follow Initial Transition
            }
        }
        else
        {
            GoInside = TRUE;
            // Outgoing Continuing Transitions
            if (NextEvent == OutgoingTransition_nnn)
            {
                // Transition: OutgoingTransition nnn
                S_ModelABC_state_exit();
                NextEvent = 1; // %%init_event
                state_SomeState_ModelABC();
            }
            else {NextEvent = 0;} // %%null_event
        }
    }
} // End of state S::ModelABC

Setup

The CurrentState and xxx_LastState system globals are set to reflect the state being processed.

Entry Action

The global variable DoEntryAction is tested and if TRUE, the state's Entry Action function is called.

Event Processing for Composite States

This section handles the processing for both Incoming and Outgoing Continuing Transitions as well as the Choice, History and Initial Point modeling components. The various transition types are discussed in Transition.

Note that Group Transitions when expanded at code-generation time become Continuing Transitions.

The processing takes place within a while loop which permits the handling of multiple internal events within the same context. (E.g., an internal Group Transition to History.)

The GoInside system global is first tested to determine whether we are transitioning to the inside of the state, or whether we are transitioning to the outside of the state.

Incoming Continuing or Group Transitions

Continuing transitions from a Composite State's border to an object contained within it's decomposition are handled here. An if (...) {...} else if (...) {...} else {...} construct is used to find the current transition or event to process by testing the system global NextEvent.

Choice Points

The NextEvent variable is checked against the internal event created for the named Choice Point and if it matches, a series of checks are made of the Guard Conditions for each of the outgoing transitions from the Choice Point. If one matches, then the script transitions to the destination state of the successfully triggered transition.

If no Guard Condition evaluates TRUE, then a transition with an ELSE guard is triggered if it exists. If there is no ELSE transition in this situation, then the model is considered ill-defined and the script terminates via a call to the TERMINATE_SCRIPT function.

Junction Points

The NextEvent variable is checked against the internal event created for the named Junction Point and if it matches, the script transitions to the destination state of the outgoing transition.

If there is no outgoing transition in this situation, then the model is considered ill-defined and the script terminates via a call to the TERMINATE_SCRIPT function.

History Points

For Shallow History processing we first set the NextEvent system global to %%init_event so that Default Entry will be invoked on the next state we transition to. The most recent active substate for the Composite State is then transitioned to by testing the xxx_LastState variable for the state. The DefaultHistory transition is also handled here if required.

The processing of Deep History is identical to that of Shallow History except that the NextEvent variable is not set to %%init_event and remainns as %%deephistory_event. Then when the transition is made to the last active substate, Deep History is invoked again if the substate is a Composite State.

Initial Point

The Initial Point and it's Initial Transition are handled as part of the code for GoInside processing. Here, the last else clause in the code handles the case of no match, which should only occur for the handling of Default Entry, when NextEvent is set to %%init_event.

The Initial Transition's action code is inserted, followed by a transition to the default substate for the Composite State. If the latter has not been specified, then a call to TERMINATE_SCRIPT will be made here.

Outgoing Continuing Transitions

Continuing transitions from a Composite State's border to a component outside the state are handled here. An if (...) {...} else if (...) {...} construct is used to find the current transition to process by testing the global variable NextEvent.

In the case of no match, NextEvent is set to the empty string, and the while loop ends causing the script to remain in the same state.

 

Default State

State Entry

All LSL scripts must include a definition for the script's default state which is entered when the script is first loaded after a compile or when reset (manually or programmatically). The state_entry() for this state is automatically generated and will immediately transition to the top-level state.

For a script named ScriptABC, we would have the following generated code for the default state:

//**********************************
// DEFAULT STATE
//**********************************
default
{
    state_entry() { state_scriptABC();}
}
Event Handlers

The section provides the front-line event handlers for originating transitions. A separate event handler is generated for each LSL Event that has been specified in the model.

Each event handler starts off by saving the event's argument data within the global EventData variable. An if ... else if ... series of statements then determines which is the currently active state. Once inside the appropriate state code block, code for the corresponding transition is inserted. This will include a test of the Guard Condition if not TRUE, the Transition Action code if any and a call to the destination state function (which will assign to a new value to CurrentState).

Note that if there are multiple transitions with the same event originating from the same state, these conflicting transitions will be consolidated together within the same state code block along with code to test each transition's Guard Condition to determine which transition to take. (See Transition Conflicts & Priority.)

The following shows the event handler code that is generated for a simple script model for a switch that moves between two states upon receiving a touch_start event.

    //**********************************
    // EVENT: touch_start
    //**********************************
    touch_start(integer num_detected)
    {
        EventData = ["num_detected",num_detected];
        // STATE: Off::Light
        if (CurrentState == 6)
        {
            // Transition: touch_start
            NextEvent = 1; // %%init_event
            state_On_Light(); return;
        }
        // STATE: On::Light
        else if (CurrentState == 8)
        {
            // Transition: touch_start
            NextEvent = 1; // %%init_event
            state_Off_Light(); return;
        }
    }

For Group Transitions the code generator will optimize where possible by combining code common to several states into a single if statement. (See Code Generation Options.) The following example shows how the timer Group Transition event handling code is consolidated for all three states.

 

    timer()
    {
        EventData = [];
        if (CurrentState == 6 || CurrentState == 8 || CurrentState == 10)
        {
            NextEvent = 13;
            DoEntryAction = FALSE;
            state_newScript(); return;
        }
    }

 

Tags:
Home: MiceOnABeam Product Documentation Copyright © 2010-2018 MiceOnABeam Software