255 lines
14 KiB
JavaScript
255 lines
14 KiB
JavaScript
/**********************************************************************************************
|
|
*
|
|
* JMRI JSON WebSockets and Ajax communication javascript
|
|
* File version 1.0 (old): script 'ThrottleMonitor.py' must be loaded for throttle support
|
|
* File version 2.0: native JSON throttle support - doesn't need script 'ThrottleMonitor.py' anymore
|
|
*
|
|
* >>> This file version: 2.1 - from original 'jquery.jmri.js' modified by Oscar Moutinho (oscar.moutinho@gmail.com)
|
|
*
|
|
* Using HTML5 and jQuery 2...
|
|
* Note that this JMRI extension relies on the jquery-websocket v.0.0.4 extension.
|
|
*
|
|
**********************************************************************************************/
|
|
|
|
//----------------------------------------- Manage JMRI WebSockets
|
|
(function($) {
|
|
$.extend({
|
|
JMRI: function(url, bindings) {
|
|
var jmri = {};
|
|
if (typeof(url) === 'string') {
|
|
jmri.url = url;
|
|
} else {
|
|
jmri.url = document.URL.split('/')[0] + '//' + document.URL.split('/')[2] + '/json/';
|
|
bindings = url;
|
|
}
|
|
// Default event handlers that do nothing
|
|
jmri.toSend = function(data) {};
|
|
jmri.fullData = function(data) {};
|
|
jmri.error = function(code, message) {};
|
|
jmri.end = function() {};
|
|
jmri.ready = function(jsonVersion, jmriVersion, railroadName) {};
|
|
jmri.throttle = function(name, address, speed, forward, fs) {};
|
|
jmri.light = function(name, userName, comment, state) {};
|
|
jmri.reporter = function(name, userName, state, comment, report, lastReport) {};
|
|
jmri.sensor = function(name, userName, comment, inverted, state) {};
|
|
jmri.turnout = function(name, userName, comment, inverted, state) {};
|
|
jmri.signalHead = function(name, userName, comment, lit, appearance, held, state, appearanceName) {};
|
|
jmri.signalMast = function(name, userName, aspect, lit, held, state) {};
|
|
jmri.route = function(name, userName, comment, state) {};
|
|
jmri.memory = function(name, userName, comment, value) {};
|
|
jmri.power = function(state) {};
|
|
// Add user-defined handlers to the settings object
|
|
$.extend(jmri, bindings);
|
|
// Constants
|
|
jmri.powerUNKNOWN = 0;
|
|
jmri.powerON = 2;
|
|
jmri.powerOFF = 4;
|
|
jmri.turnoutUNDEFINED = 0;
|
|
jmri.turnoutUNKNOWN = 1;
|
|
jmri.turnoutCLOSED = 2;
|
|
jmri.turnoutTHROWN = 4;
|
|
jmri.routeDISABLED = 0;
|
|
jmri.routeUNDEFINED = 1;
|
|
jmri.routeACTIVE = 2;
|
|
jmri.routeINACTIVE = 4;
|
|
jmri.TRUE = true;
|
|
jmri.FALSE = false;
|
|
jmri.YES = 1;
|
|
jmri.NO = 0;
|
|
jmri.EMERGENCY_STOP = '-1.0';
|
|
jmri.STOP = '0.0';
|
|
jmri.FULL_SPEED = '1.0';
|
|
// Getters and Setters
|
|
var roster = null;
|
|
jmri.getRosterGroups = function() {
|
|
if (!roster) roster = loadRoster();
|
|
var rosterGroupsList = [];
|
|
if (roster) {
|
|
roster.find('roster-config roster locomotive').each(function() {
|
|
$(this).find('attributepairs keyvaluepair').each(function() {
|
|
var key = $(this).find('key').text();
|
|
if (key.split(':')[0] == 'RosterGroup') {
|
|
var rosterGroup = key.split(':')[1];
|
|
var value = $(this).find('value').text();
|
|
if (value == 'yes' && rosterGroupsList.indexOf(rosterGroup) == -1) rosterGroupsList.push(rosterGroup);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
return rosterGroupsList.sort();
|
|
};
|
|
jmri.getRoster = function(group) {
|
|
if (!roster) roster = loadRoster();
|
|
var locoList = [];
|
|
if (roster) {
|
|
roster.find('roster-config roster locomotive').each(function() {
|
|
var inGroup = false;
|
|
if (group) {
|
|
$(this).find('attributepairs keyvaluepair').each(function() {
|
|
var key = $(this).find('key').text();
|
|
if (key.split(':')[0] == 'RosterGroup') {
|
|
key = key.split(':')[1];
|
|
var value = $(this).find('value').text();
|
|
if (key == group && value == 'yes') inGroup = true;
|
|
}
|
|
});
|
|
} else inGroup = true;
|
|
if (inGroup) {
|
|
var loco = {
|
|
name: $(this).attr('id'),
|
|
roadNumber: $(this).attr('roadNumber'),
|
|
roadName: $(this).attr('roadName'),
|
|
mfg: $(this).attr('mfg'),
|
|
owner: $(this).attr('owner'),
|
|
model: $(this).attr('model'),
|
|
dccAddress: parseInt($(this).attr('dccAddress'), 10),
|
|
imageFilePath: $.trim($(this).attr('imageFilePath')),
|
|
iconFilePath: $.trim($(this).attr('iconFilePath'))
|
|
};
|
|
locoList.push(loco);
|
|
}
|
|
});
|
|
}
|
|
return locoList;
|
|
};
|
|
jmri.getRosterItem = function(name) {
|
|
if (!roster) roster = loadRoster();
|
|
var loco = null;
|
|
if (roster) {
|
|
roster.find('roster-config roster locomotive').each(function() {
|
|
if ($(this).attr('id') == name) {
|
|
loco = {
|
|
name: $(this).attr('id'),
|
|
fileName: $(this).attr('fileName'),
|
|
roadNumber: $(this).attr('roadNumber'),
|
|
roadName: $(this).attr('roadName'),
|
|
mfg: $(this).attr('mfg'),
|
|
owner: $(this).attr('owner'),
|
|
model: $(this).attr('model'),
|
|
dccAddress: parseInt($(this).attr('dccAddress'), 10),
|
|
comment: $(this).attr('comment'),
|
|
maxSpeed: parseInt($(this).attr('maxSpeed'), 10),
|
|
imageFilePath: $.trim($(this).attr('imageFilePath')),
|
|
iconFilePath: $.trim($(this).attr('iconFilePath')),
|
|
URL: $.trim($(this).attr('URL')),
|
|
IsShuntingOn: $.trim($(this).attr('IsShuntingOn')).toUpperCase(),
|
|
f: new Array(29)
|
|
};
|
|
for (var i = 0; i < 29; i++) loco.f[i] = null;
|
|
$(this).find('functionlabels functionlabel').each(function() {
|
|
var n = parseInt($(this).attr('num'), 10);
|
|
var f = {
|
|
lockable: ($(this).attr('lockable') == 'true'),
|
|
functionlabel: $(this).text(),
|
|
functionImage: $.trim($(this).attr('functionImage')),
|
|
functionImageSelected: $.trim($(this).attr('functionImageSelected'))
|
|
};
|
|
loco.f[n] = f;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
return loco;
|
|
};
|
|
var loadRoster = function() { //Retrieve roster
|
|
var roster = null;
|
|
$.ajax({
|
|
url: '/roster?format=xml',
|
|
async: false,
|
|
cache: false,
|
|
type: 'GET',
|
|
dataType: 'xml',
|
|
error: function(jqXHR, textStatus, errorThrown) {
|
|
if(jqXHR.status == 404) jmri.error(jqXHR.status, 'Roster empty.\nNo locos defined in JMRI.');
|
|
else jmri.error(jqXHR.status, 'Response:\n' + jqXHR.responseText + '\n\nError:\n' + errorThrown);
|
|
},
|
|
success: function(xmlReturned, status, jqXHR) {
|
|
roster = $(xmlReturned);
|
|
if (xmlReturned === null) {
|
|
jmri.error(200, 'Roster empty.\nNo locos defined in JMRI.');
|
|
}
|
|
}
|
|
});
|
|
return roster;
|
|
};
|
|
jmri.getObjectList = function(listType) { //Retrieve a JSON list of objects
|
|
var list = [];
|
|
$.ajax({
|
|
url: jmri.url + listType,
|
|
async: false,
|
|
cache: false,
|
|
type: 'GET',
|
|
dataType: 'json',
|
|
error: function(jqXHR, textStatus, errorThrown) {
|
|
jmri.error(jqXHR.status, 'Response:\n' + jqXHR.responseText + '\n\nError:\n' + errorThrown);
|
|
},
|
|
success: function(listReturned, status, jqXHR) {list = listReturned;}
|
|
});
|
|
return list;
|
|
};
|
|
var heartbeatDelay; // Heartbeat timer delay
|
|
var heartbeat = null; // Heartbeat timer
|
|
jmri.closeSocket = function() {jmri.socket.close();};
|
|
jmri.getJMRI = function(type, name) {
|
|
if (!heartbeat) {jmri.error(0, 'The JMRI WebSocket service is not ready.\nSolve the problem and refresh web page.'); return;}
|
|
var lp = (name) ? {"name":name} : {};
|
|
jmri.toSend(JSON.stringify({"type":type,"data":lp}));
|
|
jmri.socket.send(type, lp);
|
|
};
|
|
jmri.setJMRI = function(type, name, args, method) {
|
|
if (!heartbeat) {jmri.error(0, 'The JMRI WebSocket service is not ready.\nSolve the problem and refresh web page.'); return;}
|
|
var lp = (name) ? {"name":name} : {};
|
|
jmri.toSend(JSON.stringify({"type":type, "data":jmri.jsonConcat(lp, args)}));
|
|
jmri.socket.send(type, jmri.jsonConcat(lp, args), method);
|
|
};
|
|
jmri.jsonConcat = function(o1, o2) { // Concatenate JSON name-value pair lists
|
|
var o = {};
|
|
var key;
|
|
for (key in o1) o[key] = o1[key];
|
|
for (key in o2) o[key] = o2[key];
|
|
return o;
|
|
};
|
|
// WebSocket
|
|
jmri.socket = $.websocket(jmri.url.replace(/^http/, 'ws'), {
|
|
close: function() { // Stop the heartbeat when the socket closes
|
|
if (heartbeat) {
|
|
clearInterval(heartbeat);
|
|
heartbeat = null;
|
|
}
|
|
},
|
|
message: function(e) {jmri.fullData(e.originalEvent.data);},
|
|
events: {
|
|
error: function(e) {jmri.error(e.data.code, e.data.message);},
|
|
pong: function(e) {},
|
|
goodbye: function(e) {
|
|
jmri.socket.close();
|
|
jmri.end();
|
|
},
|
|
hello: function(e) { // Handle the initial handshake response from the server
|
|
heartbeatDelay = e.data.heartbeat;
|
|
heartbeat = setInterval(function() {jmri.toSend(JSON.stringify({"type":"ping"})); jmri.socket.send('ping');}, heartbeatDelay);
|
|
jmri.ready(e.data.json, e.data.JMRI, e.data.railroad);
|
|
},
|
|
throttle: function(e) {
|
|
var functions = new Array(29);
|
|
for (var i = 0; i < 29; i++) functions[i] = e.data['F' + i];
|
|
jmri.throttle(e.data.throttle, e.data.address, e.data.speed, e.data.forward, functions);
|
|
},
|
|
light: function(e) {jmri.light(e.data.name, e.data.userName, e.data.comment, e.data.state);},
|
|
reporter: function(e) {jmri.reporter(e.data.name, e.data.userName, e.data.state, e.data.comment, e.data.report, e.data.lastReport);},
|
|
sensor: function(e) {jmri.sensor(e.data.name, e.data.userName, e.data.comment, e.data.inverted, e.data.state);},
|
|
turnout: function(e) {jmri.turnout(e.data.name, e.data.userName, e.data.comment, e.data.inverted, e.data.state);},
|
|
signalHead: function(e) {jmri.signalHead(e.data.name, e.data.userName, e.data.comment, e.data.lit, e.data.appearance, e.data.held, e.data.state, e.data.appearanceName);},
|
|
signalMast: function(e) {jmri.signalMast(e.data.name, e.data.userName, e.data.aspect, e.data.lit, e.data.held, e.data.state);},
|
|
route: function(e) {jmri.route(e.data.name, e.data.userName, e.data.comment, e.data.state);},
|
|
memory: function(e) {jmri.memory(e.data.name, e.data.userName, e.data.comment, e.data.value);},
|
|
power: function(e) {jmri.power(e.data.state);}
|
|
}
|
|
});
|
|
return jmri;
|
|
}
|
|
});
|
|
})(jQuery);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ End of file
|