Somewhat done control page
[matches/MCTX3420.git] / testing / MCTXWeb / public_html / static / mctx.graph.js
1 /**
2  * Graph sensor and/or actuator values
3  */
4
5 //TODO: Clean this file up, I bow to Jeremy's superior JavaScript knowledge
6
7
8 mctx.graph = {};
9 mctx.graph.api = {};
10 mctx.graph.api.sensors = mctx.api + "sensors";
11 mctx.graph.api.actuators = mctx.api + "actuators";
12 mctx.sensors = {};
13 mctx.actuators = {};
14 mctx.graph.dependent = null;
15 mctx.graph.independent = null;
16 mctx.graph.timer = null;
17 mctx.graph.running = false;
18 mctx.graph.chart = null;
19
20 /**
21  * Helper - Calculate pairs of (dependent, independent) values
22  * Given input as (time, value) pairs for dependent and independent
23  * Appends each value pair to the result
24  * @returns result
25  */
26 /**
27  * Helper - Calculate pairs of (dependent, independent) values
28  * Given input as (time, value) pairs for dependent and independent
29  * Appends each value pair to the result
30  * @param {array[][]} dependent Dependent data to be correlated with independent
31  * @param {array[][]} independent Independent data
32  * @param {array[][]} result Storage location
33  * @returns {dataMerge.result}
34  */
35 function dataMerge(dependent, independent, result) {
36         var j = 0;
37         for (var i = 0; i < dependent.length-1; ++i) {
38                 var start = dependent[i][0];
39                 var end = dependent[i+1][0];
40                 var average = 0, n = 0;
41                 for (; j < independent.length; ++j) {
42                         if (independent[j][0] < start)
43                                 continue;
44                         else if (independent[j][0] >= end)
45                                 break;
46                         average += independent[j][1];
47                         n += 1;
48                 }
49                 if (n > 0) {
50                         average /= n;
51                         result.push([dependent[i][1], average]);
52                 }
53         }
54         return result;
55 }
56
57 /**
58  * Helper function adds the sensors and actuators to a form
59  * @param input_type is it a radio? or is it a checkbox?
60  * @param check_first determines whether the first item is checked or not
61  * @param group which group this input belongs to (name field)
62  */
63 $.fn.deployDevices = function(input_type, check_first, group) {
64   var container = this;
65   var apply = function(dict, prefix) {
66     $.each(dict, function(key, val) {
67       var attributes = {
68           'type' : input_type, 'value' : key, 'alt' : val,
69           'class' : prefix, 'name' : group, 
70           'id' : prefix + '_' + val //Unique id (name mangling)
71       };
72       var entry = $("<input/>", attributes);
73       var label = $("<label/>", {'for' : prefix + '_' + val, 'text' : val}); 
74       entry.prop("checked", check_first);
75       check_first = false;
76       container.append(entry).append(label);
77     });
78   }
79   
80   apply(mctx.sensors, 'sensors');
81   apply(mctx.actuators, 'actuators');
82 };
83
84 /**
85  * Identify sensors/actuators
86  * @returns itself (Is this right?)
87  */
88 $.fn.setDevices = function() {
89   // Query for sensors and actuators
90   return $.ajax({
91     url : mctx.api + 'identify', 
92     data : {'sensors' : 1, 'actuators' : 1}
93   }).done(function (data) {
94     mctx.sensors = $.extend(mctx.sensors, data.sensors);
95     mctx.actuators = $.extend(mctx.actuators, data.actuators);
96     
97     //Always set the 'time' option to be checked
98     $("#xaxis input").prop('checked', true);  
99     $("#xaxis").deployDevices("radio", false, 'xaxis');
100     $("#yaxis").deployDevices("checkbox", true, 'yaxis');
101     $("#current_time").val(data.running_time);
102     //Add event listeners for when the
103     $(".change input").change(function () {
104       $("#graph").setGraph();
105     });
106   });
107 };
108
109 function setGraphStatus(on, failText, keep) {
110   if (on) {
111     mctx.graph.running = true;
112     $("#status-text").html("&nbsp;");
113     $("#graph-run").prop("value", "Pause");
114   } else {
115     mctx.graph.running = false;
116     if (failText) {
117       $("#status-text").text(failText).addClass("fail");
118     } else if (!keep) {
119       $("#status-text").text("Graph stopped").removeClass("fail");
120     }
121     $("#graph-run").prop("value", "Run");
122   }
123 }
124
125 function graphUpdater() {
126   var urls = {
127     'sensors' : mctx.graph.api.sensors,
128     'actuators' : mctx.graph.api.actuators
129   }
130   
131   var updater = function () {
132     var responses = [];
133     var ctime =  $("#current_time");
134     
135     var xaxis = mctx.graph.xaxis;
136     var yaxis = mctx.graph.yaxis;
137     var start_time = mctx.graph.start_time;
138     var end_time = mctx.graph.end_time;
139     var devices = mctx.graph.devices;
140     
141     if (xaxis.size() < 1 || yaxis.size() < 1) {
142       setGraphStatus(false, "No x or y axis selected.");
143       return;
144     }
145     
146     $.each(devices, function(key, val) {
147       if (val.urltype in urls) {
148         var parameters = {id : val.id};
149         if (start_time !== null) {
150           parameters.start_time = start_time;
151         }
152         if (end_time !== null) {
153           parameters.end_time = end_time;
154         }
155         responses.push($.ajax({url : urls[val.urltype], data : parameters})
156         .done(function(json) {
157           //alert("Hi from " + json.name);
158           if (!$("#status-text").checkStatus(json)) {
159             setGraphStatus(false, null, true); //Don't reset text, checkstatus just set it.
160             return;
161           }
162           
163           var dev = val.data;
164           for (var i = 0; i < json.data.length; ++i) {
165             if (dev.length <= 0 || json.data[i][0] > dev[dev.length-1][0]) {
166               dev.push(json.data[i]);
167             }
168           }
169           ctime.val(json.running_time);
170           //alert(devices[json.name].data);
171         }));
172       }
173     });
174
175     //... When the response is received, then() will happen (I think?)
176     $.when.apply(this, responses).then(function () {
177       if (mctx.graph.running) {
178         var plot_data = [];
179         
180         yaxis.each(function() {
181           //alert("Add " + $(this).val() + " to plot");
182           if (xaxis.attr("alt") === "time") {
183             //alert("Against time");
184             plot_data.push(devices[$(this).attr("alt")].data);
185           } else {
186             var result = []
187             dataMerge(devices[xaxis.attr("alt")].data, 
188                       devices[$(this).attr("alt")].data, result);
189             plot_data.push(result);
190           }
191         });
192         
193         if (mctx.graph.chart !== null) {
194           mctx.graph.chart.setData(plot_data);
195           mctx.graph.chart.setupGrid(); 
196           mctx.graph.chart.draw();
197         } else {
198           mctx.graph.chart = $.plot("#graph", plot_data);
199         }
200         mctx.graph.timer = setTimeout(updater, 1000);
201       }
202     }, function () {
203       setGraphStatus("Connection issue - graph stopped.");
204       //This will always happen when a user closes the page
205       //alert("Graph crashed"); 
206     });
207   };
208   
209   setGraphStatus(true);
210   updater();
211   return this;
212 }
213
214 /**
215  * Sets the graphs to graph stuff.
216  * @returns {$.fn}
217  */
218 $.fn.setGraph = function () {
219   // Determine which actuator/sensors to plot
220   var xaxis = $("#xaxis input[name=xaxis]:checked");
221   var yaxis = $("#yaxis input[name=yaxis]:checked");
222   if (xaxis.size() < 1 || yaxis.size() < 1) {
223     //nothing to plot...
224     setGraphStatus(false, "No x or y axis selected.");
225     return;
226   }
227   
228   var start_time = $("#start_time").val();
229   var end_time = $("#end_time").val();
230   if (!$.isNumeric(start_time)) {
231     start_time = null;
232   }
233   if (!$.isNumeric(end_time)) {
234     end_time = null;
235   }
236
237   var devices = {};
238   var populateDict = function () {
239     var dict = {};
240     dict['urltype'] = $(this).attr("class");
241     dict['id'] = $(this).attr("value");
242     dict['data'] = [];
243     dict['start_time'] = start_time;
244     dict['end_time'] = end_time;
245     devices[$(this).attr("alt")] = dict;
246   };
247   xaxis.each(populateDict);
248   yaxis.each(populateDict);
249   
250   mctx.graph.xaxis = xaxis;
251   mctx.graph.yaxis = yaxis;
252   mctx.graph.start_time = start_time;
253   mctx.graph.end_time = end_time;
254   mctx.graph.devices = devices;
255   
256   if (!mctx.graph.running) {
257     $("#graph-run").val("Pause");
258     $("#status-text").text("")
259     graphUpdater();
260   }
261   
262   return this;
263 };
264
265 $.fn.runButton = function() {
266   if (mctx.graph.running) {
267     setGraphStatus(false);
268     clearTimeout(mctx.graph.timer);
269   } else {
270     $("#graph").setGraph();
271   }
272 };

UCC git Repository :: git.ucc.asn.au