diff --git a/client/jsFiles/SEAWebClientCommunication.js b/client/jsFiles/SEAWebClientCommunication.js
index 3a8f81d..ff7d8cb 100644
--- a/client/jsFiles/SEAWebClientCommunication.js
+++ b/client/jsFiles/SEAWebClientCommunication.js
@@ -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;
diff --git a/client/jsFiles/SEAWebClientGraph.js b/client/jsFiles/SEAWebClientGraph.js
index 1a9c038..4a54cf4 100644
--- a/client/jsFiles/SEAWebClientGraph.js
+++ b/client/jsFiles/SEAWebClientGraph.js
@@ -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 on 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 on off";
+ } else {
+ autoScaleRow.innerHTML = "Autoscale on off";
+ }
+ }
+
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)
+}
\ No newline at end of file
diff --git a/client/jsFiles/SEAWebClientGroup.js b/client/jsFiles/SEAWebClientGroup.js
index 98e85de..d0ddf9d 100644
--- a/client/jsFiles/SEAWebClientGroup.js
+++ b/client/jsFiles/SEAWebClientGroup.js
@@ -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";
diff --git a/doc/protocol.txt b/doc/protocol.txt
index 225370e..cd740c5 100644
--- a/doc/protocol.txt
+++ b/doc/protocol.txt
@@ -166,18 +166,25 @@ All other connections are to be closed (getJSON):
*********************************************************************************************************************************
-/updategraph?id=
+/updategraph?id=[&variables=]
+
+ if is given, the given variables will be updated with the graph-update-message
response:
= = {
- "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=,&variables=&id=
+ the given variables are added to the list of variables being updated with the graph-update-message
+
response:
= = {
diff --git a/histgraph.py b/histgraph.py
index 775ce36..2facfde 100644
--- a/histgraph.py
+++ b/histgraph.py
@@ -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()}
diff --git a/seaweb_hist.py b/seaweb_hist.py
index e98e7e8..26d8959 100755
--- a/seaweb_hist.py
+++ b/seaweb_hist.py
@@ -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')
diff --git a/tcp_lineserver.py b/tcp_lineserver.py
index 78d171b..9453b14 100644
--- a/tcp_lineserver.py
+++ b/tcp_lineserver.py
@@ -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()