Difference between revisions of "Coordinator TestUnitsReports.lsl"

From Second Life Wiki
Jump to: navigation, search
m (lsl code tagging)
Line 1: Line 1:
<pre>
+
<lsl>
 
///////////////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////////////
 
///////
 
///////
Line 793: Line 793:
  
 
} // end default
 
} // end default
</pre>
+
</lsl>

Revision as of 08:44, 12 April 2008

<lsl> /////////////////////////////////////////////////////////////////////////////////// /////// /////// /////// /////// Coordinator_TestUnitsReports /////// /////// /////// /////// This is the coordinator script that maintains the record of testUnit reports, /////// and manages the collection and output of those reports. It should be /////// included in the coordinator. /////// //////////////////////////////////////////////////////////////////////////////////////

//Coordinator_TestUnitReports .1 -> initial framework 6.28.2007 //Coordinator_TestUnitReports .2 -> bug fixing and testing 7.8.2007 //Coordinator_TestUnitReports .3 -> added report timeout and report filter for TEST_TIMED_OUT units 7.11.2007

////////////////////////////////////////////////////////////////////////////////////// // // Command Protocol // ////////////////////////////////////////////////////////////////////////////////////// // // All commmands will be :: separated // lists in string form. // ////////////////////////////////////////////// // LINK MESSAGE commands ////////////////////////////////////////////// // // link message commands will be recieved and sent on the toAllChannel // //////// INPUT /////////// // // Reset - resets this script // format example -> Reset // // ClearAll - empties all lists // format example -> ClearAll // // OutputReports - generates report dump for whatever collection specified // format example -> OutputReports::testSelected::ALL // // SetReportMethod - provides report output parameter // format example -> SetReportMethod::CHAT::channel::0 // -> SetReportMethod::EMAIL::address::you@lindenlabs.com // -> SetReportMethod::HTTP::url::www.yoururl.com // // SetReportType - provides type of Report desired // format example -> SetReportType::NORMAL // // SetTestSelected - provides type of test selected // format example -> SetTestSelected::ALL // // SetUnitCount - provides number of registered units // format example -> SetUnitCount::1 // // SetReportTimeoutLength - report time limit // format example -> SetReportTimeoutLength::10 // // AddUnitReport - update to Coordinator on the chat broadcastChannel // format example -> AddUnitReport::unitKey::00000-0000-0000-00000::Report::Successful Completion of Test // //////// OUTPUT /////////// // // ReportUnitStats - sends out request for unit stats // format example -> ReportUnitStats // // RequestUnitCount - sends out request for number of units registered // format example -> RequestUnitCount // // ReportRequest - sends out request to main coordinator script for reports from testUnits // format example -> ReportRequest::unitName // // ReportComplete - sends out notification to main coordinator script that reporting is done // format example -> ReportComplete // /////////////////////////////////////////////////////////////////////////////////////////////////////////////



// Global Variables

integer toAllChannel = -255; // general channel - linked message

integer debug = 0; // level of debug message integer debugChannel = DEBUG_CHANNEL; // output channel for debug messages

list unitKeys = []; // object keys list specified test units list unitReports = []; // list of test unit reports logged in

string reportType = "NORMAL"; // determines length and content of report type

                                               // NORMAL - failures and summary information
                                               // QUITE - summary information only
                                               // VERBOSE - everything

string reportMethod = "CHAT::channel::0"; // determines output method of report

                                               // CHAT::channel::0
                                               // EMAIL::address::you@lindenlabs.com
                                               // HTTP::url::www.yoururl.com

integer broadcastChannel; string testSelected = "ALL"; // selected test -> ALL, unitName, or groupName

key httpManager; // handler for the HTTP event

integer unitCount = 0; // number of registered units integer reportCount = 0; // number of units that reported

integer minMemory; // minimium free memory allowed before early report dump

list unitStatsSum; //the four element list is a running count of the status types

                                               //each stored at a specific index
                                               //LATE_REGISTRATION - 0 
                                               //TEST_TIMED_OUT - 1
                                               //PASS - 2
                                               //FAIL - 3

