260 lines
10 KiB
JavaScript
260 lines
10 KiB
JavaScript
/** \file comet_statusupdates.js
|
|
* \brief File containing all functions to handle ZKL status updates through a COMET/JSON link
|
|
* \author Bart van Hest, Core|Vision
|
|
* \version 1.0
|
|
* \date 20-10-2008
|
|
* \todo
|
|
*
|
|
* This file contains the functions needed to perform 'real-time' status updates for the ZKL through a COMET/JSON link.
|
|
* It works by opening a connection to a blocking PHP script using the XMLHttpRequest object. The PHP
|
|
* script must block as long as no status updates are available. When a status update is available,
|
|
* it should spit out JScript code which can be evaluated using eval(). This code should fill in
|
|
* an array of objects named ZKL_Status, with members of the object named s_<name>.
|
|
*
|
|
* The code does only a simple header check to find out if the data send by PHP is valid. The
|
|
* header should be ZKLsu1.0 in slash-star comment, which stands for ZKL Status Update v1.0
|
|
*
|
|
* After eval(), the Array should look like this:
|
|
* ZKL_Status[0]
|
|
* s_param1 : "param1 value"
|
|
* s_param2 : "param2 value"
|
|
* ZKL_Status[1]
|
|
* s_param1 : "param1 value"
|
|
* s_param2 : "param2 value"
|
|
* ..
|
|
* ZKL_Status[n]
|
|
* s_param1 : "param1 value"
|
|
* s_param2 : "param2 value"
|
|
*
|
|
* Also, the Array sent should contain information for all ZKLs monitored, not only the updated ones.
|
|
*/
|
|
|
|
var ZKL_Status = null; // The array with new ZKL status information returned during an update
|
|
var xmlhttp = null; // The XMLHttpRequest object used to get status updates
|
|
var TimeOut = null; // our timeout counter. We can't add this to the xmlhttp object; IE won't let us. #@$^#$ browser :(
|
|
var DataRdyCallback = null; // The callback which should handle new data
|
|
var DataErrorCallback = null; // The callback which should handle an error in the data
|
|
var ProjectID = -1; // Our project ID for which to ask status updates
|
|
var LastRequestTime = -1; // The time our last request was made. Used to discriminate between intentional and unintentional timeouts`
|
|
var Sequence = 0 // Add sequence to be unique and disable cache
|
|
var ValidRequestTime = -1; // The time our last request was valid. Used to discriminate between intentional and unintentional timeouts
|
|
var CometServerScriptURL = null; // The URL of our COMET dataserver script.
|
|
|
|
var TimeOutmSecs = 12000; // our timeout in milliseconds
|
|
|
|
|
|
/**
|
|
* Default data-error callback.
|
|
*
|
|
* This default callback crates an empty ZKL_Status() array and calls the DataRdyCallback.
|
|
* This causes all devices to simply disappear from the screen
|
|
*/
|
|
function defErrorCallback() {
|
|
DbgPrint ("Default data-error callback called.");
|
|
ZKL_Status = [];
|
|
if (DataRdyCallback != null) { DataRdyCallback(); }
|
|
}
|
|
|
|
|
|
/**
|
|
* Create the XMLHttpRequest object
|
|
*
|
|
* Inputs:
|
|
* - fnDataRdyCallback: A callback which is called when new statusinfo is received.
|
|
* - fnDataErrorCallback: A callback which is called when an error occurred during data reception.
|
|
* - sesson_id: A number which make this a unique session
|
|
*/
|
|
function createUpdater(fnDataRdyCallback, fnDataErrorCallback, sessionid) {
|
|
DbgPrint ("createUpdater()");
|
|
OurURL = document.URL;
|
|
DocRoot = OurURL.split('?');
|
|
CometServerScriptURL = DocRoot[0]+"index.php?redirect=scripts/other/rtstatus_datapump.php&id="+sessionid+"&extra_path=../../";
|
|
DbgPrint ("COMET server URL: "+CometServerScriptURL);
|
|
|
|
// Create the XMLHttpRequest object in a browser-independent way
|
|
try {
|
|
xmlhttp = new XMLHttpRequest();
|
|
}
|
|
catch (e) {
|
|
try {
|
|
xmlhttp = new ActiveXObject('Msxml2.XMLHTTP');
|
|
}
|
|
catch (e) {
|
|
try {
|
|
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
|
|
}
|
|
catch (e) {
|
|
xmlhttp = null;
|
|
}
|
|
}
|
|
}
|
|
if (!xmlhttp && window.createRequest) {
|
|
try {
|
|
xmlhttp = window.createRequest();
|
|
} catch (e) {
|
|
xmlhttp=null;
|
|
}
|
|
}
|
|
|
|
if (typeof(fnDataRdyCallback) != 'undefined') {
|
|
DataRdyCallback = fnDataRdyCallback;
|
|
}
|
|
else {
|
|
DbgPrint ("INTERNAL ERROR: No DataReadyCallback passed");
|
|
}
|
|
|
|
if (typeof(fnDataErrorCallback) != 'undefined' && fnDataErrorCallback != null) {
|
|
DataErrorCallback = fnDataErrorCallback;
|
|
}
|
|
else {
|
|
// default error callback
|
|
DataErrorCallback = defErrorCallback;
|
|
DbgPrint ("No valid error callback given; reverting to default error callback..");
|
|
}
|
|
|
|
// Start the first request
|
|
xmlhttp_startrequest(true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Abort & destroy the updater.
|
|
*
|
|
* Inputs:
|
|
* none.
|
|
*/
|
|
function AbortUpdater() {
|
|
DataErrorCallback = null;
|
|
if (xmlhttp!=null) {
|
|
// Assign an empty function to the state handler; IE doesn't like setting this to null.
|
|
xmlhttp.onreadystatechange = function () {}
|
|
// Abort current transfer. This will also call the state handler, which is not what we want
|
|
xmlhttp.abort();
|
|
// Clear object
|
|
xmlhttp = null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle a timeout on the xmlhttp request
|
|
*/
|
|
function xmlhttp_Timeout() {
|
|
try {
|
|
DbgPrint ("XMLHttpRequest timed out. Restarting...");
|
|
// Assign an empty function to the state handler; IE doesn't like setting this to null.
|
|
xmlhttp.onreadystatechange = function () {}
|
|
// Abort the current status transfer
|
|
xmlhttp.abort();
|
|
|
|
// See if we need to call the data error callback
|
|
if ((new Date().getTime() - LastRequestTime) > TimeOutmSecs) {
|
|
if (DataErrorCallback != null) {
|
|
DataErrorCallback();
|
|
}
|
|
}
|
|
}
|
|
catch (ex) {
|
|
// Silent exception
|
|
}
|
|
finally {
|
|
// And start a new one (this will also re-initialise the state handler)
|
|
xmlhttp_startrequest();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Start a request for status updates.
|
|
*
|
|
* Inputs:
|
|
* - bFirstTime: A boolean indicating whether this is the first time we
|
|
* request data. The first time we need data immediately,
|
|
* subsequent requests may be stalled until there is new data
|
|
* available.
|
|
*/
|
|
function xmlhttp_startrequest(bFirstTime) {
|
|
// Check the arguments
|
|
bFirstTime = typeof(bFirstTime) == 'boolean' ? bFirstTime : false;
|
|
// start the XHR request
|
|
if (xmlhttp!=null) {
|
|
// We put a timeout on this request. Restarting the status updater script once in a while
|
|
// is not a bad idea since some Web-servers won't allow a script to run for a long time.
|
|
TimeOut = setTimeout("xmlhttp_Timeout();",TimeOutmSecs+250);
|
|
// Remember the current times so we know when a timeout was unintentional or not.
|
|
LastRequestTime = new Date().getTime();
|
|
if (ValidRequestTime == -1) {
|
|
ValidRequestTime = new Date().getTime();
|
|
}
|
|
xmlhttp.onreadystatechange=xmlhttp_statechange;
|
|
xmlhttp.open("GET",CometServerScriptURL+"&Seq="+(Sequence++)+"&blocking="+(bFirstTime?'false':'true'),true);
|
|
xmlhttp.send(null);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Data-present function which is called when new ZKL data arrives
|
|
*/
|
|
function xmlhttp_statechange() {
|
|
// Do we have an update from the server? => Else still in progress
|
|
if (xmlhttp.readyState == 4) {
|
|
try {
|
|
// Cancel settimeout
|
|
clearTimeout(TimeOut);
|
|
|
|
if (xmlhttp.status == 200 ) {
|
|
// Reset timeout
|
|
ValidRequestTime = new Date().getTime();
|
|
|
|
// Do something with the data. Check our 'magic-ID'. If it passes,
|
|
var offset = xmlhttp.responseText.search("\/[*]ZKLsu1.0[*]\/");
|
|
|
|
// Magic code available?
|
|
if (offset != -1) {
|
|
//DbgPrint(xmlhttp.responseText);
|
|
eval(xmlhttp.responseText.substring(offset));
|
|
|
|
// OK, by now we should have a ZKL_Status array with statuses of devices in the field.
|
|
// Call the user supplied callback
|
|
if (DataRdyCallback != null) {
|
|
DataRdyCallback();
|
|
}
|
|
// We had a valid request, so no DataErrorCallback may be called, so clear LastRequestTime
|
|
LastRequestTime = new Date().getTime();
|
|
}
|
|
else {
|
|
DbgPrint("Invalid data from server\n("+xmlhttp.responseText+")");
|
|
// Call our data error callback
|
|
if (DataErrorCallback != null) {
|
|
DataErrorCallback();
|
|
}
|
|
}
|
|
}
|
|
else if ((xmlhttp.status == 0) || (xmlhttp.status == 12029) || (xmlhttp.status == 12007)) {
|
|
// Catch IE/FF exceptions when no connection available
|
|
if ((new Date().getTime() - ValidRequestTime) > TimeOutmSecs) {
|
|
DbgPrint ("XMLHttpRequest FF timed out, status 0. Calling DataErrorCallback()..");
|
|
if (DataErrorCallback != null) {
|
|
DataErrorCallback();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Our request failed. Call the data error callback
|
|
DbgPrint ("xmlhttp_status: "+xmlhttp.status);
|
|
}
|
|
}
|
|
catch(ex) {
|
|
// Silent exception
|
|
}
|
|
finally {
|
|
// Redo request.
|
|
// We used to do a xmlhttp_startrequest() again. This works in every browser, except IE.
|
|
// IE won't allow us to call XMLHttpRequest methods from within the event handler.
|
|
// So, we use a hack: we shorten the timeout to 250msec. Once we have a timeout, a new
|
|
// request will be started.
|
|
TimeOut = setTimeout("xmlhttp_Timeout();",250);
|
|
}
|
|
}
|
|
} |