Coordinator TestUnitsReports.lsl
<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 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 </lsl>