Coordinator TestUnitsReports.lsl

From Second Life Wiki
Jump to navigation Jump to search
///////////////////////////////////////////////////////////////////////////////////
///////
///////
///////
///////            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 commands will be :: separated 
//   lists in string form.
//
//////////////////////////////////////////////
//        LINK MESSAGE commands 
//////////////////////////////////////////////
//
//  link message commands will be received 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;                              // minimum 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 position with the currently stored value + 1
             unitStatsSum = llListReplaceList( unitStatsSum, [llList2Integer( unitStatsSum, 0 ) + 1] , 0, 0 );
         }
         else if(status == "TEST_TIMED_OUT")
         {
             //replace the indexed position with the currently stored value + 1
             unitStatsSum = llListReplaceList( unitStatsSum, [llList2Integer( unitStatsSum, 1 ) + 1] , 1, 1 );
         }
         else if(status == "PASS")
         {
             //replace the indexed position with the currently stored value + 1
             unitStatsSum = llListReplaceList( unitStatsSum, [llList2Integer( unitStatsSum, 2 ) + 1] , 2, 2 );
         }
         else if(status == "FAIL")
         {
             //replace the indexed position 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 received
  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 because 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 parameters
//////////                    
//////////      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