integer reportTimeoutLength; // report in seconds to allow for reporting integer reportTimerInterupt = 0; // indication if reportTimer has expired

////////////////////////////////////////////////////////////////////////////////////////////////// ////////// ////////// Function: ParseCommand ////////// ////////// Input: string message - command to be parsed ////////// ////////// Output: no return value ////////// ////////// Purpose: This function calls various other functions or sets globals ////////// depending on message string. Allows external command calls ////////// from chat controlChannel ////////// ////////// Issues: no known issues ////////// ////////// ///////////////////////////////////////////////////////////////////////////////////////////////// ParseCommand(string message) {

   if(debug > 0 )llSay(debugChannel, llGetScriptName()+ "->ParseCommand: " + message  + "<<<<<<<<<<<<<<<<<<<<  " + (string)llGetFreeMemory() + "     >>>>>>>>>>>>>>>>>>>>");
  
   //reset all scripts 
   if(message == "reset")
   {
       //reset this script 
       llResetScript();                   
   }
   
   //ClearAll()
   else if(message == "ClearAll")
   {
       //reset the lists to an empty state
       unitKeys = [];                    
       unitReports = [];                   
   
   }
   // SetReportMethod()
   // format example -> SetReportMethod::CHAT::channel::0
   else if(llSubStringIndex(message, "SetReportMethod::") != -1)
   {
       //store the parameters from the message string in the reportMethod after removing the command 
       reportMethod = llDeleteSubString( message, 0 , llSubStringIndex( message, "::") + 1 );
       
       if(llSubStringIndex( message, "CHAT") != -1)
       {
           //dump report method parameters into usable list
           list methodParameters = llParseString2List( reportMethod, ["::"], [""]);
           
              //pull channel from parameters list
           integer channel = (integer)llList2String( methodParameters, llListFindList( methodParameters, ["channel"]) + 1);
           
       }
     
   }
   
   //SetReportTimeoutLength()
   //format example -> SetReportTimeoutLength::10
   else if(llSubStringIndex(message, "SetReportTimeoutLength::") != -1)
   {
       //parse value from string by deleting message upto index of ::
       //and set global reportTimeoutLength variable
       reportTimeoutLength = (integer)llDeleteSubString( message, 0, llSubStringIndex(message, "::") + 1);
   }
     
   // SetReportType()
   // format example -> SetReportType::NORMAL
   else if(llSubStringIndex(message, "SetReportType::") != -1)
   {
       //store the parameters from the message string in the reportType after removing the command 
       reportType = llDeleteSubString( message, 0 , llSubStringIndex( message, "::") + 1 );
   }
   
   // SetBroadcastChannel()
   // format example -> SetBroadcastChannel::0
   else if(llSubStringIndex(message, "SetBroadcastChannel::") != -1)
   {
       //store the parameters from the message string in the broadcastChannel after removing the command 
       broadcastChannel = (integer)llDeleteSubString( message, 0 , llSubStringIndex( message, "::") + 1 );
   }
   
   // SetTestSelected()
   // format example -> SetTestSelected::ALL
   else if(llSubStringIndex(message, "SetTestSelected::") != -1)
   {
       //store the parameters from the message string in the reportType after removing the command 
       testSelected = llDeleteSubString( message, 0 , llSubStringIndex( message, "::") + 1 );
   }

   // SetUnitCount()
   // format example -> SetUnitCount::1
   else if(llSubStringIndex(message, "SetUnitCount::") != -1)
   {
       //store the parameters from the message string in the reportType after removing the command 
       unitCount = (integer)llDeleteSubString( message, 0 , llSubStringIndex( message, "::") + 1 );
   }
   

} //end ParseCommand

