Subversion Repositories tiedf232

Rev

Rev 17 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/* Hey EMACS -*- linux-c -*- */
/* $Id$ */

/*
  tiedf - Decode la sortie tele-information d'un compteur EDF.
 
  Copyright (C) 2006-2012 Romain Liévin
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
*/


/*
  Remarque importante:
  - EDF appelle 'puissance apparente' en 'VA' ce qui est en realite la
  puissance instantanee,
  - EDF appelle 'puissance instantanee' en 'W' ce qui est en realite la
  puissance active,
*/


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#ifdef HAVE_TERMIOS_H
# include <termios.h>
# include <sys/ioctl.h>
#endif

#include "getopt.h"
#include "tiedf.h"

#define _(s) (s)

#define TMP_FILE "/tmp/tiedf.tmp"
#define CONF_FILE "/etc/tiedf.conf"

/* ------------------------------------------------------------------------- */

char *program_name;
char cmdline_device[256];
int cmdline_init =0;
int cmdline_v1 = 0;
int  cmdline_v2 = 0;
int cmdline_msc = 0;
char cmdline_label[16];
int cmdline_all = 0;
int cmdline_cycle = 0;
int cmdline_mysql = 0;
char cmdline_filename[256];

enum {DUMMY_CODE=129};

LABEL labels[];

static void usage (int status)
{
  int i;

  printf (_("%s - \
 Decode la sortie tele-information d'un compteur EDF.\n"
), program_name);
  printf (_("Usage: %s [OPTIONS] LOG_FILE\n"), program_name);
 
  printf (_("\
 Options:\n\
\
 -h, --help             display this help and exit\n\
 -V,--version           output version information and exit\n\
\
 -D,--device=TTY        serial device to use like '/dev/ttyS0'\n\
 -I,--init              init serial device and exits\n\
 -1,--v1                set power on TI-EDF v1 interface\n\
 -2,--v2                set power on TI-EDF v2 interface\n\
 -S,--msc               use higher baud-rate with MSC interface\n\
\
 -L,--label=NAME        show a valid NAME label (4..8 chars)\n"
));
   
  for(i = 0; !strcmp(labels[i].name, ""); i++)
  {
    printf("  %s\n", labels[i].name);
  }
  printf("             PACT        show active power => UI*cos(phi) in W\n");
  printf("             ETOTAL      show total energy (cumulative) in Wh\n");
  printf("             CONSO       show energy used since last call in Wh\n");
 
  printf(_("\
 -A,--all               show all labels and their values\n\
 -C,--cycle             process one data cycle and exit\n\
\
 -M,--mysql             do a mysql request (login and password\n\t\t\t must be in /etc/tiedf.conf)\n\n"
));
 
  printf (_("\
 Log File:\n\
 filename               use file rather than stdin\n\
 "
));
 
  exit (status);
}

/* Option flags and variables */
static struct option const long_options[] =
{
  {"help", no_argument, 0, 'h'},
  {"version", no_argument, 0, 'V'},
 
  {"device", required_argument, 0, 'D'},
  {"init", no_argument, 0, 'I'},
  {"v1", no_argument, 0, '1'},
  {"v2", no_argument, 0, '2'},
  {"msc", no_argument, 0, 'S'},
 
  {"label", required_argument, 0, 'L'},
 
  {"all", no_argument, 0, 'A'},
  {"cycle", no_argument, 0, 'C'},
 
  {"mysql", no_argument, 0, 'M'},
  {NULL, 0, NULL, 0}
};


/* Set all the option flags according to the switches specified.
   Return the index of the first non-option argument.  */

