2010-03-25 23:20:59 +00:00
|
|
|
/* rhino.c - driver for Microsol Rhino UPS hardware
|
|
|
|
|
|
|
|
Copyright (C) 2004 Silvino B. Magalhães <sbm2yk@gmail.com>
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
2004/11/13 - Version 0.10 - Initial release
|
|
|
|
2005/07/07 - Version 0.20 - Initial rhino commands tests
|
|
|
|
2005/10/25 - Version 0.30 - Operational-1 release
|
|
|
|
2005/10/26 - Version 0.40 - Operational-2 release
|
|
|
|
2005/11/29 - Version 0.50 - rhino commands release
|
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
|
2010-03-25 23:20:59 +00:00
|
|
|
http://www.microsol.com.br
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
#include "config.h" /* must be the first header */
|
|
|
|
|
2010-03-25 23:20:59 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include "main.h"
|
|
|
|
#include "serial.h"
|
2022-06-29 10:37:36 +00:00
|
|
|
#include "nut_float.h"
|
2010-03-25 23:20:59 +00:00
|
|
|
#include "timehead.h"
|
|
|
|
|
|
|
|
#define DRIVER_NAME "Microsol Rhino UPS driver"
|
2022-06-29 10:37:36 +00:00
|
|
|
#define DRIVER_VERSION "0.52"
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
/* driver description structure */
|
|
|
|
upsdrv_info_t upsdrv_info = {
|
|
|
|
DRIVER_NAME,
|
|
|
|
DRIVER_VERSION,
|
|
|
|
"Silvino B. Magalhaes <sbm2yk@gmail.com>",
|
|
|
|
DRV_STABLE,
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
#define UPSDELAY 500 /* 0.5 ms delay */
|
|
|
|
|
|
|
|
typedef int bool_t;
|
|
|
|
|
|
|
|
#define false 0
|
|
|
|
#define true 1
|
|
|
|
|
|
|
|
/* rhino commands */
|
|
|
|
#define CMD_INON 0x0001
|
|
|
|
#define CMD_INOFF 0x0002
|
|
|
|
#define CMD_SHUT 0x0004
|
|
|
|
#define CMD_OUTON 0x0003
|
|
|
|
#define CMD_OUTOFF 0x0004
|
|
|
|
#define CMD_PASSON 0x0005
|
|
|
|
#define CMD_PASSOFF 0x0006
|
|
|
|
#define CMD_UPSCONT 0x0053
|
|
|
|
|
2011-06-01 20:31:49 +00:00
|
|
|
/* xoff - xon protocol */
|
|
|
|
#define _SOH = 0x01; /* start of header */
|
|
|
|
#define _EOT = 0x04; /* end of transmission */
|
|
|
|
#define _ACK = 0x06; /* acknoledge (positive) */
|
|
|
|
#define _DLE = 0x10; /* data link escape */
|
|
|
|
#define _XOn = 0x11; /* transmit on */
|
|
|
|
#define _XOff = 0x13; /* transmit off */
|
|
|
|
#define _NAK = 0x15; /* negative acknoledge */
|
|
|
|
#define _SYN = 0x16; /* synchronous idle */
|
|
|
|
#define _CAN = 0x18; /* cancel */
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
static int const pacsize = 37; /* size of receive data package */
|
|
|
|
|
|
|
|
/* autonomy calcule */
|
2011-06-01 20:31:49 +00:00
|
|
|
static double const AmpH = 40; /* Amperes-hora da bateria */
|
|
|
|
static double const VbatMin = 126; /* Tensão mínina das baterias */
|
|
|
|
static double const VbatNom = 144; /* Tensão nominal das baterias */
|
|
|
|
static double const FM = 0.32; /* Fator multiplicativo de correção da autonomia */
|
|
|
|
static double const FA = -2; /* Fator aditivo de correção da autonomia */
|
|
|
|
static double const ConstInt = 250; /* Consumo interno sem o carregador */
|
|
|
|
static double const Vin = 220; /* Tensão de entrada */
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
static int Day, Month, Year;
|
|
|
|
static int dian=0, mesn=0, anon=0, weekn=0;
|
|
|
|
static int ihour,imin, isec;
|
|
|
|
/* unsigned char DaysOnWeek; */
|
|
|
|
/* char seman[4]; */
|
|
|
|
|
|
|
|
/* int FExpansaoBateria; */
|
2011-06-01 20:31:49 +00:00
|
|
|
/* internal variables */
|
|
|
|
/* package handshake variables */
|
2010-03-25 23:20:59 +00:00
|
|
|
/* int ContadorEstouro; */
|
|
|
|
static bool_t detected;
|
|
|
|
static bool_t SourceFail, Out110, RedeAnterior, OcorrenciaDeFalha;
|
|
|
|
static bool_t RetornoDaRede, SuperAquecimento, SuperAquecimentoAnterior;
|
|
|
|
static bool_t OverCharge, OldOverCharge, CriticBatt, OldCritBatt;
|
|
|
|
static bool_t Flag_inversor, BypassOn, InputOn, OutputOn;
|
|
|
|
static bool_t LowBatt, oldInversorOn;
|
|
|
|
/* data vetor from received and configuration data package - not used yet
|
|
|
|
unsigned char Dados[ 161 ]; */
|
|
|
|
/* identification group */
|
|
|
|
static int RhinoModel; /*, imodel; */
|
|
|
|
static int PotenciaNominal, PowerFactor;
|
|
|
|
/* input group */
|
|
|
|
static double AppPowerIn, UtilPowerIn, InFreq, InCurrent;
|
|
|
|
static double LimInfEntrada, LimSupEntrada, ValorNominalEntrada;
|
|
|
|
static int FatorPotEntrada;
|
|
|
|
/* output group */
|
|
|
|
static double OutVoltage, InVoltage, OutCurrent, AppPowerOut;
|
|
|
|
static double UtilPowerOut, OutFreq, LimInfSaida, LimSupSaida, ValorNominalSaida;
|
|
|
|
static int FatorPotSaida;
|
|
|
|
/* battery group */
|
|
|
|
static int Autonomy, Waiting;
|
|
|
|
static double BattVoltage, Temperature, LimInfBattSrc, LimSupBattSrc;
|
|
|
|
static double LimInfBattInv, LimSupBattInv, BattNonValue;
|
|
|
|
/* general group */
|
|
|
|
static int BoostVolt, Rendimento;
|
|
|
|
/* status group */
|
|
|
|
static unsigned char StatusEntrada, StatusSaida, StatusBateria;
|
|
|
|
/* events group */
|
|
|
|
static unsigned char EventosRede, EventosSaida, EventosBateria;
|
2011-06-01 20:31:49 +00:00
|
|
|
/* Grupo de Programação */
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
/* Methods */
|
2011-01-26 09:35:08 +00:00
|
|
|
static void ScanReceivePack(void);
|
2010-03-25 23:20:59 +00:00
|
|
|
static int AutonomyCalc( int );
|
2022-06-29 10:37:36 +00:00
|
|
|
static void CommReceive(const unsigned char*, ssize_t);
|
2011-01-26 09:35:08 +00:00
|
|
|
static void getbaseinfo(void);
|
|
|
|
static void getupdateinfo(void);
|
2022-06-29 10:37:36 +00:00
|
|
|
|
2010-03-25 23:20:59 +00:00
|
|
|
static unsigned char RecPack[37];
|
|
|
|
|
|
|
|
/* comment on english language */
|
|
|
|
/* #define PORTUGUESE */
|
|
|
|
|
|
|
|
/* The following Portuguese strings are in UTF-8. */
|
|
|
|
#ifdef PORTUGUESE
|
|
|
|
#define M_UNKN "Modêlo rhino desconhecido\n"
|
|
|
|
#define NO_RHINO "Rhino não detectado! abortando ...\n"
|
|
|
|
#define UPS_DATE "Data no UPS %4d/%02d/%02d\n"
|
|
|
|
#define SYS_DATE "Data do Sistema %4d/%02d/%02d dia da semana %s\n"
|
|
|
|
#define ERR_PACK "Pacote errado\n"
|
|
|
|
#define NO_EVENT "Não há eventos\n"
|
|
|
|
#define UPS_TIME "Hora interna UPS %0d:%02d:%02d\n"
|
|
|
|
#else
|
|
|
|
#define M_UNKN "Unknown rhino model\n"
|
|
|
|
#define NO_RHINO "Rhino not detected! aborting ...\n"
|
|
|
|
#define UPS_DATE "UPS Date %4d/%02d/%02d\n"
|
|
|
|
#define SYS_DATE "System Date %4d/%02d/%02d day of week %s\n"
|
|
|
|
#define ERR_PACK "Wrong package\n"
|
|
|
|
#define NO_EVENT "No events\n"
|
|
|
|
#define UPS_TIME "UPS internal Time %0d:%02d:%02d\n"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int
|
|
|
|
AutonomyCalc( int ia ) /* all models */
|
|
|
|
{
|
2022-06-29 10:37:36 +00:00
|
|
|
int result = 0;
|
|
|
|
double auton, calc, currin;
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
if( ia )
|
2010-03-25 23:20:59 +00:00
|
|
|
{
|
2022-06-29 10:37:36 +00:00
|
|
|
if( d_equal(BattVoltage, 0) )
|
|
|
|
result = 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
calc = ( OutVoltage * OutCurrent )* 1.0 / ( 0.08 * BattVoltage );
|
|
|
|
auton = pow( calc, 1.18 );
|
|
|
|
if( d_equal(auton, 0) )
|
|
|
|
result = 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auton = 1.0 / auton;
|
|
|
|
auton = auton * 11.07;
|
|
|
|
calc = ( BattVoltage * 1.0 / 10 ) - 168;
|
|
|
|
result = (int) ( auton * calc * 2.5 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
currin = ( UtilPowerOut + ConstInt ) *1.0 / Vin;
|
|
|
|
auton = ( ( ( AmpH *1.0 / currin ) * 60 * ( ( BattVoltage - VbatMin ) * 1.0 /( VbatNom - VbatMin ) ) * FM ) + FA );
|
|
|
|
if( ( BattVoltage > 129 ) && ( BattVoltage < 144 ) )
|
|
|
|
result = 133;
|
|
|
|
else
|
|
|
|
result = (int) auton;
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
return result;
|
2010-03-25 23:20:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Treat received package */
|
|
|
|
static void
|
|
|
|
ScanReceivePack( void )
|
|
|
|
{
|
2022-06-29 10:37:36 +00:00
|
|
|
/* model independent data */
|
|
|
|
|
|
|
|
Year = RecPack[31] + ( RecPack[32] * 100 );
|
|
|
|
Month = RecPack[30];
|
|
|
|
Day = RecPack[29];
|
|
|
|
|
|
|
|
/* UPS internal time */
|
|
|
|
ihour = RecPack[26];
|
|
|
|
imin = RecPack[27];
|
|
|
|
isec = RecPack[28];
|
|
|
|
|
|
|
|
/* Flag1 */
|
|
|
|
/* SobreTemp = ( ( 0x01 & RecPack[33]) = 0x01 ); */
|
|
|
|
/* OutputOn = ( ( 0x02 & RecPack[33]) = 0x02 ); OutputOn */
|
|
|
|
/* InputOn = ( ( 0x04 & RecPack[33]) = 0x04 ); InputOn */
|
|
|
|
/* ByPassOn = ( ( 0x08 & RecPack[33]) = 0x08 ); BypassOn */
|
|
|
|
/* Auto_HAB = ( ( 0x10 & RecPack[33]) = 0x10 ); */
|
|
|
|
/* Timer_HAB = ( ( 0x20 & RecPack[33]) = 0x20 ); */
|
|
|
|
/* Boost_Ligado = ( ( 0x40 & RecPack[33]) = 0x40 ); */
|
|
|
|
/* Bateria_Desc = ( ( 0x80 & RecPack[33]) = 0x80 ); */
|
|
|
|
|
|
|
|
/* Flag2 */
|
|
|
|
/* Quad_Ant_Ent = ( ( 0x01 & RecPack[34]) = 0x01 ); */
|
|
|
|
/* Quadratura = ( ( 0x02 & RecPack[34]) = 0x02 ); */
|
|
|
|
/* Termino_XMODEM = ( ( 0x04 & RecPack[34]) = 0x04 ); */
|
|
|
|
/* Em_Sincronismo = ( ( 0x08 & RecPack[34]) = 0x08 ); */
|
|
|
|
/* Out110 = ( ( 0x10 & RecPack[34]) = 0x10 ); Out110 */
|
|
|
|
/* Exec_Beep = ( ( 0x20 & RecPack[34]) = 0x20 ); */
|
|
|
|
/* LowBatt = ( ( 0x40 & RecPack[34]) = 0x40 ); LowBatt */
|
|
|
|
/* Boost_Sobre = ( ( 0x80 & RecPack[34]) = 0x80 ); */
|
|
|
|
|
|
|
|
/* Flag3 */
|
|
|
|
/* OverCharge = ( ( 0x01 & RecPack[35]) = 0x01 ); OverCharge */
|
|
|
|
/* SourceFail = ( ( 0x02 & RecPack[35]) = 0x02 ); SourceFail */
|
|
|
|
/* RedeAnterior = ( ( 0x04 & RecPack[35]) = 0x04 ); */
|
|
|
|
/* Cmd_Executado = ( ( 0x08 & RecPack[35]) = 0x08 ); */
|
|
|
|
/* Exec_Autoteste = ( ( 0x10 & RecPack[35]) = 0x10 ); */
|
|
|
|
/* Quad_Ant_Sai = ( ( 0x20 & RecPack[35]) = 0x20 ); */
|
|
|
|
/* ComandoSerial = ( ( 0x40 & RecPack[35]) = 0x40 ); */
|
|
|
|
/* SobreTensao = ( ( 0x80 & RecPack[35]) = 0x80 ); */
|
|
|
|
|
|
|
|
OutputOn = ( ( 0x02 & RecPack[33] ) == 0x02 );
|
|
|
|
InputOn = ( ( 0x04 & RecPack[33] ) == 0x04 );
|
|
|
|
BypassOn = ( ( 0x08 & RecPack[33] ) == 0x08 );
|
|
|
|
|
|
|
|
Out110 = ( ( 0x10 & RecPack[34] ) == 0x10 );
|
|
|
|
LowBatt = ( ( 0x40 & RecPack[34] ) == 0x40 );
|
|
|
|
|
|
|
|
OverCharge = ( ( 0x01 & RecPack[35] ) == 0x01 );
|
|
|
|
SourceFail = ( ( 0x02 & RecPack[35] ) == 0x02 );
|
|
|
|
|
|
|
|
/* model dependent data read */
|
|
|
|
|
|
|
|
PowerFactor = 800;
|
|
|
|
|
|
|
|
if( RecPack[0] == 0xC2 )
|
|
|
|
{
|
|
|
|
LimInfBattSrc = 174;
|
|
|
|
LimSupBattSrc = 192;/* 180????? carregador eh 180 (SCOPOS) */
|
|
|
|
LimInfBattInv = 174;
|
|
|
|
LimSupBattInv = 192;/* 170????? (SCOPOS) */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LimInfBattSrc = 138;
|
|
|
|
LimSupBattSrc = 162;/* 180????? carregador eh 180 (SCOPOS) */
|
|
|
|
LimInfBattInv = 126;
|
|
|
|
LimSupBattInv = 156;/* 170????? (SCOPOS) */
|
|
|
|
}
|
|
|
|
|
|
|
|
BattNonValue = 144;
|
|
|
|
/* VersaoInterna = "R10" + IntToStr( RecPack[1] ); */
|
|
|
|
InVoltage = RecPack[2];
|
|
|
|
InCurrent = RecPack[3];
|
|
|
|
UtilPowerIn = RecPack[4] + RecPack[5] * 256;
|
|
|
|
AppPowerIn = RecPack[6] + RecPack[7] * 256;
|
|
|
|
FatorPotEntrada = RecPack[8];
|
|
|
|
InFreq = ( RecPack[9] + RecPack[10] * 256 ) * 1.0 / 10;
|
|
|
|
OutVoltage = RecPack[11];
|
|
|
|
OutCurrent = RecPack[12];
|
|
|
|
UtilPowerOut = RecPack[13] + RecPack[14] * 256;
|
|
|
|
AppPowerOut = RecPack[15] + RecPack[16] * 256;
|
|
|
|
FatorPotSaida = RecPack[17];
|
|
|
|
OutFreq = ( RecPack[18] + RecPack[19] * 256 ) * 1.0 / 10;
|
|
|
|
BattVoltage = RecPack[20];
|
|
|
|
BoostVolt = RecPack[21] + RecPack[22] * 256;
|
|
|
|
Temperature = ( 0x7F & RecPack[23] );
|
|
|
|
Rendimento = RecPack[24];
|
|
|
|
|
|
|
|
/* model independent data */
|
|
|
|
|
|
|
|
if( ( BattVoltage < LimInfBattInv ) )
|
|
|
|
CriticBatt = true;
|
|
|
|
|
|
|
|
if( BypassOn )
|
|
|
|
OutVoltage = ( InVoltage * 1.0 / 2 ) + 5;
|
|
|
|
|
|
|
|
if( SourceFail && RedeAnterior ) /* falha pela primeira vez */
|
|
|
|
OcorrenciaDeFalha = true;
|
|
|
|
|
|
|
|
if( !( SourceFail ) && !( RedeAnterior ) ) /* retorno da rede */
|
|
|
|
RetornoDaRede = true;
|
|
|
|
|
|
|
|
if( RedeAnterior == !( SourceFail ) )
|
|
|
|
{
|
|
|
|
RetornoDaRede = false;
|
|
|
|
OcorrenciaDeFalha = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
RedeAnterior = !( SourceFail );
|
|
|
|
|
|
|
|
LimInfSaida = 75;
|
|
|
|
LimSupSaida = 150;
|
|
|
|
ValorNominalSaida = 110;
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
LimInfEntrada = 190;
|
|
|
|
LimSupEntrada = 250;
|
|
|
|
ValorNominalEntrada = 220;
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
if( SourceFail )
|
|
|
|
{
|
|
|
|
StatusEntrada = 2;
|
|
|
|
RecPack[8] = 200; /* ?????????????????????????????????? */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
StatusEntrada = 1;
|
|
|
|
RecPack[8] = 99; /* ??????????????????????????????????? */
|
|
|
|
}
|
|
|
|
|
|
|
|
if( OutputOn ) /* Output Status */
|
|
|
|
StatusSaida = 2;
|
|
|
|
else
|
|
|
|
StatusSaida = 1;
|
|
|
|
|
|
|
|
if( OverCharge )
|
|
|
|
StatusSaida = 3;
|
|
|
|
|
|
|
|
if( CriticBatt ) /* Battery Status */
|
|
|
|
StatusBateria = 4;
|
|
|
|
else
|
|
|
|
StatusBateria = 1;
|
|
|
|
|
|
|
|
EventosRede = 0;
|
|
|
|
|
|
|
|
if( OcorrenciaDeFalha )
|
|
|
|
EventosRede = 1;
|
|
|
|
|
|
|
|
if( RetornoDaRede )
|
|
|
|
EventosRede = 2;
|
|
|
|
|
|
|
|
/* verify InversorOn */
|
|
|
|
if( Flag_inversor )
|
|
|
|
{
|
|
|
|
oldInversorOn = InputOn;
|
|
|
|
Flag_inversor = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
EventosSaida = 0;
|
|
|
|
if( InputOn && !( oldInversorOn ) )
|
|
|
|
EventosSaida = 26;
|
|
|
|
if( oldInversorOn && !( InputOn ) )
|
|
|
|
EventosSaida = 27;
|
|
|
|
oldInversorOn = InputOn;
|
|
|
|
if( SuperAquecimento && !( SuperAquecimentoAnterior ) )
|
|
|
|
EventosSaida = 12;
|
|
|
|
if( SuperAquecimentoAnterior && !( SuperAquecimento ) )
|
|
|
|
EventosSaida = 13;
|
|
|
|
SuperAquecimentoAnterior = SuperAquecimento;
|
|
|
|
EventosBateria = 0;
|
|
|
|
OldCritBatt = CriticBatt;
|
|
|
|
|
|
|
|
if( OverCharge && !( OldOverCharge ) )
|
|
|
|
EventosSaida = 10;
|
|
|
|
if( OldOverCharge && !( OverCharge ) )
|
|
|
|
EventosSaida = 11;
|
|
|
|
OldOverCharge = OverCharge;
|
|
|
|
|
|
|
|
/* autonomy calc. */
|
|
|
|
if( RecPack[ 0 ] == 0xC2 )
|
|
|
|
Autonomy = AutonomyCalc( 1 );
|
|
|
|
else
|
|
|
|
Autonomy = AutonomyCalc( 0 );
|
2010-03-25 23:20:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2022-06-29 10:37:36 +00:00
|
|
|
CommReceive(const unsigned char *bufptr, ssize_t size)
|
2010-03-25 23:20:59 +00:00
|
|
|
{
|
2022-06-29 10:37:36 +00:00
|
|
|
int i, i_end, CheckSum, chk;
|
|
|
|
|
|
|
|
if( size == 37 )
|
|
|
|
Waiting = 0;
|
|
|
|
|
|
|
|
printf("CommReceive size = %zd waiting = %d\n", size, Waiting );
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
switch( Waiting )
|
|
|
|
{
|
|
|
|
/* normal package */
|
|
|
|
case 0:
|
|
|
|
{
|
|
|
|
if( size == 37 )
|
|
|
|
{
|
|
|
|
i_end = 37;
|
|
|
|
for( i = 0 ; i < i_end ; ++i )
|
|
|
|
{
|
|
|
|
RecPack[i] = *bufptr;
|
|
|
|
bufptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* CheckSum verify */
|
|
|
|
CheckSum = 0;
|
|
|
|
i_end = 36;
|
|
|
|
for( i = 0 ; i < i_end ; ++i )
|
|
|
|
{
|
|
|
|
chk = RecPack[ i ];
|
|
|
|
CheckSum = CheckSum + chk;
|
|
|
|
}
|
|
|
|
|
|
|
|
CheckSum = CheckSum % 256;
|
|
|
|
|
|
|
|
ser_flush_in(upsfd,"",0); /* clean port */
|
|
|
|
|
|
|
|
/* correct package */
|
|
|
|
if( ( RecPack[0] == 0xC0 || RecPack[0] == 0xC1 || RecPack[0] == 0xC2 || RecPack[0] == 0xC3 )
|
|
|
|
&& ( RecPack[ 36 ] == CheckSum ) )
|
|
|
|
{
|
|
|
|
|
|
|
|
if(!(detected))
|
|
|
|
{
|
|
|
|
RhinoModel = RecPack[0];
|
|
|
|
detected = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch( RhinoModel )
|
|
|
|
{
|
|
|
|
case 0xC0:
|
|
|
|
case 0xC1:
|
|
|
|
case 0xC2:
|
|
|
|
case 0xC3:
|
|
|
|
{
|
|
|
|
ScanReceivePack();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
printf( M_UNKN );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
/* dumping package, nothing to do yet */
|
|
|
|
Waiting = 0;
|
|
|
|
break;
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Waiting = 0;
|
2010-03-25 23:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
static ssize_t
|
|
|
|
send_command( unsigned char cmd )
|
2010-03-25 23:20:59 +00:00
|
|
|
{
|
2022-06-29 10:37:36 +00:00
|
|
|
static const size_t sizes = 19, iend = 18;
|
|
|
|
size_t i, kount; /*, j, uc; */
|
|
|
|
unsigned char chk, checksum = 0;
|
|
|
|
ssize_t ret = -1;
|
|
|
|
unsigned char ch, *psend = NULL;
|
|
|
|
|
|
|
|
if ( !(psend = xmalloc(sizeof(char) * sizes)) ) {
|
|
|
|
upslogx(LOG_ERR, "send_command() failed to allocate buffer");
|
|
|
|
return -1;
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
/* mounting buffer to send */
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2012-01-24 10:22:33 +00:00
|
|
|
for(i = 0; i < iend; i++ )
|
2010-03-25 23:20:59 +00:00
|
|
|
{
|
2012-01-24 10:22:33 +00:00
|
|
|
if ( i == 0 )
|
|
|
|
chk = 0x01;
|
|
|
|
else
|
|
|
|
{
|
2022-06-29 10:37:36 +00:00
|
|
|
if( i == 1 )
|
2012-01-24 10:22:33 +00:00
|
|
|
chk = cmd;
|
|
|
|
else
|
|
|
|
chk = 0x00; /* 0x20; */
|
|
|
|
}
|
|
|
|
|
|
|
|
ch = chk;
|
|
|
|
psend[i] = ch; /* psend[0 - 17] */
|
2022-06-29 10:37:36 +00:00
|
|
|
if( i > 0 ) /* psend[0] not computed */
|
2012-01-24 10:22:33 +00:00
|
|
|
checksum = checksum + chk;
|
2010-03-25 23:20:59 +00:00
|
|
|
}
|
|
|
|
|
2012-01-24 10:22:33 +00:00
|
|
|
ch = checksum;
|
2022-06-29 10:37:36 +00:00
|
|
|
ch = (~( ch) ); /* not ch */
|
2012-01-24 10:22:33 +00:00
|
|
|
psend[iend] = ch;
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2012-01-24 10:22:33 +00:00
|
|
|
/* send five times the command */
|
|
|
|
kount = 0;
|
|
|
|
while ( kount < 5 )
|
2010-03-25 23:20:59 +00:00
|
|
|
{
|
2012-01-24 10:22:33 +00:00
|
|
|
/* ret = ser_send_buf_pace(upsfd, UPSDELAY, psend, sizes ); */ /* optional delay */
|
|
|
|
|
|
|
|
for(i = 0 ; i < sizes ; i++)
|
|
|
|
{
|
|
|
|
ret = ser_send_char( upsfd, psend[i] );
|
|
|
|
/* usleep ( UPSDELAY ); sending without delay */
|
|
|
|
}
|
|
|
|
usleep( UPSDELAY ); /* delay between sent command */
|
|
|
|
kount++;
|
2010-03-25 23:20:59 +00:00
|
|
|
}
|
2022-06-29 10:37:36 +00:00
|
|
|
|
|
|
|
free (psend);
|
2012-01-24 10:22:33 +00:00
|
|
|
return ret;
|
2010-03-25 23:20:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void sendshut( void )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for(i=0; i < 30000; i++)
|
2022-06-29 10:37:36 +00:00
|
|
|
usleep( UPSDELAY ); /* 15 seconds delay */
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
if ( send_command( CMD_SHUT ) < 1 )
|
|
|
|
upslogx(LOG_ERR, "Ups shutdown command sending failed");
|
|
|
|
else
|
|
|
|
upslogx(LOG_NOTICE, "Ups shutdown command sent");
|
2010-03-25 23:20:59 +00:00
|
|
|
printf("Ups shutdown command sent\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void getbaseinfo(void)
|
|
|
|
{
|
2022-06-29 10:37:36 +00:00
|
|
|
unsigned char temp[256];
|
2010-03-25 23:20:59 +00:00
|
|
|
unsigned char Pacote[37];
|
2022-06-29 10:37:36 +00:00
|
|
|
ssize_t tam, i, j=0;
|
2012-06-01 13:55:19 +00:00
|
|
|
time_t tmt;
|
2010-03-25 23:20:59 +00:00
|
|
|
struct tm *now;
|
2022-06-29 10:37:36 +00:00
|
|
|
struct tm tmbuf;
|
2011-01-26 09:35:08 +00:00
|
|
|
const char *Model;
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2012-06-01 13:55:19 +00:00
|
|
|
time( &tmt );
|
2022-06-29 10:37:36 +00:00
|
|
|
now = localtime_r( &tmt, &tmbuf );
|
2010-03-25 23:20:59 +00:00
|
|
|
dian = now->tm_mday;
|
|
|
|
mesn = now->tm_mon+1;
|
|
|
|
anon = now->tm_year+1900;
|
2022-06-29 10:37:36 +00:00
|
|
|
weekn = now->tm_wday;
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
/* trying detect rhino model */
|
|
|
|
while ( ( !detected ) && ( j < 10 ) )
|
2022-06-29 10:37:36 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
temp[0] = 0; /* flush temp buffer */
|
|
|
|
tam = ser_get_buf_len(upsfd, temp, pacsize, 3, 0);
|
|
|
|
if( tam == 37 )
|
|
|
|
{
|
|
|
|
for( i = 0 ; i < tam ; i++ )
|
|
|
|
{
|
|
|
|
Pacote[i] = temp[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
j++;
|
|
|
|
if( tam == 37)
|
|
|
|
CommReceive(Pacote, tam);
|
|
|
|
else
|
|
|
|
CommReceive(temp, tam);
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
if( (!detected) )
|
2022-06-29 10:37:36 +00:00
|
|
|
{
|
|
|
|
fatalx(EXIT_FAILURE, NO_RHINO );
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
switch( RhinoModel )
|
2022-06-29 10:37:36 +00:00
|
|
|
{
|
|
|
|
case 0xC0:
|
|
|
|
{
|
|
|
|
Model = "Rhino 20.0 kVA";
|
|
|
|
PotenciaNominal = 20000;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0xC1:
|
|
|
|
{
|
|
|
|
Model = "Rhino 10.0 kVA";
|
|
|
|
PotenciaNominal = 10000;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0xC2:
|
|
|
|
{
|
|
|
|
Model = "Rhino 6.0 kVA";
|
|
|
|
PotenciaNominal = 6000;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0xC3:
|
|
|
|
{
|
|
|
|
Model = "Rhino 7.5 kVA";
|
|
|
|
PotenciaNominal = 7500;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
Model = "Rhino unknown model";
|
|
|
|
PotenciaNominal = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
/* manufacturer and model */
|
|
|
|
dstate_setinfo("ups.mfr", "%s", "Microsol");
|
|
|
|
dstate_setinfo("ups.model", "%s", Model);
|
|
|
|
/*
|
|
|
|
dstate_setinfo("input.transfer.low", "%03.1f", InDownLim); LimInfBattInv ?
|
|
|
|
dstate_setinfo("input.transfer.high", "%03.1f", InUpLim); LimSupBattInv ?
|
|
|
|
*/
|
|
|
|
|
2011-06-01 20:31:49 +00:00
|
|
|
dstate_addcmd("shutdown.stayoff"); /* CMD_SHUT */
|
2010-03-25 23:20:59 +00:00
|
|
|
/* there is no reserved words for CMD_INON and CMD_INOFF yet */
|
2011-06-01 20:31:49 +00:00
|
|
|
/* dstate_addcmd("input.on"); */ /* CMD_INON = 1 */
|
|
|
|
/* dstate_addcmd("input.off"); */ /* CMD_INOFF = 2 */
|
2010-03-25 23:20:59 +00:00
|
|
|
dstate_addcmd("load.on"); /* CMD_OUTON = 3 */
|
|
|
|
dstate_addcmd("load.off"); /* CMD_OUTOFF = 4 */
|
|
|
|
dstate_addcmd("bypass.start"); /* CMD_PASSON = 5 */
|
|
|
|
dstate_addcmd("bypass.stop"); /* CMD_PASSOFF = 6 */
|
|
|
|
|
|
|
|
printf("Detected %s on %s\n", dstate_getinfo("ups.model"), device_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void getupdateinfo(void)
|
|
|
|
{
|
2022-06-29 10:37:36 +00:00
|
|
|
unsigned char temp[256];
|
|
|
|
ssize_t tam;
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
temp[0] = 0; /* flush temp buffer */
|
|
|
|
tam = ser_get_buf_len(upsfd, temp, pacsize, 3, 0);
|
|
|
|
|
|
|
|
CommReceive(temp, tam);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int instcmd(const char *cmdname, const char *extra)
|
|
|
|
{
|
2022-06-29 10:37:36 +00:00
|
|
|
ssize_t ret = 0;
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
if (!strcasecmp(cmdname, "shutdown.stayoff"))
|
2022-06-29 10:37:36 +00:00
|
|
|
{
|
|
|
|
/* shutdown now (one way) */
|
|
|
|
/* send_command( CMD_SHUT ); */
|
|
|
|
sendshut();
|
|
|
|
return STAT_INSTCMD_HANDLED;
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
if (!strcasecmp(cmdname, "load.on"))
|
2022-06-29 10:37:36 +00:00
|
|
|
{
|
|
|
|
/* liga Saida */
|
|
|
|
ret = send_command( 3 );
|
|
|
|
if ( ret < 1 )
|
|
|
|
upslogx(LOG_ERR, "send_command 3 failed");
|
|
|
|
return STAT_INSTCMD_HANDLED;
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
if (!strcasecmp(cmdname, "load.off"))
|
2022-06-29 10:37:36 +00:00
|
|
|
{
|
|
|
|
/* desliga Saida */
|
|
|
|
ret = send_command( 4 );
|
|
|
|
if ( ret < 1 )
|
|
|
|
upslogx(LOG_ERR, "send_command 4 failed");
|
|
|
|
return STAT_INSTCMD_HANDLED;
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
if (!strcasecmp(cmdname, "bypass.start"))
|
2022-06-29 10:37:36 +00:00
|
|
|
{
|
|
|
|
/* liga Bypass */
|
|
|
|
ret = send_command( 5 );
|
|
|
|
if ( ret < 1 )
|
|
|
|
upslogx(LOG_ERR, "send_command 5 failed");
|
|
|
|
return STAT_INSTCMD_HANDLED;
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
if (!strcasecmp(cmdname, "bypass.stop"))
|
2022-06-29 10:37:36 +00:00
|
|
|
{
|
|
|
|
/* desliga Bypass */
|
|
|
|
ret = send_command( 6 );
|
|
|
|
if ( ret < 1 )
|
|
|
|
upslogx(LOG_ERR, "send_command 6 failed");
|
|
|
|
return STAT_INSTCMD_HANDLED;
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
|
2022-06-29 10:37:36 +00:00
|
|
|
upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra);
|
2010-03-25 23:20:59 +00:00
|
|
|
return STAT_INSTCMD_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
void upsdrv_initinfo(void)
|
|
|
|
{
|
|
|
|
getbaseinfo();
|
|
|
|
|
|
|
|
upsh.instcmd = instcmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
void upsdrv_updateinfo(void)
|
|
|
|
{
|
2022-06-29 10:37:36 +00:00
|
|
|
getupdateinfo(); /* new package for updates */
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
dstate_setinfo("output.voltage", "%03.1f", OutVoltage);
|
|
|
|
dstate_setinfo("input.voltage", "%03.1f", InVoltage);
|
|
|
|
dstate_setinfo("battery.voltage", "%02.1f", BattVoltage);
|
|
|
|
|
|
|
|
/* output and bypass tests */
|
|
|
|
if( OutputOn )
|
2022-06-29 10:37:36 +00:00
|
|
|
dstate_setinfo("outlet.switchable", "%s", "yes");
|
2010-03-25 23:20:59 +00:00
|
|
|
else
|
2022-06-29 10:37:36 +00:00
|
|
|
dstate_setinfo("outlet.switchable", "%s", "no");
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
if( BypassOn )
|
2022-06-29 10:37:36 +00:00
|
|
|
dstate_setinfo("outlet.1.switchable", "%s", "yes");
|
2010-03-25 23:20:59 +00:00
|
|
|
else
|
2022-06-29 10:37:36 +00:00
|
|
|
dstate_setinfo("outlet.1.switchable", "%s", "no");
|
2010-03-25 23:20:59 +00:00
|
|
|
|
|
|
|
status_init();
|
|
|
|
|
|
|
|
if (!SourceFail )
|
2022-06-29 10:37:36 +00:00
|
|
|
status_set("OL"); /* on line */
|
2010-03-25 23:20:59 +00:00
|
|
|
else
|
2022-06-29 10:37:36 +00:00
|
|
|
status_set("OB"); /* on battery */
|
|
|
|
|
2010-03-25 23:20:59 +00:00
|
|
|
if (Autonomy < 5 )
|
2022-06-29 10:37:36 +00:00
|
|
|
status_set("LB"); /* low battery */
|
|
|
|
|
2010-03-25 23:20:59 +00:00
|
|
|
status_commit();
|
|
|
|
dstate_setinfo("ups.temperature", "%2.2f", Temperature);
|
|
|
|
dstate_setinfo("input.frequency", "%2.1f", InFreq);
|
|
|
|
dstate_dataok();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* power down the attached load immediately */
|
|
|
|
void upsdrv_shutdown(void)
|
|
|
|
{
|
|
|
|
/* basic idea: find out line status and send appropriate command */
|
|
|
|
/* on line: send normal shutdown, ups will return by itself on utility */
|
|
|
|
/* on battery: send shutdown+return, ups will cycle and return soon */
|
|
|
|
|
2011-06-01 20:31:49 +00:00
|
|
|
if (!SourceFail) /* on line */
|
2022-06-29 10:37:36 +00:00
|
|
|
{
|
|
|
|
/* FIXME: Both legs of the if-clause send CMD_SHUT, where is the "forcing"? */
|
|
|
|
printf("On line, forcing shutdown command...\n");
|
|
|
|
/* send_command( CMD_SHUT ); */
|
|
|
|
sendshut();
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
else
|
2022-06-29 10:37:36 +00:00
|
|
|
{
|
|
|
|
printf("On battery, sending normal shutdown command...\n");
|
|
|
|
/* send_command( CMD_SHUT ); */
|
|
|
|
sendshut();
|
|
|
|
}
|
2010-03-25 23:20:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void upsdrv_help(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void upsdrv_makevartable(void)
|
|
|
|
{
|
|
|
|
addvar(VAR_VALUE, "battext", "Battery Extension (0-80)min");
|
|
|
|
}
|
|
|
|
|
|
|
|
void upsdrv_initups(void)
|
|
|
|
{
|
|
|
|
upsfd = ser_open(device_path);
|
|
|
|
ser_set_speed(upsfd, device_path, B19200);
|
|
|
|
|
|
|
|
/* dtr and rts setting */
|
|
|
|
ser_set_dtr(upsfd, 1);
|
|
|
|
ser_set_rts(upsfd, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void upsdrv_cleanup(void)
|
|
|
|
{
|
|
|
|
ser_close(upsfd, device_path);
|
|
|
|
}
|