////////////////////////////////////////////////////////////////////////////////////////////////// ////////// ////////// Function: ProcessStats ////////// ////////// Input: string message - stat to be processed ////////// ////////// Output: no return value ////////// ////////// Purpose: This function is initiated by the status info from the Coordinator_TestUnits ////////// script. It determines if the stat is in the current test group ////////// and processes accordingly. ////////// ////////// Issues: no known issues ////////// ////////// ///////////////////////////////////////////////////////////////////////////////////////////////// ProcessStats(string message) {

  if(debug > 0)llSay(debugChannel, llGetScriptName()+ "->ProcessStats: " + message  + "<<<<<<<<<<<<<<<<<<<<  " + (string)llGetFreeMemory() + "     >>>>>>>>>>>>>>>>>>>>"); 
   
 //format example -> ReportStats::unitKey::00000-0000-0000-00000::unitName::TestUnit1::groupName::Group1::unitStatus::PASS
 
 //decrement unit count to indicate stat was recieved
 unitCount--;
 
 // dump string parameters into usable list
 list unitParameters = llParseString2List( message, ["::"], [""]);
 
 //pull name, group and status from unitParameters
 string name = llList2String( unitParameters, llListFindList( unitParameters, ["unitName"]) + 1);
 string group = llList2String( unitParameters, llListFindList( unitParameters, ["groupName"]) + 1);
 string status = llList2String( unitParameters, llListFindList( unitParameters, ["unitStatus"]) + 1);
        
 
 // if testSelected is ALL or matches the unitName or groupName
 // AND unit status does not indicate late registration
 if(

// (

           testSelected == "ALL" 
        || testSelected == name
        || testSelected == group 

// ) // && // ( // status != "LATE_REGISTRATION" // )

    )
    {
        string uKey = llList2String( unitParameters, llListFindList( unitParameters, ["unitKey"]) + 1);
        
        //if the unit was tested, but the status is still REGISTERED
        if(status == "REGISTERED")
        {
          status = "TEST_TIMED_OUT";    
        }
       
        //if the unit registered on time and finished the test on time, expect a further report  
        if( (status != "LATE_REGISTRATION") && (status != "TEST_TIMED_OUT") )
        {
           //add unitKey to unitKeys list
           unitKeys = (unitKeys = []) + unitKeys + uKey;
           
            uKey;
           //create an entry on the unit reports list for this unit
           unitReports = (unitReports = []) + unitReports + ["Key: " + uKey + "\n" +
                                                            "Name: " + name + "\n" +
                                                            "Group: " + group + "\n" +
                                                            "Status: " + status  ];     
        }
               
        //the four element list is a running count of the status types
        //each stored at a specific index
        //LATE_REGISTRATION - 0 
        //TEST_TIMED_OUT - 1
        //PASS - 2
        //FAIL - 3
        if(status == "LATE_REGISTRATION")
        {
            //replace the indexed posistion with the currently stored value + 1
            unitStatsSum = llListReplaceList( unitStatsSum, [llList2Integer( unitStatsSum, 0 ) + 1] , 0, 0 );
        }
        else if(status == "TEST_TIMED_OUT")
        {
            //replace the indexed posistion with the currently stored value + 1
            unitStatsSum = llListReplaceList( unitStatsSum, [llList2Integer( unitStatsSum, 1 ) + 1] , 1, 1 );
        }
        else if(status == "PASS")
        {
            //replace the indexed posistion with the currently stored value + 1
            unitStatsSum = llListReplaceList( unitStatsSum, [llList2Integer( unitStatsSum, 2 ) + 1] , 2, 2 );
        }
        else if(status == "FAIL")
        {
            //replace the indexed posistion with the currently stored value + 1
            unitStatsSum = llListReplaceList( unitStatsSum, [llList2Integer( unitStatsSum, 3 ) + 1] , 3, 3 );
        }
        
        
        
        //initiate a request from Coordinator_Coordinator for detailed report from the specific testUnit
        llMessageLinked(LINK_SET, toAllChannel, "ReportRequest::" + name, NULL_KEY);
    }
 

} // end ProcessStats

