ChangeLog

9 March 2012

7 March 2012

  • Redesigned the script and included some uptime tracking - will become part of a bigger project.

Introduction

This is a script that will display simulator statuses. The script reads a notecard called Regions containing simulator names and then scans for the simulator statues and displays them in text above the prim. There are a number of states in which a certain simulator might be in, these are:

* "up": simulator currently up and running * "down": simulator currently down * "starting": simulator currently starting * "stopping": simulator currently stopping * "crashed": simulator has crashed * "unknown": simulator status unknown or unknown simulator

Connector Flow Diagram

Prowl Integration

Since we derive scripts, we integrate them as well. The integration with prowler is already in the script. All you need to do is update your API key in the code to your Prowl API key and add the prowler script to the primitive so that you receive updates when the simulator's state changes.

The inventory of the primitive will then contain:

SIM Status
xHTTP Component
Prowler
Regions

If you have not read the prowler page, Prowl allows you to receive notifications on your mobile devices. We use that in order to send a notification to an iOS device when a simulator's state changes.

Website Table (Non-Integrated)

The non-integrated version display of the SIM statuses.

The table may be integrated anywhere on a website. The display itself will change in order to reflect the changes to the notecard. All you have to do is add or remove SIMs from the notecard in the primitive and everything else is taken care of by the scripts.

Usage

The scanning for the SIM statuses can be done from any parcel and does not require the script to be present on that parcel. This is useful for estate managers to monitor the status of their SIMs.

To use this script, create a prim and add a notecard inside it called Regions in which you list all the simulator names you wish the script to monitor on every single line. For example, a notecard containing three simulators to monitor, might look something like:

Meihano Bay
Azure Falls
Jersey Island

Please note that a blank newline is needed after the simulator entries as in the example above.

After that, drop the script below in the prim and the script will take care of all the rest. After some time, the statuses of your simulators will be displayed in white text above the prim, every SIM being updated every second.

Limitations

None other than the limits imposed by limits for the notecard and the Guidelines for Private Region Naming

Code

The first script, SIM Status monitors regions status changes and the second script xHTTP Component answers to incoming HTTP requests by pulling data from SIM Status and formatting it to XML.

Code: SIM Status

sim_status.lsl
//////////////////////////////////////////////////////////
//     WaS (c) grimore.org - 2012, License: GPLv3            //
// Please see: http://www.gnu.org/licenses/gpl.html     //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
key nQuery = NULL_KEY;
integer nLine = 0;
list nList = [];
string curSim = "";
 
