Транзакционная работа с БД для избежания блокировок.
[weathermon.git] / web / cgi.php
1 <?php
2
3 $query=$_REQUEST['query'];
4
5 $client_ip = $_SERVER["REMOTE_ADDR"];
6
7 include('config_local.php');
8
9 function startsWith($haystack, $needle)
10 {
11      $length = strlen($needle);
12      return (substr($haystack, 0, $length) === $needle);
13 }
14
15 if (! ($db = new PDO("mysql:host=$mysql_host;port=$mysql_port;dbname=$mysql_schema",$mysql_user,$mysql_pwd,array( PDO::ATTR_PERSISTENT => false)))) {
16   die($err);
17 }  
18
19 $db -> exec('SET CHARACTER SET utf8');
20
21 $auth_token = $_COOKIE["auth-token"];
22 $auth = 0;
23
24 if ($auth_token) {
25
26   $sql = "
27     SELECT COUNT(*) AS auth FROM tokens WHERE str=:s and expires>now()
28   ";
29   
30   $q = $db -> prepare( $sql );
31   $q -> bindParam(':s',$auth_token,PDO::PARAM_INT);
32   $q -> execute();
33
34   $res = [];
35
36   $row = $q -> fetch(PDO::FETCH_ASSOC);
37   $auth = $row['auth'];
38
39
40
41 if ($auth || (strpos($client_ip, "192.168.") === 0) || (strpos($client_ip, "10.8.") === 0)) {
42   $filter = '';
43 } else {
44   $filter = ' and s.is_public=1';
45 }
46
47 $hash = md5($_SERVER['QUERY_STRING'].":".$filter);
48 $redis = new Redis();
49 $redis->pconnect('127.0.0.1', 6379);
50 $results = $redis->get('meteo-'.$hash);
51 if ($results) {
52
53   $results = unserialize($results);
54   print(json_encode($results));
55   return;
56
57 }
58
59 function getYears($db) {
60
61   $sql = "
62     SELECT DISTINCT DATE_FORMAT(day,'%Y') y FROM sensors_ranges ORDER BY y DESC
63   ";
64   
65   $q = $db -> prepare( $sql );
66   $q -> execute();
67
68   $res = [];
69
70   while ($row = $q -> fetch(PDO::FETCH_ASSOC)) {
71     array_push($res, $row['y']);
72   }
73
74   return $res;  
75
76 }
77
78 function getMonths($db,$year) {
79
80   $y = intval($year);
81
82   $sql = "
83     SELECT DISTINCT DATE_FORMAT(day,'%m') m FROM sensors_ranges WHERE day>=STR_TO_DATE('".strval($y)."-01-01','%Y-%m-%d') and day<STR_TO_DATE('".strval($y+1)."-01-01','%Y-%m-%d') ORDER BY m DESC
84   ";
85
86   $q = $db -> prepare( $sql );
87   $q -> execute();
88
89   $res = [];
90
91   while ($row = $q -> fetch(PDO::FETCH_ASSOC)) {
92     array_push($res, $row['m']);
93   }
94
95   return $res;  
96
97 }
98
99 function getDays($db,$year,$month) {
100
101   $y = intval($year);
102   $m = intval($month);
103
104   $sql = "
105     SELECT DISTINCT DATE_FORMAT(day,'%d') d FROM sensors_ranges WHERE day>=STR_TO_DATE('".strval($y)."-".strval($m)."-01','%Y-%m-%d') and DATE_ADD(STR_TO_DATE('".strval($y)."-".strval($m)."-01','%Y-%m-%d'), INTERVAL 1 MONTH) ORDER BY d DESC
106   ";
107
108   $q = $db -> prepare( $sql );
109   $q -> execute();
110
111   $res = [];
112
113   while ($row = $q -> fetch(PDO::FETCH_ASSOC)) {
114     array_push($res, $row['d']);
115   }
116
117   return $res;  
118
119 }
120
121 function getSensors($db,$year,$month,$day) {
122
123   global $filter;
124
125   $y = intval($year);
126   $m = intval($month);
127   $d = intval($day);
128
129   $sql = "
130     SELECT DISTINCT 
131       CONCAT(s_id,'.',t.st_abbr,'.',p.st_name) id
132     FROM
133       sensors_ranges r, sensors s, sensor_types t, st_parameters p
134     WHERE 
135       r.sensor=s.id and r.parameter=p.id and s.st_id=t.id and p.st_id=t.id ".$filter."
136       and r.day=STR_TO_DATE('".strval($y)."-".strval($m)."-".strval($d)."','%Y-%m-%d')
137   ";
138
139   $q = $db -> prepare( $sql );
140   $q -> execute();
141
142   $res = [];
143
144   while ($row = $q -> fetch(PDO::FETCH_ASSOC)) {
145     array_push($res, $row['id']);
146   }
147
148   return $res;  
149
150 }
151
152 function getCurrent($db,$id,$type,$param) {
153
154   global $filter;
155
156   $sql = "
157     SELECT s.id s_id,p.id p_id 
158     FROM sensors s,sensor_types t, st_parameters p
159     WHERE s.st_id=t.id and p.st_id=t.id and s_id=:id and t.st_abbr=:type and p.st_name=:param ".$filter."
160   ";
161
162   $q = $db -> prepare( $sql );
163   $q -> bindParam(':id',$id,PDO::PARAM_STR);
164   $q -> bindParam(':type',$type,PDO::PARAM_STR);
165   $q -> bindParam(':param',$param,PDO::PARAM_STR);
166   $q -> execute();
167   $sensor = $q -> fetch(PDO::FETCH_ASSOC);
168
169   $sql = "
170     SELECT 
171       u.id stored_unit,du.id display_unit 
172     FROM 
173       sensors s,sensor_types t,st_parameters p,units u,units du
174     WHERE s.st_id=t.id and p.st_id=t.id and p.st_unit=u.id and u.unit_group=du.unit_group and du.is_default=1 and s.id=:id and p.id=:param
175   ";
176
177   $q = $db -> prepare( $sql );
178   $q -> bindParam(':id',$sensor['s_id'],PDO::PARAM_INT);
179   $q -> bindParam(':param',$sensor['p_id'],PDO::PARAM_INT);
180   $q -> execute();
181   $units = $q -> fetch(PDO::FETCH_ASSOC);
182
183   $sql = "
184     SELECT 
185       CONCAT(subset.t,'5:00') t,UnitConv(avg(subset.y),:stored,:display) y
186     FROM (
187       SELECT 
188         substr(date_format(timestamp,'%Y-%m-%dT%H:%i'),1,15) t,value y
189       FROM 
190         sensor_values
191       WHERE 
192         sensor_id = :id and parameter_id=:param 
193         and timestamp>adddate(now(),-1)
194       ) subset 
195     GROUP BY subset.t
196     ORDER BY subset.t
197   ";
198   
199   $q = $db -> prepare( $sql );
200   $q -> bindParam(':id',$sensor['s_id'],PDO::PARAM_INT);
201   $q -> bindParam(':param',$sensor['p_id'],PDO::PARAM_INT);
202   $q -> bindParam(':stored',$units['stored_unit'],PDO::PARAM_INT);
203   $q -> bindParam(':display',$units['display_unit'],PDO::PARAM_INT);
204   $q -> execute();
205
206   return $q -> fetchAll(PDO::FETCH_ASSOC);
207
208 }
209
210 function getArchive($db,$year,$month,$day,$id,$type,$param) {
211
212   global $filter;
213
214   $y = intval($year);
215   $m = intval($month);
216   $d = intval($day);
217   
218   $date = strval($y).'-'.strval($m).'-'.strval($d);
219
220   $sql = "
221     SELECT s.id s_id,p.id p_id 
222     FROM sensors s,sensor_types t, st_parameters p
223     WHERE s.st_id=t.id and p.st_id=t.id and s_id=:id and t.st_abbr=:type and p.st_name=:param ".$filter."
224   ";
225
226   $q = $db -> prepare( $sql );
227   $q -> bindParam(':id',$id,PDO::PARAM_STR);
228   $q -> bindParam(':type',$type,PDO::PARAM_STR);
229   $q -> bindParam(':param',$param,PDO::PARAM_STR);
230   $q -> execute();
231   $sensor = $q -> fetch(PDO::FETCH_ASSOC);
232
233   $sql = "
234     SELECT 
235       u.id stored_unit,du.id display_unit 
236     FROM 
237       sensors s,sensor_types t,st_parameters p,units u,units du
238     WHERE s.st_id=t.id and p.st_id=t.id and p.st_unit=u.id and u.unit_group=du.unit_group and du.is_default=1 and s.id=:id and p.id=:param
239   ";
240
241   $q = $db -> prepare( $sql );
242   $q -> bindParam(':id',$sensor['s_id'],PDO::PARAM_INT);
243   $q -> bindParam(':param',$sensor['p_id'],PDO::PARAM_INT);
244   $q -> execute();
245   $units = $q -> fetch(PDO::FETCH_ASSOC);
246
247   $sql = "
248     SELECT 
249       CONCAT(subset.t,'5:00') t,UnitConv(avg(subset.y),:stored,:display) y
250     FROM (
251       SELECT 
252         substr(date_format(timestamp,'%Y-%m-%dT%H:%i'),1,15) t,value y
253       FROM 
254         sensor_values
255       WHERE 
256         sensor_id = :id and parameter_id=:param 
257         and timestamp>=STR_TO_DATE(:d,'%Y-%m-%d')
258         and timestamp<DATE_ADD(STR_TO_DATE(:d,'%Y-%m-%d'), interval 1 day)
259       ) subset 
260     GROUP BY subset.t
261     ORDER BY subset.t
262   ";
263
264   $q = $db -> prepare( $sql );
265   $q -> bindParam(':id',$sensor['s_id'],PDO::PARAM_INT);
266   $q -> bindParam(':param',$sensor['p_id'],PDO::PARAM_INT);
267   $q -> bindParam(':d',$date,PDO::PARAM_STR);
268   $q -> bindParam(':stored',$units['stored_unit'],PDO::PARAM_INT);
269   $q -> bindParam(':display',$units['display_unit'],PDO::PARAM_INT);
270   $q -> execute();
271
272   return $q -> fetchAll(PDO::FETCH_ASSOC);
273
274 }
275
276 function getProps($db, $localNet) {
277
278   global $filter;
279   
280   $sql = "
281     SELECT 
282       CONCAT(s_id,'.',t.st_abbr,'.',p.st_name) sensor_id,
283       p.st_description name,
284       du.name_short unit,
285       du.prec prec,
286       pl.place_name,
287       p.st_line_color color
288     FROM 
289       sensors s, sensor_types t, st_parameters p,units u,units du,places pl
290     WHERE 
291       s.st_id=t.id and p.st_id=t.id and p.st_unit=u.id and u.unit_group=du.unit_group and du.is_default=1 and pl.idplaces=s.place_id ".$filter."
292   ";
293   
294   $q = $db -> prepare( $sql );
295   $q -> execute();
296   $reply = [ 
297     "names" => [], 
298     "colors" => [], 
299     "units" => [], 
300     "scale" => [], 
301     "places" => [],
302     "fonts" => [ 
303       "axes" => [ "color" => "black", "size" => 16, "style" => "normal" ], 
304       "legend" => [ "color" => "black", "size" => 16, "style" => "normal" 
305   ] ] ];
306
307   while ($row = $q -> fetch(PDO::FETCH_ASSOC)) {
308     $reply["names"][$row["sensor_id"]] = $row["name"];
309     $reply["colors"][$row["sensor_id"]] = $row["color"];
310     $reply["units"][$row["sensor_id"]] = $row["unit"];
311     $reply["places"][$row["sensor_id"]] = $row["place_name"];
312     $reply["scale"][$row["sensor_id"]] = [ 0 => 1.0, 1 => $row["prec"] ];
313   }
314   
315   return $reply;
316   
317 }
318
319 function getState($db, $localNet) {
320
321   global $filter;
322   
323   $sql = "
324     SELECT 
325       DISTINCT 
326         s.s_id as sensor_id,
327         st.st_abbr,
328         v.sensor as sensor_int_id,
329         pl.place_name s_description,
330         p.id as param_id,
331         p.st_name as param_name,
332         p.st_description,
333         s.place_id,
334         u.id stored_unit_id,
335         du.id unit_id
336     FROM 
337       sensors_ranges v,st_parameters p,sensors s,places pl,sensor_types st,units u,units du
338     WHERE 
339       v.timestamp>addtime(now(), -43200) 
340       and s.st_id=st.id
341       and v.sensor=s.id
342       and s.st_id=st.id
343       and v.parameter=p.id  
344       and s.st_id=p.st_id
345       and p.id>=0
346       and s.place_id=pl.idplaces
347       and p.st_unit=u.id 
348       and u.unit_group=du.unit_group
349       and du.is_default=1
350       ".$filter."
351     ORDER BY
352       s_description,sensor_id,param_id
353   ";
354
355   $q = $db -> prepare( $sql );
356   $q -> execute();
357
358   $reply = [];
359
360   while ($row = $q -> fetch(PDO::FETCH_ASSOC)) {
361
362     $sensor_id = $row['sensor_id'];
363     $st_id = $row['st_abbr'];
364     $sensor_int_id = $row['sensor_int_id'];
365     $param_id = $row['param_name'];
366     $param_int_id = $row['param_id'];
367     $unit_id = $row['unit_id'];
368     $stored_unit_id = $row['stored_unit_id'];
369     $place_description = $row['s_description'];
370
371     $sql_last_val = "
372       SELECT UnitConv(value,".$stored_unit_id.",".$unit_id.") as val,timestamp
373       FROM
374         sensor_values
375       WHERE 
376         sensor_id = ".$sensor_int_id." and parameter_id=".$param_int_id."
377       ORDER BY
378         timestamp DESC
379       LIMIT 1
380     ";
381
382     $qv = $db -> prepare( $sql_last_val );
383     $qv -> execute();
384
385     $v_row = $qv -> fetch(PDO::FETCH_ASSOC);
386     
387     $value = $v_row["val"];
388     $timestamp = $v_row["timestamp"];
389
390     if (! array_key_exists($place_description,$reply)) {
391       $reply[$place_description] = [];
392     }
393
394     if (! array_key_exists($sensor_id,$reply[$place_description])) {
395       $reply[$place_description][$sensor_id] = [];
396     }
397     
398     if (! array_key_exists($st_id,$reply[$place_description][$sensor_id])) {
399       $reply[$place_description][$sensor_id][$st_id] = [];
400     }
401
402     $reply[$place_description][$sensor_id][$st_id][$param_id] = $value;
403     $reply[$place_description][$sensor_id]['timestamp'] = $timestamp;
404
405   }
406
407   return $reply;
408   
409 }
410
411 $expire = 60;
412
413 if ($query == 'props') {
414
415   $reply = getProps($db, $local_net);
416   $expire = 600;
417
418 } elseif ($query == 'state') {
419
420   $reply = getState($db, $local_net);
421
422 } elseif (startsWith($query,'get/')) {
423
424   $sensor = explode('/',substr($query,strlen('get/')));
425   $reply = getCurrent($db,$sensor[0],$sensor[1],$sensor[2]);
426
427 } elseif ($query == 'years') {
428
429   $reply = getYears($db);
430   $expire = 3600;
431
432 } elseif (startsWith($query,'months/')) {
433
434   $date = explode('/',substr($query,strlen('months/')));
435   $reply = getMonths($db,$date[0]);
436   $expire = 3600;
437
438 } elseif (startsWith($query,'days/')) {
439
440   $date = explode('/',substr($query,strlen('days/')));
441   $reply = getDays($db,$date[0],$date[1]);
442   $expire = 3600;
443
444 } elseif (startsWith($query,'sensors/')) {
445
446   $date = explode('/',substr($query,strlen('sensors/')));
447   $reply = getSensors($db,$date[0],$date[1],$date[2]);
448   $expire = 3600;
449
450 } elseif (startsWith($query,'get-archive/')) {
451
452   $path = explode('/',substr($query,strlen('get-archive/')));
453   $reply = getArchive($db,$path[0],$path[1],$path[2],$path[3],$path[4],$path[5]);
454   $expire = 14400;
455
456 }
457
458 if ($reply) {
459
460   $redis->set('meteo-'.$hash, serialize($reply));
461   $redis->expire('meteo-'.$hash, $expire);
462   
463   print(json_encode($reply));
464
465 }
466
467 ?>