////////////////////////////////////////////////////////////////////////////////////////////////// ////////// ////////// Function: ProcessReports ////////// ////////// Input: string message - report to be processed ////////// ////////// Output: 0 - indicate process is not done ////////// 1 - indicates the process is done ////////// ////////// Purpose: This function is initiated by a report from a testUnit sent via ////////// the Coordinator_Coordinator script. It processes the report according ////////// to globally specified ReportMethod and memory management ////////// ////////// Issues: llEscapeURL is limited to 255 characters, which is a significant limit ////////// on sending HTTP reports out. ////////// ////////// ///////////////////////////////////////////////////////////////////////////////////////////////// integer ProcessReports(string message) {

   if(debug > 0)llSay(debugChannel, llGetScriptName()+ "->ProcessReports: " + message + "<<<<<<<<<<<<<<<<<<<<  " + (string)llGetFreeMemory() + "     >>>>>>>>>>>>>>>>>>>>"); 
   
 //AddUnitReport::unitKey::00000-0000-0000-00000::Report::Successful Completion of Test
 
 //increment reportCount to indicate another report was recieved
 reportCount++;
 
 // dump command parameters into usable list
 list unitParameters = llParseString2List( message, ["::"], [""]);
 //dump report method parameters into usable list
 list methodParameters = llParseString2List( reportMethod, ["::"], [""]);
 
 //pull unit key from parameters list
 list uKey = llList2List( unitParameters, llListFindList( unitParameters, ["unitKey"]) + 1, llListFindList( unitParameters, ["unitKey"]) + 1);
 //find the index of key on the unitKey list
 integer index = llListFindList( unitKeys, uKey );
 //pull the report from the parameters list
 string report = llList2String( unitParameters, llListFindList( unitParameters, ["Report"]) + 1);
 
 //if CHAT is selected reporting method, and the timer is not expired
 if( ( llSubStringIndex( reportMethod, "CHAT" ) != -1) &&  reportTimerInterupt != 1)
 {
       //pull channel from parameters list
     //integer channel = (integer)llList2String( methodParameters, llListFindList( methodParameters, ["channel"]) + 1);
    
    //for QUIET, leave out everything but the summary
    if( reportType != "QUIET")
    {
        //dump status header already stored in unitReports and new report info
        llSay( broadcastChannel , "***************" +  "\n" + llList2String( unitReports, index ) + "\n" + report + "\n" + "***************" );
    }
    
     //if we have seen all the reports
     if(reportCount == llGetListLength(unitKeys) )
     {
         
           //output the summary stats
          //the four element list is a running count of the status types
        //each stored at a specific index
        //LATE_REGISTRATION - 0 
        //TEST_TIMED_OUT - 1
        //PASS - 2
        //FAIL - 3
           llSay( broadcastChannel, "SUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARY" + "\n" +
                            "LATE_REGISTRATION ->  " + llList2String( unitStatsSum, 0) + "\n" +
                            "TEST_TIMED_OUT ->  " + llList2String( unitStatsSum, 1) + "\n" +
                            "PASS ->  " + llList2String( unitStatsSum, 2) + "\n" +
                            "FAIL ->  " + llList2String( unitStatsSum, 3) + "\n" +
                            "SUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARY");
         //notify coordinator that report process is complete
         llMessageLinked(LINK_SET, toAllChannel, "ReportComplete", NULL_KEY);
         
         //shut timer down
         llSetTimerEvent(0);
         
         //return a process complete response
         return 1;
     }
 }
 //otherwise going to be EMAIL or HTTP that will be sent all at once, if the timer has not expired
 else if (reportTimerInterupt != 1)
 {
     //augment current entry with additional information
      unitReports = llListReplaceList( unitReports, [llList2String( unitReports, index ) + "\n" + report] , index, index );
 }
 
 //if units have all reported in and reports are all logged OR if memory is exceeded OR if timer has expired
 if( 
      ( 
         ( unitCount == 0 ) && ( reportCount == llGetListLength(unitKeys) )
      )
         || ( MemoryCheck() ) 
         || ( reportTimerInterupt == 1 )
   )
  {
      if(debug > 0)llSay(debugChannel, llGetScriptName()+ "->right after mem check: " +  "<<<<<<<<<<<<<<<<<<<<  " + (string)llGetFreeMemory() + "     >>>>>>>>>>>>>>>>>>>>"); 
      //if email is selected method, and the timer has not expired    
      if( ( llSubStringIndex( reportMethod, "EMAIL" ) != -1 ) && (reportTimerInterupt != 1) )
      {
            string address = llList2String( methodParameters, llListFindList( methodParameters, ["address"]) + 1);
            string subject = "Unit Test Report: " + llGetTimestamp() 
                             + " at " + llGetRegionName() + ": " + (string)llGetPos();
                     
           if(debug > 0)llSay(debugChannel, llGetScriptName()+ "->after address and subject: " +  "<<<<<<<<<<<<<<<<<<<<  " + (string)llGetFreeMemory() + "     >>>>>>>>>>>>>>>>>>>>"); 
      
                                        
            //for QUIET, leave out everything but the summary
            if( reportType != "QUIET")
            {
                llEmail( address, subject, llDumpList2String( unitReports, "\n ****************************** \n") + 
                    "SUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARY" + "\n" +
                    "LATE_REGISTRATION ->  " + llList2String( unitStatsSum, 0) + "\n" +
                    "TEST_TIMED_OUT ->  " + llList2String( unitStatsSum, 1) + "\n" +
                    "PASS ->  " + llList2String( unitStatsSum, 2) + "\n" +
                    "FAIL ->  " + llList2String( unitStatsSum, 3) + "\n" +
                    "SUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARY");
                  
            }
            
            // QUIET, just the summary 
            else
            {
                 llEmail( address, subject,"SUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARY" + "\n" +
                    "LATE_REGISTRATION ->  " + llList2String( unitStatsSum, 0) + "\n" +
                    "TEST_TIMED_OUT ->  " + llList2String( unitStatsSum, 1) + "\n" +
                    "PASS ->  " + llList2String( unitStatsSum, 2) + "\n" +
                    "FAIL ->  " + llList2String( unitStatsSum, 3) + "\n" +
                    "SUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARY");
           }     
                              
         
         llSay(0, "EMAIL Report Sent");
         
      }
      //if http is selected method, and the timer has not expired 
      else if( (llSubStringIndex( reportMethod, "HTTP" ) != -1) && (reportTimerInterupt != 1) ) 
      {
           //pull url from parameters
           string url = llList2String( methodParameters, llListFindList( methodParameters, ["url"]) + 1);

             string HttpBody;
            
            //for QUIET, leave out everything but the summary
            if( reportType != "QUIET")
            {
                //add individual unit reports
                HttpBody = (HttpBody = "") + HttpBody + llDumpList2String( unitReports, "\n ****************************** \n");
            }
            
            //add summary to the end
            HttpBody = (HttpBody = "") + HttpBody + "SUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARY" + "\n" +
                    "LATE_REGISTRATION ->  " + llList2String( unitStatsSum, 0) + "\n" +
                    "TEST_TIMED_OUT ->  " + llList2String( unitStatsSum, 1) + "\n" +
                    "PASS ->  " + llList2String( unitStatsSum, 2) + "\n" +
                    "FAIL ->  " + llList2String( unitStatsSum, 3) + "\n" +
                    "SUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARYSUMMARY";

           httpManager = llHTTPRequest( url + "?report=" + llEscapeURL(HttpBody), [HTTP_METHOD,"POST"],"");   
           llSay(0, "HTTP Report Sent");
           
      }
      
      //notify coordinator that report process is complete
      llMessageLinked(LINK_SET, toAllChannel, "ReportComplete", NULL_KEY);
      
      //shut timer down
      llSetTimerEvent(0);
      
      //return to default
      return 1;
    
  } //end of if
 
 //if still processing
 return 0;
 

} // end ProcessReports

