diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yreport.c	Tue Feb 09 13:44:25 2010 +1030
@@ -0,0 +1,301 @@
+/*
+ * Report weather conditions to remote web sites.
+ *
+ * Greg Lehey, 14 December 2009
+ *
+ * $Id: yreport.c,v 1.2 2010/02/07 03:39:12 grog Exp $
+ */
+
+#include "wh1080.h"
+
+#define STAMPSIZE 32                            /* size of time stamp to print out */
+
+/*
+ * Data returned by database query, in the form we want.
+ */
+struct weather_query
+{
+  float inside_humidity;
+  float inside_temp;
+  float inside_dewpoint;
+  float outside_humidity;
+  float outside_temp;
+  float outside_dewpoint;
+  float pressure_msl;
+  float wind_speed;
+  float wind_gust;
+  float wind_direction;
+  float rain;
+} weather_query;
+
+/* Format strings for printing out variables */
+char *rowformat1 [] = {"\t$inside_humidity = %4.0f;\n",
+                       "\t$inside_temperature = %2.1f;\n",
+                       "\t$inside_dewpoint = %2.1f;\n",
+                       "\t$outside_humidity = %4.0f;\n",
+                       "\t$outside_temperature = %2.1f;\n",
+                       "\t$outside_dewpoint = %2.1f;\n",
+                       "\t$pressure_msl = %2.1f;\n",
+                       "\t$wind_speed = %2.1f;\n",
+                       "\t$wind_gust = %2.1f;\n",
+                       "\t$wind_direction = %4.0f;\n",
+                       "\t$rain = %2.1f;\n"};
+
+char *rowformat2 [] = {"\t$max_inside_humidity = %2.3f;\n",
+                      "\t$min_inside_humidity = %2.3f;\n",
+                      "\t$max_inside_temp = %2.3f;\n",
+                      "\t$min_inside_temp = %2.3f;\n",
+                      "\t$max_inside_dewpoint = %2.3f;\n",
+                      "\t$min_inside_dewpoint = %2.3f;\n",
+                      "\t$max_outside_humidity = %2.3f;\n",
+                      "\t$min_outside_humidity = %2.3f;\n",
+                      "\t$max_outside_temp = %2.3f;\n",
+                      "\t$min_outside_temp = %2.3f;\n",
+                      "\t$max_outside_dewpoint = %2.3f;\n",
+                      "\t$min_outside_dewpoint = %2.3f;\n",
+                      "\t$max_pressure_msl = %2.3f;\n",
+                      "\t$min_pressure_msl = %2.3f;\n",
+                      "\t$max_wind_speed = %2.3f;\n",
+                      "\t$min_wind_speed = %2.3f;\n",
+                      "\t$max_wind_gust = %2.3f;\n",
+                      "\t$min_wind_gust = %2.3f;\n",
+                      "\t$sum_rain = %2.3f;\n" };
+
+char *maxmintimes [] = {"inside_humidity",
+                        "inside_temp",
+                        "inside_dewpoint",
+                        "outside_humidity",
+                        "outside_temp",
+                        "outside_dewpoint",
+                        "pressure_msl",
+                        "wind_speed",
+                        "wind_gust" };
+
+int rowformats1 = sizeof (rowformat1) / sizeof (char *);
+int rowformats2 = sizeof (rowformat2) / sizeof (char *);
+int maxmincount = sizeof (maxmintimes) / sizeof (char *);
+
+/*
+ * Create a header file for PHP pages.  The "current readings" are an average
+ * over the last config.website_generation_interval seconds.
+ */
+
+/*
+ * An alternative query method could be prepared statements.  See
+ * http://dev.mysql.com/doc/refman/5.1/en/c-api-prepared-statements.html for
+ * details.  Peter Jeremy says: "Search for MYSQL_BIND, mysql_stmt_bind_param()
+ * and mysql_stmt_bind_result()."
+ */
+void make_php_header_file ()
+{
+  time_t start;
+  time_t now;
+  FILE *header_file;
+  char date_time [DATETEXTLENGTH];              /* Wednesday, 25 November 2009 16:47:46 */
+  time_t yesterday;
+  char yesterday_text [DATETEXTLENGTH];         /* YYYY-MM-DD */
+  int row;
+  int rough_direction;                          /* out of range */
+
+  header_file = fopen (config.php_header, "w");
+  if (! header_file)
+    fprintf (stderr,
+             "Can't open %s: %s (%d)\n",
+             config.php_header,
+             strerror (errno),
+             errno );
+  else
+  {
+    now = time (NULL);
+    start = now - config.website_generation_interval; /* determine the time we're doing this for */
+    strftime (date_time, DATETEXTLENGTH, "%A, %e %B %Y %T", localtime (&now));
+    yesterday = now - SECSPERDAY;               /* this time yesterday */
+    strftime (yesterday_text, DATETEXTLENGTH, "%F", localtime (&yesterday));
+
+    fprintf (header_file,
+             "<?php\n"
+             "/* Automatically generated file.  Do not edit */\n"
+             "\t$timetext = \"%s\";\n"
+             "\t$yesterday = \"%s\";\n"
+             "\t$timestamp = %d;\n",
+             date_time,                         /* time in text form */
+             yesterday_text,                    /* yesterday's date YYYY-MM-DD */
+             (int) now );                       /* time of reading */
+
+    sprintf (mysql_querytext,
+             "SELECT AVG(inside_humidity),\n"
+             "       AVG(inside_temp),\n"
+             "       AVG(inside_dewpoint),\n"
+             "       AVG(outside_humidity),\n"
+             "       AVG(outside_temp),\n"
+             "       AVG(outside_dewpoint),\n"
+             "       AVG(pressure_msl),\n"
+             "       AVG(wind_speed),\n"
+             "       AVG(wind_gust),\n"
+             "       AVG(wind_direction),\n"
+             "       SUM(rain)\n"
+             "FROM %s\n"
+             "WHERE unix_timestamp(timestamp(date,time)) >= %d\n"
+             "  AND unix_timestamp(timestamp(date, time)) <= %d\n"
+             "  AND station_id = \"%s\"\n",
+             config.db_table,
+             start,
+             now,
+             config.station_id );
+
+    if (doquery (mysql_querytext))
+      return;                                   /* failed and reported */
+
+    if (mysql_row = mysql_fetch_row (mysql_result)) /* got something */
+    {
+      for (row = 0; row < rowformats1; row++)
+      {
+        /*
+         * We have a problem here: if a value is missing, we can't just print
+         * it.  Fake a number for the time being.
+         */
+        if (mysql_row [row] && *mysql_row [row]) /* something there */
+          fprintf (header_file, rowformat1 [row], atof (mysql_row [row]));
+        else
+          fprintf (header_file, rowformat1 [row], -999.0);
+      }
+
+      /* Isn't this ugly? */
+      rough_direction = 1000;                   /* out of range */
+      if ((mysql_row [9]) && (*mysql_row [9]))  /* something there */
+        rough_direction = (int) ((atof (mysql_row [9]) + 11.25) / 22.5);
+
+      if ((rough_direction < 16) && (rough_direction >= 0))
+        fprintf (header_file,
+                 "\t$wind_direction_text = \"%s\";\n",
+                 wind_directions [rough_direction] ) ;
+      else
+        fprintf (header_file, "\t$wind_direction_text = \"(none)\";\n");
+      mysql_free_result (mysql_result);
+    }
+
+    /* Now stats for the day */
+    strftime (date_time, DATETEXTLENGTH, "%Y-%m-%d", localtime (&now));
+    sprintf (mysql_querytext,
+             "SELECT @max_inside_humidity := MAX(inside_humidity),\n"
+             "       @min_inside_humidity := MIN(inside_humidity),\n"
+             "       @max_inside_temp := MAX(inside_temp),\n"
+             "       @min_inside_temp := MIN(inside_temp),\n"
+             "       @max_inside_dewpoint := MAX(inside_dewpoint),\n"
+             "       @min_inside_dewpoint := MIN(inside_dewpoint),\n"
+             "       @max_outside_humidity := MAX(outside_humidity),\n"
+             "       @min_outside_humidity := MIN(outside_humidity),\n"
+             "       @max_outside_temp := MAX(outside_temp),\n"
+             "       @min_outside_temp := MIN(outside_temp),\n"
+             "       @max_outside_dewpoint := MAX(outside_dewpoint),\n"
+             "       @min_outside_dewpoint := MIN(outside_dewpoint),\n"
+             "       @max_pressure_msl := MAX(pressure_msl),\n"
+             "       @min_pressure_msl := MIN(pressure_msl),\n"
+             "       @max_wind_speed := MAX(wind_speed),\n"
+             "       @min_wind_speed := MIN(wind_speed),\n"
+             "       @max_wind_gust := MAX(wind_gust),\n"
+             "       @min_wind_gust := MIN(wind_gust),\n"
+             "       SUM(rain)\n"
+             "FROM %s\n"
+             "WHERE date = \"%s\"\n"
+             "  AND station_id = \"%s\"\n",
+             config.db_table,
+             date_time,
+             config.station_id );
+
+    if (doquery (mysql_querytext))
+      return;                                   /* failed and reported */
+
+    if (mysql_row = mysql_fetch_row (mysql_result)) /* got something */
+    {
+      for (row = 0; row < rowformats2; row++)
+        fprintf (header_file, rowformat2 [row], atof (mysql_row [row]));
+      mysql_free_result (mysql_result);
+    }
+
+    /* Now get the times that go with the maxima and minima */
+    for (row = 0; row < maxmincount; row++)
+    {
+      /* Time of maximum */
+      sprintf (mysql_querytext,
+               "SELECT time from %s\n"
+               "WHERE date = \"%s\"\n"
+               "  AND station_id = \"%s\"\n"
+               "  AND %s = @max_%s\n"
+               "LIMIT 1",
+               config.db_table,
+               date_time,
+               config.station_id,
+               maxmintimes [row],
+               maxmintimes [row] );
+      if (doquery (mysql_querytext))            /* failure */
+        return;                                 /* XXX be cleverer */
+      if (mysql_row = mysql_fetch_row (mysql_result)) /* got something */
+      {
+        fprintf (header_file, "\t$max_%s_time = \"%s\";\n", maxmintimes [row], mysql_row [0]);
+        mysql_free_result (mysql_result);
+      }
+      else                                      /* dummy */
+        fprintf (header_file, "\t$max_%s_time = \"unknown\"\n", maxmintimes [row]);
+      /* Now time of minimum */
+      sprintf (mysql_querytext,
+               "SELECT time from %s\n"
+               "WHERE date = \"%s\"\n"
+               "  AND station_id = \"%s\"\n"
+               "  AND %s = @min_%s\n"
+               "LIMIT 1",
+               config.db_table,
+               date_time,
+               config.station_id,
+               maxmintimes [row],
+               maxmintimes [row] );
+      if (doquery (mysql_querytext))            /* failure */
+        return;                                 /* XXX be cleverer */
+      if (mysql_row = mysql_fetch_row (mysql_result)) /* got something */
+      {
+        fprintf (header_file, "\t$min_%s_time = \"%s\";\n", maxmintimes [row], mysql_row [0]);
+        mysql_free_result (mysql_result);
+      }
+      else                                      /* dummy */
+        fprintf (header_file, "\t$min_%s_time = \"unknown\"\n", maxmintimes [row]);
+    }
+
+    fprintf (header_file, "\t?>\n");            /* end */
+    fclose (header_file);
+  }
+}
+
+void usage (char *me)
+{
+  fprintf (stderr,
+           "Usage: %s [-d] [-n] [-v] [-1] [station ID] [db user] [password] [db host] [database]\n",
+           me);
+  exit (1);
+}
+
+int main (int argc, char *argv [])
+{
+  time_t lastrun;
+  time_t nextrun;
+  time_t now;
+
+  if (read_config (argc, argv) < 0)
+    usage (argv [0]);
+
+  while (1)
+  {
+    lastrun = time (NULL);                      /* time we ran the loop */
+    make_php_header_file ();
+    if (once)                                   /* just run once */
+      exit (0);
+    /*
+     * Informing Wunderground takes finite time.  Stay on schedule.
+     * We may end up off by a second from time to time, but we shouldn't drift.
+     *
+     */
+    nextrun = lastrun + config.website_generation_interval; /* next time to run */
+    now = time (NULL);
+    sleep (nextrun - now);
+  }
+  return 0;
+}