Files
ebusd/contrib/html/index.html
T

558 lines
17 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width">
<title>ebusd</title>
<link rel="stylesheet" href="css/jsplumb.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body data-library="dom">
<div id="main" style="width: 1137px; height: 721px;">
<div class="ebus" id="ebus">
</div>
</div>
<script src="js/dom.jsPlumb-1.7.5-min.js"></script>
<script src="js/prototype.js"></script>
<script>
jsPlumb.ready(function() {
var instance = jsPlumb.getInstance({
DragOptions: { cursor: 'pointer', zIndex: 2000 },
ConnectionOverlays: [
[ "Arrow", { location: 1 } ],
[ "Label", {
location: 0.5,
id: "label",
cssClass: "connLabel"
}]
],
Container: "main"
});
var selColor = "orange";
var offColor = "gray";
var hotColor = "red";
var warmColor = "orange";
var coldColor = "blue";
var offType = {
connector: "StateMachine",
paintStyle: { strokeStyle: offColor, lineWidth: 4 },
hoverPaintStyle: { strokeStyle: selColor },
};
var hotType = {
connector: "StateMachine",
paintStyle: { strokeStyle: hotColor, lineWidth: 4 },
hoverPaintStyle: { strokeStyle: selColor },
};
var warmType = {
connector: "StateMachine",
paintStyle: { strokeStyle: warmColor, lineWidth: 4 },
hoverPaintStyle: { strokeStyle: selColor },
};
var coldType = {
connector: "StateMachine",
paintStyle: { strokeStyle: coldColor, lineWidth: 4 },
hoverPaintStyle: { strokeStyle: selColor },
};
instance.registerConnectionType("off", offType);
instance.registerConnectionType("hot", hotType);
instance.registerConnectionType("warm", warmType);
instance.registerConnectionType("cold", coldType);
// this is the paint style for the connecting lines..
var connectorPaintStyle = {
lineWidth: 4,
strokeStyle: "#808080",
joinstyle: "round",
outlineColor: "white",
outlineWidth: 2
},
// .. and this is the hover style.
connectorHoverStyle = {
lineWidth: 4,
strokeStyle: selColor,
outlineWidth: 2,
outlineColor: "white"
},
endpointHoverStyle = {
fillStyle: selColor,
strokeStyle: selColor
},
// the definition of source endpoints
sourceEndpoint = {
endpoint: "Rectangle",
paintStyle: {
strokeStyle: offColor,
fillStyle: "transparent",
lineWidth: 4,
width: 12,
height: 12
},
maxConnections: 1,
isSource: true,
connectorStyle: connectorPaintStyle,
hoverPaintStyle: endpointHoverStyle,
connectorHoverStyle: connectorHoverStyle,
dragOptions: {}
},
// the definition of target endpoints (will appear when the user drags a connection)
targetEndpoint = {
endpoint: "Rectangle",
paintStyle: {
strokeStyle: offColor,
fillStyle: "transparent",
radius: 6,
lineWidth: 4,
width: 12,
height: 12
},
hoverPaintStyle: endpointHoverStyle,
maxConnections: 1,
dropOptions: { hoverClass: "hover", activeClass: "active" },
isTarget: true,
connector: [ "Flowchart", { stub: [20, 20], gap: 6, cornerRadius: 5, alwaysRespectStubs: true } ]
},
init = function(connection) {
connection.getOverlay("label").setLabel(connection.endpoints[0].getParameter('name'));
};
var addEndpoints = function(divId, anchors, needConnections, needUpdates) {
for (var i = 0; i < anchors.length; i++) {
var anchor = anchors[i];
var anchUUID = divId + anchor.name;
var endpoint = anchor.target ? targetEndpoint : sourceEndpoint;
if (anchor.distance && endpoint.connector) {
endpoint = jsPlumbUtil.clone(endpoint);
if (Array.isArray(anchor.distance)) {
endpoint.connector[1].stub[0] += anchor.distance[0];
endpoint.connector[1].stub[1] += anchor.distance[1];
if (anchor.distance.length>2) {
endpoint.connector[1].midpoint = anchor.distance[2];
}
} else {
endpoint.connector[1].stub[anchor.target?1:0] += anchor.distance;
}
}
if (anchor.hasOwnProperty('connectWith') && needConnections) {
needConnections.push(new Array(anchUUID).concat(anchor.connectWith)); // to-circuit, to-endpoint[, item-circuit, item-name[, item-format]]
}
var endp = instance.addEndpoint(divId, endpoint, {
anchor: anchor.anchor, uuid: anchUUID,
parameters:{name:anchor.name, priority:anchor.priority}
});
if (anchor.hasOwnProperty('update') && needUpdates) {
var update = anchor.update;
update.endpoint = endp;
needUpdates.push(update);
}
}
};
var each = function(items, fn) {
for (var i = 0; i < items.length; i++)
fn(items[i]);
}
each(instance.getSelector(".ebus .circuitLabel"), function(item) {
if (item.getAttribute('topf')) {
item.style.top = Math.floor(item.parentElement.clientHeight*item.getAttribute('topf'))+'px';
} else if (item.getAttribute('topmf')) {
item.style.top = Math.floor(item.parentElement.clientHeight*item.getAttribute('topmf')-item.clientHeight/2)+'px';
}
if (item.getAttribute('leftf')) {
item.style.left = Math.floor(item.parentElement.clientWidth*item.getAttribute('leftf'))+'px';
} else if (item.getAttribute('leftmf')) {
item.style.left = Math.floor(item.parentElement.clientWidth*item.getAttribute('leftmf')-item.clientWidth/2)+'px';
}
});
var items = {};
var sourceFlow, sourceReturn, hwcReturn, ehpFlow, hcFlow, hwcFlow, hcReturn, mixFlow, mixReturn, storHwcFlow, storHwcReturn;
// suspend drawing and initialise.
instance.batch(function() {
// listen for new connections; initialise them the same way we initialise the connections at startup.
instance.bind("connection", function(connInfo, originalEvent) {
init(connInfo.connection);
});
instance.bind("click", function(conn, originalEvent) {
if (conn.hasType("hot")) {
conn.toggleType("hot");
conn.toggleType("cold");
} else if (conn.hasType("cold")) {
conn.toggleType("cold");
} else {
conn.toggleType("hot");
}
});
instance.bind("connectionDrag", function(connection) {
console.log("connection " + connection.id + " is being dragged. suspendedElement is ", connection.suspendedElement, " of type ", connection.suspendedElementType);
});
instance.bind("connectionDragStop", function(connection) {
console.log("connection " + connection.id + " was dragged");
});
instance.bind("connectionMoved", function(params) {
console.log("connection " + params.connection.id + " was moved");
});
/*sourceFlow.setType("hot");
sourceReturn.setType("cold");
mixFlow.setType("hot");
mixReturn.setType("cold");
storHwcFlow.setType("hot");
storHwcReturn.setType("off");*/
var heat = false, water = false;
var switchActions = {
/*"broadcast/hwcStatus": function(fields){
console.log("action broadcast/hwcStatus:"+fields);
if (!fields) return;
var val = fields[0].value;
if (val=='on') {
water = true;
ehpFlow.addType("hot");
hcFlow.removeType("hot");
hwcFlow.addType("hot");
hcReturn.removeType("cold");
hwcReturn.addType("cold");
} else {
water = false;
hwcFlow.removeType("hot");
hwcReturn.removeType("cold");
if (!heat) {
ehpFlow.removeType("hot");
hcFlow.removeType("hot");
hcReturn.removeType("cold");
}
}
},
"ehp/HwcHcValve": function(fields) {
console.log("action ehp/HwcHcValve:"+fields);
if (!fields) return;
var val = fields['onoff'].value;
if (val=='off') { // heat
heat = true;
ehpFlow.addType("hot");
hwcFlow.removeType("hot");
hcFlow.addType("hot");
hwcReturn.removeType("cold");
hcReturn.addType("cold");
} else {
water = false;
hcFlow.removeType("hot");
hcReturn.removeType("cold");
if (!water) {
ehpFlow.removeType("hot");
hwcFlow.removeType("hot");
hwcReturn.removeType("cold");
}
}
}*/
};
var buildText = function(format, fields) {
var ret = '';
if (format) {
format = format.split(' ');
for (var i=0; i<format.length; i++) {
if (ret.length>0) ret += ',&nbsp;';
if (format[i][0]!='$') {
if (format[i]=='\\n')
ret += "<br>";
else
ret += format[i];
} else if (fields && fields.hasOwnProperty(format[i].substring(1))){
var field = fields[format[i].substring(1)];
ret += field.hasOwnProperty('value')&&field.value!=null?field.value:'?';
if (field.unit) {
ret += '&nbsp;'+field.unit;
}
} else {
ret += '?';
}
}
} else if (fields) {
for (var i in fields) {
if (ret.length>0) ret += ',&nbsp;';
var field = fields[i];
ret += field.hasOwnProperty('value')&&field.value!=null?field.value:'?';
if (field && field.unit) {
ret += '&nbsp;'+field.unit;
}
}
} else {
ret += '?';
}
return ret;
}
var parentElem = document.getElementById("ebus");
var getFields = function(circuit, name, numeric, verbose, required) {
var ret = Array();
new Ajax.Request('/data/'+circuit+(name?'/'+name:'')+'?exact'+(numeric?'&numeric':'')+(verbose?'&verbose':'')+(required?'&required':''),{
method:'get',
asynchronous:false,
onSuccess: function(trans) {
ret.push(trans.responseJSON[circuit][name].fields);
}
});
return ret?ret[0]:null;
};
var checkCondition = function(condition, allMessages) {
for (var k=0; k+2<condition.length; k+=3) { // "and" loop
var found = false;
var fields = allMessages[condition[k]][condition[k+1]].fields;
var cond = condition[k+2];
if (!fields || !fields[0] || !fields[0].hasOwnProperty('value') || !isNaN(Array.isArray(cond) ? cond[0] : cond) && isNaN(fields[0].value)) {
fields = getFields(condition[k], condition[k+1], true, false, true);
if (!fields || !fields[0] || !fields[0].hasOwnProperty('value')) return false;
}
if (Array.isArray(cond)) {
for (var i=0; i<cond.length; i++) { // "or" loop
if (fields[0].value==cond[i]) {
found = true;
break;
}
}
} else if (fields[0].value==cond) {
found = true;
}
if (!found) return;
}
return true;
};
var addItemFin = function(parent, name, messages, allMessages, needEndpoints, needUris, data) {
if (data.hasOwnProperty('condition') && !checkCondition(data.condition, allMessages)) return;
var child = document.createElement("div");
child.className = "circuit";
if (data.hasOwnProperty('image')) {
child.className += " back";
child.style.backgroundImage = "url(img/"+data.image+")";
if (data.hasOwnProperty('image-pos')) {
var pos = data['image-pos'];
child.style.backgroundPosition = pos[0]+'px '+pos[1]+'px';
}
}
if (data.hasOwnProperty('endpoints')) {
needEndpoints.push({"name": name, "endpoints": data.endpoints});
}
child.id = name;
child.textContent = data.title;
var width = data['bounds'][2];
var height = data['bounds'][3];
child.style.left = ((parent?parent.offsetLeft:0)+data['bounds'][0])+'px';
child.style.top = ((parent?parent.offsetTop:0)+data['bounds'][1])+'px';
child.style.width = width+'px';
child.style.height = height+'px';
parentElem.appendChild(child);
for (var i in data.items) {
var item = data.items[i];
var mname = item.name;
var cmessages = messages;
var circuit = name;
if (item.circuit) {
circuit = item.circuit;
if (!allMessages.hasOwnProperty(circuit)) {
continue;
}
cmessages = allMessages[circuit];
}
if (!cmessages.hasOwnProperty(mname)) {
continue;
}
var message = cmessages[mname];
if (!message.passive) needUris.push(circuit+'/'+mname);
var ichild = document.createElement("div");
ichild.className = "circuitLabel";
ichild.setAttribute('data-id', circuit+'/'+mname);
if (item.hasOwnProperty('format')) {
ichild.setAttribute('data-format', item.format);
}
ichild.style.left = Math.floor((item.leftc?item.leftc:item.left)*width)+'px';
ichild.style.top = Math.floor((item.topc?item.topc:item.top)*height)+'px';
ichild.innerHTML = buildText(item.format, message?message.fields:null);
child.appendChild(ichild);
if (item.leftc) {
ichild.style.left = Math.floor(item.leftc*width-ichild.clientWidth/2)+'px';
}
if (item.topc) {
ichild.style.top = Math.floor(item.topc*height-ichild.clientHeight/2)+'px';
}
}
if (data.hasOwnProperty('sub')) {
for (var i in data.sub) {
var sub = data.sub[i];
addItemFin(child, sub.name, [], allMessages, needEndpoints, needUris, sub);
}
}
};
var addItem = function(parent, name, messages, allMessages, needEndpoints, needUris) {
new Ajax.Request('/items/'+name+'.json',{
method:'get',
asynchronous:false,
onSuccess: function(trans) {
addItemFin(parent, name, messages, allMessages, needEndpoints, needUris, trans.responseJSON);
}
});
};
new Ajax.Request('/data/?verbose',{
method:'get',
onSuccess: function(trans) {
var needEndpoints = new Array();
var needUris = new Array();
var allMessages = trans.responseJSON;
// create items
for (var cname in allMessages) {
if (!allMessages.hasOwnProperty(cname))
continue;
addItem(null, cname, allMessages[cname], allMessages, needEndpoints, needUris);
}
// add endpoints
var needConnections = new Array();
var needUpdates = new Array();
for (var i=0; i<needEndpoints.length; i++) {
addEndpoints(needEndpoints[i].name, needEndpoints[i].endpoints, needConnections, needUpdates);
}
// establish connections
var connectionItems = new Array();
var endpoints = instance.selectEndpoints();
var first = true;
for (var i=0; i<needConnections.length; i++) {
var data = needConnections[i];
// fromuuid, to-circuit|null, to-endpoint[, item-circuit, item-name[, item-format]]
var src = data[0], dst;
if (data[1]==null) {
if (first) {
if (i+1>=needConnections.length) {
first = false;
i = -1;
}
continue;
}
var avail = new Array();
var best = 99;
endpoints.each(function(endpoint) {
if (endpoint.getParameter('name')==data[2] && !endpoint.isFull()) {
var prio = endpoint.getParameter('priority');
if (avail.length==0 || (isNaN(prio)?prio:99)<best) {
avail.clear();
avail.push(endpoint.getUuid());
best = isNaN(prio)?prio:99;
}
}
});
dst = avail.length>0 ? avail[0] : null;
} else {
dst = data[1]+data[2];
}
needConnections.splice(i--, 1);
if (!dst) continue;
if (instance.getEndpoint(src).isTarget) {
src = dst;
dst = data[0];
}
var type = 'off';//TODO dst.indexOf("Flow")>=0?'hot':'cold';
var conn = instance.connect({uuids: [src, dst], editable: true, type: type});
if (conn && data.length>4 && data[3]) { // add label
connectionItems.push({conn: conn, data: data});
var cmessages = allMessages[data[3]];
var message = cmessages[data[4]];
var over = conn.getOverlay("label");
over.setLabel(buildText(data.length>5?data[5]:null, message.fields));
var key = data[3]+'/'+data[4];
if (!needUris[key]) {
needUris.push(key);
}
}
}
// TODO needUpdates
// request all shown values and enable automatic polling
for (var i=0; i<needUris.length; i++) {
new Ajax.Request('/data/'+needUris[i]+'?exact&poll=2',{
method:'get',
onSuccess: function(trans) {
var cname = needUris[i].split('/');
var mname = cname[1];
cname = cname[0];
var fields = trans.responseJSON[cname][mname].fields;
if (fields) {
var elems = $$('div[data-id="'+needUris[i]+'"]');
each(elems, function(elem) {
elem.innerHTML = buildText(elem.getAttribute('data-format'), fields);
});
}
}
});
}
instance.draggable(jsPlumb.getSelector(".ebus .circuit"), { grid: [20, 20] });
},
onFailure: function() {
console.log("initial request failed");
},
onException: function(trans, e) {
console.log(e);
}
});
var running = false, firstrun = true;
var since = 0;
var startRequest = function() {
if (running) return;
running = true;
new Ajax.Request('/data/?verbose&since='+since,{
method:'get',
onSuccess: function(trans) {
if (firstrun) {
firstrun = false;
}
for (var cname in trans.responseJSON) {
if (!trans.responseJSON.hasOwnProperty(cname)) {
continue;
}
var circuit = trans.responseJSON[cname];
for (var mname in circuit) {
if (!circuit.hasOwnProperty(mname)) {
continue;
}
var message = circuit[mname];
var lastup = message.lastup;
if (lastup>since) {
since = lastup;
}
var fields = message.fields;
var key = cname+'/'+mname;
var needed = false;
if (switchActions.hasOwnProperty(key)) {
switchActions[key](fields);
needed = true;
}
var elems = $$('div[data-id="'+key+'"]');
if (elems.length>0) {
needed = true;
} else console.log(key+':'+fields);
if (!fields) {
continue;
}
each(elems, function(elem) {
elem.innerHTML = buildText(elem.getAttribute('data-format'), fields);
});
}
}
running = false;
},
onFailure: function() {
console.log("failed");
running = false;
},
onException: function(trans,e) {
console.log(e);
running = false;
}
});
};
startRequest();
new PeriodicalExecuter(startRequest, 15);
});
});
</script>
</body>
</html>