static int
decode_switches (int argc, char **argv)
{
  int c;

  while ((c = getopt_long (argc, argv, "hVDI12SLACM",              
                           long_options, (int *) 0)) != EOF)
  {
      switch (c)
      {
      case 'V':
          printf ("tiedf %s\n", VERSION);
          exit (0);
         
      case 'h':
          usage (0);
         
      case 'D':
          strncpy(cmdline_device, optarg, sizeof(cmdline_device));
          break;
         
      case 'I':
          cmdline_init =!0;
          break;
         
      case '1':
          cmdline_v1 = !0;
          break;
         
      case '2':
          cmdline_v2 = !0;
          break;
         
      case 'S':
          cmdline_msc = !0;
          break;
         
      case 'L':
          strncpy(cmdline_label, optarg, sizeof(cmdline_label));
          break;
         
      case 'A':
          cmdline_all = !0;
          break;
         
      case 'C':
          cmdline_cycle = !0;
          break;
         
      case 'M':
          cmdline_mysql = !0;
          break;
         
      default:
          usage (EXIT_FAILURE);
      }
  }
 
  if (optind < argc)
  {
      strncpy(cmdline_filename, argv[optind++], sizeof(cmdline_filename));
      printf ("%s ", cmdline_filename);
      printf ("\n");
  }
 
  return optind;
}

/* ------------------------------------------------------------------------- */

LABEL labels[] =
  {
    {"ADCO", "", "", 12, 0},
    {"OPTARIF", "", "", 4, 0},
    {"ISOUSC", "", "A", 2, 0},
    {"BASE", "", "Wh", 9, 0},
    {"HCHC", "", "Wh", 9, 0},
    {"HCHP", "", "Wh", 9, 0},
    {"EJPHN", "", "Wh", 9, 0},
    {"EJPHPM", "", "Wh", 9, 0},
    {"BBRHCJB", "", "Wh", 9, 0},
    {"BBRHPJB", "", "Wh", 9, 0},
    {"BBRHCJW", "", "Wh", 9, 0},
    {"BBRHPJW", "", "Wh", 9, 0},
    {"BBRHCJR", "", "Wh", 9, 0},
    {"BBRHPJR", "", "Wh", 9, 0},
    {"PEJP", "", "", 2, 0},
    {"PTEC", "", "", 4, 0},
    {"DEMAIN", "", "", 4, 0},
    {"IINST", "", "A", 3, 0},
    {"ADPS", "", "A", 3, 0},
    {"IMAX", "", "A", 3, 0},
    {"PAPP", "", "VA", 5, 0},
    {"HHPHC", "", "", 1, 0},
    {"MOTDETAT", "", "", 6, 0},
    {"", "", "", 0, 0},
  };

static int label2index(const char *str)
{
  int i;

  for(i = 0; labels[i].name[0]; i++)
      if(!strcmp(labels[i].name, str))
          return i;

  return -1;
}

/* ------------------------------------------------------------------------- */

static int stamp_read(time_t *stamp, unsigned long *value)
{
  FILE *f = fopen(TMP_FILE, "rt");
 
  if(f == NULL)
  {
    return -1;
  }

  fscanf(f, "%lu:%lu", stamp, value);

  fclose(f);

  return 0;
}

static int stamp_write(time_t stamp, unsigned long value)
{
  FILE *f = fopen(TMP_FILE, "wt");

  if(f == NULL)
  {
    return -1;
  }

  fprintf(f, "%lu:%lu", stamp, value);

  fclose(f);

  return 0;
}

/* ------------------------------------------------------------------------- */

SQL_CONFIG config = { 0 };

