From c5efb1a8e8a33db4a9725c418a82570044518a96 Mon Sep 17 00:00:00 2001
From: Lucio Zambon <lucio.zambon@elettra.eu>
Date: Thu, 6 Feb 2025 03:42:03 +0000
Subject: [PATCH] Add new file

---
 gof2d.js | 408 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 408 insertions(+)
 create mode 100644 gof2d.js

diff --git a/gof2d.js b/gof2d.js
new file mode 100644
index 0000000..6a7e5f6
--- /dev/null
+++ b/gof2d.js
@@ -0,0 +1,408 @@
+// jshint esversion: 6
+	// sr/feedback/profile_s/CorrHorFast
+	// sr/feedback/profile_s->GetSrCorrNames
+	// sr/feedback/profile_s/OrbitHorFast
+	let gofFactorsHor, gofFactorsVer;
+	let gofSt, gofDataTs;
+	function getParam(param, defaultValue) {
+		if (document.location.search.indexOf(param+'=')>-1) return document.location.search.split(param+'=')[1].split('&')[0];
+		return defaultValue;
+	}
+	if (document.location.search.indexOf('machine=esrf')>-1 || document.location.search.indexOf('machine=gof_esrf')>-1) {
+		gofFactorsHor = {"": 1, "position": 100000, 'beta': 0.2, 'eta': 100, 'mu': 0.2, 'sigma': 20000000000};
+		gofFactorsVer = {"": 1, "position": 1, 'beta': 0.1, 'eta': 100, 'mu': 0.2, 'sigma': 50000000000};
+	}
+	else if (document.location.search.indexOf('machine=gof_soleil')>-1) {
+		gofFactorsHor = {"": 1, "position": 50000, 'beta': 0.2, 'eta': 50, 'mu': 0.2, 'sigma': 20000000000};
+		gofFactorsVer = {"": 1, "position": 10000, 'beta': 0.1, 'eta': 100, 'mu': 0.2, 'sigma': 50000000000};
+	}
+	else if (document.location.search.indexOf('machine=elettra2')>-1) {
+		gofFactorsHor = {"": 1, "position": 500000, 'beta': 0.2, 'eta': 50, 'mu': 0.2, 'sigma': 20000000000};
+		gofFactorsVer = {"": 1, "position": 500000, 'beta': 0.1, 'eta': 100, 'mu': 0.2, 'sigma': 50000000000};
+	}
+	else {
+		gofFactorsHor = {"": 1, "position": getParam('positionScaleH', 0.005), 'beta': 0.5, 'eta': 100, 'mu': 0.2, 'sigma': 5000000000000};
+		gofFactorsVer = {"": 1, "position": getParam('positionScaleV', 0.005), 'beta': 0.25, 'eta': 100, 'mu': 0.2, 'sigma': 5000000000000};
+	}
+	let gof = document.location.search.indexOf('gof=')>-1? document.location.search.split('gof=')[1].split('&')[0]: '';
+	const gofTimeout = document.location.search.indexOf('gofTimeout=')>-1? document.location.search.split('gofTimeout=')[1].split('&')[0]: false;
+	const gofPeriod = document.location.search.indexOf('gofPeriod=')>-1? document.location.search.split('gofPeriod=')[1].split('&')[0]: 200;
+	const gofDisplayerPeriod = document.location.search.indexOf('gofDisplayerPeriod=')>-1? document.location.search.split('gofDisplayerPeriod=')[1].split('&')[0]: 200;
+	const gofVisibility = gof==''? 'hidden': 'visible';
+	const gofScaleType = 'slider';
+	const gofSimfacilities = [];
+	let gofDataHor = [];
+	let gofDataVer = [];
+	let gofScaleHor;
+	let gofScaleVer;
+	let gofBuffer = false;
+	let gofLogScaleHor = false;
+	let gofLogScaleVer = false;
+	let gofFactorHor = document.location.search.indexOf('gofFactorHor=')>-1? document.location.search.split('gofFactorHor=')[1].split('&')[0]*1000: localStorage.getItem('reference'+gof+'hor')!=null? localStorage.getItem('reference'+gof+'hor'): gofFactorsHor[gof]*1000;
+	let gofFactorVer = document.location.search.indexOf('gofFactorVer=')>-1? document.location.search.split('gofFactorVer=')[1].split('&')[0]*1000: localStorage.getItem('reference'+gof+'ver')!=null? localStorage.getItem('reference'+gof+'ver'): gofFactorsVer[gof]*1000;
+	const gofScaleSteps = 10;
+	// let ruler = document.location.search.indexOf('ruler')>-1;
+	rulerInit();
+	main.addEventListener('mousemove', function(e) {getRulerPosition(main, e, 'sr');});
+	if (ruler) {
+		main.style.cursor = 'crosshair';
+	}
+	else {
+		$('#rulerg').hide();
+		$('#trajectoryWidth').hide();
+	}
+	function myreference(param) {
+		const dir = param.split('_')[0];
+		const action = param.split('_')[1];
+		if (action=='set') {
+			localStorage.setItem('reference'+gof+dir, gofBuffer[dir=='hor'? 0: 1]);
+			$("#sr_gofreference"+dir).attr('gofVisibility', "visible");
+		}
+		if (action=='clear') {
+			localStorage.removeItem('reference'+gof+dir);
+			localStorage.removeItem('scale'+gof+dir);
+			$("#sr_gofreference"+dir).attr('gofVisibility', "hidden");
+		}
+	}
+	function elapseHor(step, up) {
+		// console.log('elapseHor()',step,gofScaleHor.$fill.style.width, (gofScaleSteps-step)/gofScaleSteps*90);
+		if (up) {
+			if (step<gofScaleSteps) {const pc = Math.round((gofScaleSteps-step)/gofScaleSteps*90); gofScaleHor.$fill.style.width=pc+"%";setTimeout(elapseHor, 20, step+1, up); return;}
+			const buf = gofScaleHor._max; 
+			gofScaleHor.max(buf*10); 
+			gofScaleHor.min(buf/10); 
+			gofScaleHor.step(buf/100);
+		}
+		else {
+			if (step<gofScaleSteps) {const pc = Math.round(step/gofScaleSteps*10); gofScaleHor.$fill.style.width=pc+"%";setTimeout(elapseHor, 20, step+1, up); return;}
+			const buf = gofScaleHor._max/10; 
+			gofScaleHor.max(buf); 
+			gofScaleHor.min(buf/100);
+			gofScaleHor.step(buf/1000);
+		}
+		gofScaleHor.$fill.style.width="10%";
+		gofScaleHor.$fill.style.borderColor = '#2cc9ff';
+	}
+	function elapseVer(step, up) {
+		if (up) {
+			if (step<gofScaleSteps) {const pc = Math.round((gofScaleSteps-step)/gofScaleSteps*90); gofScaleVer.$fill.style.width=pc+"%";setTimeout(elapseVer, 20, step+1, up); return;}
+			const buf = gofScaleVer._max; 
+			gofScaleVer.max(buf*10); 
+			gofScaleVer.min(buf/10); 
+			gofScaleVer.step(buf/100);
+		}
+		else {
+			if (step<gofScaleSteps) {const pc = Math.round(step/gofScaleSteps*10); gofScaleVer.$fill.style.width=pc+"%";setTimeout(elapseVer, 20, step+1, up); return;}
+			const buf = gofScaleVer._max/10; 
+			gofScaleVer.max(buf); 
+			gofScaleVer.min(buf/100);
+			gofScaleVer.step(buf/1000);
+		}
+		gofScaleVer.$fill.style.width="10%";
+		gofScaleVer.$fill.style.borderColor = '#2cc9ff';
+	}
+	function finishHor() {
+		// console.log('finishHor()', gofScaleHor.$fill.style.width);
+		if (gofScaleHor.$fill.style.width=="100%" || gofScaleHor.$fill.style.width=="0%") {
+			// gofScaleHor.$fill.style.borderColor = 'yellow';
+			setTimeout(elapseHor, 200, 1, gofScaleHor.$fill.style.width=="100%");
+		}
+	}
+	function finishVer() {
+		if (gofScaleVer.$fill.style.width=="100%" || gofScaleVer.$fill.style.width=="0%") {
+			setTimeout(elapseVer, 200, 1, gofScaleVer.$fill.style.width=="100%");
+		}
+	}
+	function factorHor(v) {
+		// console.log('factorHor(v)', v, gofScaleHor.$fill.style.width);
+		gofScaleHor.$fill.style.borderColor = (gofScaleHor.$fill.style.width=="100%" || gofScaleHor.$fill.style.width=="0%")? 'yellow': '#2cc9ff';
+		gofFactorHor = v * 1000;
+		if (localStorage.getItem('reference'+gof+'hor')!=null) localStorage.setItem('scale'+gof+'hor', gofFactorHor);
+		rulerScale('');
+	}
+	function factorVer(v) {
+		console.log('factorVer(v)', v, gofScaleVer.$fill.style.width);
+		gofScaleVer.$fill.style.borderColor = (gofScaleVer.$fill.style.width=="100%" || gofScaleVer.$fill.style.width=="0%")? 'yellow': '#2cc9ff';
+		gofFactorVer = v * 1000;
+		if (localStorage.getItem('reference'+gof+'ver')!=null) localStorage.setItem('scale'+gof+'ver', gofFactorVer);
+		rulerScale('');
+	}
+	function logscale(dirNum) {
+		if (dirNum==0) gofLogScaleHor = $('#logHor').attr('checked')=='checked';
+		else gofLogScaleVer = $('#logVer').attr('checked')=='checked';
+	}
+	function gofMenu(lattice, facilities, params) {
+		console.log('gofMenu',lattice, facilities, params, gui);
+		$('#notice').hide();
+		if (['elettra2', 'esrf', 'soleil'].indexOf(machine)>-1) for (let i=0; i<gui.children.length; i++) {
+			if (['vlv', 'ps', 'bpm', 'blm'].indexOf(gui.children[i].property)>-1) gui.children[i].domElement.style.display = 'none';
+		}
+		// params.gof = false;
+		$('.gofhor').css('display', 'none');
+		$('.gofver').css('display', 'none');
+		params.gof = document.location.search.indexOf('gof')>-1 && document.location.search.indexOf('=gof')==-1? 'position': '';
+		if (document.location.search.indexOf('gof=')>-1) params.gof = document.location.search.split('gof=')[1].split('&')[0];
+		const options = ['', 'position', 'beta', 'eta', 'mu', 'sigma'];
+		if (options.indexOf(params.gof)==-1) params.gof = 'position';
+		gui.add(params, 'gof', options).name('gof H <img id="h" src="red.svg" style="margin-bottom: -4px;">&nbsp;&nbsp;&nbsp;V <img id="v" src="green.svg" style="margin-bottom: -4px;"> ').onChange(function() {gofSwitch(compData, params);});
+		const maxVer = Math.pow(10, Math.floor(Math.log10(gofFactorVer/1000))+2);
+		const maxHor = Math.pow(10, Math.floor(Math.log10(gofFactorHor/1000))+2);
+		if (gofScaleType == 'slider') {
+			/*menuParams['ruler'] = ruler;
+			gui.add(menuParams, 'ruler').onChange(function() {rulerSwitch();});
+			guiconf(gui, 'ruler');*/
+			menuParams['scale H'] = gofFactorHor/1000;
+			gofScaleHor = gui.add(menuParams, 'scale H', maxVer/100, maxVer, Math.round(maxVer/1000)).onChange(function() {factorHor(menuParams['scale H'], gui);}).onFinishChange(function() {finishHor(gui);});
+			guiconf(gui, 'scaleh');
+			$('#scaleh').children().eq(0).css('color','red');
+			menuParams['scale V'] = gofFactorHor/1000;
+			gofScaleVer = gui.add(menuParams, 'scale V', maxHor/100, maxHor, Math.round(maxHor/1000)).onChange(function() {factorVer(menuParams['scale V'], gui);}).onFinishChange(function() {finishVer(gui);});
+			guiconf(gui, 'scalev');
+			$('#scalev').children().eq(0).css('color','green');
+		}
+		else {
+			const controllerOption = $('.controller.option');
+			for (let i=0; i<controllerOption.length; i++) {
+				if (controllerOption.eq(i).children()[0].innerText.indexOf("gof H") > -1) {
+					$('<div class="gofgauge"><iframe id="vergauge" style="width: 100%;height:250px;" src="../misc/gauge.html?dark&r=115&ringwidth=30&max='+maxVer+'&throttlingPeriod=50&apply=factorVer&extbackground=green&exthighlight=darkgreen&intbackground=green&inthighlight=darkgreen&val='+gofFactorVer/1000+'"></iframe></div>').insertAfter(controllerOption.eq(i));
+					$('<div class="gofgauge"><iframe id="horgauge" style="width: 100%;height:250px;" src="../misc/gauge.html?dark&r=115&ringwidth=30&max='+maxHor+'&throttlingPeriod=50&apply=factorHor&extbackground=red&exthighlight=darkred&intbackground=red&inthighlight=darkred&val='+gofFactorHor/1000+'"></iframe></div>').insertAfter(controllerOption.eq(i));
+					$('<div style="padding: 8px;" class="gofgauge">log scale  H <img id="h" src="red.svg" style="margin-bottom: -2px;"> <input type="checkbox" id="logHor" onChange="logscale(0)">&nbsp;&nbsp;&nbsp;log V <img id="h" src="green.svg" style="margin-bottom: -2px;"> <input type="checkbox" id="logVer" onChange="logscale(1)"></div>').insertAfter(controllerOption.eq(i));
+					if (gof=='') $('.gofgauge').hide();
+				}
+			}
+		}
+		for (let f in facilities) {
+			const b = facilities[f];
+			if (b!='sr') continue;
+			console.log('gofMenu() - ', b, lattice[b], conf.gofSrcUrl);
+			compData[b].map = [];
+			console.log((conf.gofSrcUrl+'&machine='+machine).replace('?&','?'));
+			fetch((conf.gofSrcUrl+'&machine='+machine).replace('?&','?'), {cache: "no-store"})
+			.then((response) => {return response.json();})
+			.then((gofNaming) => {
+				console.log('gofNaming', gofNaming, compData[b].map);
+				for (let i in gofNaming) {
+					const name = gofNaming[i];
+					for (let bl in compData[b].obj) {
+						if (name==compData[b].obj[bl].replace('CHV', 'CORR') || name==compData[b].obj[bl].replace('CHV', 'CORR').replace('.','_')) {compData[b].map[i] = bl;}
+					}
+				}
+				console.log('gofMenu(), name', b, compData[b].map);
+				for (let fi in facilities) {
+					const facility = facilities[fi];
+					if (facility!='sr') continue;
+					console.log(fi, facility, compData);
+					if (facility.length>0 && typeof compData[facility]!='undefined' && compData[facility].obj && compData[facility].obj.length>0) {
+						console.log('gofInit', facility, compData[facility]);
+						const dhor = [];
+						const dver = [];
+						for (i=0; i<compData[facility].pos.length; i++) {
+							// if (i>threshold) break; 
+							const dir = compData[facility].dir[i]; 
+							const beta = Math.PI*dir/180;
+							if (threshold<1000) console.log('i', i, 'pos',compData[facility].pos[i], 'dir', dir, beta, Math.cos(beta), Math.sin(beta));
+							dhor.push((i==0?'M':'L')+ 
+								  Math.round(compData[facility].pos[i][0] - f*Math.sin(beta)) + ' ' + 
+								  Math.round(compData[facility].pos[i][1] + f*Math.cos(beta))
+							);
+							dver.push((i==0?'M':'L')+ 
+								  Math.round(compData[facility].pos[i][0] - f*1.05*Math.sin(beta)) + ' ' + 
+								  Math.round(compData[facility].pos[i][1] + f*1.05*Math.cos(beta))
+							);
+						}
+						appendSvg("path", {id:facility+"_gofhor", class: "gofhor trajectory "+facility, gofVisibility:gofVisibility, name:"gofhor", fill: "none", "stroke-width":"150", "stroke":"red", "stroke-opacity":"0.6", d: dhor.join(' ')+(lattice[facility].sections[0].chamber?' Z':'')}, gof_clicked_hor, false, false, '.svg-pan-zoom_viewport');
+						// appendSvg("path", {id:facility+"_gofreferencehor", class: "gofhor trajectory "+facility, gofVisibility:gofVisibility, name:"gofreferencehor", fill: "none", "stroke-width":"150", "stroke":"darkred", "stroke-opacity":"0.9", d: dhor.join(' ')+(lattice[facility].sections[0].chamber?' Z':'')}, gof_clicked_hor, false, false, '.svg-pan-zoom_viewport');
+						appendSvg("path", {id:facility+"_gofver", class: "gofver trajectory "+facility, gofVisibility:gofVisibility, name:"gofver", fill: "none", "stroke-width":"150", "stroke":"limegreen", "stroke-opacity":"0.6", d: dver.join(' ')+(lattice[facility].sections[0].chamber?' Z':'')}, gof_clicked_ver, false, false, '.svg-pan-zoom_viewport');
+						// appendSvg("path", {id:facility+"_gofreferencever", class: "gofver trajectory "+facility, gofVisibility:gofVisibility, name:"gofreferencever", fill: "none", "stroke-width":"150", "stroke":"darkgreen", "stroke-opacity":"0.9", d: dver.join(' ')+(lattice[facility].sections[0].chamber?' Z':'')}, gof_clicked_ver, false, false, '.svg-pan-zoom_viewport');
+						// console.log("path", {id:facility+"_gofhor", name:"gofhor", d: dver.join(' ')+' Z'});
+					}
+				}
+				rulerScale('');
+			});
+		}
+		if (document.location.search.indexOf('gof')>-1) {/*params.gof=true;*/ setTimeout(gofSwitch, 500, compData, params);}
+	}
+	// const threshold = document.location.search.indexOf('threshold=')>-1? document.location.search.split('threshold=')[1].split('&')[0]: 1000;
+	// const f = document.location.search.indexOf('factor=')>-1? document.location.search.split('factor=')[1].split('&')[0]: 3000;
+	function gof_clicked_hor(event) {
+		// console.log('gof hor', event);
+		openTooltip('trajectory hor '+event.clientX+' '+event.clientY);
+	}
+	function gof_clicked_ver(lattice, params) {
+		// console.log('gof ver');
+		openTooltip('trajectory ver'+event.clientX+' '+event.clientY);
+	}
+	function gof2d(lattice, params) {
+		console.log('gof2d',lattice, params);
+		for (let i in lattice) {if (i!='conf') gofSimfacilities.push(i);}
+		gofMenu(lattice, gofSimfacilities, params);
+	}
+	function gofSwitch(gofData, params) {
+		gof = params.gof;
+		// $("#sr_gofreferencehor").attr('gofVisibility', "hidden");
+		// $("#sr_gofreferencever").attr('gofVisibility', "hidden");
+		gofScaleHor.$fill.style.width = "50%";
+		gofScaleVer.$fill.style.width = "50%";
+		if (gofData.reader !== false) {
+			clearInterval(gofData.reader);
+		}
+		gofData.reader = false;
+		if (params.gof=='position') {
+			if (machine=='elettra') {
+				if (document.location.host!="mac3d.elettra.eu") { 
+					$('#applicationFrame').attr("src", 'https://puma-01.elettra.eu/spa/index.html?s=miniSynoptic');
+					$('#applicationFrame').css("height", '1000px');
+				}
+			}
+			else {
+				$('#applicationFrame').attr("src", $('#applicationFrame').attr("data-src"));
+			} 
+		}
+		else {
+			$('#applicationFrame').removeAttr("src");
+		}
+		if (params.gof=='') {
+			for (let facility in gofData) {
+				if (typeof $('#'+facility+"_gofhor").attr('gofVisibility') != 'undefined') {
+					$('#'+facility+"_gofhor").attr('gofVisibility', "hidden");
+					$('#'+facility+"_gofver").attr('gofVisibility', "hidden");
+					// $('#'+facility+"_gofreferencehor").attr('gofVisibility', "hidden");
+					// $('#'+facility+"_gofreferencever").attr('gofVisibility', "hidden");
+				}
+			}
+			$('.gofgauge').hide();
+			$('#scaleh').hide();
+			$('#scalev').hide();
+			$('#ruler').hide();
+			$('#rulerg').hide();
+			$('#notice').hide();
+			$('#trajectoryWidth').hide();
+			$('#application').hide();
+			$('#applicationReference').hide();
+			gofData.reader.clearInterval(); 
+		} 
+		else {
+			$('#application').show();
+			$('#applicationReference').show();
+			$('#scaleh').show();
+			$('#scalev').show();
+			$('#trajectoryWidth').show();
+			$('#ruler').show();
+			$('#notice').show();
+			for (let facility in gofData) {
+				if (typeof $('#'+facility+"_gofhor").attr('gofVisibility') != 'undefined') {
+					$('#'+facility+"_gofhor").attr('gofVisibility', "visible");
+					$('#'+facility+"_gofver").attr('gofVisibility', "visible");
+				}
+				// console.log('#'+facility+"_gofhor", $('#'+facility+"_gofhor").attr('gofVisibility'), typeof gofData[facility]);
+				if (typeof gofData[facility] == 'undefined' || typeof gofData[facility].map == 'undefined') continue;
+				gofData.oldIndex = facility;
+				gofRead(gofData, params);
+			}
+			/* */
+			displayer = setInterval(gofDisplay, gofDisplayerPeriod, params);
+			if (gofTimeout) {
+				setTimeout(gofRead, gofTimeout, gofData, params);
+			}
+			else {
+				gofData.reader = setInterval(gofRead, gofPeriod, gofData, params);
+			}
+			/* */
+			$('.gofhor').css('display', 'block');
+			$('.gofver').css('display', 'block');
+			gofFactorHor = localStorage.getItem('scale'+gof+'hor')!=null? localStorage.getItem('scale'+gof+'hor')-0: gofFactorsHor[menuParams.gof]*1000;
+			gofFactorVer = localStorage.getItem('scale'+gof+'ver')!=null? localStorage.getItem('scale'+gof+'ver')-0: gofFactorsVer[menuParams.gof]*1000;
+			if (gofScaleType == 'slider') {
+				const fHor = gofFactorHor/1000;
+				menuParams['scale H'] = fHor;
+				const mxHor = Math.pow(10, Math.floor(Math.log10(fHor))+1);
+				gofScaleHor.max(mxHor);
+				gofScaleHor.min(mxHor/100);
+				gofScaleHor.step(mxHor/1000);
+				gofScaleHor.setValue(fHor);
+				const fVer = gofFactorVer/1000;
+				menuParams['scale V'] = fVer;
+				const mxVer = Math.pow(10, Math.floor(Math.log10(fVer))+1);
+				gofScaleVer.max(mxVer);
+				gofScaleVer.min(mxVer/100);
+				gofScaleVer.step(mxVer/1000);
+				gofScaleVer.setValue(fVer);
+			}
+			else {
+				document.getElementById("horgauge").contentWindow.maxval = Math.pow(10, Math.floor(Math.log10(gofFactorsHor[params.gof]))+2);
+				document.getElementById("horgauge").contentWindow.reset();
+				document.getElementById("horgauge").contentWindow.setVal(gofFactorsHor[params.gof]);
+				document.getElementById("vergauge").contentWindow.maxval = Math.pow(10, Math.floor(Math.log10(gofFactorsVer[params.gof]))+2);
+				document.getElementById("vergauge").contentWindow.reset();
+				document.getElementById("vergauge").contentWindow.setVal(gofFactorsVer[params.gof]);
+				$('.gofgauge').show();
+			}
+		}		
+	}
+	function gofRender(facility, gb, gof) {
+		// console.log('gofRender(), val', val, gof, compData[facility]);
+		gofDataHor = [];
+		gofDataVer = [];
+		let refHor, refVer;
+		const referencehor = JSON.parse(localStorage.getItem('reference'+gof+'hor'));
+		const referencever = JSON.parse(localStorage.getItem('reference'+gof+'ver'));
+		$('#hor_set').css('background-color',localStorage.getItem('reference'+gof+'hor')!=null?'red':'');
+		$('#ver_set').css('background-color',localStorage.getItem('reference'+gof+'ver')!=null?'green':'');
+		if (gb[0]) {
+			const valh = gb[0].split(',');
+			const valv = gb[1].split(',');
+			for (i=0; i<valh.length; i++) {
+				if (typeof compData[facility].map[i] == 'undefined') continue;
+				const j = compData[facility].map[i];
+				if (document.location.search.indexOf('pulse=')>-1 && i!=document.location.search.split('pulse=')[1].split('&')[0]) {valh[i] = 0;}
+				const pos = compData[facility].pos[j];
+				if (pos==null) continue;
+				const beta = Math.PI*compData[facility].dir[j]/180;
+				const vh = localStorage.getItem('reference'+gof+'hor')==null? valh[i]-0: valh[i] - referencehor[i];
+				// https://en.wikipedia.org/wiki/Logarithmic_scale#Extensions
+				const valHor = gofLogScaleHor? Math.sign(vh)*Math.log10(1+ Math.abs(vh*Math.LN10)): vh;
+				const vv = localStorage.getItem('reference'+gof+'ver')==null? valv[i]-0: valv[i] - referencever[i];
+				const valVer = gofLogScaleVer? Math.sign(vv)*Math.log10(1+ Math.abs(vv*Math.LN10)): vv;
+				gofDataHor.push((gofDataHor.length==0?'M':'L')+ 
+					Math.round(pos[0] - valHor*gofFactorHor*Math.sin(beta)) + ' ' + 
+					Math.round(pos[1] + valHor*gofFactorHor*Math.cos(beta))
+				);
+				gofDataVer.push((gofDataVer.length==0?'M':'L')+ 
+					Math.round(pos[0] - valVer*gofFactorVer*Math.sin(beta)) + ' ' + 
+					Math.round(pos[1] + valVer*gofFactorVer*Math.cos(beta))
+				);
+			}
+			// console.log('Hor', gofDataHor.join(' ')+(lattice[facility].sections[0].chamber?' Z':''));
+			$('#'+facility+"_gofhor").attr('d', gofDataHor.join(' ')+(lattice[facility].sections[0].chamber?' Z':''));
+			$('#'+facility+"_gofver").attr('d', gofDataVer.join(' ')+(lattice[facility].sections[0].chamber?' Z':''));
+		}
+	}
+	function gofDisplay(params) {
+		if (gofBuffer==false) return;
+		gofRender('sr', gofBuffer, params.gof);
+		$('#delay').html((new Date() - gofDataTs));
+	}
+	if (document.location.search.indexOf('nostats')==-1) {
+		$('#notice').show(); 
+		$('#notice').css('color', 'orange');												  
+		$('#notice').html('<table style="font-size: 60%"><tr><td colspan="4" style="font-size: 150%" id="lt"></td></tr><tr><td style="width: 50%;">last refresh</td><td style="width: 50%; text-align: right;" id="requiredt"></td></tr><tr><td style="width: 50%;">delay</td><td style="width: 50%; text-align: right;" id="delay"></td></tr></table>');
+	}
+	function gofRead(gofData, params) {
+		console.log('fetch()',conf.gofUrl+'&param='+params.gof);
+		gofSt = new Date();
+		$('#lt').html(gofSt.getFullYear()+'-'+pad(gofSt.getMonth()+1, 2)+'-'+pad(gofSt.getDate(), 2)+' '+pad(gofSt.getHours(), 2)+':'+pad(gofSt.getMinutes(), 2)+':'+pad(gofSt.getSeconds(), 2));
+		fetch((conf.gofUrl+'&machine='+machine+'&param='+params.gof).replace('?&','?'), {cache: "no-store"})
+		.then((response) => {return response.text();})
+		.then((eventData) => {
+			if (document.location.search.indexOf('nostats')==-1) {
+				console.log('fetch(eventData)',eventData);
+				const lt = new Date();
+				const requiredt = new Date() - gofSt;
+				gofDataTs = eventData.split(':')[0];
+				$('#requiredt').html(requiredt);
+				$('#delay').html(lt - gofDataTs);
+			}
+			gofBuffer = eventData.split(':')[1].split(';');
+			if (gofTimeout && params.gof!='') setTimeout(gofRead, gofTimeout, gofData, params);
+		});
+	}
-- 
GitLab