make web client work

This commit is contained in:
2020-12-10 16:58:59 +01:00
parent 9e1d3b4e07
commit 1de819cd26
7 changed files with 380 additions and 209 deletions

View File

@ -86,7 +86,7 @@ function handleUpdateMessage(src, message) {
sizeChange();
} else {
clientTitle = message.instrument + " " + message.device;
console.log('loadBlocks')
console.log('loadBlocks', message);
loadFirstBlocks();
}
document.title = "SEA "+clientTitle;
@ -155,7 +155,6 @@ function handleUpdateMessage(src, message) {
case "graph-update":
//if (getUpdatesGraphics) {
//timeServer = message.time;
console.log("graph-update");
updateCharts2(message.graph);
//}
break;
@ -207,11 +206,12 @@ function updateValues(message, src) {
} else if (type == "input") {
var row = matches[j].parentNode.parentNode.parentNode;
row.style.backgroundColor = "white";
var oldValue = matches[j].getAttribute("oldValue")
|| matches[j].value;
if (value != matches[j].value && value != oldValue) {
var mval = matches[j].value;
var oldValue = matches[j].getAttribute("oldValue");
if (oldValue === null) oldValue = mval;
if (value != mval && parseFloat(value) != parseFloat(mval) && value != oldValue) {
if (matches[j] == document.activeElement
|| oldValue != matches[j].value) {
|| oldValue != mval) {
row.style.backgroundColor = "orange";
} else {
matches[j].value = value;

View File

@ -106,51 +106,20 @@ function doubleTap(callback){
return {stop: function(){ window.removeEventListener('touchend', handler) }}
}
function maxAr(array){
function maxAr(array, tmin, tmax){
return Math.max.apply(Math, array.map(function(o) {
if (o.y == null) return -1e99;
if (o.y == null || o.x < tmin || o.x > tmax) return -1e99;
return o.y;
}));
}
function minAr(array){
function minAr(array, tmin, tmax){
return Math.min.apply(Math, array.map(function(o) {
if (o.y == null) return 1e99;
if (o.y == null || o.x < tmin || o.x > tmax) return 1e99;
return o.y;
}));
}
function autoScale(chart) {
axis = chart.options.scales.yAxes[0]
datasets = chart.data.datasets;
let max = -1e99;
let min = 1e99;
for (testwidth = 1; testwidth >= 0; testwidth--) {
for(let i = 0; i < datasets.length; i++){
ds = datasets[i];
if (ds.borderWidth == testwidth) continue;
let lmax = maxAr(ds.data);
let lmin = minAr(ds.data);
if(lmax > max)
max = lmax;
if(lmin < min)
min = lmin;
}
if (min < max) {
break;
} else if (min == max) {
max = min + 0.5;
min = max - 1.0;
break;
}
}
//console.log('autoScale', min, max);
axis.ticks.min = min - (max - min) * 0.05;
axis.ticks.max = max + (max - min) * 0.01;
axis.min = axis.ticks.min;
axis.max = axis.ticks.max;
}
function strFormat(str, significant_digits) {
if (str == null) return '';
evalue = str.toExponential(significant_digits-1).replace(/0*e/, 'e').replace(/\.e/, 'e').replace("e+", "e");
@ -163,7 +132,7 @@ function strFormat(str, significant_digits) {
let graphs = (function (){
let dataset_to_graph_map = {},
blocks, doUpdates=true, top_vars=[], bottom_vars=[], zoomed =false;
blocks, liveMode=true, top_vars=[], bottom_vars=[], zoomed =false;
let type = 'linear';
@ -175,6 +144,8 @@ let graphs = (function (){
let container = document.createElement('div');
container.classList.add("graphs-container");
let currentMinTime = 0;
let currentMaxTime = 0;
for (let i = 0; i < ngraphs; i++) {
let gr = document.createElement('div');
@ -242,7 +213,7 @@ let graphs = (function (){
}
//let varlist = top_vars.concat(bottom_vars);
varlist = vars_array[gindex];
let el =graph_array[gindex];
let el = graph_array[gindex];
AJAX("http://" + hostPort + "/graph?time=" + minTime/1000 + "," + maxTime/1000 + "&variables=" + varlist + "&id=" + clientID).getJSON().then(function(data){
//console.log('Graph', block, data)
@ -255,13 +226,10 @@ let graphs = (function (){
}
let pdata = [];
for(let e of data.graph[key]){
//if(e[1] == null || e[1] == null){
// continue;
//}
pdata.push({x: e[0]*1000, y: e[1]});
}
if(pdata.length > 0){
addDataset(gindex, key, [dict[key].label, dict[key].color, pdata])
addDataset(gindex, key, [dict[key].label, dict[key].color, pdata, dict[key].continuous])
/*console.log(timeRange);
if(data[data.length-1].x-data[0].x > d && data[data.length-1].x-data[0].x < (30*60+10)*1000){ // Adjust to requested time
d = data[data.length-1].x-data[0].x
@ -271,20 +239,91 @@ let graphs = (function (){
}
}
chart.setMinMax(minTime,maxTime);
// chart.autoScaleChart();
chart.autoScaleIf();
chart.update();
AJAX( "http://" + hostPort + "/updategraph?id=" + clientID).getJSON(); // why this?
result = AJAX( "http://" + hostPort +
"/updategraph?variables=" + variables() +
"&id=" + clientID).getJSON().then(function(data) {
liveMode = data.live;
console.log('LIVE create', liveMode)
})
//console.log('UPDATE LIVE', result);
})
}
// add dataset to graph with graph_id
function addDataset(gindex, key, dataset){
let g = chart_array[gindex];
dataset_to_graph_map[key] = [gindex, g.addDataset(dataset)];
dataset_to_graph_map[key] = [gindex, g.addDataset(key, dataset)];
}
function autoScale(chart) {
axis = chart.options.scales.yAxes[0];
tax = chart.options.scales.xAxes[0].ticks;
datasets = chart.data.datasets;
let max = -1e99;
let min = 1e99;
// if there are datasets with values and think lines,
// consider them only. if not, consider all (second pass in the following loop)
let extraMin = min;
let extraMax = max;
for (testwidth = 1; testwidth >= 0; testwidth--) {
for (let i = 0; i < datasets.length; i++){
ds = datasets[i];
if (ds.borderWidth <= testwidth) continue;
let lmax = maxAr(ds.data, tax.min, tax.max);
let lmin = minAr(ds.data, tax.min, tax.max);
if(lmax > max)
max = lmax;
if(lmin < min)
min = lmin;
if (ds.data.length && liveMode) {
lasty = ds.data.slice(-1)[0].y;
console.log('LASTY', lasty);
extraMin = Math.min(extraMin, lasty);
extraMax = Math.max(extraMax, lasty);
}
}
if (min > max) continue; // do a second pass over all curves
break;
}
if (min > max) return;
if (min == max) {
if (min == 0) {
ystep = 1;
} else {
ystep = Math.abs(min * 0.01);
}
min -= ystep;
max += ystep;
} else {
ystep = (max - min) * 0.1;
if (liveMode) {
extraMin = Math.min(min, extraMin - ystep);
extraMax = Math.max(max, extraMax + ystep);
} else {
extraMin = min - ystep * 0.5;
extraMax = max + ystep * 0.5;
}
if (min >= axis.ticks.min && axis.ticks.min >= extraMin &&
max <= axis.ticks.max && axis.ticks.max <= extraMax) {
console.log('NOCHANGE')
return; // do not yet change
}
console.log(min, axis.ticks.min, extraMin)
console.log(max, axis.ticks.max, extraMax)
min = extraMin;
max = extraMax;
}
//console.log('autoScale', min, max, tax.min, tax.max);
axis.min = axis.ticks.min = min;
axis.max = axis.ticks.max = max;
}
function setMinMax(min, max){
currentMaxTime = max;
currentMinTime = min;
for (let ch of chart_array) {
if (ch) ch.setMinMax(min, max);
}
@ -326,6 +365,16 @@ let graphs = (function (){
chart_array[i[0]].reloadData(i[1], data);
}
function variables() {
let vardict = {};
for (let vars of vars_array) {
for (let v of vars) {
vardict[v] = 1;
}
}
return Object.keys(vardict);
}
function reloadData(min, max){
min = min/1000;
@ -334,14 +383,8 @@ let graphs = (function (){
}else{
max = max/1000;
}
let vardict = {};
for (let vars of vars_array) {
for (let v of vars) {
vardict[v] = 1;
}
}
AJAX("http://" + hostPort + "/graph?time=" + min + ","+max+"&variables=" + Object.keys(vardict) + "&id=" + clientID).getJSON().then(function(data){
AJAX("http://" + hostPort + "/graph?time=" + min + ","+max+"&variables=" + variables() + "&id=" + clientID).getJSON().then(function(data){
for(let key in data.graph){
let pdata = [];
for(let e of data.graph[key]){
@ -354,10 +397,13 @@ let graphs = (function (){
reloadDataFlag(key, pdata);
}
}
for (let ch of chart_array) {
if (ch) ch.autoScaleChart(); // should depend on a flag
}
AJAX( "http://" + hostPort + "/updategraph?id=" + clientID).getJSON(); // why this ?
// AJAX( "http://" + hostPort + "/updategraph?id=" + clientID).getJSON(); // activate updates
result = AJAX("http://" + hostPort +
"/updategraph?variables=" + variables() +
"&id=" + clientID).getJSON().then(function(data) {
liveMode = data.live;
console.log('LIVE reload', liveMode)
})
update();
});
}
@ -365,19 +411,21 @@ let graphs = (function (){
function checkReload(chart){
let xmin = chart.options.scales.xAxes[0].ticks.min,
xmax = chart.options.scales.xAxes[0].ticks.max;
if(xmax < now()-10000){ // was 100000 = 100sec
doUpdates = false;
if (xmax < now()-100000) { // was 100000 = 100sec
if (liveMode) console.log('UPDATES OFF?')
//doUpdates = false;
}else{
doUpdates = true;
if (!liveMode) console.log('UPDATES ON?')
//doUpdates = true;
}
if(xmin < minTime || xmax > maxTime || xmax - xmin < 0.5 * (maxTime - minTime)){
if (xmin < minTime || xmax > maxTime || xmax - xmin < 0.5 * (maxTime - minTime)) {
//TODO: the criterium for getting finer resolution data should depend, if better res. is available
// this information has to come from the server
reloadData(xmin, xmax);
minTime = xmin;
maxTime = xmax;
} else {
autoScale(chart); // TODO: must depend on autoScale flag
if (chart.autoScaleFlag) autoScale(chart);
chart.update();
}
}
@ -387,7 +435,9 @@ let graphs = (function (){
xmax = chart.options.scales.xAxes[0].ticks.max;
setMinMax(xmin,xmax);
//console.log('zoompan', autoScaleFlag);
for (let ch of chart_array) {
ch.autoScaleIf();
if (ch) ch.redraw(redrawX);
}
update();
@ -399,15 +449,27 @@ let graphs = (function (){
}
}
let g_varlist = [];
function updateAllDisabled(){
// not used anymore?
AJAX("http://" + hostPort + "/graph?time="+timeRange+"&variables=" + g_varlist + "&id=" + clientID).getJSON().then(function(a){
AJAX( "http://" + hostPort + "/updategraph?id=" + clientID).getJSON()
});
function updateAuto(){
if (liveMode) {
max = now();
if (currentMaxTime && max > currentMaxTime) {
max = currentMaxTime + Math.min(60000, 0.1 * (currentMaxTime - currentMinTime));
setMinMax(currentMinTime, max);
reloadData(currentMinTime, max);
return;
}
}
for (let ch of chart_array) {
if (ch) {
ch.autoScaleIf();
ch.update();
}
}
}
/*
let g_varlist = [];
function getVarlist(blocks){
var varlist = [];
for (var i = 0; i < blocks.length; i++) {
@ -417,6 +479,7 @@ let graphs = (function (){
}
return varlist;
}
*/
let startTime, recvTime, minTime, maxTime;
@ -425,13 +488,14 @@ let graphs = (function (){
minTime = timeRange[0]*1000;
AJAX("http://" + hostPort + "/gettime?time=-1800,0&id="+ clientID).getJSON().then(function(data){
startTime = data.time[1]*1000;
maxTime = startTime;
maxTime = startTime + 60000;
console.log('MAXTIME', maxTime - Date.now());
minTime = data.time[0]*1000;
recvTime = performance.now();
});
g_varlist = getVarlist(nblocks)
// g_varlist = getVarlist(nblocks)
let f = 0;
insertSlide(f, "graphics", "graphics", container);
blocks = nblocks;
@ -514,13 +578,15 @@ let graphs = (function (){
zoompan: zoompan,
receivedVars: receivedVars,
createSelection: createSelection,
doUpdates: function(){return doUpdates},
doUpdates: function(){return liveMode},
update: update,
updateAuto: updateAuto,
now: now,
zoomed: zoomed,
checkReload: checkReload,
getBlocks: getBlocks,
createGraph: createGraph,
autoScale: autoScale,
}
})();
@ -567,7 +633,7 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
y1 = ticks[0];
y0 = ticks.slice(-1)[0];
span = y1 - y0;
step = Math.abs(span * 0.1).toExponential(0);
step = Math.abs(span * 0.3).toExponential(0);
if (step[0] > '5') {
step = '5' + step.substr(1);
} else if (step[0] > '2') {
@ -651,6 +717,7 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
}
}
});
let autoScaleFlag = true;
//console.log('create legend')
let legend = document.createElement('div');
@ -724,9 +791,8 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
graphs.zoompan(chart);
});*/
addControl("Autoscale Y", function(){
autoScale(chart);
update();
let autoScaleRow = addControl("Autoscale Y <strong>on</strong> off", function(){
toggleAutoScale();
});
addControl("Go to now", function(){
@ -847,60 +913,91 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
chart.update();
}
function addDataset(data){
function addDataset(key, data){
let dataset_index = chart.data.datasets.length;
chart.data.datasets.push({data: data[2], label: data[0], origLabel: data[0],
spanGaps: false, lineJoin: 'round', borderWidth: 2,
chart.data.datasets.push({data: data[2], label: data[0], key: key,
spanGaps: false, lineJoin: 'round', borderWidth: 2, steppedLine: data[3] == 0,
borderColor: data[1],fill: false, pointRadius: 0, tension:0, showLine: true});
let dataset = chart.data.datasets[dataset_index];
let legendel = document.createElement('div');
let legendelvalue = document.createElement('div');
legendelvalue.classList.add('value');
legendels[data[0]] = legendelvalue;
legendels[key] = legendelvalue;
legendel.classList.add('legendel')
let color = document.createElement('div');
color.classList.add('color')
color.style.backgroundColor = dataset.borderColor;
legendel.appendChild(color);
legendel.innerHTML += dataset.label;
legendel.addEventListener('click', function(){
let dlabel = document.createElement('div');
dlabel.innerHTML = dataset.label;
dlabel.addEventListener('click', function(evt){
/*
console.log('LABEL', evt.target)
if(legendmoving)
return
let meta = dataset._meta[Object.keys(dataset._meta)[0]]
/*
if(meta.hidden == null){
meta.hidden = true;
addClass(legendel, 'hidden');
}else{
meta.hidden = null;
delClass(legendel, 'hidden');
}
legendel.firstChild.style.height = '2px';
dataset.borderWidth = 2;
labelClicked = true;
//console.log('LABEL', evt.target)
*/
if (dataset.borderWidth == 1) {
dataset.borderWidth = 2;
} else {
dataset.borderWidth = 1;
}
autoScale(chart);
chart.update();
})
});
legendel.appendChild(dlabel);
legendel.appendChild(legendelvalue);
legendel.addEventListener('click', function(evt){
if (legendmoving) return;
for (let k in legendels) {
// set all labels to normal font
legendels[k].parentNode.children[1].style.fontWeight = 400;
}
if (evt.target == dlabel) {
// disable all
for (let k in legendels) {
legendels[k].parentNode.firstChild.style.height = '1px';
}
for (ds of chart.data.datasets) {
ds.borderWidth = 1;
}
color.style.height = '2px';
dataset.borderWidth = 2;
dlabel.style.fontWeight = 700; // bold
} else {
if (dataset.borderWidth == 1) {
legendel.firstChild.style.height = '2px';
dataset.borderWidth = 2;
} else {
legendel.firstChild.style.height = '1px';
dataset.borderWidth = 1;
}
}
graphs.autoScale(chart);
chart.update();
});
legend.appendChild(legendel);
return dataset_index;
}
function autoScaleChart() {
autoScale(chart);
function autoScaleIf() {
if (autoScaleFlag) graphs.autoScale(chart);
}
function pushData(dataset_index, data_point){
chart.data.datasets[dataset_index].data.push(data_point);
if(!graphs.zoomed){
chart.options.scales.xAxes[0].ticks.max = data_point.x;
}else{
update_max = data_point.x;
data = chart.data.datasets[dataset_index].data;
//if (chart.data.datasets[dataset_index].key == 'tt:target')
// console.log('BEFORE', data.slice(-3))
if (data.slice(-1)[0] && data.slice(-1)[0].x >= data_point.x) {
removed = data.pop();
}
data.push(data_point);
//if (chart.data.datasets[dataset_index].key == 'tt:target')
// console.log('PUSHED', data.slice(-3))
/*
if (graphs.zoomed) {
update_max = data_point.x;
} else {
chart.options.scales.xAxes[0].ticks.max = data_point.x;
}
*/
}
function reloadData(index, data){
@ -959,6 +1056,17 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
ax.ticks.min = min;
}
function toggleAutoScale () {
autoScaleFlag = !autoScaleFlag;
if (autoScaleFlag) {
graphs.autoScale(chart);
update();
autoScaleRow.innerHTML = "Autoscale <strong>on</strong> off";
} else {
autoScaleRow.innerHTML = "Autoscale on <strong>off</strong>";
}
}
function toggleAxesType(){
setAxesType((chart.options.scales.yAxes[0].type=== 'linear') ? 'logarithmic' : 'linear');
}
@ -972,6 +1080,7 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
}
chart.options.scales.yAxes[0].type = type;
chart.options.animation.duration = 800;
if (autoScaleFlag) graphs.autoScale(chart);
update();
setTimeout(function(){chart.options.animation.duration = 0;},850)
}
@ -996,7 +1105,7 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
let d2 = Math.abs(dp._model.x - x)
if(d == 0 || d2 < d){
d = d2;
legendels[chart.data.datasets[i].label].innerHTML =
legendels[chart.data.datasets[i].key].innerHTML =
strFormat(chart.data.datasets[i].data[dp._index].y, 6);
test[i]=dp
}
@ -1045,7 +1154,8 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
redraw: redraw,
update: update,
reloadData: reloadData,
autoScaleChart: autoScaleChart,
autoScaleIf: autoScaleIf,
chart: chart,
}
}
@ -1096,13 +1206,17 @@ function updateCharts2(graph){
return;
}
for(let key in graph){
for (pt of graph[key]) {
if (graph[key][1] != null) {
// there is at least ONE valid datapoint
graphs.newDataHandler(key, {x: graph[key][0][0]*1000, y: graph[key][0][1]});
break;
if (graph[key][0] != null) {
// there is at least ONE valid datapoint
for (pt of graph[key]) {
graphs.newDataHandler(key, {x: pt[0]*1000, y: pt[1]});
}
}
}
graphs.update();
graphs.updateAuto();
// graphs.update();
}
function createCharts2(arg) {
console.log('C2', arg)
}

View File

@ -250,6 +250,10 @@ function createInput(s, name, title, info) {
input.setAttribute("name", name);
input.setAttribute("__ctype__", "input");
input.style.width = "100px";
input.addEventListener("focus", function(evt) {
let elm = evt.target;
setTimeout(function(){elm.setSelectionRange(0, elm.value.length);},0);
});
input.onkeydown = function (e) {
if (e.which === 27 || e.key == "Escape") {
@ -281,7 +285,8 @@ function createInput(s, name, title, info) {
input.setAttribute("actualValue", oldValue);
}
var actualValue = input.getAttribute("actualValue");
if (value === actualValue || value === oldValue) {
if (value == actualValue || value == oldValue ||
parseFloat(value) == parseFloat(actualValue) || parseFloat(value) == parseFloat(oldValue)) {
input.value = actualValue;
// nothing to do.
row.style.backgroundColor = "white";

View File

@ -166,18 +166,25 @@ All other connections are to be closed (getJSON):
*********************************************************************************************************************************
/updategraph?id=<id>
/updategraph?id=<id>[&variables=<variable list>]
if <variables list> is given, the given variables will be updated with the graph-update-message
response: <accept-graph-message>
<accept-graph-message> = = {
"type": "accept-graph"
"type": "accept-graph",
"live": true/false
}
the "live" flag is true when the last /graph query was including the actual time
*********************************************************************************************************************************
/graph?time=<start>,<end>&variables=<variable list>&id=<id>
the given variables are added to the list of variables being updated with the graph-update-message
response: <graph-draw-message>
<graph-draw-message> = = {

View File

@ -18,51 +18,67 @@ def get_abs_time(*times):
class ColorMap(object):
'''
""""
ColorMap is using official CSS color names, with the exception of Green, as this
is defined differently with X11 colors than in SEA, and used heavily in config files.
Here Green is an alias to Lime (#00FF00) and MidGreen is #008000, which is called Green in CSS.
The function to_code is case insensitive and accepts also names with underscores.
The order is choosen by M. Zolliker for the SEA client, originally only the first 16 were used.
'''
hex_name = (("#FFFFFF","White"), ("#FF0000","Red"), ("#00FF00","Lime"), ("#0000FF","Blue"), ("#FF00FF","Magenta"),
("#FFFF00","Yellow"), ("#00FFFF","Cyan"), ("#000000","Black"), ("#FFA500","Orange"), ("#006400","DarkGreen"),
("#9400D3","DarkViolet"), ("#A52A2A","Brown"), ("#87CEEB","SkyBlue"), ("#808080","Gray"), ("#FF69B4","HotPink"),
("#FFFFE0","LightYellow"), ("#00FF7F","SpringGreen"), ("#000080","Navy"), ("#1E90FF","DodgerBlue"),
("#9ACD32","YellowGreen"), ("#008B8B","DarkCyan"), ("#808000","Olive"), ("#DEB887","BurlyWood"),
("#7B68EE","MediumSlateBlue"), ("#483D8B","DarkSlateBlue"), ("#98FB98","PaleGreen"), ("#FF1493","DeepPink"),
("#FF6347","Tomato"), ("#32CD32","LimeGreen"), ("#DDA0DD","Plum"), ("#7FFF00","Chartreuse"), ("#800080","Purple"),
("#00CED1","DarkTurquoise"), ("#8FBC8F","DarkSeaGreen"), ("#4682B4","SteelBlue"), ("#800000","Maroon"),
("#3CB371","MediumSeaGreen"), ("#FF4500","OrangeRed"), ("#BA55D3","MediumOrchid"), ("#2F4F4F","DarkSlateGray"),
("#CD853F","Peru"), ("#228B22","ForestGreen"), ("#48D1CC","MediumTurquoise"), ("#DC143C","Crimson"),
("#D3D3D3","LightGray"), ("#ADFF2F","GreenYellow"), ("#7FFFD4","Aquamarine"), ("#BC8F8F","RosyBrown"),
("#20B2AA","LightSeaGreen"), ("#C71585","MediumVioletRed"), ("#F0E68C","Khaki"), ("#6495ED","CornflowerBlue"),
("#556B2F","DarkOliveGreen"), ("#CD5C5C","IndianRed "), ("#2E8B57","SeaGreen"), ("#F08080","LightCoral"),
("#8A2BE2","BlueViolet"), ("#AFEEEE","PaleTurquoise"), ("#4169E1","RoyalBlue"), ("#0000CD","MediumBlue"),
("#B8860B","DarkGoldenRod"), ("#00BFFF","DeepSkyBlue"), ("#FFC0CB","Pink"), ("#4B0082","Indigo "), ("#A0522D","Sienna"),
("#FFD700","Gold"), ("#F4A460","SandyBrown"), ("#DAA520","GoldenRod"), ("#DA70D6","Orchid"), ("#E6E6FA","Lavender"),
("#5F9EA0","CadetBlue"), ("#D2691E","Chocolate"), ("#66CDAA","MediumAquaMarine"), ("#6B8E23","OliveDrab"),
("#A9A9A9","DarkGray"), ("#BDB76B","DarkKhaki"), ("#696969","DimGray"), ("#B0C4DE","LightSteelBlue"),
("#191970","MidnightBlue"), ("#FFE4C4","Bisque"), ("#6A5ACD","SlateBlue"), ("#EE82EE","Violet"),
("#8B4513","SaddleBrown"), ("#FF7F50","Coral"), ("#008000","MidGreen"), ("#DB7093","PaleVioletRed"), ("#C0C0C0","Silver"),
("#E0FFFF","LightCyan"), ("#9370DB","MediumPurple"), ("#FF8C00","DarkOrange"), ("#00FA9A","MediumSpringGreen"),
("#E9967A","DarkSalmon"), ("#778899","LightSlateGray"), ("#9932CC","DarkOrchid"), ("#EEE8AA","PaleGoldenRod"),
("#F8F8FF","GhostWhite"), ("#FFA07A","LightSalmon"), ("#ADD8E6","LightBlue"), ("#D8BFD8","Thistle"),
("#FFE4E1","MistyRose"), ("#FFDEAD","NavajoWhite"), ("#40E0D0","Turquoise"), ("#90EE90","LightGreen"),
("#B22222","FireBrick"), ("#008080","Teal"), ("#F0FFF0","HoneyDew"), ("#FFFACD","LemonChiffon"), ("#FFF5EE","SeaShell"),
("#F5F5DC","Beige"), ("#DCDCDC","Gainsboro"), ("#FA8072","Salmon"), ("#8B008B","DarkMagenta"), ("#FFB6C1","LightPink"),
("#708090","SlateGray"), ("#87CEFA","LightSkyBlue"), ("#FFEFD5","PapayaWhip"), ("#D2B48C","Tan"), ("#FFFFF0","Ivory"),
("#F0FFFF","Azure"), ("#F5DEB3","Wheat"), ("#00008B","DarkBlue"), ("#FFDAB9","PeachPuff"), ("#8B0000","DarkRed"),
("#FAF0E6","Linen"), ("#B0E0E6","PowderBlue"), ("#FFE4B5","Moccasin"), ("#F5F5F5","WhiteSmoke"), ("#FFF8DC","Cornsilk"),
("#FFFAFA","Snow"), ("#FFF0F5","LavenderBlush"), ("#FFEBCD","BlanchedAlmond"), ("#F0F8FF","AliceBlue"),
("#FAEBD7","AntiqueWhite"), ("#FDF5E6","OldLace"), ("#FAFAD2","LightGoldenRodYellow"), ("#F5FFFA","MintCream"),
("#FFFAF0","FloralWhite"), ("#7CFC00","LawnGreen"), ("#663399","RebeccaPurple"))
"""
hex_name = (
("#FFFFFF", "White"), ("#FF0000", "Red"), ("#00FF00", "Lime"), ("#0000FF", "Blue"), ("#FF00FF", "Magenta"),
("#FFFF00", "Yellow"), ("#00FFFF", "Cyan"), ("#000000", "Black"), ("#FFA500", "Orange"),
("#006400", "DarkGreen"), ("#9400D3", "DarkViolet"), ("#A52A2A", "Brown"), ("#87CEEB", "SkyBlue"),
("#808080", "Gray"), ("#FF69B4", "HotPink"), ("#FFFFE0", "LightYellow"), ("#00FF7F", "SpringGreen"),
("#000080", "Navy"), ("#1E90FF", "DodgerBlue"), ("#9ACD32", "YellowGreen"), ("#008B8B", "DarkCyan"),
("#808000", "Olive"), ("#DEB887", "BurlyWood"),
("#7B68EE", "MediumSlateBlue"), ("#483D8B", "DarkSlateBlue"), ("#98FB98", "PaleGreen"), ("#FF1493", "DeepPink"),
("#FF6347", "Tomato"), ("#32CD32", "LimeGreen"), ("#DDA0DD", "Plum"), ("#7FFF00", "Chartreuse"),
("#800080", "Purple"), ("#00CED1", "DarkTurquoise"), ("#8FBC8F", "DarkSeaGreen"), ("#4682B4", "SteelBlue"),
("#800000", "Maroon"),
("#3CB371", "MediumSeaGreen"), ("#FF4500", "OrangeRed"), ("#BA55D3", "MediumOrchid"),
("#2F4F4F", "DarkSlateGray"), ("#CD853F", "Peru"), ("#228B22", "ForestGreen"), ("#48D1CC", "MediumTurquoise"),
("#DC143C", "Crimson"),
("#D3D3D3", "LightGray"), ("#ADFF2F", "GreenYellow"), ("#7FFFD4", "Aquamarine"), ("#BC8F8F", "RosyBrown"),
("#20B2AA", "LightSeaGreen"), ("#C71585", "MediumVioletRed"), ("#F0E68C", "Khaki"),
("#6495ED", "CornflowerBlue"),
("#556B2F", "DarkOliveGreen"), ("#CD5C5C", "IndianRed "), ("#2E8B57", "SeaGreen"), ("#F08080", "LightCoral"),
("#8A2BE2", "BlueViolet"), ("#AFEEEE", "PaleTurquoise"), ("#4169E1", "RoyalBlue"), ("#0000CD", "MediumBlue"),
("#B8860B", "DarkGoldenRod"), ("#00BFFF", "DeepSkyBlue"), ("#FFC0CB", "Pink"), ("#4B0082", "Indigo "),
("#A0522D", "Sienna"),
("#FFD700", "Gold"), ("#F4A460", "SandyBrown"), ("#DAA520", "GoldenRod"), ("#DA70D6", "Orchid"),
("#E6E6FA", "Lavender"),
("#5F9EA0", "CadetBlue"), ("#D2691E", "Chocolate"), ("#66CDAA", "MediumAquaMarine"), ("#6B8E23", "OliveDrab"),
("#A9A9A9", "DarkGray"), ("#BDB76B", "DarkKhaki"), ("#696969", "DimGray"), ("#B0C4DE", "LightSteelBlue"),
("#191970", "MidnightBlue"), ("#FFE4C4", "Bisque"), ("#6A5ACD", "SlateBlue"), ("#EE82EE", "Violet"),
("#8B4513", "SaddleBrown"), ("#FF7F50", "Coral"), ("#008000", "MidGreen"), ("#DB7093", "PaleVioletRed"),
("#C0C0C0", "Silver"),
("#E0FFFF", "LightCyan"), ("#9370DB", "MediumPurple"), ("#FF8C00", "DarkOrange"),
("#00FA9A", "MediumSpringGreen"),
("#E9967A", "DarkSalmon"), ("#778899", "LightSlateGray"), ("#9932CC", "DarkOrchid"),
("#EEE8AA", "PaleGoldenRod"),
("#F8F8FF", "GhostWhite"), ("#FFA07A", "LightSalmon"), ("#ADD8E6", "LightBlue"), ("#D8BFD8", "Thistle"),
("#FFE4E1", "MistyRose"), ("#FFDEAD", "NavajoWhite"), ("#40E0D0", "Turquoise"), ("#90EE90", "LightGreen"),
("#B22222", "FireBrick"), ("#008080", "Teal"), ("#F0FFF0", "HoneyDew"), ("#FFFACD", "LemonChiffon"),
("#FFF5EE", "SeaShell"),
("#F5F5DC", "Beige"), ("#DCDCDC", "Gainsboro"), ("#FA8072", "Salmon"), ("#8B008B", "DarkMagenta"),
("#FFB6C1", "LightPink"),
("#708090", "SlateGray"), ("#87CEFA", "LightSkyBlue"), ("#FFEFD5", "PapayaWhip"), ("#D2B48C", "Tan"),
("#FFFFF0", "Ivory"),
("#F0FFFF", "Azure"), ("#F5DEB3", "Wheat"), ("#00008B", "DarkBlue"), ("#FFDAB9", "PeachPuff"),
("#8B0000", "DarkRed"),
("#FAF0E6", "Linen"), ("#B0E0E6", "PowderBlue"), ("#FFE4B5", "Moccasin"), ("#F5F5F5", "WhiteSmoke"),
("#FFF8DC", "Cornsilk"),
("#FFFAFA", "Snow"), ("#FFF0F5", "LavenderBlush"), ("#FFEBCD", "BlanchedAlmond"), ("#F0F8FF", "AliceBlue"),
("#FAEBD7", "AntiqueWhite"), ("#FDF5E6", "OldLace"), ("#FAFAD2", "LightGoldenRodYellow"),
("#F5FFFA", "MintCream"),
("#FFFAF0", "FloralWhite"), ("#7CFC00", "LawnGreen"), ("#663399", "RebeccaPurple"))
codes = {}
for i, pair in enumerate(hex_name):
codes[pair[0]] = i
low = pair[1].lower()
codes[low] = i
codes[low.replace("gray","grey")] = i
codes[low.replace("gray", "grey")] = i
codes["green"] = 2
codes["fuchsia"] = 4
codes["aqua"] = 6
@ -72,7 +88,7 @@ class ColorMap(object):
try:
return int(colortext)
except ValueError:
return ColorMap.codes.get(colortext.lower().replace("_",""),-1)
return ColorMap.codes.get(colortext.lower().replace("_", ""),-1)
@staticmethod
def check_hex(code):
@ -96,7 +112,6 @@ class ColorMap(object):
return -1
def get_vars(main, time):
result = {}
@ -112,10 +127,13 @@ def get_vars(main, time):
vars.append(vars[0])
if len(vars) == 3:
vars.append("")
name, unit, label, color = vars
if len(vars) == 4:
vars.append("") # exact flag
name, unit, label, color, continuous = vars
continuous = int(continuous) if continuous else 0
if not unit in result:
result[unit] = dict(tag = unit, unit = unit.split("_")[0], curves=Dict())
result[unit]["curves"][name] = dict(name=name, label=label, color=color)
result[unit] = dict(tag=unit, unit=unit.split("_")[0], curves=Dict())
result[unit]["curves"][name] = dict(name=name, label=label, color=color, continuous=continuous)
for unit, curvegroup in result.items():
color_set = set()
@ -137,14 +155,19 @@ def get_vars(main, time):
color_set.add(c)
curve["original_color"] = col
curve["color"] = ColorMap.to_hex(c)
c = 1 # omit white
c = 1 # omit white
for curve in auto_curves:
while c in color_set: c += 1 # find unused color
while c in color_set:
c += 1 # find unused color
curve["color"] = ColorMap.to_hex(c)
c += 1
return result
def get_curves(main, keys, timerange, show_empty=True):
curves = main.get_curves(keys, get_abs_time(*timerange), maxpoints=500)
def get_curves(main, keys, timerange, cut_begin=True):
curves = main.get_curves(keys, get_abs_time(*timerange), maxpoints=500, cut_begin=cut_begin)
#if 'tt:target' in curves:
# print('---')
# print(curves['tt:target'].fmtm())
# print('TT', curves['tt:target'].for_json()[-5:])
return {k: c.for_json() for k, c in curves.items()}

View File

@ -64,7 +64,7 @@ def get_update(path=None):
@flask.stream_with_context
def generator():
logging.info('UPDATE %s %s', client.id, socket.getfqdn(flask.request.remote_addr.split(':')[-1]))
msg = dict(type='id', id=client.id, title=instrument.title, device=instrument.device);
msg = dict(type='id', id=client.id, instrument=instrument.title, device=instrument.device);
yield to_json_sse(msg)
try:
lastmsg = time.time()
@ -130,6 +130,7 @@ def reply():
try:
id = kwargs.pop('id')
client = instrument.clients[id]
print('PATH', path)
msg = getattr(client, "w_" + path[1:])(**kwargs)
except Exception as e:
logging.error('%s', traceback.format_exc())
@ -257,6 +258,7 @@ class Instrument:
self.clients[client.id] = client
return client
class SeaInstrument(Instrument):
# convert SEA layout tag like "-W" to more meaningful name.
# the code: 0: modifier, 1: enum name, 2: input element
@ -502,17 +504,18 @@ class SeaGraph:
LIVE = 2
def __init__(self):
self.livemode = self.HISTORICAL
self.livemode = self.ACTUAL
self.time = [0, 0]
self.lastvalues = {}
self.variables = []
def graphpoll(self):
if self.livemode == self.LIVE:
self.time[1], = get_abs_time(0)
else:
self.time[1] = self.time[0] # do not update
self.time[1] = self.time[0] # do not update
if self.time[1] > self.time[0]:
result = get_curves(main_cache, self.variables, self.time, show_empty=False)
result = get_curves(main_cache, self.variables, self.time, cut_begin=False)
self.strip_future(result)
if int(self.time[1] / 60) != int(self.time[0] / 60):
# update unchanged values
@ -533,14 +536,8 @@ class SeaGraph:
self.lastvalues[var] = (endtime, lastx)
def strip_future(self, result):
"""strip future points (happens only on dummy test_day)"""
# if self.livemode == self.LIVE:
for c in result.values():
while len(c):
lastt, lastx = c[-1]
if lastt <= self.time[1]:
break
c.pop()
"""strip future points (implemented on dummy test_day)"""
pass # do nothing here
def w_gettime(self, time):
result = get_abs_time(*[float(t) for t in time.split(',')])
@ -552,7 +549,8 @@ class SeaGraph:
self.last_t = 0
start, end, now = get_abs_time(*time, 0)
self.time = [start, end]
self.variables = variables.split(',')
oldvars = set(self.variables)
self.variables.extend((v for v in variables.split(',') if v not in oldvars))
self.livemode = self.ACTUAL if end >= now else self.HISTORICAL
logging.info('LIVE %g %g %d %d', end, now, end >= now, self.livemode)
# self.scanner = seagraph.NumericScanner(instrument.logger_dir, instrument.test_day)
@ -561,7 +559,7 @@ class SeaGraph:
self.strip_future(result)
logging.info('VARIABLES: %r %r %r', self.variables, start-now, end-now)
for var, curve in list(result.items()):
logging.info(' %s %r len=%d', var, curve[-1][0] - curve[0][0], len(curve))
logging.info(' %s %r len=%d', var, curve[-1][0] - curve[0][0] if len(curve) else None, len(curve))
self.complete_to_end(result, end)
self.time[0] = self.time[1]
# reduction not yet implemented
@ -575,8 +573,10 @@ class SeaGraph:
logging.info('GotVARS %r', result)
return result
def w_updategraph(self):
logging.info("UPD GRAPH %d", self.livemode)
def w_updategraph(self, variables=''):
if variables:
self.variables = variables.split(',')
logging.info("UPD GRAPH %d %r", self.livemode, self.variables)
if self.livemode == self.HISTORICAL:
return dict(type='accept-graph', live=False)
else:
@ -658,7 +658,7 @@ class SeaClient(SeaGraph):
class DummyClient(SeaGraph):
async = set(('id','update','redraw','command','reply','graph-update','graph-redraw'))
asynch = set(('id','update','redraw','command','reply','graph-update','graph-redraw'))
def __init__(self, host_port):
self.linesocket = tcp_lineserver.LineClient(host_port)
@ -678,7 +678,7 @@ class DummyClient(SeaGraph):
line = self.linesocket.get_line()
if line != None:
msg = json.loads(line)
if msg.type in self.async:
if msg.type in self.asynch:
t = 0
# print 'PUSH',msg, replytype
self.queue.append(msg)
@ -693,6 +693,12 @@ class DummyClient(SeaGraph):
logging.error('REPLY MISMATCH %s %s <> %s', command, replytype, msg.type)
return msg
def strip_future(self, result):
for c in result.values():
while len(c):
if c[-1][0] <= self.time[1]:
break
c.pop()
def w_getblock(self, path):
return self.cmd_reply(dict(type='getblock', path=path, id=self.id), 'draw')
@ -718,7 +724,7 @@ class DummyClient(SeaGraph):
messages = []
if line:
msg = json.loads(line)
if msg.type in self.async:
if msg.type in self.asynch:
messages.append(msg)
else:
self.syncreply.append(msg)
@ -761,7 +767,7 @@ class SecopMsg:
self.par = sl[1]
if len(sl) > 2:
self.value = json.loads(' '.join(sl[2:]))
self.async = self.type in ('update', 'error_update')
self.asynch = self.type in ('update', 'error_update')
def __repr__(self):
value = repr(self.value)
@ -828,15 +834,15 @@ class SecopClient(SeaGraph):
msg = self.syncreply.pop(0)
break
line = self.linesocket.get_line()
if line != None:
if line is not None:
msg = SecopMsg(line)
if msg.async:
if msg.asynch:
self.consolequeue.append(dict(type='reply',line=line,origin='async'))
else:
self.consolequeue.append(dict(type='reply',line=line,origin='other'))
if self.out: self.out.write("<"+line+"\n")
#print '<', msg.type, msg.par
if msg.async and replytype != msg.type + "=" + msg.par:
if msg.asynch and replytype != msg.type + "=" + msg.par:
t = 0
self.queue.append(msg)
else:
@ -895,31 +901,41 @@ class SecopClient(SeaGraph):
if not command:
return dict(type='accept-command')
cmd = "change " + command
return self.cmd_reply(cmd, 'changed ' + command.split(' ')[0])
self.cmd_reply(cmd, 'changed ' + command.split(' ')[0])
return dict(type='accept-command')
def poll(self):
messages = []
if self.consolequeue:
messages = self.consolequeue
messages.extend(self.consolequeue)
self.consolequeue = []
return messages
if self.queue:
messages = convert_event(self.queue)
messages.extend(convert_event(self.queue))
self.queue = []
return messages
line = self.linesocket.get_line()
# logging.info('poll %s', line)
if line:
if self.out: self.out.write("<"+line+"\n")
while True:
line = self.linesocket.get_line()
if not line:
break
# logging.info('poll %s', line)
if self.out:
self.out.write("<"+line+"\n")
msg = SecopMsg(line)
if msg.async: # do not flood console with updates
self.consolequeue.append(dict(type='reply',line=line,origin='async'))
if msg.asynch: # do not flood console with updates
if msg.par == 'tt:target':
print(msg)
self.consolequeue.append(dict(type='reply', line=line, origin='async'))
else:
self.consolequeue.append(dict(type='reply',line=line,origin='other'))
# logging.info('GOT MSG %r %r', msg.async, convert_event(SecopMsg(line)))
if msg.async:
return convert_event(SecopMsg(line))
self.syncreply.append(msg)
return []
self.consolequeue.append(dict(type='reply', line=line, origin='other'))
# logging.info('GOT MSG %r %r', msg.asynch, convert_event(SecopMsg(line)))
if msg.asynch:
messages.extend(convert_event(SecopMsg(line)))
else:
self.syncreply.append(msg)
# graph messages
msg = self.graphpoll()
if msg:
messages.append(msg)
return messages
def info(self):
return ["na"]
@ -939,6 +955,7 @@ class SecopInstrument(Instrument):
def newClient(self):
cl = SecopClient(self.host_port)
self.device = cl.description['equipment_id']
print('EQU', self.device)
logging.info('init done %s %s', self.host_port, self.device)
return self.register(cl)
@ -989,8 +1006,8 @@ if __name__ == '__main__':
instrument_config = instrument_list[inst_name]
frd = FrappyReader('/home/l_samenv/sea/%s' % inst_name, gevent=gevent)
main_cache = MainCache('/home/l_samenv/histreader/%s_hist' % inst_name, frd, gevent=gevent)
frd = FrappyReader('/Users/zolliker/frappyhist/%s' % inst_name, gevent=gevent)
main_cache = MainCache('/Users/zolliker/frappyhist/%s_hist' % inst_name, frd, gevent=gevent)
# logging.basicConfig(filename=inst_name+".log", filemode='w', level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')

View File

@ -5,6 +5,7 @@ import re
import circularlog
import logging
class LineHandler(asyncore.dispatcher_with_send):
def __init__(self, sock):
@ -38,6 +39,7 @@ class LineHandler(asyncore.dispatcher_with_send):
'''
self.send_line("> " + line)
class LineServer(asyncore.dispatcher):
def __init__(self, host, port, lineHandlerClass):
@ -58,9 +60,11 @@ class LineServer(asyncore.dispatcher):
def loop(self):
asyncore.loop()
class Disconnected(Exception):
pass
class LineClient(object):
def __init__(self, host_port, announcement=None, filter_ascii=False, ridername="r"):
@ -122,7 +126,8 @@ class LineClient(object):
def close(self):
self.socket.close()
self.connected = False
if __name__ == "__main__":
server = LineServer("localhost", 9999, LineHandler)
server.loop()