static int conf_read(void)
{
  FILE *f = fopen(CONF_FILE, "rt");

  if(f == NULL)
    return -1;

  while(!feof(f))
  {
    char line[256];
    char *tok;
   
    fgets(line, sizeof(line), f);
    line[strlen(line)-1] = '\0';

    if(feof(f))
    {
      break;
    }

    if(!strncmp(line, "---", 3))
    {
      break;
    }

    if(feof(f))
    {
      break;
    }

    tok = strchr(line, '=');
    if(tok)
      {
        if(strstr(line, "user"))
          {
            strcpy(config.login, line+5);
            continue;
          }
        if(strstr(line, "passwd"))
          {
            strcpy(config.passwd, line+7);
            continue;
          }
      }
   
    tok = strchr(line, ':');
    if(tok)
      {
        char label[14];
        int idx;
       
        strncpy(label, line, tok - line);
        label[tok-line] = '\0';

        idx = label2index(label);
        if(idx == -1)
          {
            fprintf(stderr, "Error in config file (%s): %s\n",CONF_FILE,label);
            break;
          }

        config.list[config.count].edf_index = idx;
        strcpy(config.list[config.count].edf_label, label);
        strcpy(config.list[config.count].sql_column, ++tok);
        /*
        printf("<%s> (%i) <%s>\n",
               config.list[config.count].edf_label,
               config.list[config.count].edf_index,
               config.list[config.count].sql_column);
        */

        config.count++;
      }
  }

  fclose(f);
  return 0;
}

/* ------------------------------------------------------------------------- */

void sig_alarm(int s)
{
  fprintf(stdout, "Timeout elapsed ! Exiting...\n");
  exit(-1);
}

/* ------------------------------------------------------------------------- */

