comparison yreport.c @ 0:9dab44dcb331

Initial commit of Greg's code from http://www.lemis.com/grog/tmp/wh1080.tar.gz
author Daniel O'Connor <darius@dons.net.au>
date Tue, 09 Feb 2010 13:44:25 +1030
parents
children 9da35e705144
comparison
equal deleted inserted replaced
-1:000000000000 0:9dab44dcb331
1 /*
2 * Report weather conditions to remote web sites.
3 *
4 * Greg Lehey, 14 December 2009
5 *
6 * $Id: yreport.c,v 1.2 2010/02/07 03:39:12 grog Exp $
7 */
8
9 #include "wh1080.h"
10
11 #define STAMPSIZE 32 /* size of time stamp to print out */
12
13 /*
14 * Data returned by database query, in the form we want.
15 */
16 struct weather_query
17 {
18 float inside_humidity;
19 float inside_temp;
20 float inside_dewpoint;
21 float outside_humidity;
22 float outside_temp;
23 float outside_dewpoint;
24 float pressure_msl;
25 float wind_speed;
26 float wind_gust;
27 float wind_direction;
28 float rain;
29 } weather_query;
30
31 /* Format strings for printing out variables */
32 char *rowformat1 [] = {"\t$inside_humidity = %4.0f;\n",
33 "\t$inside_temperature = %2.1f;\n",
34 "\t$inside_dewpoint = %2.1f;\n",
35 "\t$outside_humidity = %4.0f;\n",
36 "\t$outside_temperature = %2.1f;\n",
37 "\t$outside_dewpoint = %2.1f;\n",
38 "\t$pressure_msl = %2.1f;\n",
39 "\t$wind_speed = %2.1f;\n",
40 "\t$wind_gust = %2.1f;\n",
41 "\t$wind_direction = %4.0f;\n",
42 "\t$rain = %2.1f;\n"};
43
44 char *rowformat2 [] = {"\t$max_inside_humidity = %2.3f;\n",
45 "\t$min_inside_humidity = %2.3f;\n",
46 "\t$max_inside_temp = %2.3f;\n",
47 "\t$min_inside_temp = %2.3f;\n",
48 "\t$max_inside_dewpoint = %2.3f;\n",
49 "\t$min_inside_dewpoint = %2.3f;\n",
50 "\t$max_outside_humidity = %2.3f;\n",
51 "\t$min_outside_humidity = %2.3f;\n",
52 "\t$max_outside_temp = %2.3f;\n",
53 "\t$min_outside_temp = %2.3f;\n",
54 "\t$max_outside_dewpoint = %2.3f;\n",
55 "\t$min_outside_dewpoint = %2.3f;\n",
56 "\t$max_pressure_msl = %2.3f;\n",
57 "\t$min_pressure_msl = %2.3f;\n",
58 "\t$max_wind_speed = %2.3f;\n",
59 "\t$min_wind_speed = %2.3f;\n",
60 "\t$max_wind_gust = %2.3f;\n",
61 "\t$min_wind_gust = %2.3f;\n",
62 "\t$sum_rain = %2.3f;\n" };
63
64 char *maxmintimes [] = {"inside_humidity",
65 "inside_temp",
66 "inside_dewpoint",
67 "outside_humidity",
68 "outside_temp",
69 "outside_dewpoint",
70 "pressure_msl",
71 "wind_speed",
72 "wind_gust" };
73
74 int rowformats1 = sizeof (rowformat1) / sizeof (char *);
75 int rowformats2 = sizeof (rowformat2) / sizeof (char *);
76 int maxmincount = sizeof (maxmintimes) / sizeof (char *);
77
78 /*
79 * Create a header file for PHP pages. The "current readings" are an average
80 * over the last config.website_generation_interval seconds.
81 */
82
83 /*
84 * An alternative query method could be prepared statements. See
85 * http://dev.mysql.com/doc/refman/5.1/en/c-api-prepared-statements.html for
86 * details. Peter Jeremy says: "Search for MYSQL_BIND, mysql_stmt_bind_param()
87 * and mysql_stmt_bind_result()."
88 */
89 void make_php_header_file ()
90 {
91 time_t start;
92 time_t now;
93 FILE *header_file;
94 char date_time [DATETEXTLENGTH]; /* Wednesday, 25 November 2009 16:47:46 */
95 time_t yesterday;
96 char yesterday_text [DATETEXTLENGTH]; /* YYYY-MM-DD */
97 int row;
98 int rough_direction; /* out of range */
99
100 header_file = fopen (config.php_header, "w");
101 if (! header_file)
102 fprintf (stderr,
103 "Can't open %s: %s (%d)\n",
104 config.php_header,
105 strerror (errno),
106 errno );
107 else
108 {
109 now = time (NULL);
110 start = now - config.website_generation_interval; /* determine the time we're doing this for */
111 strftime (date_time, DATETEXTLENGTH, "%A, %e %B %Y %T", localtime (&now));
112 yesterday = now - SECSPERDAY; /* this time yesterday */
113 strftime (yesterday_text, DATETEXTLENGTH, "%F", localtime (&yesterday));
114
115 fprintf (header_file,
116 "<?php\n"
117 "/* Automatically generated file. Do not edit */\n"
118 "\t$timetext = \"%s\";\n"
119 "\t$yesterday = \"%s\";\n"
120 "\t$timestamp = %d;\n",
121 date_time, /* time in text form */
122 yesterday_text, /* yesterday's date YYYY-MM-DD */
123 (int) now ); /* time of reading */
124
125 sprintf (mysql_querytext,
126 "SELECT AVG(inside_humidity),\n"
127 " AVG(inside_temp),\n"
128 " AVG(inside_dewpoint),\n"
129 " AVG(outside_humidity),\n"
130 " AVG(outside_temp),\n"
131 " AVG(outside_dewpoint),\n"
132 " AVG(pressure_msl),\n"
133 " AVG(wind_speed),\n"
134 " AVG(wind_gust),\n"
135 " AVG(wind_direction),\n"
136 " SUM(rain)\n"
137 "FROM %s\n"
138 "WHERE unix_timestamp(timestamp(date,time)) >= %d\n"
139 " AND unix_timestamp(timestamp(date, time)) <= %d\n"
140 " AND station_id = \"%s\"\n",
141 config.db_table,
142 start,
143 now,
144 config.station_id );
145
146 if (doquery (mysql_querytext))
147 return; /* failed and reported */
148
149 if (mysql_row = mysql_fetch_row (mysql_result)) /* got something */
150 {
151 for (row = 0; row < rowformats1; row++)
152 {
153 /*
154 * We have a problem here: if a value is missing, we can't just print
155 * it. Fake a number for the time being.
156 */
157 if (mysql_row [row] && *mysql_row [row]) /* something there */
158 fprintf (header_file, rowformat1 [row], atof (mysql_row [row]));
159 else
160 fprintf (header_file, rowformat1 [row], -999.0);
161 }
162
163 /* Isn't this ugly? */
164 rough_direction = 1000; /* out of range */
165 if ((mysql_row [9]) && (*mysql_row [9])) /* something there */
166 rough_direction = (int) ((atof (mysql_row [9]) + 11.25) / 22.5);
167
168 if ((rough_direction < 16) && (rough_direction >= 0))
169 fprintf (header_file,
170 "\t$wind_direction_text = \"%s\";\n",
171 wind_directions [rough_direction] ) ;
172 else
173 fprintf (header_file, "\t$wind_direction_text = \"(none)\";\n");
174 mysql_free_result (mysql_result);
175 }
176
177 /* Now stats for the day */
178 strftime (date_time, DATETEXTLENGTH, "%Y-%m-%d", localtime (&now));
179 sprintf (mysql_querytext,
180 "SELECT @max_inside_humidity := MAX(inside_humidity),\n"
181 " @min_inside_humidity := MIN(inside_humidity),\n"
182 " @max_inside_temp := MAX(inside_temp),\n"
183 " @min_inside_temp := MIN(inside_temp),\n"
184 " @max_inside_dewpoint := MAX(inside_dewpoint),\n"
185 " @min_inside_dewpoint := MIN(inside_dewpoint),\n"
186 " @max_outside_humidity := MAX(outside_humidity),\n"
187 " @min_outside_humidity := MIN(outside_humidity),\n"
188 " @max_outside_temp := MAX(outside_temp),\n"
189 " @min_outside_temp := MIN(outside_temp),\n"
190 " @max_outside_dewpoint := MAX(outside_dewpoint),\n"
191 " @min_outside_dewpoint := MIN(outside_dewpoint),\n"
192 " @max_pressure_msl := MAX(pressure_msl),\n"
193 " @min_pressure_msl := MIN(pressure_msl),\n"
194 " @max_wind_speed := MAX(wind_speed),\n"
195 " @min_wind_speed := MIN(wind_speed),\n"
196 " @max_wind_gust := MAX(wind_gust),\n"
197 " @min_wind_gust := MIN(wind_gust),\n"
198 " SUM(rain)\n"
199 "FROM %s\n"
200 "WHERE date = \"%s\"\n"
201 " AND station_id = \"%s\"\n",
202 config.db_table,
203 date_time,
204 config.station_id );
205
206 if (doquery (mysql_querytext))
207 return; /* failed and reported */
208
209 if (mysql_row = mysql_fetch_row (mysql_result)) /* got something */
210 {
211 for (row = 0; row < rowformats2; row++)
212 fprintf (header_file, rowformat2 [row], atof (mysql_row [row]));
213 mysql_free_result (mysql_result);
214 }
215
216 /* Now get the times that go with the maxima and minima */
217 for (row = 0; row < maxmincount; row++)
218 {
219 /* Time of maximum */
220 sprintf (mysql_querytext,
221 "SELECT time from %s\n"
222 "WHERE date = \"%s\"\n"
223 " AND station_id = \"%s\"\n"
224 " AND %s = @max_%s\n"
225 "LIMIT 1",
226 config.db_table,
227 date_time,
228 config.station_id,
229 maxmintimes [row],
230 maxmintimes [row] );
231 if (doquery (mysql_querytext)) /* failure */
232 return; /* XXX be cleverer */
233 if (mysql_row = mysql_fetch_row (mysql_result)) /* got something */
234 {
235 fprintf (header_file, "\t$max_%s_time = \"%s\";\n", maxmintimes [row], mysql_row [0]);
236 mysql_free_result (mysql_result);
237 }
238 else /* dummy */
239 fprintf (header_file, "\t$max_%s_time = \"unknown\"\n", maxmintimes [row]);
240 /* Now time of minimum */
241 sprintf (mysql_querytext,
242 "SELECT time from %s\n"
243 "WHERE date = \"%s\"\n"
244 " AND station_id = \"%s\"\n"
245 " AND %s = @min_%s\n"
246 "LIMIT 1",
247 config.db_table,
248 date_time,
249 config.station_id,
250 maxmintimes [row],
251 maxmintimes [row] );
252 if (doquery (mysql_querytext)) /* failure */
253 return; /* XXX be cleverer */
254 if (mysql_row = mysql_fetch_row (mysql_result)) /* got something */
255 {
256 fprintf (header_file, "\t$min_%s_time = \"%s\";\n", maxmintimes [row], mysql_row [0]);
257 mysql_free_result (mysql_result);
258 }
259 else /* dummy */
260 fprintf (header_file, "\t$min_%s_time = \"unknown\"\n", maxmintimes [row]);
261 }
262
263 fprintf (header_file, "\t?>\n"); /* end */
264 fclose (header_file);
265 }
266 }
267
268 void usage (char *me)
269 {
270 fprintf (stderr,
271 "Usage: %s [-d] [-n] [-v] [-1] [station ID] [db user] [password] [db host] [database]\n",
272 me);
273 exit (1);
274 }
275
276 int main (int argc, char *argv [])
277 {
278 time_t lastrun;
279 time_t nextrun;
280 time_t now;
281
282 if (read_config (argc, argv) < 0)
283 usage (argv [0]);
284
285 while (1)
286 {
287 lastrun = time (NULL); /* time we ran the loop */
288 make_php_header_file ();
289 if (once) /* just run once */
290 exit (0);
291 /*
292 * Informing Wunderground takes finite time. Stay on schedule.
293 * We may end up off by a second from time to time, but we shouldn't drift.
294 *
295 */
296 nextrun = lastrun + config.website_generation_interval; /* next time to run */
297 now = time (NULL);
298 sleep (nextrun - now);
299 }
300 return 0;
301 }