-
Lucio Zambon authored9e61857b
panther2d.js 33.14 KiB
// jshint esversion: 6
let gui = new lil.GUI();
let hideTimeout = false;
let svg;
let zoom = 1;
let oldZoom = 1;
let panZoomPanther;
let foundAnimate = 1;
let foundScale = 1.05;
let myPanZoomTimer = null;
let historytime = + new Date();
let fel1 = false;
let fel2 = false;
const panZoomTime = 500;
const facilities = [''];
const names = [];
const serviceareanames = [];
const alias = [];
let maxZoom = 230;
const status = [];
const point = {x: 0, y: 0};
const pa = document.location.search.replace('?','').split('&');
const parameters = {};
const state = document.location.search.indexOf('ps')>-1;
const vlv = document.location.search.indexOf('vlv')>-1;
const vlvs = [];
for (let i=0; i<pa.length; i++) {const p = pa[i].split('='); parameters[p[0]] = p[1];}
const machineCaseSensitive = document.location.search.indexOf('machine=')>-1? document.location.search.split('machine=')[1].split('&')[0]: 'elettra';
const machine = machineCaseSensitive.toLowerCase();
//https://puma-01.elettra.eu/rchan.php?json&valueOnly&src=srv-tango-srf-01.fcs.elettra.trieste.it:20000/f/access_control/safety/Undulator_access_state -> 5 FEL1, 6 FEL2
if (machine.indexOf('fermi')>-1) {
fetch(conf.rchan+'srv-tango-srf-01:20000/f/access_control/safety/Undulator_access_state').then((response) => {return response.json();}).then((fel) => {
fel1 = fel[5] == 1;
fel2 = fel[6] == 1;
});
}
const latticeFile = document.location.href.split('?')[0].split('/').slice(0,-1).join('/')+'/'+machine+'_lattice.json';
const params = {machine: machineCaseSensitive.toLowerCase(), search: '', backgroundColor: '#333333'};
gui.title('PAnTHer - controls');
// if (navigator.userAgent.indexOf('Firefox/63')>-1) {$( "body" ).append('<button id="starter" style="align: center;height: 500px; width: 95%;background-color: #449944; font-size: 100px;" onClick="mystart()">START</button>');}
function mystart() {
panZoomPanther = svgPanZoom('#panther', {beforeZoom: myZoom, fit: false, contain: false});
$("#sname").on("keydown", searchText);
$('#starter').hide();
}
// Polyfill for parentNode.replaceChildren()
if (typeof Element.prototype.replaceChildren !== 'function') {
Object.defineProperty(Element.prototype, 'replaceChildren', {
configurable: true,
writable: true,
value: function replaceChildren(...nodes) {
// Remove all existing child nodes
while (this.firstChild) {
this.removeChild(this.firstChild);
}
// Append new DOM objects
this.append(...nodes);
}
});
}
function toggleMachine(machine) {
document.location = document.location.href.split('?')[0] + '?machine='+machine;
/*if (document.location.search.indexOf('machine=')==-1) document.location = document.location.href + (document.location.href.indexOf('?')==-1? '?': '&') + 'machine='+machine;
let search = document.location.search.split('machine=')[1].split('&')[0];
document.location = document.location.href.replace('machine='+search, 'machine='+machine);*/
}
function compLink(event) {
console.log('compLink()', event, document.getElementById("compdb").href);
window.open(document.getElementById("compdb").href, '_blank').focus();
event.stopPropagation();
return false;
}
function searchText(e) {
console.log('searchText()', e, e.keyCode || e.which, $("#sname").val().toUpperCase().replace('.','_'));
if (e.keyCode == 13) findComponent($("#sname").val().toUpperCase().replace('.','_'));
// return -1;
}
// if (navigator.userAgent.indexOf('Firefox/63')==-1) {gui.add(params, 'machine', conf.machineList).onChange(function() {toggleMachine(params.machine);});}
gui.add(params, 'machine', conf.machineList).onChange(function() {toggleMachine(params.machine);});
gui.add(params, 'search');
gui.addColor(params, 'backgroundColor').onChange(function() {toggleParam('backgroundColor');});
params.vlv = document.location.search.indexOf('vlv')>-1;
gui.add(params, 'vlv').name('vlv & bst').onChange(function() {toggleParam('vlv');});
params.ps = document.location.search.indexOf('ps')>-1;
gui.add(params, 'ps').onChange(function() {toggleParam('ps');});
const sstring = $('.controller.string').children().eq(1).children().eq(0);
sstring.attr('id', 'sname');
sstring.attr('name', 'sname');
sstring.addClass("form-control sname");
function findComponent(name) {
let servicearea = false;
console.log('lattice', lattice);
if (lattice.servicearea) for (let i in lattice.servicearea.sections) {
for (let j in lattice.servicearea.sections[i].components) {
if (name.replace('.', '_')==lattice.servicearea.sections[i].components[j].name.replace('.', '_')) {servicearea = true;}
for (let k in lattice.servicearea.sections[i].components[j].embedded) {
if (name==lattice.servicearea.sections[i].components[j].embedded[k]) {servicearea = true;}
}
}
}
console.trace(name, servicearea, document.location.search.indexOf('servicearea'), document.location.search.indexOf('search='+name)); // return;
if ((servicearea != document.location.search.indexOf('servicearea')>-1) && document.location.search.indexOf('search='+name)==-1) {
document.location = './panther2d.php?machine='+machine+'&search='+name+(servicearea? '&servicearea': '');
}
if (name==null) name = document.getElementById('sname').value;
for (let i=0; i<alias.length; i++) if (alias[i][1]==name) name = alias[i][0];
document.getElementById('sname').value = name;
if (typeof $('#'+name)[0] == 'undefined') return;
console.log(name, window.innerWidth/2, $('#'+name)[0].getCTM().e, $('#'+name)[0].getCTM().f);
// panZoomPanther.zoomAtPoint(10, {x: window.innerWidth/2 - $('#'+name)[0].getCTM().e, y: window.innerHeight/2 - $('#'+name)[0].getCTM().f})
panZoomPanther.zoom(10);
// leave a delay between zoom and pan
setTimeout(mypan, 1200, name);
}
function mypan(name) {
$('.label').show();
const x = window.innerWidth/2 - $('#'+name)[0].getCTM().e + panZoomPanther.getPan().x;
const y = window.innerHeight/2 - $('#'+name)[0].getCTM().f + panZoomPanther.getPan().y;
const m = $("svg")[0].getTransformToElement($('#'+name)[0]);
panZoomPanther.pan({x: x, y: y});
setTimeout(pinhide, 100, name, $('#'+name).eq(0).attr('transform'));
}
function pinhide(name, transform) {
console.log(foundAnimate, foundScale);
foundAnimate *= foundScale;
if (foundAnimate>1.5) foundScale = 0.95;
if (foundAnimate<1) {foundScale = 1.05; foundAnimate = 1;} else setTimeout(pinhide, 100, name, transform);
$('#'+name).eq(0).attr('transform',transform+',scale('+foundAnimate+')');
}
// $(function() {$(".sname").autocomplete({source: names, close: function(event, ui) {findComponent($('#sname').val()); }, select: function(event, ui) {findComponent(ui.item.value); return false;}});});
$(function() {$(".sname").autocomplete({source: names, select: function(event, ui) {findComponent(ui.item.value); return false;}});});
function toggleParam(name) {
if (name=='backgroundColor') {$('body').css('backgroundColor', params.backgroundColor); return;}
const urlparam = {};
let search = document.location.search.replace('?', '').split('&');
if (search[0]=='') search.splice(0,1);
const i=search.indexOf(name);
if (i==-1) search.push(name); else search.splice(i,1);
const res = search.join('&');
document.location = document.location.href.split('?')[0]+(res.length? '?'+res: '');
}
async function subscribe() {
let response = await fetch("./misc/talk.php?read");
if (response.status == 502) {
await subscribe();
} else if (response.status != 200) {
// An error - let's show it
mylog('subscribe() ERROR ', response.statusText);
// Reconnect in one second
await new Promise(resolve => setTimeout(resolve, 1000));
await subscribe();
} else {
// Get and show the message
let message = await response.text();
mylog('subscribe()', message);
highlightobjects(message);
// Call subscribe() again to get the next message
await subscribe();
}
}
// subscribe();
function mylog(...args) {
if (document.location.search.indexOf('debug')>-1) $('#debug').html($('#debug').html()+JSON.stringify(args).replaceAll(':',': ').replaceAll(',',', ')+'\n');
else {console.trace.apply(null, args);}
}
function initIndex(lattice) {
const index = [];
for (let l in lattice.conf.index) {
if (l=='start') continue;
const cmd = "findComponent('"+lattice.conf.index[l].replace('.','_')+"')";
index.push('<button onclick="'+cmd+'">'+l+'</button>');
}
$('body').append('<div style="position: absolute; left: 5px; bottom: 5px;">'+index.join(' ')+'</div>');
}
function init() {
fetch(latticeFile).then((response) => {return response.json();}).then((flattice) => {
lattice = flattice;
if (Object.keys(lattice).length>0) {
for (let i in lattice) {if (i!='conf') facilities.push(i);}
for (let i in lattice) {
if (i == 'conf') continue;
// logic XOR https://stackoverflow.com/questions/2335979/is-there-anyway-to-implement-xor-in-javascript
if ((document.location.search.indexOf('servicearea')==-1) != (i=='servicearea')) initLattice(lattice[i].sections, i); else initSearch(lattice[i].sections, i);
}
bpmInit(facilities);
if (document.location.search.indexOf('servicearea')>-1) initSearch(lattice.servicearea.sections, 'servicearea');
if (typeof blm != 'undefined') blmMenu(lattice, facilities, params);
if (typeof bpmData != 'undefined') bpmMenu(lattice, facilities, params);
params.gotoAdmin = function() {document.location = './admin.php';};
gui.add(params, 'gotoAdmin').name('Admin');
params.goto3D = function() {document.location = './panther.php?machine='+params.machine;}; gui.add(params, 'goto3D').name('3D');
if (lattice.conf && lattice.conf.index) initIndex(lattice);
}
$('.scale').attr('transform', "scale(2)");
// {$('<div><iframe style="width: 100%;height:250px;" src="../misc/gauge.html?dark&r1only=1&r=100&max=360&throttlingPeriod=50&apply=rotate&val='+0+'"></iframe></div>').insertBefore('.function');}
// panZoomPanther = svgPanZoom('#panther', {beforeZoom: myZoom, fit: false, contain: false}); // https://github.com/bastienmoulia/svg-pan-zoom-rotate
// if( /Android|webOS|iPhone|iPad|Mac|Macintosh|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
panZoomPanther = svgPanZoom('#panther', {
zoomEnabled: true,
controlIconsEnabled: false,
fit: 0,
center: 1,
maxZoom: maxZoom*2,
customEventsHandler: eventsHandler,
//beforeZoom: myZoom,
});
}
else {
panZoomPanther = svgPanZoom('#panther', {
zoomEnabled: true,
zoomScaleSensitivity: 0.3,
controlIconsEnabled: false,
fit: false,
center: false,
minZoom: 0.2,
maxZoom: maxZoom,
beforePan: myPan,
beforeZoom: myZoom,
});
}
if (document.location.search.indexOf('debug')>-1) {
$('#tooltip').show();
$('#tooltip').css('width', '100%');
$('#tooltip').html('<textarea id="debug" style="height: 100px; width:100%;"></textarea>');
}
let pan = [window.innerWidth/2, window.innerHeight/2];
if (document.location.search.indexOf('pan=')>-1) pan = document.location.search.split('pan=')[1].split('&')[0].split(',');
if (document.location.search.indexOf('search=')>-1) findComponent(document.location.search.split('search=')[1].split('&')[0]);
else {
const zoom = document.location.search.indexOf('zoom=')>-1? document.location.search.split('zoom=')[1].split('&')[0]-0: 0.5;
panZoomPanther.zoom(zoom);
setTimeout(panZoomPanther.pan, 600, {x: pan[0], y: pan[1]});
}
if (state) {
fetch(conf.stateSrcUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => {
const statSrc = data.toUpperCase().split('.').join('_').split(',');
for (let j=0; j<statSrc.length; j++) {
// if (statSrc[j].split('/')[2]=='POWER_SUPPLY' || statSrc[j].split('/')[2]=='INJECTION' || statSrc[j].split('/')[2]=='EXTRACTION')
statSrc[j] = statSrc[j].split('/')[3].replace('PS', '');
}
for (let i=0; i<status.length; i++) {
if (status[i].name.indexOf('KICK')>-1) console.log('KICK state', i, status[i].name, statSrc);
for (let j=0; j<statSrc.length; j++) {
if (status[i].name.indexOf(statSrc[j])>-1) {status[i].statsrc = state; status[i].statindex = j;}
}
}
// console.log('statSrc', data, statSrc, status);
});
setInterval(updateStatus, 1000);
}
if (vlv) {
for (let i in conf.bstmap) { if (i.indexOf('.')>-1) conf.bstmap[i.replace('.','_')] = conf.bstmap[i];}
fetch(conf.vlvSrcUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => {
const vlvSrc = data.toUpperCase().substring(14).split(',');
for (let i=0; i<vlvs.length; i++) {
for (let j=0; j<vlvSrc.length; j++) {
const vlva = vlvSrc[j].split('/');
if (vlva.length < 4) continue;
const name = vlva[3].replace('.','_');
if (vlvs[i].name.replace('.','_').indexOf(name)>-1) {vlvs[i].vlvsrc = state; vlvs[i].vlvindex = j; vlvs[i].type = 'vlv';}
if (vlvSrc[j].split('/')[2].indexOf(conf.bstmap.base)>-1) {
if (conf.bstmap[vlvs[i].name] && conf.bstmap[vlvs[i].name].indexOf(vlvSrc[j].split('/')[4])>-1) {
vlvs[i].vlvsrc = 'bst'; vlvs[i].vlvindex = j; vlvs[i].type = 'bst';
}
}
}
}
//console.log('vlvSrc', data, vlvSrc, vlvs);
});
setInterval(updateVlv, 1000);
}
$("#sname").on("keydown", searchText);
});
}
function showStatus(i, stat) {
if (stat == 0 || stat == 'null' || stat == '' || stat == 'ON' || stat == 'RUNNING' || (!fel1 && $('#'+status[i].name)[0].classList[0]=='fel1') || (!fel2 && $('#'+status[i].name)[0].classList[0]=='fel2')) {$('#'+status[i].name).hide();}
else {$('#'+status[i].name).show(); document.getElementById(status[i].name).style.fill = conf.stateLabelColor[stat];}
// console.log(i, status[i], stat);
}
function clearStatus() {
$('.ps').hide();
}
function updateStatus() {
fetch(conf.stateUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => {
const statVal = data.split(';');
// console.log(conf.stateUrl, statVal, status);
for (let i=0; i<status.length; i++) {
if (status[i].statsrc==state) {
if (status[i].statindex) showStatus(i, statVal[status[i].statindex]);
// console.log(statVal, status, status[i].name, i, status[i].statindex, statVal[status[i].statindex]);
}
}
setTimeout(clearStatus, 600);
});
}
function updateVlv() {
fetch(conf.vlvUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => {
const vlvVal = data.split(':')[1].split(';');
// console.log('updateVlv()', conf.vlvUrl, vlvVal, vlvs);
for (let i=0; i<vlvs.length; i++) {
if (vlvs[i].type=='vlv') $('#'+vlvs[i].name).css('fill', vlvVal[vlvs[i].vlvindex]=='CLOSED'? 'yellow': (vlvVal[vlvs[i].vlvindex]=='OPENED'? 'limegreen': 'grey'));
if (vlvs[i].type=='bst') {
if (typeof vlvVal[vlvs[i].vlvindex] != 'string') continue;
const val = vlvVal[vlvs[i].vlvindex].split(',');
// console.log('updateVlv(): ', i, vlvs[i], vlvs[i].type, vlvs[i].vlvindex, vlvVal[vlvs[i].vlvindex], '#'+vlvs[i].name, vlvs[i].comp, val[0]=='true'? 'limegreen': (val[1]=='true'? 'yellow': 'grey'));
$('#'+vlvs[i].name).css('fill', val[0]=='true'? 'limegreen': (val[1]=='true'? 'yellow': 'grey'));
}
}
});
}
function rescale(x) {return x;}
String.prototype.replaceAt = function(index, replacement) {
return this.substring(0, index) + replacement + this.substring(index + replacement.length);
};
function evalId(base) {
if (base.indexOf('.')>-1) return base;
return base.replaceAt(base.lastIndexOf('_'), '.').replace('RTBPM','BPM');
}
function openTooltip(event) {
if (document.location.search.indexOf('debug')==-1) {
const type = this.href? this.href.baseVal.replace('#',''): (this.id.indexOf('BLM')>-1 || this.id.indexOf('BERGOZ')>-1? 'blm': '???');
mylog('openTooltip()',type, this.id, event, event.clientY);
$('#tooltip').css('left', event.clientX+30);
$('#tooltip').css('top', event.clientY+30);
document.getElementById('tooltipFrame').src = conf.tooltipApp+'?s='+type.replace('fast', '')+'¶m='+this.id;
console.log('openTooltip(event)', type.toLowerCase(), type.toLowerCase().indexOf('beamline'));
document.getElementById('tooltip').style.display = 'block';
if (hideTimeout!==false) clearTimeout(hideTimeout);
hideTimeout = setTimeout(hideTooltip, 120000);
document.getElementById('compdb').style.display = (type.indexOf('rv')==0 || type.indexOf('rc')==0 || type.indexOf('rid')==0 || type.indexOf('rd')==0 || type.indexOf('rps')==0 || type.indexOf('plc')==0)? 'none': 'block';
const id = evalId(this.id);
if (document.getElementById('compname')) document.getElementById('compname').innerHTML = id;
document.getElementById('compdb').href = conf.compdb + id;
document.getElementById("compdb").addEventListener("click", compLink);
if (type.toLowerCase().indexOf('beamline')>-1) {
document.getElementById('compdb').href = 'http://adam.elettra.trieste.it/projects/blcs/beamwatch/';
document.getElementById('compdb').innerHTML = 'search '+this.id+' in ADAM Beamwatch <span id="compname"/>';
}
else if (machine=='elettra') document.getElementById('compdb').setAttribute("disabled", true);
event.stopPropagation();
}
else {
for (let l in lattice) {
if (l=='confg') continue;
let servicearea = false;
for (let i in lattice[l].sections) {
for (let j in lattice[l].sections[i].components) {
if (this.id==lattice[l].sections[i].components[j].name.replace('.', '_')) {mylog('openTooltip()', lattice[l].sections[i].components[j], lattice[l].sections[i].components[j].position-document.location.search.split('offset=')[1].split('&')[0]);}
}
}
}
}
}
function hideTooltip() {
if (hideTimeout!==false) clearTimeout(hideTimeout);
hideTimeout = false;
// mylog('hideTooltip');
if (document.location.search.indexOf('debug')==-1) {
document.getElementById('tooltipFrame').src = '';
document.getElementById('tooltip').style.display = 'none';
}
}
function transformLabel(x, y, beta, labelReverse) {
if (typeof labelReverse == 'object') return "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta+labelReverse[0])+") translate("+labelReverse[1]+" "+labelReverse[2]+")";
return labelReverse==180? "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta-90)+") translate(1800 100)":
"translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta+90)+") translate("+(labelReverse? -250: 250)+" "+(labelReverse? 200: 300)+")";
}
function appendLabel(id, labelclass, display, x, y, beta, labelReverse) {
if ((beta+3600)%360 <180 && typeof labelReverse != 'object') labelReverse = [-90, -250, -100];
appendSvg("text", {
id: id+'label',
class: labelclass,
x:0, y:0, style:"display: "+display, fill:"white", stroke:"#eeeeee","stroke-width":10, "font-family":"Arial", "font-size":200, "font-weight":"bold",
"text-anchor": (labelReverse? "end": "start"),
transform: transformLabel(x, y, beta, labelReverse)
}, false, id);
}
function appendLabel2(param, labelclass, display, x, y, beta, labelReverse) {
const id = param.name;
const fontsize = labelclass.indexOf('bl')>-1? 800: 500;
// console.log("appendLabel2()",param, labelclass, display, x, y, beta, labelReverse);
if (labelclass.indexOf('bl')>-1 && typeof labelReverse != 'object') {
labelReverse = [180, 17000, param.type=='beamlineUp'? -500: +800];
if ((beta+3690)%360 < 180) labelReverse = [0, -14000, param.type=='beamlineUp'? 1000: -300];
}
else if ((beta+3600)%360 < 180 && typeof labelReverse != 'object') labelReverse = [-90, -250, -100];
appendSvg("text", {
id: id+'label',
class: labelclass,
x:0, y:0, style:"display: block", fill:"red", stroke:"pink","stroke-width":10, "font-family":"Arial", "font-size":fontsize, "font-weight":"bold",
"text-anchor": (labelReverse? "end": "start"),
transform: transformLabel(x, y, beta, labelReverse)
}, false, id);
}
function appendSvg(tagName, attrib, onClickCall=false, text=false, myclass=false) {
const elem = document.createElementNS("http://www.w3.org/2000/svg", tagName);
if (onClickCall) elem.addEventListener("click", onClickCall, false);
if (text) {
const textNode = document.createTextNode(text);
elem.appendChild(textNode);
}
const jelem = $(elem);
if (myclass) {/*mylog(elem, jelem, myclass);*/ elem.classList.add(myclass);}
for (let i in attrib) {
jelem.attr(i, attrib[i]);
}
$("svg").append(jelem);
}
function appendSearch(component, facility) {
// mylog('appendSearch()',component, facility);
if (component) {
const comp = component.type.replace('booster', '');
if (comp && comp!=' ' && $('#'+comp)[0]) {
const id = extractId(component.name);
names.push(id[0]);
if (facility=='servicearea') serviceareanames.push(id[0]);
if (component.embedded) {
for (let j=0; j<component.embedded.length; j++) {names.push(component.embedded[j]); alias.push([id[0],component.embedded[j]]);}
}
}
}
}
function appendComponent(components, x0, y0, x1, y1, facility) {
const dx = x1 - x0;
const dy = y1 - y0;
const d = Math.sqrt(dx*dx + dy*dy);
const beta = 180*Math.atan2(y0-y1, x0-x1)/Math.PI;
if (components) for (let i=0; i<components.length; i++) {
const comp = components[i].type.replace('booster', '');
let x = x0+components[i].position/d*dx;
let y = y0+components[i].position/d*dy;
if (components[i].offset2d) {x += components[i].offset2d[0]; y += components[i].offset2d[1];}
if (typeof blm != 'undefined' && comp=='blm') {
const name = components[i].name.replace('BPM','BLM');
// mylog('appendComponent(), ', name, blm.obj);
blm.obj.push(name);
blm.dir.push(beta);
appendSvg("rect", {id:name, name:name, x:0, y:0, width:40, height:40, rx:20, ry:20, transform:"translate("+rescale(x)+" "+rescale(y)+") rotate("+(name.indexOf('_L')>-1? beta + 180: beta)+")"}, openTooltip, false, "blm");
}
if (typeof bpmData != 'undefined' && comp=='bpm') {
// mylog('appendComponent()',components[i], x0, y0, x1, y1, beta, facility, 180*Math.atan2(y0-y1, x0-x1)/Math.PI);
bpmData[facility].obj.push(components[i].name);
bpmData[facility].dir.push(beta);
bpmData[facility].pos.push([rescale(x), rescale(y)]);
}
if (typeof bpmData != 'undefined' && comp=='corrector') {
corr[facility].obj.push(components[i].name);
corr[facility].dir.push(beta);
corr[facility].pos.push([x, y]);
}
if ($('#'+comp)[0]) {
// mylog('components['+i+']',components[i]);
const id = extractId(components[i].name);
const section = components[i].name.indexOf('_')>-1? components[i].name.split('_')[1].split('.')[0]+' ': '';
const offset = components[i].type == 'beamlineUp'? "-2100 -3500": "0 -200";
const transform = "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta+180)+") translate("+offset+")";
appendSvg("use", {href:"#"+comp, id: id[0], name:components[i].name, class: comp+' '+section+facility, style:"cursor: pointer", transform:transform}, openTooltip);
if (components[i].type == 'label' || components[i].type == 'beamlineDown' || components[i].type == 'beamlineUp')
appendLabel2(components[i], section+facility, 'none', x, y, beta, components[i].labelReverse);
else
appendLabel(components[i].name, comp+' label '+section+facility, 'none', x, y, beta, components[i].labelReverse);
names.push(id[0]);
if (components[i].embedded) {
for (let j=0; j<components[i].embedded.length; j++) {names.push(components[i].embedded[j]); alias.push([id[0],components[i].embedded[j]]);}
}
if (id[1]) {names.push(id[1]); alias.push(id);}
if (state) {
if (components[i].ps) {
for (let pi=0; pi<components[i].ps.length; pi++) {
const name = components[i].ps[pi].replace('PS','').replace('.','_') + '_status';
status.push({name: name});
appendSvg('circle', {id: name, class: facility+' ps', style:"display: none", cx: 150+pi*100, cy: 200, transform:transform, r: 80, stroke: 10, strokeColor: 'blue', fill: 'gray'}, false, false, 'status');
}
}
else {
status.push({name: id[0]+'_status'});
appendSvg('circle', {id: id[0]+'_status', class: facility+' ps', style:"display: none", cx: 200, cy: 200, transform:transform, r: 80, stroke: 10, strokeColor: 'blue', fill: 'gray'}, false, false, 'status');
}
if (components[i].name.toUpperCase().indexOf('KICK')>-1) console.log('PS: ', components[i].name, components[i].name.toUpperCase().indexOf('KICK'), status);
}
if (vlv && (components[i].type == 'vlv' || components[i].type == 'bst')) {
// appendSvg('rect', {id: id[0]+'_disable', style:"display: none", x: x+75, y: y-90, width: 50, height: 200, stroke: 10, fill: 'black'}, false, false, 'vlvs');
vlvs.push({name: id[0], type: components[i].type});
}
}
}
}
if (!String.prototype.replaceAll) {
String.prototype.replaceAll = function(search, replace) {
return this.split(search).join(replace);
};
}
function extractId(name) {
if (name.indexOf('(')>-1) {
let tok = name.split('(');
return [tok[0].replaceAll('.','_').replaceAll(' ','_').replace(/_+$/, ''), tok[1].replaceAll('.','_').replaceAll(' ','_').replaceAll(')','').replace(/_+$/, '')];
}
// .replace(/_+$/, '') => https://stackoverflow.com/questions/8141718/how-to-trim-specific-characters-from-the-end-of-the-javascript-string
return [name.replaceAll('.','_').replaceAll(' ','_').replace(/_+$/, ''), false];
}
function highlightobjects(objclass) {
$("use").css('opacity',0.3);
$("text").css('opacity',0.3);
$("."+objclass).css('opacity',1);
}
function magnifyobjects(objclass) {
$("text").css('opacity',0.15);
$("text."+objclass).css('opacity',1);
$('.scale').attr('transform', "scale(1) translate(90,100)");
let x = -40;
let y = -40;
let s = 3;
if (objclass=="vlv") {x = -50; y = -50; s = 5;}
$('.'+objclass+'scale').attr('transform', "scale("+s+") translate("+x+","+y+")");
}
function initSearch(sections, facility) {
console.log('initSearch()', sections, facility);
for (i=0; i<sections.length; i++) { // if(i>1) break;
if (sections[i].bending && sections[i].bending.type) {
console.log('names.push()', sections[i].bending.name);
names.push(sections[i].bending.name.replace('.','_'));
}
if (typeof sections[i].components == 'object') for (let j=0; j<sections[i].components.length; j++) appendSearch(sections[i].components[j], facility);
}
}
function initLattice(sections, facility) {
initSearch(sections, facility);
if (document.location.search.indexOf('facility=')>-1 && facility!=document.location.search.split('facility=')[1].split('&')[0]) return;
if (typeof bpmData != 'undefined') {
bpmData[facility] = {obj: [], dir: [], pos: []};
corr[facility] = {obj: [], dir: [], pos: []};
}
// vacuum chamber
let d = '';
let m = true;
let wall = false;
for (let i=0; i<sections.length; i++) {
d = d + (m? 'M ': ' L ')+rescale(sections[i].start.x)+' '+rescale(sections[i].start.z);
if (i<sections.length-1 && typeof sections[i+1].chamber == 'undefined') {m = true;} else if (i<sections.length-1 && sections[i+1].chamber.type=="chamber") {m = false;}
if (i<sections.length-1 && typeof sections[i+1].chamber != 'undefined' && sections[i+1].chamber.type=="wall") wall = true;
}
if (sections[0].chamber && sections[0].chamber.type=='chamber') d = d + ' Z';
console.log('sections', sections);
if (d!='') {
if (wall) appendSvg("path", {d: d, fill: "none", stroke: "#990000", "stroke-dasharray": "200 200", "stroke-width": 50, id:"wall"+facility});
else appendSvg("path", {d: d, fill: "none", stroke: "#999999", "stroke-width": 20, id:"chamber"+facility});
}
let j = 0;
let i = sections.length - 1;
let k = sections.length - 2;
let alpha = 180/Math.PI*Math.atan2(sections[j].start.z-sections[i].start.z, sections[j].start.x-sections[i].start.x);
for (i=0; i<sections.length; i++) { // if(i>1) break;
j = (i + 1) % sections.length;
k = (i + sections.length - 1) % sections.length;
let beta = 180/Math.PI*Math.atan2(sections[j].start.z-sections[i].start.z, sections[j].start.x-sections[i].start.x);
let gamma = (beta+alpha)/2;
if (sections[k] && sections[j].start.z<sections[i].start.z && sections[i].start.z>sections[k].start.z) gamma = gamma + 180;
if (alpha>beta && gamma<0) gamma = gamma + 180;
if (sections[i].bending && sections[i].bending.type) {
const section = sections[i].bending.name.indexOf('_')>-1? sections[i].bending.name.split('_')[1].split('.')[0]+' ': '';
appendSvg("use", {href:"#"+sections[i].bending.type.replace('dipolefermi','bending'), id:sections[i].bending.name.replace('.','_'), class: 'bending '+section+facility, style:"cursor: pointer", transform:"translate("+rescale(sections[i].start.x)+" "+rescale(sections[i].start.z)+") rotate("+gamma+") translate(-600 -200)"}, openTooltip);
appendLabel(sections[i].bending.name, 'bending '+section+facility, 'block', sections[i].start.x, sections[i].start.z, gamma+180, sections[i].bending.labelReverse);
// appendSvg("text", {id:sections[i].bending.name+'label', class: 'bending '+section+facility, x:0, y:0, fill:"white", stroke:"#888888","stroke-width":10, "font-family":"Arial", "font-size":300, "font-weight":"bold", transform:"translate("+rescale(sections[i].start.x)+" "+rescale(sections[i].start.z)+") rotate("+gamma+") translate(-600 -250)"}, false, sections[i].bending.name);
}
alpha = beta;
appendComponent(sections[i].components, sections[i].start.x, sections[i].start.z, sections[j].start.x, sections[j].start.z, facility);
// if (typeof bpmData != 'undefined') bpmInit(facility);
}
}
$(document).ready(function() {
$("svg").attr('height', window.innerHeight+'px');
init();
});
var eventsHandler;
let myhammer;
eventsHandler = {
haltEventListeners: ['touchstart', 'touchend', 'touchmove', 'touchleave', 'touchcancel','tap', 'pinch','pinchstart','pinchmove'],
init: function(options) {
var instance = options.instance, initialScale = 1, pannedX = 0, pannedY = 0;
// mylog('init', options, panZoomPanther);
myhammer = Hammer(options.svgElement, {
inputClass: Hammer.SUPPORT_POINTER_EVENTS ? Hammer.PointerEventInput : Hammer.TouchInput
});
myhammer.get('pinch').set({enable: true});
myhammer.on('panstart panmove', function(ev){
if (ev.type === 'panstart') {
pannedX = 0;
pannedY = 0;
}
panZoomPanther.panBy({x: ev.deltaX - pannedX, y: ev.deltaY - pannedY});
pannedX = ev.deltaX;
pannedY = ev.deltaY;
});
myhammer.on('pinchstart pinchmove', function(ev){
if (ev.type === 'pinchstart') {
initialScale = panZoomPanther.getZoom();
panZoomPanther.zoomAtPoint(initialScale * ev.scale, {x: ev.center.x, y: ev.center.y}, zoom, ev.scale);
// mylog(''); mylog('panZoomPanther.zoomAtPoint('+(initialScale * ev.scale)+', {x: '+ev.center.x+', y: '+ev.center.y+'})');
}
if (ev.type === 'pinchmove') {
oldZoom = zoom;
zoom = initialScale * ev.scale;
// mylog('zoom', zoom, ev.center.x);
point.x = ev.center.x;
visibleX = 0 - point.x/zoom;
visibleWidth = document.getElementById('panther').clientWidth/zoom;
}
panZoomPanther.zoomAtPoint(initialScale * ev.scale, {x: ev.center.x, y: ev.center.y});
zoom = panZoomPanther.getZoom();
if (zoom>2) $('.label').show(); else $('.label').hide();
// mylog('panZoomPanther.zoomAtPoint('+(initialScale * ev.scale)+', {x: '+ev.center.x+', y: '+ev.center.y+'})', zoom, ev.scale);
});
options.svgElement.addEventListener('touchmove', function(e){e.preventDefault(); });
},
destroy: function(){
myhammer.destroy();
}
};
function myPanZoomDelayed(event) {
if (event) event.preventDefault();
visibleX = 0 - point.x/zoom;
visibleWidth = document.getElementById('panther').clientWidth/zoom;
// mylog('myPanZoomDelayed, event', event, 'point', point, 'zoom:', zoom, 'visibleX', visibleX, 'visibleWidth', visibleWidth);
myPanZoomTimer = null;
oldZoom = zoom;
}
let firstUrl = true;
function setUrl(name, value) {
let t = + new Date();
if (t - historytime < 100) return;
historytime = t;
parameters[name] = value;
const pp = [];
for (let i in parameters) {pp.push(i+'='+parameters[i]);}
const url = document.location.origin+document.location.pathname+'?'+pp.join('&').replaceAll('=undefined', '').replace('search=','s=');
// console.log('setUrl()',name, value, url);
if (firstUrl) window.history.pushState({"html":'panther2d.php',"pageTitle":'PAnTHer'},"", url);
else window.history.replaceState({"html":'panther2d.php',"pageTitle":'PAnTHer'},"", url);
firstUrl = false;
}
function myPan(oldPan, newPan) {
setUrl('pan', Math.round(newPan.x*100)/100+','+Math.round(newPan.y*100)/100);
point.x = newPan.x;
point.y = newPan.y;
if (myPanZoomTimer == null && document.location.search.indexOf('&autoPanZoom')==-1) myPanZoomTimer = setTimeout(myPanZoomDelayed, panZoomTime);
// mylog('myPan', p,q);
}
function myZoom(oldScale, newScale) {
setUrl('zoom', Math.round(newScale*100)/100);
zoom = newScale;
if (newScale>2) $('.label').show(); else $('.label').hide();
if (myPanZoomTimer == null && document.location.search.indexOf('&autoPanZoom')==-1) myPanZoomTimer = setTimeout(myPanZoomDelayed, panZoomTime);
// mylog('myZoom', zoom);
}
/*
https://github.com/dagrejs/dagre-d3/issues/202
*/
SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(elem) {
return elem.getScreenCTM().inverse().multiply(this.getScreenCTM());
};