On Thu, 29 Dec 2011, Charles Lepple wrote:

On Dec 28, 2011, at 9:16 AM, Ariel wrote:

On Wed, 28 Dec 2011, Charles Lepple wrote:

Is it possible that UPS.PowerSummary.Voltage is really an output voltage divided by 10? (This has happened on other devices.)

Well, I have two 12 volt batteries, so 24 volts makes sense, 240 doesn't.

I meant AC output voltage, not DC.

No, I'm in the US, so 120 volts.

I agree - I think the scaling should be configured in ups.conf, and be general for any field. To make it very easy support +-*/ without precedence - operations are done strictly left to right. Perhaps 3 fields: operation, min, max (min/max are applied after scaling, not before).

We will need to think about this a bit more to bound the scaling/offset problem.

In case you wish to do it, I wrote some functions to parse and execute a scale/offset/min/max calculation.

The code pre-parses a text specification, then there is a function to run the calculation on a specific value.

I did not try to integrate it into the code since I'm not very familiar with it, but I would expect that instead of things like:

{ "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, 
cps_battvolt },

You would have:

{ "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, "* 0.667" 
},

i.e. just a textual specification of the scaling/offset, and the driver could read in user specified options that would override the default (either on the command line or in ups.conf).

Or the divide_by_10_conversion_fun() function would be replaced by a default scaling (that can be overridden by the user - which is the main point of doing it in text), and then just a single generic output function.

I attached the files with the code.

An example of usage:

#include <stdio.h>
#include "str_math.h"

int main(void) {
  struct str_math spec;
  /* minus 6 times 3 divide by 8, min value 1, max 20 */
  printf(init_str_math(&spec, " - 6 * 3  /  8,  1, 20") ? "valid spec\n" : "invalid 
spec\n");

  printf("%f\n", str_math(&spec, 500));

  free_str_math(&spec);
  return 0;
}

        -Ariel
/* str_math - Parse and calculate an offset/scale text specification with min and max

   Copyright (C) 2011  Ariel Shkedi <[email protected]>

   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 of the License, 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
*/

struct str_math {
  double min;
  double max;
  int count;
  struct str_math_op *ops;
};

void free_str_math(struct str_math *str_math);
int init_str_math(struct str_math *str_math, char *str);
double str_math(struct str_math *str_math, double val);
/* str_math - Parse and calculate an offset/scale text specification with min and max

   Copyright (C) 2011  Ariel Shkedi <[email protected]>

   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 of the License, 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
*/

#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <math.h>
#include <float.h>

#include "str_math.h"
struct str_math_op {
  enum op {ADD, MUL} op;
  double val;
};

/* frees data in an str_math struct that was created by init_str_math */
void free_str_math(struct str_math *str_math) {
  free(str_math->ops);
  str_math->ops = NULL;
  str_math->count = 0;
}

/* Pass it a pointer to an str_math struct, and a string with the scale/offset specification
   Returns 1 for a valid string, 0 for an invalid string (error)
   This function initializes the structure to prepare for calculation.
   To actually do a calculation call str_math()

   format is:
   scale/offset, min, max
   all fields are optional and can be left blank (and commas can be omitted if at the end)
   eg: just a max of 10: ,,10
   eg: just a scale of 2 plus an offset of 3: *2 +3
   the scale/offset calculates in order, and does not have any precedence rules
   pass it any number of <operator value> pairs (operator is + - * /)
*/
int init_str_math(struct str_math *str_math, char *str) {
  char op;
  char *ptr;

  str_math->count = 0;
  str_math->ops = NULL;

  str--;
  while(isspace(*++str));

  while(*str != '\0' && *str != ',') {
    str_math->ops = realloc(str_math->ops, sizeof(struct str_math_op) * ++str_math->count);
    op = *str++;

    errno = 0;
    str_math->ops[str_math->count-1].val = strtod(str, &ptr);
    if(errno) {
      /* underflow or overflow */
      free_str_math(str_math);
      return 0;
    }
    if (str == ptr) {
      /* invalid digits */
      free_str_math(str_math);
      return 0;
    }
    str = ptr;

    switch(op) {
      case '-':
        str_math->ops[str_math->count-1].val *= -1;
      case '+':
        str_math->ops[str_math->count-1].op = ADD;
        break;

      case '/':
        str_math->ops[str_math->count-1].val = 1/str_math->ops[str_math->count-1].val;
      case '*':
        str_math->ops[str_math->count-1].op = MUL;
        break;

      default:
        /* not a valid operator */
        free_str_math(str_math);
        return 0;
    }
    str--;
    while(isspace(*++str));
  }

  if(*str != '\0') str++;
  str_math->min = strtod(str, &ptr);
  if(errno || str == ptr) {
    /* underflow, overflow, invalid or blank */
    str_math->min = -DBL_MAX;
  }

  str = ptr-1;
  while(isspace(*++str));

  if(*str != '\0' && *str != ',') {  /* invalid specification */
    free_str_math(str_math);
    return 0;
  }

  if(*str != '\0') str++;
  str_math->max = strtod(str, &ptr);
  if(errno || str == ptr) {
    /* underflow, overflow, invalid or blank */
    str_math->max = DBL_MAX;
  }
  str = ptr-1;
  while(isspace(*++str));

  if(*str != '\0' && *str != ',') {  /* invalid specification */
    free_str_math(str_math);
    return 0;
  }

  return 1;
}

/* runs the calculation in str_math */
double str_math(struct str_math *str_math, double val) {
  int i;
  for(i = 0; i < str_math->count; i++) {
    switch(str_math->ops[i].op) {
      case ADD:
        val += str_math->ops[i].val;
        break;

      case MUL:
        val *= str_math->ops[i].val;
        break;
    }
  }

  if(val > str_math->max) val = str_math->max;
  if(val < str_math->min) val = str_math->min;

  return val;
}
_______________________________________________
Nut-upsdev mailing list
[email protected]
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/nut-upsdev

Reply via email to