Logo Search packages:      
Sourcecode: nut version File versions

nitram.c

/* nitram.c - Network UPS Tools driver for Nitram Systems units
   This driver support:
    - Nitram Elite 2005 (manufactured by Cyber Power)

   Copyrights:
   (C) 2005 Olivier Albiez <oalbiez@free.fr>
   (C) 2005 Nadine Albiez <oalbiez@free.fr>

   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 <string.h>
#include <sys/ioctl.h>

#include "main.h"
#include "serial.h"
#include "nitram.h"

#define ENDCHAR   '\r'
#define IGNCHARS ""
#define UPSDELAY 50000

#define VOLTAGE_RANGE 3


struct status_t
{
      char input_tag;
      char input[5];
      char output_tag;
      char output[5];
      char load_tag;
      char load[3];
      char battery_tag;
      char battery[3];
      char temperature_tag;
      char temperature[3];
      char frequency_tag;
      char frequency[5];
      char flags_tag;
      char flags[2];
};


struct fields_t
{
      char* field[10];
      int count;
};


struct buffer_t
{
      char frame[128];
      char* payload;
      size_t length;
};


static void send_command(const char* command)
{
      ser_send_pace(upsfd, UPSDELAY, "%s\r", command);
}


static void send_command_and_flush(const char* command)
{
      struct buffer_t reply;
      send_command(command);
      usleep(100000);
      ser_get_line(upsfd, reply.frame, sizeof(reply.frame), ENDCHAR, IGNCHARS, 3, 0);
}


static int execute_command(const char* command, struct buffer_t* reply)
{
      int status;
      send_command(command);
      usleep(100000);
      status = ser_get_line(upsfd, reply->frame, sizeof(reply->frame), ENDCHAR, IGNCHARS, 3, 0);

      if (status <= 1)
      {
            ser_comm_fail("Invalid frame size");
            memset(reply, 0, sizeof(struct buffer_t));
            return 0;
      }
      else if (reply->frame[0] != '#')
      {
            ser_comm_fail("Invalid frame marker 0x%02x", reply->frame[0]);
            memset(reply, 0, sizeof(struct buffer_t));
            return 0;
      }

      ser_comm_good();
      reply->payload = reply->frame+1;
      reply->length = status-1;
      return 1;
}


static struct fields_t extract_fields(struct buffer_t* reply)
{
      struct fields_t result;
      char* payload = reply->payload;
      char* position;
      result.count = 0;
      while ((position = strchr(payload, ',')) != NULL)
      {
            *position++ = 0;
            result.field[result.count] = payload;
            ++result.count;
            payload = position;
      }
      result.field[result.count] = payload;
      ++result.count;
      return result;
}


static int extract_status(struct buffer_t* reply, struct status_t** status)
{
      if (reply->length != sizeof(struct status_t))
      {
            return 0;
      }

      *status = (struct status_t*)(reply->payload);
      #define CHECK_AND_CLEAR_TAG(name, value) if ((*status)->name != (value)) return 0; else (*status)->name = 0
      CHECK_AND_CLEAR_TAG(input_tag, 'I');
      CHECK_AND_CLEAR_TAG(output_tag, 'O');
      CHECK_AND_CLEAR_TAG(load_tag, 'L');
      CHECK_AND_CLEAR_TAG(battery_tag, 'B');
      CHECK_AND_CLEAR_TAG(temperature_tag, 'T');
      CHECK_AND_CLEAR_TAG(frequency_tag, 'F');
      CHECK_AND_CLEAR_TAG(flags_tag, 'S');
      #undef CHECK_AND_CLEAR_TAG
      return 1;
}


static int get_identification(struct buffer_t* reply)
{
      int tries;
      ser_flush_in(upsfd, IGNCHARS, 0);
      for (tries = 0; tries < 3; tries++)
      {
            if (execute_command("P4", reply) == 1)
                  return 1;
      }
      upslogx(LOG_INFO, "Giving up on hardware detection after 3 tries");
      return 0;
}


static int instcmd(const char *command, const char *extra)
{
      #define DEFINE_COMMAND(name, nitram_command) \
            if (strcasecmp(command, (name)) == 0) \
            { \
                  send_command_and_flush(nitram_command); \
                  return STAT_INSTCMD_HANDLED; \
            }
      DEFINE_COMMAND("test.battery.start", "T.1");
      DEFINE_COMMAND("test.battery.stop", "CT");
      DEFINE_COMMAND("beeper.on", "C7:1");
      DEFINE_COMMAND("beeper.off", "C7:0");
      #undef DEFINE_COMMAND

      upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", command);
      return STAT_INSTCMD_UNKNOWN;
}


static int setvar(const char *varname, const char *val)
{
      struct buffer_t reply;
      char command[50];

      if (strcasecmp(varname, "battery.charge.low") == 0)
      {
            snprintf(command, sizeof(command), "C4:%s", val);
            command[sizeof(command)] = 0;
            if (execute_command(command, &reply) == 1)
            {
                  dstate_setinfo("battery.charge.low", val);
            }
            return STAT_INSTCMD_HANDLED;
      }

      upslogx(LOG_NOTICE, "setvar: unknown var [%s]", varname);
      return STAT_SET_UNKNOWN;
}


void upsdrv_initinfo(void)
{
      struct buffer_t reply;
      struct fields_t fields;

      if (get_identification(&reply) == 1)
      {
            fields = extract_fields(&reply);
            dstate_setinfo("ups.model", "%s", fields.field[0]);
            dstate_setinfo("ups.firmware", "%s", fields.field[1]);
            dstate_setinfo("ups.mfr", "CyberPower for Nitram");
            dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
      }
      else
      {
            fatalx("Unable to get initial hardware info string");
      }

      dstate_setinfo("battery.charge.low", "20");

      if (execute_command("P1", &reply) == 1)
      {
            fields = extract_fields(&reply);
            dstate_setinfo("output.voltage.nominal", "%s", fields.field[0]);
            dstate_setinfo("input.voltage.maximum", "%s", fields.field[1]);
            dstate_setinfo("input.voltage.minimum", "%s", fields.field[2]);
            dstate_setinfo("battery.charge.low", "%s", fields.field[3]);
      }

      if (execute_command("P3", &reply) == 1)
      {
            fields = extract_fields(&reply);
            dstate_setinfo("battery.voltage", "%s", fields.field[0]);
            dstate_setinfo("battery.packs", "%s", fields.field[1]);
            dstate_setinfo("battery.current", "%s", fields.field[2]);
      }

      dstate_setflags("battery.charge.low", ST_FLAG_STRING | ST_FLAG_RW);
      dstate_setaux("battery.charge.low", 20);
      dstate_addcmd("test.battery.start");
      dstate_addcmd("test.battery.stop");
      dstate_addcmd("beeper.on");
      dstate_addcmd("beeper.off");
      upsh.instcmd = instcmd;
      upsh.setvar = setvar;
}


void upsdrv_updateinfo(void)
{
      struct buffer_t reply;
      struct status_t* status;

      if (execute_command("D", &reply) == 0)
      {
            dstate_datastale();
            return;
      }

      if (extract_status(&reply, &status) == 0)
      {
            dstate_datastale();
            return;
      }

      dstate_setinfo("input.frequency", "%s", status->frequency);
      dstate_setinfo("ups.temperature", "%s", status->temperature);
      dstate_setinfo("battery.charge", "%s", status->battery);
      dstate_setinfo("ups.load", "%s", status->load);
      dstate_setinfo("input.voltage", "%s", status->input);
      dstate_setinfo("output.voltage", "%s", status->output);

      status_init();
      if (status->flags[0] & 0x40)
            status_set("OB");
      else
            status_set("OL");

      if (status->flags[0] & 0x20)
            status_set("LB");

      if (atoi(status->input) > atoi(status->output) + VOLTAGE_RANGE)
            status_set("TRIM");

      if (atoi(status->input) < atoi(status->output) - VOLTAGE_RANGE)
            status_set("BOOST");

      if ((atoi(status->battery) == 100) && !(status->flags[0] & 0x40))
            status_set("BYPASS");

      status_commit();
      dstate_dataok();

      upsdebugx(LOG_DEBUG, "status:%s; load:%s; battery:%s; input:%s; output:%s; frequency:%s;",
            dstate_getinfo("ups.status"),
            status->load,
            status->battery,
            status->input,
            status->output,
            status->frequency);
}


void upsdrv_shutdown(void)
{
      fatalx("shutdown not supported");
}


void upsdrv_help(void)
{
}


void upsdrv_makevartable(void)
{
}


void upsdrv_banner(void)
{
      printf("Network UPS Tools - Nitram UPS driver %s (%s)\n\n",
            DRV_VERSION, UPS_VERSION);
}


void upsdrv_initups(void)
{
      int dtr_bit = TIOCM_DTR;
      int rts_bit = TIOCM_RTS;

      upsfd = ser_open(device_path);
      ser_set_speed(upsfd, device_path, B2400);

      /* dtr high, rts high */
      ioctl(upsfd, TIOCMBIS, &rts_bit);
      ioctl(upsfd, TIOCMBIS, &dtr_bit);
}


void upsdrv_cleanup(void)
{
      ser_close(upsfd, device_path);
}




Generated by  Doxygen 1.6.0   Back to index