////////////////////////////////////////////////////////////////////////////////////////////////// ////////// ////////// Function: MemoryCheck ////////// ////////// Input: no input parameter ////////// ////////// Output: integer 0 - PASS ////////// integer 1 - FAIL ////////// ////////// Purpose: This function manages the memory check to see if the reports should ////////// start outputting before they are all collected. This only applies ////////// EMAIL and HTTP becuase CHAT outputs automatically. ////////// ////////// Issues: no known issues ////////// ////////// ///////////////////////////////////////////////////////////////////////////////////////////////// integer MemoryCheck( ) {

   //llSay(0, "<<<<<<<<<<<<<<<<<<<<  " + (string)llGetFreeMemory() + "     >>>>>>>>>>>>>>>>>>>>");
   
   
   if( llSubStringIndex( reportMethod, "CHAT") != -1 )
   {
       minMemory = 1000;
   }
   else
   {
       minMemory = 3000;
   }
   
   //if free memory is lower then acceptable threshold
   if ( llGetFreeMemory() < minMemory )
   {
       llSay(0, "MEM FAIL");
       //return fail
       return 1;
   }
   
   //return pass
   return 0;
   llSay(0, "MEM PASS");

}

////////////////////////////////////////////////////////////////////////////////////////////////// ////////// ////////// Function: Initialize ////////// ////////// Input: no input paramaters ////////// ////////// Output: no return value ////////// ////////// Purpose: This function initializes any variables or functions necessary ////////// to get us started ////////// ////////// Issues: no known issues ////////// ////////// ///////////////////////////////////////////////////////////////////////////////////////////////// //Initialize() //{ // if(debug > 0)llSay(debugChannel, llGetScriptName()+ "->Init: " + "<<<<<<<<<<<<<<<<<<<< " + (string)llGetFreeMemory() + " >>>>>>>>>>>>>>>>>>>>");