//pragma inline
readRegions() {
    if(llGetInventoryType("Regions") == INVENTORY_NOTECARD) jump read;
    llOwnerSay("Failed to find notecard.");
    return;
@read;
    nQuery = llGetNotecardLine("Regions", 0);
}
 
 
default
{
    state_entry() {
        llSetText("", <0,0,0>, 0);
        readRegions();
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) {
            llSensorRemove();
            llSetTimerEvent(0);
            llResetScript();
        }
    }
    timer() {
        llSetTimerEvent(0);
        curSim = llList2String(llParseString2List(llList2String(nList, 0), [" - "], []), 0);
        nQuery = llRequestSimulatorData(curSim, 6);
    }
    no_sensor() {
        if(llList2String(llParseString2List(llList2String(nList, 0), ["[ ", " ]"], []), 1) != nQuery) {
            nQuery = curSim + " - [ " + (string)nQuery + " ] T0:0:0";
            // Comment the following lines out between the --MARK-- if you do not want Prowler support.
            // Otherwise, make sure that you replace your PROWL_API_KEY. You can find instructions on
            // the Wizardry and Steamworks wiki @ http://grimore.org/wiki/Prowler
            // --MARK--
            integer cha = ((integer)("0x"+llGetSubString((string)llGetOwner(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
            llMessageLinked(LINK_THIS, cha, "PROWL_API_KEY,6f5902ac237024bdd0c176cb93063dc4,PROWL_APPLICATION,SIM Status,PROWL_EVENT,SIM Status Change,PROWL_DESCRIPTION," + "SIM: " + curSim + " is now: " + (string)nQuery, "");
            // --MARK--
            jump updated;
        }
        nQuery = llList2String(llParseString2List(llList2String(nList, 0), ["T"], []), 1);
        nLine = llList2Integer(llParseString2List(nQuery, [":"], []), 2) + llList2Integer(llParseString2List(nQuery, [":"], []), 1) * 60 + llList2Integer(llParseString2List(nQuery, [":"], []), 0) * 3600 + 1;
        nQuery = llList2String(llParseString2List(llList2String(nList, 0), ["T"], []), 0) + "T" + (string)(nLine/3600) + ":" + (string)((nLine%3600)/60) + ":" + (string)((nLine%3600)%60);
@updated;
        nList = llDeleteSubList(nList, 0, 0);
        nList += nQuery;     
        llSetText(llDumpList2String(nList, "\n"), <1,1,1>, 1);
        llSetTimerEvent(1);
    }
    link_message(integer sender_num, integer num, string str, key id) {
        if(str == "SYN") llMessageLinked(LINK_THIS, 0, llList2CSV(nList), "ACK");
    }
    dataserver(key id, string data) {
        if(id != nQuery) return;
        if(data == EOF || data == "" || llStringLength(data) > 8) jump read;
        llSensor("", nQuery = data, AGENT, .1, 0);
        return;
@read;
        if(data == EOF) {
            llSetTimerEvent(1);
            return;
        }
        if(data == "") jump skip;
        nList += data;
@skip;
        nQuery = llGetNotecardLine("Regions", ++nLine);
    }
}

Code: xHTTP Component

The description of the XML structure is given by:

<xsim>
  <sim>name of the region</sim>
  <status>reported status, up, down, crashed, etc...</status>
  <valid>time since when the status is valid</valid>
</xsim>

The <valid> tag represents the time since the <status> tag has been updated. In that sense:

* If the simulator is up, then the <valid> tag reports how much time it has been up. * If the simulator is down, then the <valid> tag reports how much time the simulator has been down. etc…

The code uses jumps to concatenate and convert the data to a simple XML format:

sim_status_xhttp.lsl
//////////////////////////////////////////////////////////
//     WaS (c) grimore.org - 2012, License: GPLv3              //
// Please see: http://www.gnu.org/licenses/gpl.html     //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////
//                   CONFIGURATION                      //
//////////////////////////////////////////////////////////
 
// Set this URL to point to the simStatus.php script 
// on the webserver hosting the PHP script:
string llURL = "http://grimore.org/SIM/simStatus.php";
 
// Set this to the password in the PHP script.
// Please see the PHP script headers for documentation.
string apiKey = "bbd16d2772593ea5f890973c027dbc7f4a096164";
 
///////////////////////////////////////////////////////////////////////////
//                          END CONFIGURATION                            //
///////////////////////////////////////////////////////////////////////////
 
default
{
    state_entry() {
        llRequestURL();
        llSetTimerEvent(5);
    }
    timer() {
        llSetTimerEvent(0);
        if(llURL == "") {
            llRequestURL();
            llSetTimerEvent(5);
            return;
        }
 
    }
    http_request(key id, string method, string body) {
        if (method == URL_REQUEST_GRANTED) {
            llHTTPRequest(llURL + "?url=" + llEscapeURL(body) + "&key="+apiKey, [], ""); 
            llURL = body;
            return;
        }
    }
    http_response(key request_id, integer status, list metadata, string body) {
        if(status == 200) {
            state catch;
        }
    }
}
state catch
{
    http_request(key id, string method, string body) {
        if (method == "GET") {
            llURL = id;
            state finally;
            return;
        }
    }
    changed(integer change) {
        llResetScript();
    }
}
 
state finally
{
    state_entry() {
        llMessageLinked(LINK_THIS, 0, "SYN", "");
    }
    link_message(integer sender_num, integer num, string str, key id) {
        if(id != "ACK") return;
        list xData = llParseString2List(llDumpList2String(llCSV2List(str), "|"), ["|", " - ", "[ ", " ]", " ˉ|ˉ"], []);
        str = "<xsim>";
@tag;
        id = llList2String(xData, 0);
        xData = llDeleteSubList(xData, 0, 0);
        if((string)id == "up" || (string)id == "down" || (string)id == "starting" || (string)id == "stopping" || (string)id == "crashed" || (string)id == "unknown") {
            str += "<status>" + (string)id + "</status>";
            jump tag;
        }
        if(~llSubStringIndex((string)id, ":")) {
            str += "<valid>" + (string)id + "</valid>";
            jump tag;
        }
        if((string)id != "") {
            str += "<sim>" + (string)id + "</sim>"; 
            jump tag;
        }
        llHTTPResponse(llURL, 200, str+"</xsim>");
        state catch;
    }
}

Code: PHP Server-Side module

The following script, along with an sqlite database has to be placed on a webserver with PHP support. All the set-up instructions are contained within the PHP script below.

simStatus.php
<?php
 
	//////////////////////////////////////////////////////////
	//     WaS (c) grimore.org - 2012, License: GPLv3              //
	// Please see: http://www.gnu.org/licenses/gpl.html     //
	// for legal details, rights of fair usage and          //
	// the disclaimer and warranty conditions.              //
	//////////////////////////////////////////////////////////
 
	// This script requires:
	// pecl_http, you can install pecl with:
	// sh$ pecl install pecl_http
 
	// This script relies on the following PHP support:
	// PHP simplexml (usually built in)
	// PHP JSON (usually built in)
	// PHP sqlitev3 support (usually built in)
	// PHP PDO, sqlite connector (usually built in)
 
	// This script relies on the following services:
	// Google Charts
 
	// You need to create an sqlite database on the webserver
	// which will update the URLs provided by llRequestURL().
 
	// The following represent the shell commands you would
	// need to execute in order to set-up the sqlite database.
 
	// The database has to be created in the same directory 
	// where this PHP script is placed.
 
	// Create Database:
	// sh$ sqlite3 simStatus.sqlite
	// SQLite version 3.6.12
	// Enter ".help" for instructions
	// Enter SQL statements terminated with a ";"
	// sqlite> CREATE TABLE "main"."urls" (
	//    ...> "URL" text NOT NULL,
	//    ...> PRIMARY KEY("URL")
	//    ...> );
	// sqlite> .quit
	// sh$
 
	// The folder on the webserver will contain the following:
	// simStatus.php
	// simStatus.sqlite
 
	// Enjoy. [WaS-K]
 
	// For privacy, you need to generate a password. You can do that
	// by issuing the following command on an *nix-like system and 
	// replace "super-duper secret password" with a secret shared between
	// this PHP script and the LSL counterpart:
	// sh$ echo "super-duper secret password" | sha1sum  | awk '{ print $1 }'
	$apiKey = 'bbd16d2772593ea5f890973c027dbc7f4a096164';
 
	if(isset($_GET['url']) && isset($_GET['key'])) {
		if($_GET['key'] != $apiKey) die;
		$llURL = $_GET['url'];
		$db = new PDO('sqlite:simStatus.sqlite');
		$q = $db->prepare("REPLACE INTO urls(URL) VALUES(:url)");
		try {
			$q->execute(array(':url' => $llURL));
		}
		catch(PDOException $e) {
			print $e->getMessage();
		}
		return 'OK';
	}
	$db = new PDO('sqlite:simStatus.sqlite');
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	$q = $db->prepare("SELECT URL FROM urls");
	$q->execute();
	while($res = $q->fetchObject()) {
		$req = new HttpRequest($res->URL, HttpRequest::METH_GET);
		try {
			$req->send();
		}
		catch(HttpException $ex) {
			print $ex;
			die;
		}
		if($req->getResponseCode() != 200) {
			$d = $db->prepare("DELETE FROM urls WHERE URL=:url");
			$d->execute(array(':url' => $res->URL));
			continue;
		}
		$simStatus = json_decode(json_encode(simplexml_load_string($req->getResponseBody())),TRUE);
	}
?>
 
<html>
  <head>
    <script type='text/javascript' src='https://www.google.com/jsapi'></script>
    <script type='text/javascript'>
      google.load('visualization', '1', {packages:['table']});
      google.setOnLoadCallback(drawTable);
      function drawTable() {
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Name');
        data.addColumn('string', 'Status');
        data.addColumn('string', 'Valid');
<?php
	print "\t".'data.addRows('.count(current($simStatus)).');'."\n";
	$i=0; $j=0;
	foreach($simStatus as $node => $child) {
		$cellData = '';
		foreach($child as $value) {
			$cellData .= "\t".'data.setCell('.$i++.','.$j.','.'\''.$value.'\''.');'."\n";
		}
		++$j; $i=0;
		print $cellData;
	}
?>
        var table = new google.visualization.Table(document.getElementById('table_div'));
        table.draw(data, {showRowNumber: true});
      }
    </script>
  </head>
 
  <body>
    <div id='table_div'></div>
  </body>
</html>

Code: Prowler

May be found on the separate prowler page.


secondlife/sim_status.txt · Last modified: 2022/11/24 07:46 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.