2015-04-30 16:53:36 +03:00
/* nutdrv_qx_bestups.c - Subdriver for Best Power/Sola Australia UPSes
*
* Copyright ( C )
* 2014 Daniele Pezzini < hyouko @ gmail . com >
* Based on :
* bestups . c - Copyright ( C )
* 1999 Russell Kroll < rkroll @ exploits . org >
* Jason White < jdwhite @ jdwhite . org >
*
* 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"
2022-07-10 10:23:45 +03:00
# include "nut_float.h"
# include "nut_stdint.h"
2015-04-30 16:53:36 +03:00
# include "nutdrv_qx.h"
# include "nutdrv_qx_blazer-common.h"
# include "nutdrv_qx_bestups.h"
2016-07-18 03:11:41 +03:00
# define BESTUPS_VERSION "BestUPS 0.06"
2015-04-30 16:53:36 +03:00
/* Support functions */
static int bestups_claim ( void ) ;
static void bestups_initups ( void ) ;
static void bestups_makevartable ( void ) ;
/* Answer preprocess functions */
static int bestups_preprocess_id_answer ( item_t * item , const int len ) ;
/* Preprocess functions */
2016-07-18 03:11:41 +03:00
static int bestups_process_setvar ( item_t * item , char * value , const size_t valuelen ) ;
static int bestups_process_bbb_status_bit ( item_t * item , char * value , const size_t valuelen ) ;
static int bestups_manufacturer ( item_t * item , char * value , const size_t valuelen ) ;
static int bestups_model ( item_t * item , char * value , const size_t valuelen ) ;
static int bestups_batt_runtime ( item_t * item , char * value , const size_t valuelen ) ;
static int bestups_batt_packs ( item_t * item , char * value , const size_t valuelen ) ;
static int bestups_get_pins_shutdown_mode ( item_t * item , char * value , const size_t valuelen ) ;
static int bestups_voltage_settings ( item_t * item , char * value , const size_t valuelen ) ;
2015-04-30 16:53:36 +03:00
/* ups.conf settings */
static int pins_shutdown_mode ;
/* General settings */
static int inverted_bbb_bit = 0 ;
/* == Ranges/enums == */
/* Range for ups.delay.start */
2022-07-10 10:23:45 +03:00
static info_rw_t bestups_r_ondelay [ ] = {
2015-04-30 16:53:36 +03:00
{ " 60 " , 0 } ,
{ " 599940 " , 0 } ,
{ " " , 0 }
} ;
/* Range for ups.delay.shutdown */
static info_rw_t bestups_r_offdelay [ ] = {
{ " 12 " , 0 } ,
{ " 5940 " , 0 } ,
{ " " , 0 }
} ;
/* Range for number of battery packs */
static info_rw_t bestups_r_batt_packs [ ] = {
{ " 0 " , 0 } ,
{ " 5 " , 0 } ,
{ " " , 0 }
} ;
/* Range for pin shutdown mode */
static info_rw_t bestups_r_pins_shutdown_mode [ ] = {
{ " 0 " , 0 } ,
{ " 6 " , 0 } ,
{ " " , 0 }
} ;
/* == qx2nut lookup table == */
static item_t bestups_qx2nut [ ] = {
/* Query UPS for status
* > [ Q1 \ r ]
* < [ ( 226.0 195.0 226.0 014 49.0 27.5 30.0 00001000 \ r ]
* 01234567 890123456789012345678901234567890123456
* 0 1 2 3 4
*/
2016-07-18 03:11:41 +03:00
{ " input.voltage " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 1 , 5 , " %.1f " , 0 , NULL , NULL , NULL } ,
{ " input.voltage.fault " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 7 , 11 , " %.1f " , 0 , NULL , NULL , NULL } ,
{ " output.voltage " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 13 , 17 , " %.1f " , 0 , NULL , NULL , NULL } ,
{ " ups.load " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 19 , 21 , " %.0f " , 0 , NULL , NULL , NULL } ,
{ " input.frequency " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 23 , 26 , " %.1f " , 0 , NULL , NULL , NULL } ,
{ " battery.voltage " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 28 , 31 , " %.2f " , 0 , NULL , NULL , NULL } ,
{ " ups.temperature " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 33 , 36 , " %.1f " , 0 , NULL , NULL , NULL } ,
2015-04-30 16:53:36 +03:00
/* Status bits */
2016-07-18 03:11:41 +03:00
{ " ups.status " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 38 , 38 , NULL , QX_FLAG_QUICK_POLL , NULL , NULL , blazer_process_status_bits } , /* Utility Fail (Immediate) */
{ " ups.status " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 39 , 39 , NULL , QX_FLAG_QUICK_POLL , NULL , NULL , blazer_process_status_bits } , /* Battery Low */
{ " ups.alarm " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 41 , 41 , NULL , 0 , NULL , NULL , blazer_process_status_bits } , /* UPS Failed */
{ " ups.type " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 42 , 42 , " %s " , QX_FLAG_STATIC , NULL , NULL , blazer_process_status_bits } , /* UPS Type */
{ " ups.status " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 43 , 43 , NULL , QX_FLAG_QUICK_POLL , NULL , NULL , blazer_process_status_bits } , /* Test in Progress */
{ " ups.status " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 44 , 44 , NULL , QX_FLAG_QUICK_POLL , NULL , NULL , blazer_process_status_bits } , /* Shutdown Active */
/* { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, */ /* Beeper status: not supported; always 0 */
{ " ups.status " , 0 , NULL , " Q1 \r " , " " , 47 , ' ( ' , " " , 40 , 40 , NULL , QX_FLAG_QUICK_POLL , NULL , NULL , bestups_process_bbb_status_bit } , /* Bypass/Boost or Buck Active - keep this one at the end as it needs the processed data from the previous items */
2015-04-30 16:53:36 +03:00
/* Query UPS for ratings and model infos
* > [ ID \ r ]
* < [ FOR , 750 , 120 , 120 , 20.0 , 27.6 \ r ] case # 1 : length = 26
* < [ FOR , 1500 , 120 , 120 , 20.0 , 27.6 \ r ] case # 2 : length = 27
* < [ FOR , 3000 , 120 , 120 , 20.0 , 100.6 \ r ] case # 3 : length = 28
* < [ FOR , 750 , 120 , 120 , 20.0 , 27.6 \ r ] after being preprocessed : length = 28
* 01234567 89012345678901234567
* 0 1 2
*/
2016-07-18 03:11:41 +03:00
{ " device.mfr " , 0 , NULL , " ID \r " , " " , 28 , 0 , " " , 0 , 2 , " %s " , QX_FLAG_STATIC , NULL , bestups_preprocess_id_answer , bestups_manufacturer } ,
{ " device.model " , 0 , NULL , " ID \r " , " " , 28 , 0 , " " , 0 , 2 , " %s " , QX_FLAG_STATIC , NULL , bestups_preprocess_id_answer , bestups_model } ,
{ " ups.power.nominal " , 0 , NULL , " ID \r " , " " , 28 , 0 , " " , 4 , 7 , " %.0f " , QX_FLAG_STATIC , NULL , bestups_preprocess_id_answer , NULL } ,
{ " input.voltage.nominal " , 0 , NULL , " ID \r " , " " , 28 , 0 , " " , 9 , 11 , " %.0f " , QX_FLAG_STATIC , NULL , bestups_preprocess_id_answer , NULL } ,
{ " output.voltage.nominal " , 0 , NULL , " ID \r " , " " , 28 , 0 , " " , 13 , 15 , " %.0f " , QX_FLAG_STATIC , NULL , bestups_preprocess_id_answer , NULL } ,
{ " battery.voltage.low " , 0 , NULL , " ID \r " , " " , 28 , 0 , " " , 17 , 20 , " %.1f " , QX_FLAG_SEMI_STATIC , NULL , bestups_preprocess_id_answer , NULL } ,
{ " battery.voltage.high " , 0 , NULL , " ID \r " , " " , 28 , 0 , " " , 22 , 26 , " %.1f " , QX_FLAG_SEMI_STATIC , NULL , bestups_preprocess_id_answer , NULL } ,
2015-04-30 16:53:36 +03:00
/* Query UPS for battery runtime (not available on the Patriot Pro/Sola 320 model series)
* > [ RT \ r ]
* < [ 025 \ r ]
* 0123
* 0
*/
2016-07-18 03:11:41 +03:00
{ " battery.runtime " , 0 , NULL , " RT \r " , " " , 4 , 0 , " " , 0 , 2 , " %.0f " , QX_FLAG_SKIP , NULL , NULL , bestups_batt_runtime } ,
2015-04-30 16:53:36 +03:00
/* Query UPS for number of battery packs (available only on the Axxium/Sola 620 model series)
* > [ BP ? \ r ]
* < [ 02 \ r ]
* 012
* 0
*/
2016-07-18 03:11:41 +03:00
{ " battery.packs " , ST_FLAG_RW , bestups_r_batt_packs , " BP? \r " , " " , 3 , 0 , " " , 0 , 1 , " %d " , QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP , NULL , NULL , bestups_batt_packs } ,
2015-04-30 16:53:36 +03:00
/* Set number of battery packs to n (integer, 0-5) (available only on the Axxium/Sola 620 model series)
* > [ BPn \ r ]
* < [ ]
*/
2016-07-18 03:11:41 +03:00
{ " battery.packs " , 0 , bestups_r_batt_packs , " BP%.0f \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP , NULL , NULL , bestups_process_setvar } ,
2015-04-30 16:53:36 +03:00
2022-07-10 10:23:45 +03:00
/* Query UPS for shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power's EPS-0059)
2015-04-30 16:53:36 +03:00
* > [ SS ? \ r ]
* < [ 0 \ r ]
* 01
* 0
*/
2016-07-18 03:11:41 +03:00
{ " pins_shutdown_mode " , ST_FLAG_RW , bestups_r_pins_shutdown_mode , " SS? \r " , " " , 2 , 0 , " " , 0 , 0 , " %.0f " , QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT , NULL , NULL , bestups_get_pins_shutdown_mode } ,
2015-04-30 16:53:36 +03:00
2022-07-10 10:23:45 +03:00
/* Set shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power's EPS-0059) to n (integer, 0-6)
2015-04-30 16:53:36 +03:00
* > [ SSn \ r ]
* < [ ]
*/
2016-07-18 03:11:41 +03:00
{ " pins_shutdown_mode " , 0 , bestups_r_pins_shutdown_mode , " SS%.0f \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP , NULL , NULL , bestups_process_setvar } ,
2015-04-30 16:53:36 +03:00
/* Query UPS for voltage settings
* > [ M \ r ]
* < [ 0 \ r ]
* 01
* 0
*/
2016-07-18 03:11:41 +03:00
{ " input.transfer.low " , 0 , NULL , " M \r " , " " , 2 , 0 , " " , 0 , 0 , " %d " , 0 , NULL , NULL , bestups_voltage_settings } ,
{ " input.transfer.boost.low " , 0 , NULL , " M \r " , " " , 2 , 0 , " " , 0 , 0 , " %d " , 0 , NULL , NULL , bestups_voltage_settings } ,
{ " input.transfer.boost.high " , 0 , NULL , " M \r " , " " , 2 , 0 , " " , 0 , 0 , " %d " , 0 , NULL , NULL , bestups_voltage_settings } ,
{ " input.voltage.nominal " , 0 , NULL , " M \r " , " " , 2 , 0 , " " , 0 , 0 , " %d " , 0 , NULL , NULL , bestups_voltage_settings } ,
{ " output.voltage.nominal " , 0 , NULL , " M \r " , " " , 2 , 0 , " " , 0 , 0 , " %d " , 0 , NULL , NULL , bestups_voltage_settings } ,
{ " input.transfer.trim.low " , 0 , NULL , " M \r " , " " , 2 , 0 , " " , 0 , 0 , " %d " , 0 , NULL , NULL , bestups_voltage_settings } ,
{ " input.transfer.trim.high " , 0 , NULL , " M \r " , " " , 2 , 0 , " " , 0 , 0 , " %d " , 0 , NULL , NULL , bestups_voltage_settings } ,
{ " input.transfer.high " , 0 , NULL , " M \r " , " " , 2 , 0 , " " , 0 , 0 , " %d " , 0 , NULL , NULL , bestups_voltage_settings } ,
2015-04-30 16:53:36 +03:00
/* Instant commands */
2016-07-18 03:11:41 +03:00
{ " 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%s \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 } ,
{ " load.on " , 0 , NULL , " C \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 } ,
{ " test.battery.start " , 0 , NULL , " T%02d \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_CMD , NULL , NULL , blazer_process_command } ,
{ " test.battery.start.deep " , 0 , NULL , " TL \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 , NULL , NULL , NULL } ,
{ " test.battery.stop " , 0 , NULL , " CT \r " , " " , 0 , 0 , " " , 0 , 0 , NULL , QX_FLAG_CMD , 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 , bestups_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 , bestups_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 bestups_testing [ ] = {
{ " Q1 \r " , " (215.0 195.0 230.0 014 49.0 22.7 30.0 00100000 \r " , - 1 } ,
{ " ID \r " , " FOR,750,120,120,20.0,27.6 \r " , - 1 } ,
{ " RT \r " , " 015 \r " , - 1 } ,
{ " BP? \r " , " 02 \r " , - 1 } ,
{ " BP1 \r " , " " , - 1 } ,
{ " SS? \r " , " 0 \r " , - 1 } ,
{ " SS2 \r " , " " , - 1 } ,
{ " M \r " , " 0 \r " , - 1 } ,
{ " S03 \r " , " " , - 1 } ,
{ " C \r " , " " , - 1 } ,
{ " S02R0005 \r " , " " , - 1 } ,
{ " S.5R0001 \r " , " " , - 1 } ,
{ " T04 \r " , " " , - 1 } ,
{ " TL \r " , " " , - 1 } ,
{ " T \r " , " " , - 1 } ,
{ " CT \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 bestups_claim ( void )
{
/* We need at least Q1 and ID to run this subdriver */
item_t * item = find_nut_info ( " input.voltage " , 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 */
if ( ups_infoval_set ( item ) ! = 1 )
return 0 ;
/* UPS Model */
item = find_nut_info ( " device.model " , 0 , 0 ) ;
/* Don't know what happened */
if ( ! item ) {
dstate_delinfo ( " input.voltage " ) ;
return 0 ;
}
/* No reply/Unable to get value */
if ( qx_process ( item , NULL ) ) {
dstate_delinfo ( " input.voltage " ) ;
return 0 ;
}
/* Unable to process value */
if ( ups_infoval_set ( item ) ! = 1 ) {
dstate_delinfo ( " input.voltage " ) ;
return 0 ;
}
return 1 ;
}
/* Subdriver-specific initups */
static void bestups_initups ( void )
{
blazer_initups_light ( bestups_qx2nut ) ;
}
/* Subdriver-specific flags/vars */
static void bestups_makevartable ( void )
{
2022-07-10 10:23:45 +03:00
addvar ( VAR_VALUE , " pins_shutdown_mode " , " Set shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power's EPS-0059) to n (integer, 0-6) " ) ;
2015-04-30 16:53:36 +03:00
blazer_makevartable_light ( ) ;
}
/* == Answer preprocess functions == */
/* Preprocess the answer we got back from the UPS when queried with 'ID\r': make data begin always at the same indexes */
static int bestups_preprocess_id_answer ( item_t * item , const int len )
{
int i ;
char refined [ SMALLBUF ] = " " ,
rawval [ SMALLBUF ] = " " ,
* token ,
* saveptr = NULL ;
if ( len < = 0 )
return len ;
if ( len < 25 | | len > 27 ) {
upsdebugx ( 4 , " %s: wrong length [%s: %d] " , __func__ , item - > info_type , len ) ;
return - 1 ;
}
/* e.g.:
* 1. item - > answer = " FOR,750,120,120,20.0,27.6 \r " ; len = 26
* 2. item - > answer = " FOR,1500,120,120,20.0,27.6 \r " ; len = 27
* 3. item - > answer = " FOR,3000,120,120,20.0,100.6 \r " ; len = 28 */
upsdebugx ( 4 , " read: '%.*s' " , ( int ) strcspn ( item - > answer , " \r " ) , item - > answer ) ;
snprintf ( rawval , sizeof ( rawval ) , " %s " , item - > answer ) ;
for ( i = 1 , token = strtok_r ( rawval , " , " , & saveptr ) ; token ! = NULL ; i + + , token = strtok_r ( NULL , " , " , & saveptr ) ) {
switch ( i )
{
case 1 :
snprintf ( refined , sizeof ( refined ) , " %s " , token ) ;
continue ;
case 2 : /* Output power */
snprintfcat ( refined , sizeof ( refined ) , " ,%4s " , token ) ;
continue ;
case 6 : /* Battery voltage at full charge (+ trailing CR) */
snprintfcat ( refined , sizeof ( refined ) , " ,%6s " , token ) ;
continue ;
default :
snprintfcat ( refined , sizeof ( refined ) , " ,%s " , token ) ;
}
}
if ( i ! = 7 | | strlen ( refined ) ! = 28 ) {
upsdebugx ( 2 , " noncompliant reply: '%.*s' " , ( int ) strcspn ( refined , " \r " ) , refined ) ;
return - 1 ;
}
upsdebugx ( 4 , " read: '%.*s' " , ( int ) strcspn ( refined , " \r " ) , refined ) ;
/* e.g.: item->answer = "FOR, 750,120,120,20.0, 27.6\r"; len = 28 */
return snprintf ( item - > answer , sizeof ( item - > answer ) , " %s " , refined ) ;
}
/* == Preprocess functions == */
/* *SETVAR(/NONUT)* Preprocess setvars */
2016-07-18 03:11:41 +03:00
static int bestups_process_setvar ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
if ( ! strlen ( value ) ) {
upsdebugx ( 2 , " %s: value not given for %s " , __func__ , item - > info_type ) ;
return - 1 ;
}
double val = strtod ( value , NULL ) ;
if ( ! strcasecmp ( item - > info_type , " pins_shutdown_mode " ) ) {
2022-07-10 10:23:45 +03:00
if ( d_equal ( val , pins_shutdown_mode ) ) {
2015-04-30 16:53:36 +03:00
upslogx ( LOG_INFO , " %s is already set to %.0f " , item - > info_type , val ) ;
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 - > command , val ) ;
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 ;
}
/* Bypass/Boost or Buck status */
2016-07-18 03:11:41 +03:00
static int bestups_process_bbb_status_bit ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
/* Bypass/Boost/Buck bit is not reliable when a battery test, shutdown or on battery condition occurs: always ignore it in these cases */
2022-07-10 10:23:45 +03:00
if ( ! ( ( unsigned int ) ( qx_status ( ) ) & STATUS ( OL ) ) | | ( ( unsigned int ) ( qx_status ( ) ) & ( STATUS ( CAL ) | STATUS ( FSD ) ) ) ) {
2015-04-30 16:53:36 +03:00
if ( item - > value [ 0 ] = = ' 1 ' )
item - > value [ 0 ] = ' 0 ' ;
return blazer_process_status_bits ( item , value , valuelen ) ;
}
/* UPSes with inverted bypass/boost/buck bit */
if ( inverted_bbb_bit ) {
if ( item - > value [ 0 ] = = ' 1 ' )
item - > value [ 0 ] = ' 0 ' ;
else if ( item - > value [ 0 ] = = ' 0 ' )
item - > value [ 0 ] = ' 1 ' ;
}
return blazer_process_status_bits ( item , value , valuelen ) ;
}
/* Identify UPS manufacturer */
2016-07-18 03:11:41 +03:00
static int bestups_manufacturer ( 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
# 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
/* Best Power devices */
if (
! strcmp ( item - > value , " AX1 " ) | |
! strcmp ( item - > value , " FOR " ) | |
! strcmp ( item - > value , " FTC " ) | |
! strcmp ( item - > value , " PR2 " ) | |
! strcmp ( item - > value , " PRO " )
) {
snprintf ( value , valuelen , item - > dfl , " Best Power " ) ;
return 0 ;
}
/* Sola Australia devices */
if (
! strcmp ( item - > value , " 325 " ) | |
! strcmp ( item - > value , " 520 " ) | |
! strcmp ( item - > value , " 620 " )
) {
snprintf ( value , valuelen , item - > dfl , " Sola Australia " ) ;
return 0 ;
}
/* Unknown devices */
snprintf ( value , valuelen , item - > dfl , " Unknown " ) ;
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 ;
}
/* Identify UPS model and unskip qx2nut table's items accordingly */
2016-07-18 03:11:41 +03:00
static int bestups_model ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
item_t * unskip ;
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
/* Best Power devices */
if ( ! strcmp ( item - > value , " AX1 " ) ) {
snprintf ( value , valuelen , item - > dfl , " Axxium Rackmount " ) ;
} else if ( ! strcmp ( item - > value , " FOR " ) ) {
snprintf ( value , valuelen , item - > dfl , " Fortress " ) ;
} else if ( ! strcmp ( item - > value , " FTC " ) ) {
snprintf ( value , valuelen , item - > dfl , " Fortress Telecom " ) ;
} else if ( ! strcmp ( item - > value , " PR2 " ) ) {
snprintf ( value , valuelen , item - > dfl , " Patriot Pro II " ) ;
inverted_bbb_bit = 1 ;
} else if ( ! strcmp ( item - > value , " PRO " ) ) {
snprintf ( value , valuelen , item - > dfl , " Patriot Pro " ) ;
inverted_bbb_bit = 1 ;
/* Sola Australia devices */
} else if (
! strcmp ( item - > value , " 320 " ) | |
! strcmp ( item - > value , " 325 " ) | |
! strcmp ( item - > value , " 520 " ) | |
! strcmp ( item - > value , " 525 " ) | |
! strcmp ( item - > value , " 620 " )
) {
snprintf ( value , valuelen , " Sola %s " , item - > value ) ;
/* Unknown devices */
} else {
snprintf ( value , valuelen , item - > dfl , " Unknown (%s) " , item - > value ) ;
upslogx ( LOG_INFO , " Unknown model detected - please report this ID: '%s' " , item - > value ) ;
}
/* Unskip qx2nut table's items according to the UPS model */
/* battery.runtime var is not available on the Patriot Pro/Sola 320 model series: leave it skipped in these cases, otherwise unskip it */
if ( strcmp ( item - > value , " PRO " ) & & strcmp ( item - > value , " 320 " ) ) {
unskip = find_nut_info ( " battery.runtime " , 0 , 0 ) ;
/* Don't know what happened */
if ( ! unskip )
return - 1 ;
unskip - > qxflags & = ~ QX_FLAG_SKIP ;
}
/* battery.packs var is available only on the Axxium/Sola 620 model series: unskip it in these cases */
if ( ! strcmp ( item - > value , " AX1 " ) | | ! strcmp ( item - > value , " 620 " ) ) {
unskip = find_nut_info ( " battery.packs " , 0 , QX_FLAG_SETVAR ) ;
/* Don't know what happened */
if ( ! unskip )
return - 1 ;
unskip - > qxflags & = ~ QX_FLAG_SKIP ;
}
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 runtime */
2016-07-18 03:11:41 +03:00
static int bestups_batt_runtime ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
double runtime ;
if ( strspn ( item - > value , " 0123456789 . " ) ! = strlen ( item - > value ) ) {
upsdebugx ( 2 , " %s: non numerical value [%s: %s] " , __func__ , item - > info_type , item - > value ) ;
return - 1 ;
}
/* Battery runtime is reported by the UPS in minutes, NUT expects seconds */
runtime = strtod ( item - > value , NULL ) * 60 ;
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 , runtime ) ;
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 packs */
2016-07-18 03:11:41 +03:00
static int bestups_batt_packs ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
item_t * unskip ;
if ( strspn ( item - > value , " 0123456789 " ) ! = 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 , 10 ) ) ;
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
/* Unskip battery.packs setvar */
unskip = find_nut_info ( " battery.packs " , QX_FLAG_SETVAR , 0 ) ;
/* Don't know what happened */
if ( ! unskip )
return - 1 ;
unskip - > qxflags & = ~ QX_FLAG_SKIP ;
return 0 ;
}
2022-07-10 10:23:45 +03:00
/* *NONUT* Get shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power's EPS-0059) as set in the UPS */
2016-07-18 03:11:41 +03:00
static int bestups_get_pins_shutdown_mode ( item_t * item , char * value , const size_t valuelen )
2015-04-30 16:53:36 +03:00
{
item_t * unskip ;
2022-07-10 10:23:45 +03:00
long l ;
2015-04-30 16:53:36 +03:00
if ( strspn ( item - > value , " 0123456789 " ) ! = 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
l = strtol ( item - > value , NULL , 10 ) ;
if ( l > INT_MAX ) {
upsdebugx ( 2 , " %s: pins_shutdown_mode out of range [%s: %s] " ,
__func__ , item - > info_type , item - > value ) ;
return - 1 ;
}
pins_shutdown_mode = ( int ) l ;
# 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 , pins_shutdown_mode ) ;
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
/* We were not asked by the user to change the value */
if ( ( item - > qxflags & QX_FLAG_NONUT ) & & ! getval ( item - > info_type ) )
return 0 ;
/* Unskip setvar */
unskip = find_nut_info ( item - > info_type , QX_FLAG_SETVAR , 0 ) ;
/* Don't know what happened */
if ( ! unskip )
return - 1 ;
unskip - > qxflags & = ~ QX_FLAG_SKIP ;
return 0 ;
}
/* Voltage settings */
2016-07-18 03:11:41 +03:00
static int bestups_voltage_settings ( 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 index ;
int val ;
2015-04-30 16:53:36 +03:00
const char * nominal_voltage ;
const struct {
const int low ; /* Low voltage -> input.transfer.low / input.transfer.boost.low */
const int boost ; /* Boost voltage -> input.transfer.boost.high */
const int nominal ; /* Nominal voltage -> input.voltage.nominal / output.voltage.nominal */
const int buck ; /* Buck voltage -> input.transfer.trim.low */
const int high ; /* High voltage -> input.transfer.high / input.transfer.trim.high */
} voltage_settings [ ] = {
/* U models voltage limits, for:
* - Fortress ( 750U , 1050U , 1425U , 1800U and 2250U )
* - Fortress Rackmount ( 750 , 1050 , 1425 , 1800 , and 2250 VA )
* - Patriot Pro II ( 400U , 750U , and 1000U ) */
/* M low boost nominal buck high */
/* 0 */ { 96 , 109 , 120 , 130 , 146 } , /* LEDs lit: 2,3,4 (Default) */
/* 1 */ { 96 , 109 , 120 , 138 , 156 } , /* LEDs lit: 1,3,4 */
/* 2 */ { 90 , 104 , 120 , 130 , 146 } , /* LEDs lit: 2,3,5 */
/* 3 */ { 90 , 104 , 120 , 138 , 156 } , /* LEDs lit: 1,3,5 */
/* 4 */ { 90 , 104 , 110 , 120 , 130 } , /* LEDs lit: 3,4,5 */
/* 5 */ { 90 , 104 , 110 , 130 , 146 } , /* LEDs lit: 2,4,5 */
/* 6 */ { 90 , 96 , 110 , 120 , 130 } , /* LEDs lit: 3,4,6 */
/* 7 */ { 90 , 96 , 110 , 130 , 146 } , /* LEDs lit: 2,4,6 */
/* 8 */ { 96 , 109 , 128 , 146 , 156 } , /* LEDs lit: 1,2,4 */
/* 9 */ { 90 , 104 , 128 , 146 , 156 } , /* LEDs lit: 1,2,5 */
/* E models voltage limits, for:
* - Fortress ( 750 E , 1050 E , 1425 E , and 2250 E )
* - Fortress Rackmount ( 750 , 1050 , 1425 , and 2250 VA )
* - Patriot Pro II ( 400 E , 750 E , and 1000 E ) */
/* M low boost nominal buck high */
/* 0 */ { 200 , 222 , 240 , 250 , 284 } , /* LEDs lit: 2,3,4 */
/* 1 */ { 200 , 222 , 240 , 264 , 290 } , /* LEDs lit: 1,3,4 */
/* 2 */ { 188 , 210 , 240 , 250 , 284 } , /* LEDs lit: 2,3,5 */
/* 3 */ { 188 , 210 , 240 , 264 , 290 } , /* LEDs lit: 1,3,5 */
/* 4 */ { 188 , 210 , 230 , 244 , 270 } , /* LEDs lit: 3,4,5 (Default) */
/* 5 */ { 188 , 210 , 230 , 250 , 284 } , /* LEDs lit: 2,4,5 */
/* 6 */ { 180 , 200 , 230 , 244 , 270 } , /* LEDs lit: 3,4,6 */
/* 7 */ { 180 , 200 , 230 , 250 , 284 } , /* LEDs lit: 2,4,6 */
/* 8 */ { 165 , 188 , 208 , 222 , 244 } , /* LEDs lit: 4,5,6 */
/* 9 */ { 165 , 188 , 208 , 244 , 270 } /* LEDs lit: 3,5,6 */
} ;
if ( strspn ( item - > value , " 0123456789 " ) ! = strlen ( item - > value ) ) {
upsdebugx ( 2 , " %s: non numerical value [%s: %s] " , __func__ , item - > info_type , item - > value ) ;
return - 1 ;
}
index = strtol ( item - > value , NULL , 10 ) ;
if ( index < 0 | | index > 9 ) {
2022-07-10 10:23:45 +03:00
upsdebugx ( 2 , " %s: value '%ld' out of range [0..9] " , __func__ , index ) ;
2015-04-30 16:53:36 +03:00
return - 1 ;
}
nominal_voltage = dstate_getinfo ( " input.voltage.nominal " ) ;
if ( ! nominal_voltage )
nominal_voltage = dstate_getinfo ( " output.voltage.nominal " ) ;
if ( ! nominal_voltage ) {
upsdebugx ( 2 , " %s: unable to get nominal voltage " , __func__ ) ;
return - 1 ;
}
/* E models */
if ( strtol ( nominal_voltage , NULL , 10 ) > 160 )
index + = 10 ;
if ( ! strcasecmp ( item - > info_type , " input.transfer.low " ) | | ! strcasecmp ( item - > info_type , " input.transfer.boost.low " ) ) {
val = voltage_settings [ index ] . low ;
} else if ( ! strcasecmp ( item - > info_type , " input.transfer.boost.high " ) ) {
val = voltage_settings [ index ] . boost ;
} else if ( ! strcasecmp ( item - > info_type , " input.voltage.nominal " ) | | ! strcasecmp ( item - > info_type , " output.voltage.nominal " ) ) {
val = voltage_settings [ index ] . nominal ;
} else if ( ! strcasecmp ( item - > info_type , " input.transfer.trim.low " ) ) {
val = voltage_settings [ index ] . buck ;
} else if ( ! strcasecmp ( item - > info_type , " input.transfer.trim.high " ) | | ! strcasecmp ( item - > info_type , " input.transfer.high " ) ) {
val = voltage_settings [ index ] . high ;
} else {
/* 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
2015-04-30 16:53:36 +03:00
snprintf ( value , valuelen , item - > dfl , val ) ;
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 ;
}
/* == Subdriver interface == */
subdriver_t bestups_subdriver = {
BESTUPS_VERSION ,
bestups_claim ,
bestups_qx2nut ,
bestups_initups ,
NULL ,
bestups_makevartable ,
NULL ,
NULL ,
# ifdef TESTING
bestups_testing ,
# endif /* TESTING */
} ;