//}

/////////////////////////////////////////////////////////////////////////////////////// //STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE// /////////////////////////////////////////////////////////////////////////////////////// // // // // // DEFAULT STATE // // // // // /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// default { /////////////////////////////////////////////////////// // State Entry of default state // ///////////////////////////////////////////////////////

  state_entry()
   {
       if(debug > 0)llSay(debugChannel, llGetScriptName()+ "->Init: " + "<<<<<<<<<<<<<<<<<<<<  " + (string)llGetFreeMemory() + "     >>>>>>>>>>>>>>>>>>>>");
       //Initialize();
   }

//////////////////////////////////////////////////////// // On Rez of default state // ////////////////////////////////////////////////////////

   on_rez(integer start_param)
   {
       //Initialize();
   }


/////////////////////////////////////////////////////// // Link Message of default state // ///////////////////////////////////////////////////////

   link_message(integer sender_number, integer number, string message, key id)
   {
       //if link message is on the correct channel
       if(number == toAllChannel)
       {
          // OutputReports()
          // format example -> OutputReports::testSelected::ALL
          if(llSubStringIndex(message, "OutputReports::") != -1)
          {
             // dump command parameters into usable list
             list unitParameters = llParseString2List( message, ["::"], [""]);
 
             //find testSelected indicator, and pull value from unitParameters list
             testSelected = llList2String( unitParameters, llListFindList( unitParameters, ["testSelected"]) + 1);
       
             ////////////////////
             //  State Change  //
             ////////////////////
             state REPORT;
          }
           
           ParseCommand( message );
           
       }
       
   } //end of link message
   

/////////////////////////////////////////////////////// // Http Response of REPORT state // ///////////////////////////////////////////////////////

http_response(key id, integer status, list metadata, string body)
   {
       if(debug > 10){llSay(debugChannel, llGetScriptName()+ ":FUNCTION: HTTP");}
       if(debug > 10){llSay(debugChannel, body);}
  
            
       if(id == httpManager)
       {
           llSay( 0, body );
       }
   } 
   
   

} // end default

