2015-04-30 16:53:36 +03:00
/* nutdrv_qx_voltronic-qs-hex.c - Subdriver for Voltronic Power UPSes with QS-Hex protocol
*
* Copyright ( C )
* 2014 Daniele Pezzini < hyouko @ 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
*
*/
# include "main.h"
# include "nutdrv_qx.h"
# include "nutdrv_qx_blazer-common.h"
# include "nutdrv_qx_voltronic-qs-hex.h"
2016-07-18 03:11:41 +03:00
# define VOLTRONIC_QS_HEX_VERSION "Voltronic-QS-Hex 0.10"
2015-04-30 16:53:36 +03:00
/* Support functions */
static int voltronic_qs_hex_claim ( void ) ;
static void voltronic_qs_hex_initups ( void ) ;
/* Answer preprocess functions */
static int voltronic_qs_hex_preprocess_qs_answer ( item_t * item , const int len ) ;
2016-07-18 03:11:41 +03:00
static int voltronic_qs_hex_char_to_binary ( const unsigned char value ) ;
2015-04-30 16:53:36 +03:00
/* Preprocess functions */
2016-07-18 03:11:41 +03:00
static int voltronic_qs_hex_protocol ( item_t * item , char * value , const size_t valuelen ) ;
static int voltronic_qs_hex_input_output_voltage ( item_t * item , char * value , const size_t valuelen ) ;
static int voltronic_qs_hex_load ( item_t * item , char * value , const size_t valuelen ) ;
static int voltronic_qs_hex_frequency ( item_t * item , char * value , const size_t valuelen ) ;
static int voltronic_qs_hex_battery_voltage ( item_t * item , char * value , const size_t valuelen ) ;
static int voltronic_qs_hex_process_ratings_bits ( item_t * item , char * value , const size_t valuelen ) ;
2015-04-30 16:53:36 +03:00
/* == Ranges == */
/* Range for ups.delay.start */
static info_rw_t voltronic_qs_hex_r_ondelay [ ] = {
{ " 60 " , 0 } ,
{ " 599940 " , 0 } ,
{ " " , 0 }
} ;
/* Range for ups.delay.shutdown */
static info_rw_t voltronic_qs_hex_r_offdelay [ ] = {
{ " 12 " , 0 } ,
{ " 540 " , 0 } ,
{ " " , 0 }
} ;
/* == qx2nut lookup table == */
static item_t voltronic_qs_hex_qx2nut [ ] = {
/* Query UPS for protocol
* > [ M \ r ]
* < [ P \ r ]
* 01
* 0
*/
2016-07-18 03:11:41 +03:00
{ " ups.firmware.aux " , 0 , NULL , " M \r " , " " , 2 , 0 , " " , 0 , 0 , " PM-%s " , QX_FLAG_STATIC , NULL , NULL , voltronic_qs_hex_protocol } ,
2015-04-30 16:53:36 +03:00
/* Query UPS for status
* > [ QS \ r ]
2016-07-18 03:11:41 +03:00
* < [ # 6 C01 35 6 C01 35 03 519 A 1312 D0 E6 1 E 00001001 \ r ] ( ' P ' protocol , after being preprocessed )
* < [ # 6901 6 C 6802 6 C 00 5F D7 12 C000 E4 1 E 00001001 00000010 \ r ] ( ' T ' protocol , after being preprocessed )
* 01234567 890123456789012345678901234567890123456789012345
* 0 1 2 3 4 5
2015-04-30 16:53:36 +03:00
*/
2016-07-18 03:11:41 +03:00
{ " input.voltage " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 1 , 7 , " %.1f " , 0 , NULL , voltronic_qs_hex_preprocess_qs_answer , voltronic_qs_hex_input_output_voltage } ,
{ " output.voltage " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 9 , 15 , " %.1f " , 0 , NULL , voltronic_qs_hex_preprocess_qs_answer , voltronic_qs_hex_input_output_voltage } ,
{ " ups.load " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 17 , 18 , " %d " , 0 , NULL , voltronic_qs_hex_preprocess_qs_answer , voltronic_qs_hex_load } ,
{ " output.frequency " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 20 , 30 , " %.1f " , 0 , NULL , voltronic_qs_hex_preprocess_qs_answer , voltronic_qs_hex_frequency } ,
{ " battery.voltage " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 32 , 36 , " %.2f " , 0 , NULL , voltronic_qs_hex_preprocess_qs_answer , voltronic_qs_hex_battery_voltage } ,
2015-04-30 16:53:36 +03:00
/* Status bits */
2016-07-18 03:11:41 +03:00
{ " ups.status " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 38 , 38 , NULL , QX_FLAG_QUICK_POLL , NULL , voltronic_qs_hex_preprocess_qs_answer , blazer_process_status_bits } , /* Utility Fail (Immediate) */
{ " ups.status " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 39 , 39 , NULL , QX_FLAG_QUICK_POLL , NULL , voltronic_qs_hex_preprocess_qs_answer , blazer_process_status_bits } , /* Battery Low */
{ " ups.status " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 40 , 40 , NULL , QX_FLAG_QUICK_POLL , NULL , voltronic_qs_hex_preprocess_qs_answer , blazer_process_status_bits } , /* Bypass/Boost or Buck Active */
{ " ups.alarm " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 41 , 41 , NULL , 0 , NULL , voltronic_qs_hex_preprocess_qs_answer , blazer_process_status_bits } , /* UPS Failed */
{ " ups.type " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 42 , 42 , " %s " , QX_FLAG_STATIC , NULL , voltronic_qs_hex_preprocess_qs_answer , blazer_process_status_bits } , /* UPS Type */
{ " ups.status " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 43 , 43 , NULL , QX_FLAG_QUICK_POLL , NULL , voltronic_qs_hex_preprocess_qs_answer , blazer_process_status_bits } , /* Test in Progress */
{ " ups.status " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 44 , 44 , NULL , QX_FLAG_QUICK_POLL , NULL , voltronic_qs_hex_preprocess_qs_answer , blazer_process_status_bits } , /* Shutdown Active */
{ " ups.beeper.status " , 0 , NULL , " QS \r " , " " , 47 , ' # ' , " " , 45 , 45 , " %s " , 0 , NULL , voltronic_qs_hex_preprocess_qs_answer , blazer_process_status_bits } , /* Beeper status */
/* Ratings bits */
{ " output.frequency.nominal " , 0 , NULL , " QS \r " , " " , 56 , ' # ' , " " , 47 , 47 , " %.1f " , QX_FLAG_SKIP , NULL , voltronic_qs_hex_preprocess_qs_answer , voltronic_qs_hex_process_ratings_bits } ,
{ " battery.voltage.nominal " , 0 , NULL , " QS \r " , " " , 56 , ' # ' , " " , 48 , 49 , " %.1f " , QX_FLAG_SKIP , NULL , voltronic_qs_hex_preprocess_qs_answer , voltronic_qs_hex_process_ratings_bits } ,
/* { "reserved.1", 0, NULL, "QS\r", "", 56, '#', "", 50, 50, "%s", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, */ /* Reserved */
/* { "reserved.2", 0, NULL, "QS\r", "", 56, '#', "", 51, 51, "%s", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, */ /* Reserved */
{ " output.voltage.nominal " , 0 , NULL , " QS \r " , " " , 56 , ' # ' , " " , 52 , 54 , " %.1f " , QX_FLAG_SKIP , NULL , voltronic_qs_hex_preprocess_qs_answer , voltronic_qs_hex_process_ratings_bits } ,
2015-04-30 16:53:36 +03:00
/* Instant commands */
2016-07-18 03:11:41 +03:00
{ " beeper.toggle " , 0 , NULL , " Q \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_CMD , NULL , NULL , NULL } ,
{ " load.off " , 0 , NULL , " S00R0000 \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_CMD , NULL , NULL , NULL } ,
{ " load.on " , 0 , NULL , " C \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_CMD , NULL , NULL , NULL } ,
{ " shutdown.return " , 0 , NULL , " S%s \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_CMD , NULL , NULL , blazer_process_command } ,
{ " shutdown.stayoff " , 0 , NULL , " S%sR0000 \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_CMD , NULL , NULL , blazer_process_command } ,
{ " shutdown.stop " , 0 , NULL , " C \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_CMD , NULL , NULL , NULL } ,
{ " test.battery.start.quick " , 0 , NULL , " T \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_CMD | QX_FLAG_SKIP , NULL , NULL , NULL } ,
2015-04-30 16:53:36 +03:00
/* Server-side settable vars */
2016-07-18 03:11:41 +03:00
{ " ups.delay.start " , ST_FLAG_RW , voltronic_qs_hex_r_ondelay , NULL , " " , 0 , 0 , " " , 0 , 0 , DEFAULT_ONDELAY , QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE , NULL , NULL , blazer_process_setvar } ,
{ " ups.delay.shutdown " , ST_FLAG_RW , voltronic_qs_hex_r_offdelay , NULL , " " , 0 , 0 , " " , 0 , 0 , DEFAULT_OFFDELAY , QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE , NULL , NULL , blazer_process_setvar } ,
2015-04-30 16:53:36 +03:00
/* End of structure. */
2016-07-18 03:11:41 +03:00
{ NULL , 0 , NULL , NULL , " " , 0 , 0 , " " , 0 , 0 , NULL , 0 , NULL , NULL , NULL }
2015-04-30 16:53:36 +03:00
} ;
/* == Testing table == */
# ifdef TESTING
static testing_t voltronic_qs_hex_testing [ ] = {
{ " QS \r " , " # \x6C \x01 \x35 \x6C \x01 \x35 \x03 \x51 \x9A \x28 \x02 \x12 \xD0 \xE6 \x1E \x09 \r " , 27 } ,
{ " M \r " , " P \r " , - 1 } ,
{ " Q \r " , " " , - 1 } ,
2016-07-18 03:11:41 +03:00
{ " S00R0000 \r " , " " , - 1 } ,
2015-04-30 16:53:36 +03:00
{ " C \r " , " " , - 1 } ,
{ " S02R0005 \r " , " " , - 1 } ,
{ " S.5R0000 \r " , " N \r " , - 1 } ,
{ " T \r " , " " , - 1 } ,
{ NULL }
} ;
# endif /* TESTING */
/* == Support functions == */
/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */
static int voltronic_qs_hex_claim ( void )
{
/* We need at least M and QS to run this subdriver */
/* UPS Protocol */
item_t * item = find_nut_info ( " ups.firmware.aux " , 0 , 0 ) ;
/* Don't know what happened */
if ( ! item )
return 0 ;
/* No reply/Unable to get value */
if ( qx_process ( item , NULL ) )
return 0 ;
/* Unable to process value/Protocol not supported */
if ( ups_infoval_set ( item ) ! = 1 )
return 0 ;
item = find_nut_info ( " input.voltage " , 0 , 0 ) ;
/* Don't know what happened */
if ( ! item ) {
dstate_delinfo ( " ups.firmware.aux " ) ;
return 0 ;
}
/* No reply/Unable to get value */
if ( qx_process ( item , NULL ) ) {
dstate_delinfo ( " ups.firmware.aux " ) ;
return 0 ;
}
/* Unable to process value */
if ( ups_infoval_set ( item ) ! = 1 ) {
dstate_delinfo ( " ups.firmware.aux " ) ;
return 0 ;
}
return 1 ;
}
/* Subdriver-specific initups */
static void voltronic_qs_hex_initups ( void )
{
blazer_initups_light ( voltronic_qs_hex_qx2nut ) ;
}
/* == Answer preprocess functions == */
/* Preprocess the answer we got back from the UPS when queried with 'QS\r' */
static int voltronic_qs_hex_preprocess_qs_answer ( item_t * item , const int len )
{
int i , token ;
char refined [ SMALLBUF ] = " " ;
if ( len < = 0 )
return len ;
if ( item - > answer [ 0 ] ! = ' # ' ) {
upsdebugx ( 4 , " %s: wrong leading character [%s: 0x%0x] " , __func__ , item - > info_type , item - > answer [ 0 ] ) ;
return - 1 ;
}
snprintf ( refined , sizeof ( refined ) , " %s " , " # " ) ;
/* e.g.: item->answer = "#\x6C\x01 \x35 \x6C\x01 \x35 \x03 \x51\x9A \x28\x02\x12\xD0 \xE6 \x1E \x09\r" */
2022-07-10 10:23:45 +03:00
upsdebug_hex ( 4 , " read " , item - > answer , ( size_t ) len ) ;
2015-04-30 16:53:36 +03:00
for ( i = 1 , token = 1 ; i < len ; i + + ) {
/* New token */
if ( item - > answer [ i ] = = 0x20 ) {
snprintfcat ( refined , sizeof ( refined ) , " %s " , " " ) ;
token + + ;
continue ;
}
/* 'Unescape' raw data */
2022-07-10 10:23:45 +03:00
if ( i < len & & item - > answer [ i ] = = 0x28 ) {
2015-04-30 16:53:36 +03:00
switch ( item - > answer [ i + 1 ] )
{
case 0x00 : /* Escaped because: CR */
snprintfcat ( refined , sizeof ( refined ) , " %02x " , 0x0D ) ;
break ;
case 0x01 : /* Escaped because: XON */
snprintfcat ( refined , sizeof ( refined ) , " %02x " , 0x11 ) ;
break ;
case 0x02 : /* Escaped because: XOFF */
snprintfcat ( refined , sizeof ( refined ) , " %02x " , 0x13 ) ;
break ;
case 0x03 : /* Escaped because: LF */
snprintfcat ( refined , sizeof ( refined ) , " %02x " , 0x0A ) ;
break ;
case 0x04 : /* Escaped because: space */
snprintfcat ( refined , sizeof ( refined ) , " %02x " , 0x20 ) ;
break ;
default :
2016-07-18 03:11:41 +03:00
if ( token ! = 10 & & token ! = 11 )
2015-04-30 16:53:36 +03:00
snprintfcat ( refined , sizeof ( refined ) , " %02x " , ( ( unsigned char * ) item - > answer ) [ i ] ) ;
else
2016-07-18 03:11:41 +03:00
snprintfcat ( refined , sizeof ( refined ) , " %08d " , voltronic_qs_hex_char_to_binary ( ( ( unsigned char * ) item - > answer ) [ i ] ) ) ;
2015-04-30 16:53:36 +03:00
continue ;
}
i + + ;
continue ;
}
/* Trailing CR */
if ( item - > answer [ i ] = = 0x0D )
break ;
2016-07-18 03:11:41 +03:00
if ( token ! = 10 & & token ! = 11 )
2015-04-30 16:53:36 +03:00
snprintfcat ( refined , sizeof ( refined ) , " %02x " , ( ( unsigned char * ) item - > answer ) [ i ] ) ;
else
2016-07-18 03:11:41 +03:00
snprintfcat ( refined , sizeof ( refined ) , " %08d " , voltronic_qs_hex_char_to_binary ( ( ( unsigned char * ) item - > answer ) [ i ] ) ) ;
2015-04-30 16:53:36 +03:00
}
2016-07-18 03:11:41 +03:00
if (
token < 10 | |
token > 11 | |
( token = = 10 & & strlen ( refined ) ! = 46 ) | |
( token = = 11 & & strlen ( refined ) ! = 55 )
) {
2015-04-30 16:53:36 +03:00
upsdebugx ( 2 , " noncompliant reply: %s " , refined ) ;
return - 1 ;
}
upsdebugx ( 4 , " read: %s " , refined ) ;
/* e.g.: item->answer = "#6C01 35 6C01 35 03 519A 1312D0 E6 1E 00001001" */
return snprintf ( item - > answer , sizeof ( item - > answer ) , " %s \r " , refined ) ;
}
2016-07-18 03:11:41 +03:00
/* Transform a char into its binary form (as an int) */
static int voltronic_qs_hex_char_to_binary ( const unsigned char value )
2015-04-30 16:53:36 +03:00
{
unsigned char remainder = value ;
int ret = 0 ,
power = 1 ;
while ( remainder ) {
if ( remainder & 1 )
ret + = power ;
power * = 10 ;
remainder > > = 1 ;
}
return ret ;
}
/* == Preprocess functions == */
/* Protocol used by the UPS */
2016-07-18 03:11:41 +03:00
static int voltronic_qs_hex_protocol ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
item_t * unskip ;
2016-07-18 03:11:41 +03:00
int i ;
const struct {
const char * info_type ; /* info_type of the item to be unskipped */
const unsigned long flags ; /* qxflags that have to be set in the item */
const unsigned long noflags ; /* qxflags that have to be absent in the item */
} items_to_be_unskipped [ ] = {
{ " test.battery.start.quick " , QX_FLAG_CMD , 0 } ,
{ " output.frequency.nominal " , 0 , 0 } ,
{ " battery.voltage.nominal " , 0 , 0 } ,
{ " output.voltage.nominal " , 0 , 0 } ,
{ NULL , 0 , 0 }
} ;
if ( strcasecmp ( item - > value , " P " ) & & strcasecmp ( item - > value , " T " ) ) {
2015-04-30 16:53:36 +03:00
upsdebugx ( 2 , " %s: invalid protocol [%s] " , __func__ , item - > value ) ;
return - 1 ;
}
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic push
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
# pragma GCC diagnostic ignored "-Wformat-security"
# endif
2015-04-30 16:53:36 +03:00
snprintf ( value , valuelen , item - > dfl , item - > value ) ;
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic pop
# endif
2015-04-30 16:53:36 +03:00
2016-07-18 03:11:41 +03:00
/* Unskip items supported only by devices that implement 'T' protocol */
2015-04-30 16:53:36 +03:00
if ( ! strcasecmp ( item - > value , " P " ) )
return 0 ;
2016-07-18 03:11:41 +03:00
for ( i = 0 ; items_to_be_unskipped [ i ] . info_type ; i + + ) {
unskip = find_nut_info ( items_to_be_unskipped [ i ] . info_type , items_to_be_unskipped [ i ] . flags , items_to_be_unskipped [ i ] . noflags ) ;
/* Don't know what happened */
if ( ! unskip )
return - 1 ;
unskip - > qxflags & = ~ QX_FLAG_SKIP ;
}
2015-04-30 16:53:36 +03:00
return 0 ;
}
/* Input/Output voltage */
2016-07-18 03:11:41 +03:00
static int voltronic_qs_hex_input_output_voltage ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
2022-07-10 10:23:45 +03:00
long val ;
2015-04-30 16:53:36 +03:00
double ret ;
2016-07-18 03:11:41 +03:00
char * str_end ;
2015-04-30 16:53:36 +03:00
if ( strspn ( item - > value , " 0123456789ABCDEFabcdef " ) ! = strlen ( item - > value ) ) {
upsdebugx ( 2 , " %s: non numerical value [%s: %s] " , __func__ , item - > info_type , item - > value ) ;
return - 1 ;
}
val = strtol ( item - > value , & str_end , 16 ) * strtol ( str_end , NULL , 16 ) / 51 ;
2016-07-18 03:11:41 +03:00
ret = val / 256.0 ;
2015-04-30 16:53:36 +03:00
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic push
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
# pragma GCC diagnostic ignored "-Wformat-security"
# endif
2015-04-30 16:53:36 +03:00
snprintf ( value , valuelen , item - > dfl , ret ) ;
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic pop
# endif
2015-04-30 16:53:36 +03:00
return 0 ;
}
/* Device load */
2016-07-18 03:11:41 +03:00
static int voltronic_qs_hex_load ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
if ( strspn ( item - > value , " 0123456789ABCDEFabcdef " ) ! = strlen ( item - > value ) ) {
upsdebugx ( 2 , " %s: non numerical value [%s: %s] " , __func__ , item - > info_type , item - > value ) ;
return - 1 ;
}
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic push
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
# pragma GCC diagnostic ignored "-Wformat-security"
# endif
2015-04-30 16:53:36 +03:00
snprintf ( value , valuelen , item - > dfl , strtol ( item - > value , NULL , 16 ) ) ;
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic pop
# endif
2015-04-30 16:53:36 +03:00
return 0 ;
}
/* Output frequency */
2016-07-18 03:11:41 +03:00
static int voltronic_qs_hex_frequency ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
double val1 , val2 , ret ;
char * str_end ;
if ( strspn ( item - > value , " 0123456789ABCDEFabcdef " ) ! = strlen ( item - > value ) ) {
upsdebugx ( 2 , " %s: non numerical value [%s: %s] " , __func__ , item - > info_type , item - > value ) ;
return - 1 ;
}
val1 = strtol ( item - > value , & str_end , 16 ) ;
val2 = strtol ( str_end , NULL , 16 ) ;
ret = val2 / val1 ;
ret = ret > 99.9 ? 99.9 : ret ;
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic push
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
# pragma GCC diagnostic ignored "-Wformat-security"
# endif
2015-04-30 16:53:36 +03:00
snprintf ( value , valuelen , item - > dfl , ret ) ;
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic pop
# endif
2015-04-30 16:53:36 +03:00
return 0 ;
}
/* Battery voltage */
2016-07-18 03:11:41 +03:00
static int voltronic_qs_hex_battery_voltage ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
2022-07-10 10:23:45 +03:00
long val1 , val2 ;
2015-04-30 16:53:36 +03:00
char * str_end ;
if ( strspn ( item - > value , " 0123456789ABCDEFabcdef " ) ! = strlen ( item - > value ) ) {
upsdebugx ( 2 , " %s: non numerical value [%s: %s] " , __func__ , item - > info_type , item - > value ) ;
return - 1 ;
}
val1 = strtol ( item - > value , & str_end , 16 ) ;
val2 = strtol ( str_end , NULL , 16 ) ;
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic push
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
# pragma GCC diagnostic ignored "-Wformat-security"
# endif
2015-04-30 16:53:36 +03:00
snprintf ( value , valuelen , item - > dfl , ( val1 * val2 ) / 510.0 ) ;
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic pop
# endif
2015-04-30 16:53:36 +03:00
return 0 ;
}
2016-07-18 03:11:41 +03:00
/* Ratings bits */
static int voltronic_qs_hex_process_ratings_bits ( item_t * item , char * value , const size_t valuelen )
{
2022-07-10 10:23:45 +03:00
long val ;
2016-07-18 03:11:41 +03:00
double ret ;
if ( strspn ( item - > value , " 01 " ) ! = strlen ( item - > value ) ) {
upsdebugx ( 3 , " %s: unexpected value %s@%d->%s " , __func__ , item - > info_type , item - > from , item - > value ) ;
return - 1 ;
}
val = strtol ( item - > value , NULL , 10 ) ;
switch ( item - > from )
{
case 47 : /* Nominal output frequency */
if ( val = = 0 ) /* 0 -> 50 Hz */
ret = 50 ;
else /* 1 -> 60 Hz */
ret = 60 ;
break ;
case 48 : /* Nominal battery voltage */
if ( val = = 0 ) /* 0 -> 12 V */
ret = 12 ;
else if ( val = = 1 ) /* 1 -> 24 V */
ret = 24 ;
else if ( val = = 10 ) /* 10 -> 36 V */
ret = 36 ;
else /* 11 -> 48 V */
ret = 48 ;
break ;
/* case 50: */ /* Reserved */
/* break;*/
/* case 51: */ /* Reserved */
/* break;*/
case 52 : /* Nominal output voltage */
switch ( val )
{
case 0 :
ret = 110 ;
break ;
case 1 :
ret = 120 ;
break ;
case 10 :
ret = 220 ;
break ;
case 11 :
ret = 230 ;
break ;
case 100 :
ret = 240 ;
break ;
default :
/* Unknown */
return - 1 ;
}
break ;
default :
/* Don't know what happened */
return - 1 ;
}
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic push
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
# pragma GCC diagnostic ignored "-Wformat-security"
# endif
2016-07-18 03:11:41 +03:00
snprintf ( value , valuelen , item - > dfl , ret ) ;
2022-07-10 10:23:45 +03:00
# ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
# pragma GCC diagnostic pop
# endif
2016-07-18 03:11:41 +03:00
return 0 ;
}
2015-04-30 16:53:36 +03:00
/* == Subdriver interface == */
subdriver_t voltronic_qs_hex_subdriver = {
VOLTRONIC_QS_HEX_VERSION ,
voltronic_qs_hex_claim ,
voltronic_qs_hex_qx2nut ,
voltronic_qs_hex_initups ,
NULL ,
blazer_makevartable_light ,
NULL ,
" N \r " ,
# ifdef TESTING
voltronic_qs_hex_testing ,
# endif /* TESTING */
} ;