int main (int argc, char **argv)
{
  int i, j;
  const char *var = NULL;
  char *filename = NULL;
  int fd;

#ifdef HAVE_TERMIOS_H
  struct termios t = {0};
  unsigned int flags = 0;
#endif
 
  FILE *f = stdin;
  char buf[1024];
  unsigned char sum, chksum;
  int idx;

  unsigned long energie = 0;
  unsigned long power = 0;
  unsigned long conso = 0;
 
  // parse command line
  program_name = argv[0];
  i = decode_switches (argc, argv);
 
#ifdef HAVE_TERMIOS_H
  if(cmdline_device)
  {
      int n;
     
      // open com
      fprintf(stdout, "Init'ing serial device '%s':\n", cmdline_device);
      fd = open(cmdline_device, O_RDWR | O_SYNC);
      if(fd == -1)
        {
          perror("open");
          return -1;
        }
     
      // set power on v1
      if(cmdline_v1)
      {
          i = TIOCM_DTR;
          if(ioctl(fd, TIOCMBIS, &i) == -1)
          {
              perror("ioctl");
              return -1;
          }
         
          i = TIOCM_RTS;
          if(ioctl(fd, TIOCMBIS, &i) == -1)
          {
              perror("ioctl");
              return -1;
          }
          fprintf(stdout, " - power switched on\n");
      }
     
      // set power on v2
      if(cmdline_v2)
      {
          i = TIOCM_DTR;
          if(ioctl(fd, TIOCMBIC, &i) == -1)
          {
              perror("ioctl");
              return -1;
          }
         
          i = TIOCM_RTS;
          if(ioctl(fd, TIOCMBIS, &i) == -1)
          {
              perror("ioctl");
              return -1;
          }
          fprintf(stdout, " - power switched on\n");
      }

      // set comm (1200 7E1)
      t.c_iflag = IGNBRK | INPCK | ISTRIP; // strip 8th bit (remove parity)
      t.c_oflag = 0;
      t.c_cflag = PARENB | CS7 | CREAD | CLOCAL;
      t.c_lflag = 0;
      t.c_cc[VMIN] = 1;
      t.c_cc[VTIME] = 5;
      if(cmdline_msc)
      {
          cfsetispeed(&t, B57600);
      }
      else
      {
          cfsetispeed(&t, B1200);
      }
     
      if(tcsetattr(fd, TCSANOW, &t) == -1)
        {
          perror("tcsetattr");
          return -1;
        }
      fprintf(stdout, " - communication set-up to 1200 7E1\n");
     
      // flush chars
      if(tcflush(fd, TCIOFLUSH) == -1)
        {
          perror("flush");
          return -1;
        }
      fprintf(stdout, " - buffer flushed\n");
      /*
      n = read(fd, buf, 10);
      printf("n = %i\n", n);
      printf("buf = <%s>\n", buf);
      */

      close(fd);
      fprintf(stdout, "Done.\n");

      //      if(init)
      //return 0;
    }

  // load conf parameters for MySQL
  conf_read();

  if(var == NULL && !cmdline_all)
    return 0;
 
  // File or stdin: job is the same !
  if(filename)
    {
      f = fopen(filename, "rb");
      if(f == NULL)
        {
          perror("fopen");
          return -1;
        }
    }
  else if(cmdline_device)
    {
      f = fopen(cmdline_device, "rb");
      if(f == NULL)
        {
          perror("fopen");
          return -1;
        }
    }

  // dead man timer (exits infinite loop)
  if(cmdline_cycle)
    {
      signal(SIGALRM, sig_alarm);
      alarm(10);
    }

  // synch on beginning of cycle (ETX followed by STX)
  {
    char c;

    fprintf(stdout, "Waiting for synch:\n");
    do { c = fgetc(f); } while(c != ETX && c != EOF);
    fprintf(stdout, " - ETX\n");
    do { c = fgetc(f); } while(c != STX && c != EOF);
    fprintf(stdout, " - STX\n");
    if(c == EOF)
      exit(-1);
    fprintf(stdout, "Done.\n");
  }    

  // main loop
  fprintf(stdout, "Data:\n");
  while(!feof(f))
    {
      char c;
      char label[9];
      char data[13];
      static int ncycles = 0;
     
      /* check for start of group */
      c = fgetc(f);
      if(c == ETX)
        ncycles++;

      if(c == ETX && cmdline_cycle && ncycles > 0)
        break;
     
      if(c != LF) continue;
      sum = 0;
     
      /* get label (8 chars max) */
      for(j = 0; j < 9; j++)
        {
          c = fgetc(f);
          label[j] = c;
          sum += label[j];
         
          if(c == SP)
            break;             
        }
      label[j] = '\0';
     
      /* check for separator */
      if(c != SP) continue;
      sum += SP;
     
      /* get data (12 chars max) */
      for(j = 0; j < 13; j++)
        {
          c = fgetc(f);
          data[j] = c;
          sum += data[j];
         
          if(c == SP)
            break;             
        }
      data[j] = '\0';
     
      /* check for separator */
      if(c != SP) continue;
     
      /* get checksum */
      chksum = c = fgetc(f);
      if(CHKSUM(sum) != chksum)
        {
          printf("chk error: %02x %02x !\n", CHKSUM(sum), chksum);
          continue;
        }
     
      /* check for end of group */
      c = fgetc(f);
      if(c != CR) continue;
     
      // process label
      idx = label2index(label);
      if(idx != -1)
        {
          strcpy(labels[idx].value, data);
          labels[idx].valid = !0;

          if(cmdline_all)
            fprintf(stdout, " %s=%s %s\n",
                   labels[idx].name, labels[idx].value, labels[idx].unit);
          else if(var != NULL && idx == label2index(var))
            {
              fprintf(stdout, " %s=%s %s\n", labels[idx].name,
                      labels[idx].value, labels[idx].unit);
            }
        }
    }
 
  // Close file
  if(filename)
    fclose(f);

  if(cmdline_all || !strcmp(var, "ETOTAL")
     || !strcmp(var, "PACT") || !strcmp(var, "CONSO"))
    {
      time_t stamp;
         
      if(labels[OPTARIF].valid)
        {
          if(!strcmp(labels[OPTARIF].value, "BASE"))
            if(labels[BASE].valid)
              energie = atoi(labels[BASE].value);

          if(!strcmp(labels[OPTARIF].value, "HC.."))
            {
              if(labels[HCHP].valid)
                energie += atoi(labels[HCHP].value);
              if(labels[HCHC].valid)             
                energie += atoi(labels[HCHC].value);
            }
         
          if(!strcmp(labels[OPTARIF].value, "EJP."))
            {
              if(labels[EJPHPM].valid)
                energie += atoi(labels[EJPHPM].value);
              if(labels[EJPHN].valid)          
                energie += atoi(labels[EJPHN].value);
            }
         
          if(!strncmp(labels[OPTARIF].value, "BBR", 3))
            {
              if(labels[BBRHCJB].valid)
                energie += atoi(labels[BBRHCJB].value);
              if(labels[BBRHPJB].valid)
                energie += atoi(labels[BBRHPJB].value);
              if(labels[BBRHCJW].valid)
                energie += atoi(labels[BBRHCJW].value);
              if(labels[BBRHPJW].valid)
                energie += atoi(labels[BBRHPJW].value);
              if(labels[BBRHCJR].valid)
                energie += atoi(labels[BBRHCJR].value);
              if(labels[BBRHPJR].valid)
                energie += atoi(labels[BBRHPJR].value);
            }
        }

      if(cmdline_all || !strcmp(var, "PACT") || !strcmp(var, "CONSO"))
        {
          time_t old_stamp;
          unsigned long old_energie;
          unsigned long delta_t;
          unsigned long delta_e;

          stamp_read(&old_stamp, &old_energie);
          stamp = time(NULL);
          stamp_write(stamp, energie);

          delta_t = difftime(stamp, old_stamp);
          delta_e = energie - old_energie;

          if(delta_e > 2000) // can't use 2000Wh in 5 minutes
            delta_e = 0;

          power = (3600 * delta_e) / delta_t;
          //printf("%lu %lu %lu\n", delta_e, delta_t, power);

          if(cmdline_all || !strcmp(var, "PACT"))
            fprintf(stdout, " PACT=%lu W\n", power);
          if(cmdline_all || !strcmp(var, "CONSO"))
            fprintf(stdout, " CONSO=%lu Wh\n", conso = delta_e);
        }

      if(cmdline_all || !strcmp(var, "ETOTAL"))
        fprintf(stdout, " ETOTAL=%lu Wh\n", energie);
    }
  else
  {
    return -1;
  }

  fprintf(stdout, "Done.\n");

  if(cmdline_mysql)
    {
      time_t t = time(NULL);
      struct tm *tm = localtime(&t);
      char str[65536];
      char tmp[65536];
      int i;

      // nexen.net: MySQL book
#if 1
      fprintf(stdout, "Preparing MySQL request:\n");
      sprintf(str, "mysql -u %s -p%s --exec='use edf; insert into mesures (stamp, ptec, hchc, hchp, iinst, papp) values (\"%04i-%02i-%02i %02i:%02i:%02i\", \"%s\", %s, %s, %s, %s);'",
              config.login,
              config.passwd,
              1900+tm->tm_year, tm->tm_mon+1, tm->tm_mday,
              tm->tm_hour, tm->tm_min, tm->tm_sec,
              strlen(labels[PTEC].value) ? labels[PTEC].value : "?",
              strlen(labels[HCHC].value) ? labels[HCHC].value : "0",
              strlen(labels[HCHP].value) ? labels[HCHP].value : "0",
              strlen(labels[IINST].value) ? labels[IINST].value : "0",
              strlen(labels[PAPP].value) ? labels[PAPP].value : "0");
#else
      sprintf(str, "mysql -u %s -p%s --exec='use edf; ",
              config.login, config.passwd);

      sprintf(tmp, "insert into mesures (stamp, ");
      strcat(str, tmp);

      for(i = 0; i < config.count; i++)
        {
          sprintf(tmp, ", %s", config.list[i].sql_column);
          strcat(str, tmp);
        }

      // not finished: how to make diff between strings and ints ?
#endif
      fprintf(stdout, " ");
      fprintf(stdout, str);
      fprintf(stdout, "\n");
      if(system(str) == -1)
        {
          fprintf(stderr, "system() failed.\n");
        }
      else
        fprintf(stdout, "Done.\n");
    }
   
#else
  fprintf(stdout, "Unable to to anything: no serial/termios support.");
#endif
       
  return 0;
}