/////////////////////////////////////////////////////////////////////////////////////// //STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE// /////////////////////////////////////////////////////////////////////////////////////// // // // // // REPORT STATE // // // // // /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// state REPORT { /////////////////////////////////////////////////////// // State Entry of REPORT state // ///////////////////////////////////////////////////////

  state_entry()
   {
       if(debug > 0)llSay(debugChannel, llGetScriptName()+ "->Report State Entry: " + "<<<<<<<<<<<<<<<<<<<<  " + (string)llGetFreeMemory() + "     >>>>>>>>>>>>>>>>>>>>");
       
       //clear counters
       unitCount = 0;                         
       reportCount = 0;
       
       //clear lists
       unitKeys = [];                         
       unitReports = [];
       unitStatsSum = [ 0, 0, 0, 0];
       if( llSubStringIndex( reportMethod , "CHAT") != -1)
       {
           
            //if reportMethod is CHAT we need to create the output report header here
            llSay( broadcastChannel , "************************************************************" );
            llSay( broadcastChannel , "Time: " + llGetTimestamp() );
            llSay( broadcastChannel , "Location: " + llGetRegionName() + " " + (string)llGetPos() );
       }
   
       //broadcast to other TestUnits script a request for stats
       llMessageLinked(LINK_SET, toAllChannel, "ReportUnitStats", NULL_KEY);
       
       llSay(0, "reportTimeout: " + (string)reportTimeoutLength );
       //need a timer in case not all expected units respond
       llSetTimerEvent( reportTimeoutLength );
       //clear timer Interupt
       reportTimerInterupt = 0;
       
   }
   

/////////////////////////////////////////////////////// // State Exit of REPORT state // ///////////////////////////////////////////////////////

  state_exit()
   {
       unitKeys = [];                         
       unitReports = [];
       unitStatsSum = [];
   }   

//////////////////////////////////////////////////////// // On Rez of REPORT state // ////////////////////////////////////////////////////////

   on_rez(integer start_param)
   {
       ////////////////////
       //  State Change  //
       ////////////////////
       state default;
   }


/////////////////////////////////////////////////////// // Link Message of REPORT state // ///////////////////////////////////////////////////////

   link_message(integer sender_number, integer number, string message, key id)
   {
       //if link message is on the correct channel
       if(number == toAllChannel)
       {
           //if message is stats from Coordinator_TestUnits script
           if( llSubStringIndex( message, "ReportStats") != -1)
           {
               ProcessStats( message );    
           }
           //if message is report from a testUnit via Coordinator_Coordinator
           if( llSubStringIndex( message, "AddUnitReport") != -1)
           {
               //if process is complete for what ever reason
               if( ProcessReports( message ) )
               {
                   ////////////////////
                   //  State Change  //
                   ////////////////////
                  state default;
               }    
           }
           //otherwise assume general command from Coordinator
           else
           {
               ParseCommand( message );
           }
       } // end if toAll
       
   } //end of link message
   

/////////////////////////////////////////////////////// // Http Response of REPORT state // ///////////////////////////////////////////////////////

http_response(key id, integer status, list metadata, string body)
   {
       if(debug > 10){llSay(debugChannel, llGetScriptName()+ ":FUNCTION: HTTP");}
       if(debug > 10){llSay(debugChannel, body);}
  
       //for testing purposes
       if(id == httpManager)
       {
           llSay( 0, body );
       }
   } //end http reponse

/////////////////////////////////////////////////////// // timer of REPORT state // ///////////////////////////////////////////////////////

   timer()
   {
       //indicate timer has expired
       reportTimerInterupt = 1;
       
       //we proceeReports in case EMAIL or HTTP is selecte
       //and if successful we return to default
       if( ProcessReports( "VOID" ) )
       {
          ////////////////////
          //  State Change  //
          ////////////////////
          state default;
        }   
               
               
       
   } //end of timer
   

} // end default </lsl>