From bbf2d51a7900e74e9849732258adc066edfecc6c Mon Sep 17 00:00:00 2001 From: dtsecon Date: Thu, 23 Dec 2021 18:12:12 +0200 Subject: [PATCH 01/18] handle broken pine error in modbus TCP connections, configurable modbus response timeouts between requests and frame bytes --- drivers/generic_modbus.c | 125 +++++++++++++++++++++++++++++++++++++-- drivers/generic_modbus.h | 10 ++++ 2 files changed, 129 insertions(+), 6 deletions(-) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 3b35d92013..6aa709462e 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -41,6 +41,11 @@ static int ser_data_bit = DATA_BIT; /* serial port data b static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ static int FSD_pulse_duration = SHTDOWN_PULSE_DURATION; /* set the FSD pulse duration */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); @@ -48,6 +53,9 @@ void get_config_vars(void); /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); +/* reconnect upon communication error */ +void modbus_reconnect(); + /* modbus register read function */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); @@ -100,23 +108,37 @@ void upsdrv_initups(void) get_config_vars(); - /* open serial port */ + /* open communication port */ mbctx = modbus_new(device_path); if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open serial port context"); + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); } /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); /* slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); if (rval < 0) { modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d\n", rio_slave_id); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); } /* connect to modbus device */ if (modbus_connect(mbctx) == -1) { modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s\n", modbus_strerror(errno)); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } } @@ -305,6 +327,10 @@ void upsdrv_makevartable(void) addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); addvar(VAR_VALUE, "OL_addr", "modbus address for OL state"); addvar(VAR_VALUE, "OB_addr", "modbus address for OB state"); addvar(VAR_VALUE, "LB_addr", "modbus address for LB state"); @@ -396,6 +422,12 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) (type == INPUT_R) ? "INPUT_R" : "HOLDING", device_path ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } } upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); return rval; @@ -446,6 +478,12 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) (type == INPUT_R) ? "INPUT_R" : "HOLDING", device_path ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } } upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); return rval; @@ -677,7 +715,37 @@ void get_config_vars() } upsdebugx(2, "rio_slave_id %d", rio_slave_id); - /* check if OL address is set and get the value */ + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (int)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (int)strtol(getval("mod_resp_to_us"), NULL, 10); + } + if (mod_resp_to_us > 999999 || mod_resp_to_us < 0) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (int)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (int)strtol(getval("mod_byte_to_us"), NULL, 10); + } + if (mod_byte_to_us > 999999 || mod_byte_to_us < 0) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); + + /* check if OL address is set and get the value */ if (testvar("OL_addr")) { sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); if (testvar("OL_noro")) { @@ -908,3 +976,48 @@ modbus_t *modbus_new(const char *port) } return mb; } + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect() +{ + int rval; + + upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} \ No newline at end of file diff --git a/drivers/generic_modbus.h b/drivers/generic_modbus.h index a68b96f7b1..318300d7d3 100644 --- a/drivers/generic_modbus.h +++ b/drivers/generic_modbus.h @@ -33,6 +33,16 @@ #define DATA_BIT 8 #define STOP_BIT 1 +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + + /* modbus access parameters */ #define MODBUS_SLAVE_ID 5 From 4d9d63a0c99768d45e7181945f9384c1a3f951a8 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Fri, 24 Dec 2021 00:47:30 +0200 Subject: [PATCH 02/18] fix in modbus response value check --- drivers/generic_modbus.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 6aa709462e..186f4c981e 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -168,6 +168,7 @@ void upsdrv_updateinfo(void) } else { status_set("OB"); online = 0; + /* if DISCHRG state is not mapped to a contact and UPS is on * batteries set status to DISCHRG state */ if (sigar[DISCHRG_T].addr == NOTUSED) { @@ -194,10 +195,10 @@ void upsdrv_updateinfo(void) } } - /* - * update UPS status regarding CHARGING state via HB. HB is usually - * mapped to "ready" contact when closed indicates a charging state > 85% - */ + /* + * update UPS status regarding CHARGING state via HB. HB is usually + * mapped to "ready" contact when closed indicates a charging state > 85% + */ if (sigar[HB_T].addr != NOTUSED) { rval = get_signal_state(HB_T); upsdebugx(2, "HB value: %d", rval); @@ -717,31 +718,31 @@ void get_config_vars() /* check if response time out (s) is set ang get the value */ if (testvar("mod_resp_to_s")) { - mod_resp_to_s = (int)strtol(getval("mod_resp_to_s"), NULL, 10); + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); } upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); /* check if response time out (us) is set ang get the value */ if (testvar("mod_resp_to_us")) { - mod_resp_to_us = (int)strtol(getval("mod_resp_to_us"), NULL, 10); - } - if (mod_resp_to_us > 999999 || mod_resp_to_us < 0) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } } upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); /* check if byte time out (s) is set ang get the value */ if (testvar("mod_byte_to_s")) { - mod_byte_to_s = (int)strtol(getval("mod_byte_to_s"), NULL, 10); + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); } upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); /* check if byte time out (us) is set ang get the value */ if (testvar("mod_byte_to_us")) { - mod_byte_to_us = (int)strtol(getval("mod_byte_to_us"), NULL, 10); - } - if (mod_byte_to_us > 999999 || mod_byte_to_us < 0) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } } upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); @@ -755,6 +756,7 @@ void get_config_vars() } } } + /* check if OL register type is set and get the value otherwise set to INPUT_B */ if (testvar("OL_regtype")) { sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); @@ -775,6 +777,7 @@ void get_config_vars() sigar[OB_T].noro = 0; } } + /* check if OB register type is set and get the value otherwise set to INPUT_B */ if (testvar("OB_regtype")) { sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); @@ -795,6 +798,7 @@ void get_config_vars() } } } + /* check if LB register type is set and get the value otherwise set to INPUT_B */ if (testvar("LB_regtype")) { sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); @@ -815,6 +819,7 @@ void get_config_vars() } } } + /* check if HB register type is set and get the value otherwise set to INPUT_B */ if (testvar("HB_regtype")) { sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); @@ -835,6 +840,7 @@ void get_config_vars() } } } + /* check if RB register type is set and get the value otherwise set to INPUT_B */ if (testvar("RB_regtype")) { sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); @@ -855,6 +861,7 @@ void get_config_vars() } } } + /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ if (testvar("CHRG_regtype")) { sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); @@ -875,6 +882,7 @@ void get_config_vars() } } } + /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ if (testvar("DISCHRG_regtype")) { sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); @@ -895,6 +903,7 @@ void get_config_vars() } } } + /* check if FSD register type is set and get the value otherwise set to COIL */ if (testvar("FSD_regtype")) { sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); From f42aec3d028a5ed1d8aa97601a4abf1f359a7d39 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 4 Jan 2022 18:54:35 +0200 Subject: [PATCH 03/18] comment and code alignment fixes --- drivers/generic_modbus.c | 1536 +++++++++++++++++++------------------- 1 file changed, 768 insertions(+), 768 deletions(-) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 186f4c981e..e30c25c3f1 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -25,8 +25,8 @@ #include #include -#define DRIVER_NAME "NUT Generic Modbus driver" -#define DRIVER_VERSION "0.01" +#define DRIVER_NAME "NUT Generic Modbus driver" +#define DRIVER_VERSION "0.01" /* variables */ static modbus_t *mbctx = NULL; /* modbus memory context */ @@ -72,11 +72,11 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data); /* driver description structure */ upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} }; /* @@ -85,232 +85,232 @@ upsdrv_info_t upsdrv_info = { /* initialize ups driver information */ void upsdrv_initinfo(void) { - upsdebugx(2, "upsdrv_initinfo"); + upsdebugx(2, "upsdrv_initinfo"); - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); - /* register instant commands */ - if (sigar[FSD_T].addr != NOTUSED) { - dstate_addcmd("load.off"); - } + /* register instant commands */ + if (sigar[FSD_T].addr != NOTUSED) { + dstate_addcmd("load.off"); + } - /* set callback for instant commands */ - upsh.instcmd = upscmd; + /* set callback for instant commands */ + upsh.instcmd = upscmd; } /* open serial connection and connect to modbus RIO */ void upsdrv_initups(void) { - int rval; - upsdebugx(2, "upsdrv_initups"); - - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } + int rval; + upsdebugx(2, "upsdrv_initups"); + + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus byte time out */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } } /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; - int online = -1; /* keep online state */ - errcnt = 0; - - upsdebugx(2, "upsdrv_updateinfo"); - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ - - /* - * update UPS status regarding MAINS state either via OL | OB. - * if both statuses are mapped to contacts then only OL is evaluated. - */ - if (sigar[OL_T].addr != NOTUSED) { - rval = get_signal_state(OL_T); - upsdebugx(2, "OL value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OL_T].noro)) { - status_set("OL"); - online = 1; - } else { - status_set("OB"); - online = 0; - - /* if DISCHRG state is not mapped to a contact and UPS is on - * batteries set status to DISCHRG state */ - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - - } - } else if (sigar[OB_T].addr != NOTUSED) { - rval = get_signal_state(OB_T); - upsdebugx(2, "OB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OB_T].noro)) { - status_set("OB"); - online = 0; - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } else { - status_set("OL"); - online = 1; - } - } + int rval; + int online = -1; /* keep online state */ + errcnt = 0; + + upsdebugx(2, "upsdrv_updateinfo"); + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS state either via OL | OB. + * if both statuses are mapped to contacts then only OL is evaluated. + */ + if (sigar[OL_T].addr != NOTUSED) { + rval = get_signal_state(OL_T); + upsdebugx(2, "OL value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OL_T].noro)) { + status_set("OL"); + online = 1; + } else { + status_set("OB"); + online = 0; + + /* if DISCHRG state is not mapped to a contact and UPS is on + * batteries set status to DISCHRG state */ + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + + } + } else if (sigar[OB_T].addr != NOTUSED) { + rval = get_signal_state(OB_T); + upsdebugx(2, "OB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OB_T].noro)) { + status_set("OB"); + online = 0; + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } else { + status_set("OL"); + online = 1; + } + } /* * update UPS status regarding CHARGING state via HB. HB is usually - * mapped to "ready" contact when closed indicates a charging state > 85% - */ - if (sigar[HB_T].addr != NOTUSED) { - rval = get_signal_state(HB_T); - upsdebugx(2, "HB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[HB_T].noro)) { - status_set("HB"); - dstate_setinfo("battery.charger.status", "resting"); - } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } - - /* - * update UPS status regarding DISCHARGING state via LB. LB is mapped - * to "battery low" contact. - */ - if (sigar[LB_T].addr != NOTUSED) { - rval = get_signal_state(LB_T); - upsdebugx(2, "LB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[LB_T].noro)) { - status_set("LB"); - alarm_set("Low Battery (Charge)"); - } - } - - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[RB_T].addr != NOTUSED) { - rval = get_signal_state(RB_T); - upsdebugx(2, "RB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[RB_T].noro)) { - status_set("RB"); - alarm_set("Replace Battery"); - } - } - - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[CHRG_T].addr != NOTUSED) { - rval = get_signal_state(CHRG_T); - upsdebugx(2, "CHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[CHRG_T].noro)) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } - } else if (sigar[DISCHRG_T].addr != NOTUSED) { - rval = get_signal_state(DISCHRG_T); - upsdebugx(2, "DISCHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } - - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2,"Communication errors: %d", errcnt); - dstate_datastale(); - } + * mapped to "ready" contact when closed indicates a charging state > 85% + */ + if (sigar[HB_T].addr != NOTUSED) { + rval = get_signal_state(HB_T); + upsdebugx(2, "HB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[HB_T].noro)) { + status_set("HB"); + dstate_setinfo("battery.charger.status", "resting"); + } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* + * update UPS status regarding DISCHARGING state via LB. LB is mapped + * to "battery low" contact. + */ + if (sigar[LB_T].addr != NOTUSED) { + rval = get_signal_state(LB_T); + upsdebugx(2, "LB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[LB_T].noro)) { + status_set("LB"); + alarm_set("Low Battery (Charge)"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[RB_T].addr != NOTUSED) { + rval = get_signal_state(RB_T); + upsdebugx(2, "RB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[RB_T].noro)) { + status_set("RB"); + alarm_set("Replace Battery"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[CHRG_T].addr != NOTUSED) { + rval = get_signal_state(CHRG_T); + upsdebugx(2, "CHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[CHRG_T].noro)) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } + } else if (sigar[DISCHRG_T].addr != NOTUSED) { + rval = get_signal_state(DISCHRG_T); + upsdebugx(2, "DISCHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2,"Communication errors: %d", errcnt); + dstate_datastale(); + } } /* shutdown UPS */ void upsdrv_shutdown(void) { - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); } /* print driver usage info */ @@ -321,51 +321,51 @@ void upsdrv_help(void) /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); - addvar(VAR_VALUE, "OL_addr", "modbus address for OL state"); - addvar(VAR_VALUE, "OB_addr", "modbus address for OB state"); - addvar(VAR_VALUE, "LB_addr", "modbus address for LB state"); - addvar(VAR_VALUE, "HB_addr", "modbus address for HB state"); - addvar(VAR_VALUE, "RB_addr", "modbus address for RB state"); - addvar(VAR_VALUE, "CHRG_addr", "modbus address for CHRG state"); - addvar(VAR_VALUE, "DISCHRG_addr", "modbus address for DISCHRG state"); - addvar(VAR_VALUE, "FSD_addr", "modbus address for FSD command"); - addvar(VAR_VALUE, "OL_regtype", "modbus register type for OL state"); - addvar(VAR_VALUE, "OB_regtype", "modbus register type for OB state"); - addvar(VAR_VALUE, "LB_regtype", "modbus register type for LB state"); - addvar(VAR_VALUE, "HB_regtype", "modbus register type for HB state"); - addvar(VAR_VALUE, "RB_regtype", "modbus register type for RB state"); - addvar(VAR_VALUE, "CHRG_regtype", "modbus register type for CHRG state"); - addvar(VAR_VALUE, "DISCHRG_regtype", "modbus register type for DISCHRG state"); - addvar(VAR_VALUE, "FSD_regtype", "modbus register type for FSD command"); - addvar(VAR_VALUE, "OL_noro", "NO/NC configuration for OL state"); - addvar(VAR_VALUE, "OB_noro", "NO/NC configuration for OB state"); - addvar(VAR_VALUE, "LB_noro", "NO/NC configuration for LB state"); - addvar(VAR_VALUE, "HB_noro", "NO/NC configuration for HB state"); - addvar(VAR_VALUE, "RB_noro", "NO/NC configuration for RB state"); - addvar(VAR_VALUE, "CHRG_noro", "NO/NC configuration for CHRG state"); - addvar(VAR_VALUE, "DISCHRG_noro", "NO/NC configuration for DISCHRG state"); - addvar(VAR_VALUE, "FSD_noro", "NO/NC configuration for FSD state"); - addvar(VAR_VALUE, "FSD_pulse_duration", "FSD pulse duration"); + addvar(VAR_VALUE, "OL_addr", "modbus address for OL state"); + addvar(VAR_VALUE, "OB_addr", "modbus address for OB state"); + addvar(VAR_VALUE, "LB_addr", "modbus address for LB state"); + addvar(VAR_VALUE, "HB_addr", "modbus address for HB state"); + addvar(VAR_VALUE, "RB_addr", "modbus address for RB state"); + addvar(VAR_VALUE, "CHRG_addr", "modbus address for CHRG state"); + addvar(VAR_VALUE, "DISCHRG_addr", "modbus address for DISCHRG state"); + addvar(VAR_VALUE, "FSD_addr", "modbus address for FSD command"); + addvar(VAR_VALUE, "OL_regtype", "modbus register type for OL state"); + addvar(VAR_VALUE, "OB_regtype", "modbus register type for OB state"); + addvar(VAR_VALUE, "LB_regtype", "modbus register type for LB state"); + addvar(VAR_VALUE, "HB_regtype", "modbus register type for HB state"); + addvar(VAR_VALUE, "RB_regtype", "modbus register type for RB state"); + addvar(VAR_VALUE, "CHRG_regtype", "modbus register type for CHRG state"); + addvar(VAR_VALUE, "DISCHRG_regtype", "modbus register type for DISCHRG state"); + addvar(VAR_VALUE, "FSD_regtype", "modbus register type for FSD command"); + addvar(VAR_VALUE, "OL_noro", "NO/NC configuration for OL state"); + addvar(VAR_VALUE, "OB_noro", "NO/NC configuration for OB state"); + addvar(VAR_VALUE, "LB_noro", "NO/NC configuration for LB state"); + addvar(VAR_VALUE, "HB_noro", "NO/NC configuration for HB state"); + addvar(VAR_VALUE, "RB_noro", "NO/NC configuration for RB state"); + addvar(VAR_VALUE, "CHRG_noro", "NO/NC configuration for CHRG state"); + addvar(VAR_VALUE, "DISCHRG_noro", "NO/NC configuration for DISCHRG state"); + addvar(VAR_VALUE, "FSD_noro", "NO/NC configuration for FSD state"); + addvar(VAR_VALUE, "FSD_pulse_duration", "FSD pulse duration"); } /* close modbus connection and free modbus context allocated memory */ void upsdrv_cleanup(void) { - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } } /* @@ -375,346 +375,346 @@ void upsdrv_cleanup(void) /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); + upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); /* on BROKEN PIPE error try to reconnect */ if (errno == EPIPE) { upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); modbus_reconnect(); } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* write a modbus register */ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* returns the time elapsed since start in milliseconds */ long time_elapsed(struct timeval *start) { - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; } /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg) { - int rval; - int data; - struct timeval start; - long etime; - - if (!strcasecmp(cmd, "load.off")) { - if (sigar[FSD_T].addr != NOTUSED && - (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) - ) { - data = 1 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); - rval = STAT_INSTCMD_HANDLED; - } - - /* if pulse has been defined and rising edge was successful */ - if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for FSD_pulse_duration ms */ - while ((etime = time_elapsed(&start)) < FSD_pulse_duration); - - data = 0 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", - sigar[FSD_T].addr, - data, - etime - ); - rval = STAT_INSTCMD_HANDLED; - } - } - } else { - upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", - cmd, - arg - ); - rval = STAT_INSTCMD_FAILED; - } - } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; + int rval; + int data; + struct timeval start; + long etime; + + if (!strcasecmp(cmd, "load.off")) { + if (sigar[FSD_T].addr != NOTUSED && + (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) + ) { + data = 1 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); + rval = STAT_INSTCMD_HANDLED; + } + + /* if pulse has been defined and rising edge was successful */ + if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for FSD_pulse_duration ms */ + while ((etime = time_elapsed(&start)) < FSD_pulse_duration); + + data = 0 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", + sigar[FSD_T].addr, + data, + etime + ); + rval = STAT_INSTCMD_HANDLED; + } + } + } else { + upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", + cmd, + arg + ); + rval = STAT_INSTCMD_FAILED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; } /* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ int get_signal_state(devstate_t state) { - int rval = -1; - int reg_val; - regtype_t rtype = 0; /* register type */ - int addr = -1; /* register address */ - - /* assign register address and type */ - switch (state) { - case OL_T: - addr = sigar[OL_T].addr; - rtype = sigar[OL_T].type; - break; - case OB_T: - addr = sigar[OB_T].addr; - rtype = sigar[OB_T].type; - break; - case LB_T: - addr = sigar[LB_T].addr; - rtype = sigar[LB_T].type; - break; - case HB_T: - addr = sigar[HB_T].addr; - rtype = sigar[HB_T].type; - break; - case RB_T: - addr = sigar[RB_T].addr; - rtype = sigar[RB_T].type; - break; - case CHRG_T: - addr = sigar[CHRG_T].addr; - rtype = sigar[CHRG_T].type; - break; - case DISCHRG_T: - addr = sigar[DISCHRG_T].addr; - rtype = sigar[DISCHRG_T].type; - break; - - case BYPASS_T: - case CAL_T: - case FSD_T: - case OFF_T: - case OVER_T: - case TRIM_T: - case BOOST_T: + int rval = -1; + int reg_val; + regtype_t rtype = 0; /* register type */ + int addr = -1; /* register address */ + + /* assign register address and type */ + switch (state) { + case OL_T: + addr = sigar[OL_T].addr; + rtype = sigar[OL_T].type; + break; + case OB_T: + addr = sigar[OB_T].addr; + rtype = sigar[OB_T].type; + break; + case LB_T: + addr = sigar[LB_T].addr; + rtype = sigar[LB_T].type; + break; + case HB_T: + addr = sigar[HB_T].addr; + rtype = sigar[HB_T].type; + break; + case RB_T: + addr = sigar[RB_T].addr; + rtype = sigar[RB_T].type; + break; + case CHRG_T: + addr = sigar[CHRG_T].addr; + rtype = sigar[CHRG_T].type; + break; + case DISCHRG_T: + addr = sigar[DISCHRG_T].addr; + rtype = sigar[DISCHRG_T].type; + break; + + case BYPASS_T: + case CAL_T: + case FSD_T: + case OFF_T: + case OVER_T: + case TRIM_T: + case BOOST_T: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - break; - } - - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval > -1) { - rval = reg_val; - } - upsdebugx(3, "get_signal_state: state: %d", reg_val); - return rval; + break; + } + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval > -1) { + rval = reg_val; + } + upsdebugx(3, "get_signal_state: state: %d", reg_val); + return rval; } /* get driver configuration parameters */ void get_config_vars() { - int i; /* local index */ - - /* initialize sigar table */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - sigar[i].addr = NOTUSED; - sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ - } - - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); + int i; /* local index */ + + /* initialize sigar table */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + sigar[i].addr = NOTUSED; + sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ + } + + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); /* check if response time out (s) is set ang get the value */ if (testvar("mod_resp_to_s")) { @@ -747,243 +747,243 @@ void get_config_vars() upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); /* check if OL address is set and get the value */ - if (testvar("OL_addr")) { - sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); - if (testvar("OL_noro")) { - sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); - if (sigar[OL_T].noro != 1) { - sigar[OL_T].noro = 0; - } - } - } - - /* check if OL register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OL_regtype")) { - sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); - if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { - sigar[OL_T].type = INPUT_B; - } - } else { - sigar[OL_T].type = INPUT_B; - } - - /* check if OB address is set and get the value */ - if (testvar("OB_addr")) { - sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); - } - if (testvar("OB_noro")) { - sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); - if (sigar[OB_T].noro != 1) { - sigar[OB_T].noro = 0; - } - } - - /* check if OB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OB_regtype")) { - sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { - sigar[OB_T].type = INPUT_B; - } - } else { - sigar[OB_T].type = INPUT_B; - } - - /* check if LB address is set and get the value */ - if (testvar("LB_addr")) { - sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); - if (testvar("LB_noro")) { - sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); - if (sigar[LB_T].noro != 1) { - sigar[LB_T].noro = 0; - } - } - } - - /* check if LB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("LB_regtype")) { - sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { - sigar[LB_T].type = INPUT_B; - } - } else { - sigar[LB_T].type = INPUT_B; - } - - /* check if HB address is set and get the value */ - if (testvar("HB_addr")) { - sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); - if (testvar("HB_noro")) { - sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); - if (sigar[HB_T].noro != 1) { - sigar[HB_T].noro = 0; - } - } - } - - /* check if HB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("HB_regtype")) { - sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); - if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { - sigar[HB_T].type = INPUT_B; - } - } else { - sigar[HB_T].type = INPUT_B; - } - - /* check if RB address is set and get the value */ - if (testvar("RB_addr")) { - sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); - if (testvar("RB_noro")) { - sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); - if (sigar[RB_T].noro != 1) { - sigar[RB_T].noro = 0; - } - } - } - - /* check if RB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("RB_regtype")) { - sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); - if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { - sigar[RB_T].type = INPUT_B; - } - } else { - sigar[RB_T].type = INPUT_B; - } - - /* check if CHRG address is set and get the value */ - if (testvar("CHRG_addr")) { - sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); - if (testvar("CHRG_noro")) { - sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); - if (sigar[CHRG_T].noro != 1) { - sigar[CHRG_T].noro = 0; - } - } - } - - /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("CHRG_regtype")) { - sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); - if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { - sigar[CHRG_T].type = INPUT_B; - } - } else { - sigar[CHRG_T].type = INPUT_B; - } - - /* check if DISCHRG address is set and get the value */ - if (testvar("DISCHRG_addr")) { - sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); - if (testvar("DISCHRG_noro")) { - sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); - if (sigar[DISCHRG_T].noro != 1) { - sigar[DISCHRG_T].noro = 0; - } - } - } - - /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("DISCHRG_regtype")) { - sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); - if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { - sigar[DISCHRG_T].type = INPUT_B; - } - } else { - sigar[DISCHRG_T].type = INPUT_B; - } - - /* check if FSD address is set and get the value */ - if (testvar("FSD_addr")) { - sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); - if (testvar("FSD_noro")) { - sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); - if (sigar[FSD_T].noro != 1) { - sigar[FSD_T].noro = 0; - } - } - } - - /* check if FSD register type is set and get the value otherwise set to COIL */ - if (testvar("FSD_regtype")) { - sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); - if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { - sigar[FSD_T].type = COIL; - } - } else { - sigar[FSD_T].type = COIL; - } - - /* check if FSD pulse duration is set and get the value */ - if (testvar("FSD_pulse_duration")) { - FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); - } - upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); - - /* debug loop over signal array */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - if (sigar[i].addr != NOTUSED) { - char *signame; - switch (i) { - case OL_T: - signame = "OL"; - break; - case OB_T: - signame = "OB"; - break; - case LB_T: - signame = "LB"; - break; - case HB_T: - signame = "HB"; - break; - case RB_T: - signame = "RB"; - break; - case FSD_T: - signame = "FSD"; - break; - case CHRG_T: - signame = "CHRG"; - break; - case DISCHRG_T: - signame = "DISCHRG"; - break; - default: - signame = "NOTUSED"; - break; - } - upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); - } - } + if (testvar("OL_addr")) { + sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); + if (testvar("OL_noro")) { + sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); + if (sigar[OL_T].noro != 1) { + sigar[OL_T].noro = 0; + } + } + } + + /* check if OL register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OL_regtype")) { + sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); + if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { + sigar[OL_T].type = INPUT_B; + } + } else { + sigar[OL_T].type = INPUT_B; + } + + /* check if OB address is set and get the value */ + if (testvar("OB_addr")) { + sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); + } + if (testvar("OB_noro")) { + sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); + if (sigar[OB_T].noro != 1) { + sigar[OB_T].noro = 0; + } + } + + /* check if OB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OB_regtype")) { + sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { + sigar[OB_T].type = INPUT_B; + } + } else { + sigar[OB_T].type = INPUT_B; + } + + /* check if LB address is set and get the value */ + if (testvar("LB_addr")) { + sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); + if (testvar("LB_noro")) { + sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); + if (sigar[LB_T].noro != 1) { + sigar[LB_T].noro = 0; + } + } + } + + /* check if LB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("LB_regtype")) { + sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { + sigar[LB_T].type = INPUT_B; + } + } else { + sigar[LB_T].type = INPUT_B; + } + + /* check if HB address is set and get the value */ + if (testvar("HB_addr")) { + sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); + if (testvar("HB_noro")) { + sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); + if (sigar[HB_T].noro != 1) { + sigar[HB_T].noro = 0; + } + } + } + + /* check if HB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("HB_regtype")) { + sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); + if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { + sigar[HB_T].type = INPUT_B; + } + } else { + sigar[HB_T].type = INPUT_B; + } + + /* check if RB address is set and get the value */ + if (testvar("RB_addr")) { + sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); + if (testvar("RB_noro")) { + sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); + if (sigar[RB_T].noro != 1) { + sigar[RB_T].noro = 0; + } + } + } + + /* check if RB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("RB_regtype")) { + sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); + if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { + sigar[RB_T].type = INPUT_B; + } + } else { + sigar[RB_T].type = INPUT_B; + } + + /* check if CHRG address is set and get the value */ + if (testvar("CHRG_addr")) { + sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); + if (testvar("CHRG_noro")) { + sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); + if (sigar[CHRG_T].noro != 1) { + sigar[CHRG_T].noro = 0; + } + } + } + + /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("CHRG_regtype")) { + sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); + if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { + sigar[CHRG_T].type = INPUT_B; + } + } else { + sigar[CHRG_T].type = INPUT_B; + } + + /* check if DISCHRG address is set and get the value */ + if (testvar("DISCHRG_addr")) { + sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); + if (testvar("DISCHRG_noro")) { + sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); + if (sigar[DISCHRG_T].noro != 1) { + sigar[DISCHRG_T].noro = 0; + } + } + } + + /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("DISCHRG_regtype")) { + sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); + if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { + sigar[DISCHRG_T].type = INPUT_B; + } + } else { + sigar[DISCHRG_T].type = INPUT_B; + } + + /* check if FSD address is set and get the value */ + if (testvar("FSD_addr")) { + sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); + if (testvar("FSD_noro")) { + sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); + if (sigar[FSD_T].noro != 1) { + sigar[FSD_T].noro = 0; + } + } + } + + /* check if FSD register type is set and get the value otherwise set to COIL */ + if (testvar("FSD_regtype")) { + sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); + if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { + sigar[FSD_T].type = COIL; + } + } else { + sigar[FSD_T].type = COIL; + } + + /* check if FSD pulse duration is set and get the value */ + if (testvar("FSD_pulse_duration")) { + FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); + } + upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); + + /* debug loop over signal array */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + if (sigar[i].addr != NOTUSED) { + char *signame; + switch (i) { + case OL_T: + signame = "OL"; + break; + case OB_T: + signame = "OB"; + break; + case LB_T: + signame = "LB"; + break; + case HB_T: + signame = "HB"; + break; + case RB_T: + signame = "RB"; + break; + case FSD_T: + signame = "FSD"; + break; + case CHRG_T: + signame = "CHRG"; + break; + case DISCHRG_T: + signame = "DISCHRG"; + break; + default: + signame = "NOTUSED"; + break; + } + upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); + } + } } /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port) { - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; } /* reconnect to modbus server upon connection error */ @@ -1023,10 +1023,10 @@ void modbus_reconnect() fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); } - /* set modbus response timeout */ + /* set modbus byte timeout */ rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); if (rval < 0) { modbus_free(mbctx); fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } -} \ No newline at end of file +} From 9a0cb006d6df71b3f62439722d7612904c90c624 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 09:52:38 +0000 Subject: [PATCH 04/18] drivers/generic_modbus.c: bump DRIVER_VERSION for feature change (connection timeout handling) --- drivers/generic_modbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index e30c25c3f1..0bb5ba9fcf 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -26,7 +26,7 @@ #include #define DRIVER_NAME "NUT Generic Modbus driver" -#define DRIVER_VERSION "0.01" +#define DRIVER_VERSION "0.02" /* variables */ static modbus_t *mbctx = NULL; /* modbus memory context */ From e489dd8c973d29a361ca003c07847cd45929f565 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 08:46:19 +0000 Subject: [PATCH 05/18] m4/ax_c_pragmas.m4: add guards to call AX_C_PRINTF_STRING_NULL and AX_C_PRAGMAS logic only once --- m4/ax_c_pragmas.m4 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/m4/ax_c_pragmas.m4 b/m4/ax_c_pragmas.m4 index 112627eeb5..6265b4fe1b 100644 --- a/m4/ax_c_pragmas.m4 +++ b/m4/ax_c_pragmas.m4 @@ -2,6 +2,9 @@ dnl Check for current compiler support of specific pragmas we use, dnl e.g. diagnostics management to keep warnings quiet sometimes AC_DEFUN([AX_C_PRAGMAS], [ +if test -z "${nut_have_ax_c_pragmas_seen}"; then + nut_have_ax_c_pragmas_seen="yes" + CFLAGS_SAVED="${CFLAGS}" CXXFLAGS_SAVED="${CXXFLAGS}" @@ -787,9 +790,13 @@ dnl ### [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-warning" CFLAGS="${CFLAGS_SAVED}" CXXFLAGS="${CXXFLAGS_SAVED}" +fi ]) AC_DEFUN([AX_C_PRINTF_STRING_NULL], [ +if test -z "${nut_have_ax_c_printf_string_null_seen}"; then + nut_have_ax_c_printf_string_null_seen="yes" + dnl ### To be sure, bolt the language AC_LANG_PUSH([C]) @@ -829,4 +836,5 @@ exit 0; ]) AC_LANG_POP([C]) +fi ]) From 6a1caa18808b6db2675466f466ca4e2bcac2d618 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 09:44:46 +0000 Subject: [PATCH 06/18] m4/ax_c_pragmas.m4: detect support for (ignoring) "-Wsign-conversion" --- m4/ax_c_pragmas.m4 | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/m4/ax_c_pragmas.m4 b/m4/ax_c_pragmas.m4 index 6265b4fe1b..4f3c1728dd 100644 --- a/m4/ax_c_pragmas.m4 +++ b/m4/ax_c_pragmas.m4 @@ -397,6 +397,33 @@ dnl ### [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-warning" AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wsign-compare" (outside functions)]) ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wsign-conversion"], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wsign-conversion" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion=yes], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_sign_conversion" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wsign-conversion"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wsign-conversion" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wsign-conversion"]], [])], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wsign-conversion" (outside functions)]) + ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wunreachable-code-break"], [ax_cv__pragma__gcc__diags_ignored_unreachable_code_break], [AC_COMPILE_IFELSE( From 0fd64647c9252b0ad73f22034f65c65aed267d1f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 08:48:07 +0000 Subject: [PATCH 07/18] m4/nut_check_libmodbus.m4: test for presence of modbus_set_byte_timeout() and modbus_set_response_timeout() --- m4/nut_check_libmodbus.m4 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/m4/nut_check_libmodbus.m4 b/m4/nut_check_libmodbus.m4 index 9405a5988c..45e3be28f3 100644 --- a/m4/nut_check_libmodbus.m4 +++ b/m4/nut_check_libmodbus.m4 @@ -69,6 +69,8 @@ if test -z "${nut_have_libmodbus_seen}"; then AC_CHECK_HEADERS(modbus.h, [nut_have_libmodbus=yes], [nut_have_libmodbus=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(modbus_new_rtu, [], [nut_have_libmodbus=no]) AC_CHECK_FUNCS(modbus_new_tcp, [], [nut_have_libmodbus=no]) + AC_CHECK_FUNCS(modbus_set_byte_timeout, [], [nut_have_libmodbus=no]) + AC_CHECK_FUNCS(modbus_set_response_timeout, [], [nut_have_libmodbus=no]) if test "${nut_have_libmodbus}" = "yes"; then LIBMODBUS_CFLAGS="${CFLAGS}" From 36bb70fb3cd7d9526daedd4e65b0540e76573abf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 08:49:14 +0000 Subject: [PATCH 08/18] m4/nut_check_libmodbus.m4: convert a code block from plain shell to autoscript --- m4/nut_check_libmodbus.m4 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/m4/nut_check_libmodbus.m4 b/m4/nut_check_libmodbus.m4 index 45e3be28f3..1cea6e6336 100644 --- a/m4/nut_check_libmodbus.m4 +++ b/m4/nut_check_libmodbus.m4 @@ -72,10 +72,10 @@ if test -z "${nut_have_libmodbus_seen}"; then AC_CHECK_FUNCS(modbus_set_byte_timeout, [], [nut_have_libmodbus=no]) AC_CHECK_FUNCS(modbus_set_response_timeout, [], [nut_have_libmodbus=no]) - if test "${nut_have_libmodbus}" = "yes"; then - LIBMODBUS_CFLAGS="${CFLAGS}" - LIBMODBUS_LIBS="${LIBS}" - fi + AS_IF([test x"${nut_have_libmodbus}" = x"yes"], + [LIBMODBUS_CFLAGS="${CFLAGS}" + LIBMODBUS_LIBS="${LIBS}"] + ) dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" From 537151f2458627cf0c9e76e6d10c6c5e6ee8aebf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 08:50:11 +0000 Subject: [PATCH 09/18] m4/nut_check_libmodbus.m4: test actively for the modbus timeout API (count and type of args used) --- m4/nut_check_libmodbus.m4 | 72 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/m4/nut_check_libmodbus.m4 b/m4/nut_check_libmodbus.m4 index 1cea6e6336..0bba2bb616 100644 --- a/m4/nut_check_libmodbus.m4 +++ b/m4/nut_check_libmodbus.m4 @@ -72,6 +72,78 @@ if test -z "${nut_have_libmodbus_seen}"; then AC_CHECK_FUNCS(modbus_set_byte_timeout, [], [nut_have_libmodbus=no]) AC_CHECK_FUNCS(modbus_set_response_timeout, [], [nut_have_libmodbus=no]) + dnl modbus_set_byte_timeout() and modbus_set_response_timeout() + dnl in 3.0.x and 3.1.x have different args (since ~2013): the + dnl older version used to accept timeout as a struct timeval + dnl instead of seconds and microseconds. Detect which we use?.. + AS_IF([test x"$nut_have_libmodbus" = xyes], + [dnl Do not rely on versions if we can test actual API + AX_C_PRAGMAS + AC_LANG_PUSH([C]) + AC_CACHE_CHECK([types of arguments for modbus_set_byte_timeout], + [nut_cv_func_modbus_set_byte_timeout_args], + [nut_cv_func_modbus_set_byte_timeout_args="unknown" + AC_COMPILE_IFELSE( + [dnl Try purely the new API (timeval) + AC_LANG_PROGRAM([ +#include +#include +], [modbus_t ctx = (modbus_t){0}; struct timeval to = (struct timeval){0}; +modbus_set_byte_timeout(&ctx, &to);]) + ], [nut_cv_func_modbus_set_byte_timeout_args="timeval"], + [dnl Try another API variant: old API with + dnl fields of struct timeval as numbers + dnl (checks they exist, and are compatible + dnl numeric types so compiler can convert) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +#include +#include +#include +], [modbus_t ctx = (modbus_t){0}; struct timeval to = (struct timeval){0}; +/* TODO: Clarify and detect warning names and + * so pragmas for signed/unsigned assignment (e.g. + * for timeval definitions that have "long" fields) + */ +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE +# pragma GCC diagnostic ignored "-Wsign-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +uint32_t to_sec = to.tv_sec, to_usec = to.tv_usec; +modbus_set_byte_timeout(&ctx, to_sec, to_usec); +]) + ], [nut_cv_func_modbus_set_byte_timeout_args="sec_usec_uint32_cast_timeval_fields"], + [dnl Try another API variant: old API purely + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +#include +#include +], [modbus_t ctx = (modbus_t){0}; uint32_t to_sec = 0, to_usec = 0; +modbus_set_byte_timeout(&ctx, to_sec, to_usec);]) + ], [nut_cv_func_modbus_set_byte_timeout_args="sec_usec_uint32"]) + ]) + ]) + ]) + + dnl NOTE: We could add similar tests to above for + dnl other time-related methods, but for now keep + dnl it simple -- and assume some same approach + dnl applies to the same generation of the library. + AC_LANG_POP([C]) + AS_IF([test x"$nut_cv_func_modbus_set_byte_timeout_args" = x"unknown"], + [AC_MSG_WARN([Cannot find proper types to use for modbus_set_byte_timeout]) + nut_have_libmodbus=no], + [AC_MSG_RESULT([Found types to use for modbus_set_byte_timeout: ${nut_cv_func_modbus_set_byte_timeout_args}]) + dnl NOTE: code should check for having a token name defined e.g.: + dnl #ifdef NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32 + AC_DEFINE_UNQUOTED( + [NUT_MODBUS_TIMEOUT_ARG_${nut_cv_func_modbus_set_byte_timeout_args}], + 1, [Define to specify timeout method args approach for libmodbus]) + ]) + ]) + AS_IF([test x"${nut_have_libmodbus}" = x"yes"], [LIBMODBUS_CFLAGS="${CFLAGS}" LIBMODBUS_LIBS="${LIBS}"] From d0366e1b5fee393a0bc078965e3c2c5975ce0ab4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 10:02:00 +0000 Subject: [PATCH 10/18] m4/nut_check_libmodbus.m4: in tests for API, initialize modbus_t *ctx differently --- m4/nut_check_libmodbus.m4 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/m4/nut_check_libmodbus.m4 b/m4/nut_check_libmodbus.m4 index 0bba2bb616..3f0a671c8b 100644 --- a/m4/nut_check_libmodbus.m4 +++ b/m4/nut_check_libmodbus.m4 @@ -88,8 +88,8 @@ if test -z "${nut_have_libmodbus_seen}"; then AC_LANG_PROGRAM([ #include #include -], [modbus_t ctx = (modbus_t){0}; struct timeval to = (struct timeval){0}; -modbus_set_byte_timeout(&ctx, &to);]) +], [modbus_t *ctx; struct timeval to = (struct timeval){0}; +modbus_set_byte_timeout(ctx, &to);]) ], [nut_cv_func_modbus_set_byte_timeout_args="timeval"], [dnl Try another API variant: old API with dnl fields of struct timeval as numbers @@ -100,7 +100,7 @@ modbus_set_byte_timeout(&ctx, &to);]) #include #include #include -], [modbus_t ctx = (modbus_t){0}; struct timeval to = (struct timeval){0}; +], [modbus_t *ctx; struct timeval to = (struct timeval){0}; /* TODO: Clarify and detect warning names and * so pragmas for signed/unsigned assignment (e.g. * for timeval definitions that have "long" fields) @@ -112,7 +112,7 @@ modbus_set_byte_timeout(&ctx, &to);]) # pragma GCC diagnostic ignored "-Wsign-conversion" #endif uint32_t to_sec = to.tv_sec, to_usec = to.tv_usec; -modbus_set_byte_timeout(&ctx, to_sec, to_usec); +modbus_set_byte_timeout(ctx, to_sec, to_usec); ]) ], [nut_cv_func_modbus_set_byte_timeout_args="sec_usec_uint32_cast_timeval_fields"], [dnl Try another API variant: old API purely @@ -120,8 +120,8 @@ modbus_set_byte_timeout(&ctx, to_sec, to_usec); [AC_LANG_PROGRAM([ #include #include -], [modbus_t ctx = (modbus_t){0}; uint32_t to_sec = 0, to_usec = 0; -modbus_set_byte_timeout(&ctx, to_sec, to_usec);]) +], [modbus_t *ctx; uint32_t to_sec = 0, to_usec = 0; +modbus_set_byte_timeout(ctx, to_sec, to_usec);]) ], [nut_cv_func_modbus_set_byte_timeout_args="sec_usec_uint32"]) ]) ]) From 55ac0a7df4a56179a1c6ffb462f23a9d105e00a4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 10:12:12 +0000 Subject: [PATCH 11/18] m4/nut_check_libmodbus.m4: in tests for API, "new/old" comments were confused --- m4/nut_check_libmodbus.m4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/m4/nut_check_libmodbus.m4 b/m4/nut_check_libmodbus.m4 index 3f0a671c8b..1e4b5af58f 100644 --- a/m4/nut_check_libmodbus.m4 +++ b/m4/nut_check_libmodbus.m4 @@ -84,14 +84,14 @@ if test -z "${nut_have_libmodbus_seen}"; then [nut_cv_func_modbus_set_byte_timeout_args], [nut_cv_func_modbus_set_byte_timeout_args="unknown" AC_COMPILE_IFELSE( - [dnl Try purely the new API (timeval) + [dnl Try purely the old API (timeval) AC_LANG_PROGRAM([ #include #include ], [modbus_t *ctx; struct timeval to = (struct timeval){0}; modbus_set_byte_timeout(ctx, &to);]) ], [nut_cv_func_modbus_set_byte_timeout_args="timeval"], - [dnl Try another API variant: old API with + [dnl Try another API variant: new API with dnl fields of struct timeval as numbers dnl (checks they exist, and are compatible dnl numeric types so compiler can convert) @@ -115,7 +115,7 @@ uint32_t to_sec = to.tv_sec, to_usec = to.tv_usec; modbus_set_byte_timeout(ctx, to_sec, to_usec); ]) ], [nut_cv_func_modbus_set_byte_timeout_args="sec_usec_uint32_cast_timeval_fields"], - [dnl Try another API variant: old API purely + [dnl Try another API variant: new API purely (two uint32's) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([ #include From 40120edfafb34c218c67c2978e2b2ce88cb90938 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 10:13:11 +0000 Subject: [PATCH 12/18] m4/nut_check_libmodbus.m4: in tests for API, clarify if old API (timeval) can be used with variables from new API (uint32) set into the struct --- m4/nut_check_libmodbus.m4 | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/m4/nut_check_libmodbus.m4 b/m4/nut_check_libmodbus.m4 index 1e4b5af58f..0cbea50069 100644 --- a/m4/nut_check_libmodbus.m4 +++ b/m4/nut_check_libmodbus.m4 @@ -90,7 +90,34 @@ if test -z "${nut_have_libmodbus_seen}"; then #include ], [modbus_t *ctx; struct timeval to = (struct timeval){0}; modbus_set_byte_timeout(ctx, &to);]) - ], [nut_cv_func_modbus_set_byte_timeout_args="timeval"], + ], [nut_cv_func_modbus_set_byte_timeout_args="timeval" + dnl Try the old API in more detail: check + dnl if we can just assign uint32's for new + dnl code into timeval fields (exist+numeric)? + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +#include +#include +#include +], [modbus_t *ctx; uint32_t to_sec = 10, to_usec = 50; +struct timeval to = (struct timeval){0}; +/* TODO: Clarify and detect warning names and + * so pragmas for signed/unsigned assignment (e.g. + * for timeval definitions that have "long" fields) + */ +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE +# pragma GCC diagnostic ignored "-Wsign-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +to.tv_sec = to_sec; +to.tv_usec = to_usec; +modbus_set_byte_timeout(ctx, &to); +]) + ], [nut_cv_func_modbus_set_byte_timeout_args="timeval_numeric_fields"]) + ], + [dnl Try another API variant: new API with dnl fields of struct timeval as numbers dnl (checks they exist, and are compatible From f55037af7104ae697856c2f7a7329ffd8268531c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 10:32:45 +0000 Subject: [PATCH 13/18] m4/nut_check_libmodbus.m4: in tests for API, the AC_DEFINE must be used with exact macro names (not m4/shell variables) --- m4/nut_check_libmodbus.m4 | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/m4/nut_check_libmodbus.m4 b/m4/nut_check_libmodbus.m4 index 0cbea50069..ea08dd3659 100644 --- a/m4/nut_check_libmodbus.m4 +++ b/m4/nut_check_libmodbus.m4 @@ -159,16 +159,20 @@ modbus_set_byte_timeout(ctx, to_sec, to_usec);]) dnl it simple -- and assume some same approach dnl applies to the same generation of the library. AC_LANG_POP([C]) - AS_IF([test x"$nut_cv_func_modbus_set_byte_timeout_args" = x"unknown"], - [AC_MSG_WARN([Cannot find proper types to use for modbus_set_byte_timeout]) - nut_have_libmodbus=no], - [AC_MSG_RESULT([Found types to use for modbus_set_byte_timeout: ${nut_cv_func_modbus_set_byte_timeout_args}]) - dnl NOTE: code should check for having a token name defined e.g.: - dnl #ifdef NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32 - AC_DEFINE_UNQUOTED( - [NUT_MODBUS_TIMEOUT_ARG_${nut_cv_func_modbus_set_byte_timeout_args}], - 1, [Define to specify timeout method args approach for libmodbus]) - ]) + AC_MSG_RESULT([Found types to use for modbus_set_byte_timeout: ${nut_cv_func_modbus_set_byte_timeout_args}]) + dnl NOTE: code should check for having a token name defined e.g.: + dnl #ifdef NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32 + dnl Alas, we can't pass variables as macro name to AC_DEFINE + COMMENT="Define to specify timeout method args approach for libmodbus" + AS_CASE(["${nut_cv_func_modbus_set_byte_timeout_args}"], + [timeval_numeric_fields], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields], 1, [${COMMENT}])], + [timeval], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_timeval], 1, [${COMMENT}])], + [sec_usec_uint32_cast_timeval_fields], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields], 1, [${COMMENT}])], + [sec_usec_uint32], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32], 1, [${COMMENT}])], + [dnl default + AC_MSG_WARN([Cannot find proper types to use for modbus_set_byte_timeout]) + nut_have_libmodbus=no] + ) ]) AS_IF([test x"${nut_have_libmodbus}" = x"yes"], From 09b8882449fb5e47a69730843c18f73b2d582051 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 11 Jan 2022 10:55:25 +0000 Subject: [PATCH 14/18] drivers/generic_modbus.c: handle the two APIs for libmodbus timeout setting --- drivers/generic_modbus.c | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 0bb5ba9fcf..71f7167eaa 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -128,18 +128,47 @@ void upsdrv_initups(void) } /* set modbus response timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); if (rval < 0) { modbus_free(mbctx); fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { + /* Older libmodbus API (with timeval), and we have + * checked at configure time that we can put uint32_t + * into its fields. They are probably "long" on many + * systems as respectively time_t and suseconds_t - + * but that is not guaranteed; for more details see + * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html + */ + struct timeval to = (struct timeval){0}; + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#else +# error "Can not use libmodbus API for timeouts" +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ /* set modbus byte time out */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); if (rval < 0) { modbus_free(mbctx); fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to = (struct timeval){0}; + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ } /* update UPS signal state */ @@ -1017,16 +1046,36 @@ void modbus_reconnect() } /* set modbus response timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); if (rval < 0) { modbus_free(mbctx); fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to = (struct timeval){0}; + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ /* set modbus byte timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); if (rval < 0) { modbus_free(mbctx); fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to = (struct timeval){0}; + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ } From 7b7df606d3c25b85088ff6da77e5051d34714ed4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 12 Jan 2022 16:07:09 +0000 Subject: [PATCH 15/18] drivers/generic_modbus.c: fix whitespace style (in PR-1239) --- drivers/generic_modbus.c | 1758 +++++++++++++++++++------------------- 1 file changed, 879 insertions(+), 879 deletions(-) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 71f7167eaa..590cf15d5c 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -72,11 +72,11 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data); /* driver description structure */ upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} }; /* @@ -85,88 +85,88 @@ upsdrv_info_t upsdrv_info = { /* initialize ups driver information */ void upsdrv_initinfo(void) { - upsdebugx(2, "upsdrv_initinfo"); + upsdebugx(2, "upsdrv_initinfo"); - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); - /* register instant commands */ - if (sigar[FSD_T].addr != NOTUSED) { - dstate_addcmd("load.off"); - } + /* register instant commands */ + if (sigar[FSD_T].addr != NOTUSED) { + dstate_addcmd("load.off"); + } - /* set callback for instant commands */ - upsh.instcmd = upscmd; + /* set callback for instant commands */ + upsh.instcmd = upscmd; } /* open serial connection and connect to modbus RIO */ void upsdrv_initups(void) { - int rval; - upsdebugx(2, "upsdrv_initups"); - - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ + int rval; + upsdebugx(2, "upsdrv_initups"); + + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ #if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) - { - /* Older libmodbus API (with timeval), and we have - * checked at configure time that we can put uint32_t - * into its fields. They are probably "long" on many - * systems as respectively time_t and suseconds_t - - * but that is not guaranteed; for more details see - * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html - */ - struct timeval to = (struct timeval){0}; - to.tv_sec = mod_resp_to_s; - to.tv_usec = mod_resp_to_us; - /* void */ modbus_set_response_timeout(mbctx, &to); - } + { + /* Older libmodbus API (with timeval), and we have + * checked at configure time that we can put uint32_t + * into its fields. They are probably "long" on many + * systems as respectively time_t and suseconds_t - + * but that is not guaranteed; for more details see + * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html + */ + struct timeval to = (struct timeval){0}; + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } /* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ #else # error "Can not use libmodbus API for timeouts" #endif /* NUT_MODBUS_TIMEOUT_ARG_* */ - /* set modbus byte time out */ + /* set modbus byte time out */ #if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) - { /* see comments above */ - struct timeval to = (struct timeval){0}; - to.tv_sec = mod_byte_to_s; - to.tv_usec = mod_byte_to_us; - /* void */ modbus_set_byte_timeout(mbctx, &to); - } + { /* see comments above */ + struct timeval to = (struct timeval){0}; + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } /* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ #endif /* NUT_MODBUS_TIMEOUT_ARG_* */ } @@ -174,172 +174,172 @@ void upsdrv_initups(void) /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; - int online = -1; /* keep online state */ - errcnt = 0; - - upsdebugx(2, "upsdrv_updateinfo"); - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ - - /* - * update UPS status regarding MAINS state either via OL | OB. - * if both statuses are mapped to contacts then only OL is evaluated. - */ - if (sigar[OL_T].addr != NOTUSED) { - rval = get_signal_state(OL_T); - upsdebugx(2, "OL value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OL_T].noro)) { - status_set("OL"); - online = 1; - } else { - status_set("OB"); - online = 0; - - /* if DISCHRG state is not mapped to a contact and UPS is on - * batteries set status to DISCHRG state */ - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - - } - } else if (sigar[OB_T].addr != NOTUSED) { - rval = get_signal_state(OB_T); - upsdebugx(2, "OB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OB_T].noro)) { - status_set("OB"); - online = 0; - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } else { - status_set("OL"); - online = 1; - } - } - - /* - * update UPS status regarding CHARGING state via HB. HB is usually - * mapped to "ready" contact when closed indicates a charging state > 85% - */ - if (sigar[HB_T].addr != NOTUSED) { - rval = get_signal_state(HB_T); - upsdebugx(2, "HB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[HB_T].noro)) { - status_set("HB"); - dstate_setinfo("battery.charger.status", "resting"); - } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } - - /* - * update UPS status regarding DISCHARGING state via LB. LB is mapped - * to "battery low" contact. - */ - if (sigar[LB_T].addr != NOTUSED) { - rval = get_signal_state(LB_T); - upsdebugx(2, "LB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[LB_T].noro)) { - status_set("LB"); - alarm_set("Low Battery (Charge)"); - } - } - - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[RB_T].addr != NOTUSED) { - rval = get_signal_state(RB_T); - upsdebugx(2, "RB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[RB_T].noro)) { - status_set("RB"); - alarm_set("Replace Battery"); - } - } - - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[CHRG_T].addr != NOTUSED) { - rval = get_signal_state(CHRG_T); - upsdebugx(2, "CHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[CHRG_T].noro)) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } - } else if (sigar[DISCHRG_T].addr != NOTUSED) { - rval = get_signal_state(DISCHRG_T); - upsdebugx(2, "DISCHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } - - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2,"Communication errors: %d", errcnt); - dstate_datastale(); - } + int rval; + int online = -1; /* keep online state */ + errcnt = 0; + + upsdebugx(2, "upsdrv_updateinfo"); + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS state either via OL | OB. + * if both statuses are mapped to contacts then only OL is evaluated. + */ + if (sigar[OL_T].addr != NOTUSED) { + rval = get_signal_state(OL_T); + upsdebugx(2, "OL value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OL_T].noro)) { + status_set("OL"); + online = 1; + } else { + status_set("OB"); + online = 0; + + /* if DISCHRG state is not mapped to a contact and UPS is on + * batteries set status to DISCHRG state */ + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + + } + } else if (sigar[OB_T].addr != NOTUSED) { + rval = get_signal_state(OB_T); + upsdebugx(2, "OB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OB_T].noro)) { + status_set("OB"); + online = 0; + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } else { + status_set("OL"); + online = 1; + } + } + + /* + * update UPS status regarding CHARGING state via HB. HB is usually + * mapped to "ready" contact when closed indicates a charging state > 85% + */ + if (sigar[HB_T].addr != NOTUSED) { + rval = get_signal_state(HB_T); + upsdebugx(2, "HB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[HB_T].noro)) { + status_set("HB"); + dstate_setinfo("battery.charger.status", "resting"); + } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* + * update UPS status regarding DISCHARGING state via LB. LB is mapped + * to "battery low" contact. + */ + if (sigar[LB_T].addr != NOTUSED) { + rval = get_signal_state(LB_T); + upsdebugx(2, "LB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[LB_T].noro)) { + status_set("LB"); + alarm_set("Low Battery (Charge)"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[RB_T].addr != NOTUSED) { + rval = get_signal_state(RB_T); + upsdebugx(2, "RB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[RB_T].noro)) { + status_set("RB"); + alarm_set("Replace Battery"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[CHRG_T].addr != NOTUSED) { + rval = get_signal_state(CHRG_T); + upsdebugx(2, "CHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[CHRG_T].noro)) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } + } else if (sigar[DISCHRG_T].addr != NOTUSED) { + rval = get_signal_state(DISCHRG_T); + upsdebugx(2, "DISCHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2,"Communication errors: %d", errcnt); + dstate_datastale(); + } } /* shutdown UPS */ void upsdrv_shutdown(void) { - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); } /* print driver usage info */ @@ -350,51 +350,51 @@ void upsdrv_help(void) /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); - addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); - addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); - addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); - addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); - addvar(VAR_VALUE, "OL_addr", "modbus address for OL state"); - addvar(VAR_VALUE, "OB_addr", "modbus address for OB state"); - addvar(VAR_VALUE, "LB_addr", "modbus address for LB state"); - addvar(VAR_VALUE, "HB_addr", "modbus address for HB state"); - addvar(VAR_VALUE, "RB_addr", "modbus address for RB state"); - addvar(VAR_VALUE, "CHRG_addr", "modbus address for CHRG state"); - addvar(VAR_VALUE, "DISCHRG_addr", "modbus address for DISCHRG state"); - addvar(VAR_VALUE, "FSD_addr", "modbus address for FSD command"); - addvar(VAR_VALUE, "OL_regtype", "modbus register type for OL state"); - addvar(VAR_VALUE, "OB_regtype", "modbus register type for OB state"); - addvar(VAR_VALUE, "LB_regtype", "modbus register type for LB state"); - addvar(VAR_VALUE, "HB_regtype", "modbus register type for HB state"); - addvar(VAR_VALUE, "RB_regtype", "modbus register type for RB state"); - addvar(VAR_VALUE, "CHRG_regtype", "modbus register type for CHRG state"); - addvar(VAR_VALUE, "DISCHRG_regtype", "modbus register type for DISCHRG state"); - addvar(VAR_VALUE, "FSD_regtype", "modbus register type for FSD command"); - addvar(VAR_VALUE, "OL_noro", "NO/NC configuration for OL state"); - addvar(VAR_VALUE, "OB_noro", "NO/NC configuration for OB state"); - addvar(VAR_VALUE, "LB_noro", "NO/NC configuration for LB state"); - addvar(VAR_VALUE, "HB_noro", "NO/NC configuration for HB state"); - addvar(VAR_VALUE, "RB_noro", "NO/NC configuration for RB state"); - addvar(VAR_VALUE, "CHRG_noro", "NO/NC configuration for CHRG state"); - addvar(VAR_VALUE, "DISCHRG_noro", "NO/NC configuration for DISCHRG state"); - addvar(VAR_VALUE, "FSD_noro", "NO/NC configuration for FSD state"); - addvar(VAR_VALUE, "FSD_pulse_duration", "FSD pulse duration"); + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); + addvar(VAR_VALUE, "OL_addr", "modbus address for OL state"); + addvar(VAR_VALUE, "OB_addr", "modbus address for OB state"); + addvar(VAR_VALUE, "LB_addr", "modbus address for LB state"); + addvar(VAR_VALUE, "HB_addr", "modbus address for HB state"); + addvar(VAR_VALUE, "RB_addr", "modbus address for RB state"); + addvar(VAR_VALUE, "CHRG_addr", "modbus address for CHRG state"); + addvar(VAR_VALUE, "DISCHRG_addr", "modbus address for DISCHRG state"); + addvar(VAR_VALUE, "FSD_addr", "modbus address for FSD command"); + addvar(VAR_VALUE, "OL_regtype", "modbus register type for OL state"); + addvar(VAR_VALUE, "OB_regtype", "modbus register type for OB state"); + addvar(VAR_VALUE, "LB_regtype", "modbus register type for LB state"); + addvar(VAR_VALUE, "HB_regtype", "modbus register type for HB state"); + addvar(VAR_VALUE, "RB_regtype", "modbus register type for RB state"); + addvar(VAR_VALUE, "CHRG_regtype", "modbus register type for CHRG state"); + addvar(VAR_VALUE, "DISCHRG_regtype", "modbus register type for DISCHRG state"); + addvar(VAR_VALUE, "FSD_regtype", "modbus register type for FSD command"); + addvar(VAR_VALUE, "OL_noro", "NO/NC configuration for OL state"); + addvar(VAR_VALUE, "OB_noro", "NO/NC configuration for OB state"); + addvar(VAR_VALUE, "LB_noro", "NO/NC configuration for LB state"); + addvar(VAR_VALUE, "HB_noro", "NO/NC configuration for HB state"); + addvar(VAR_VALUE, "RB_noro", "NO/NC configuration for RB state"); + addvar(VAR_VALUE, "CHRG_noro", "NO/NC configuration for CHRG state"); + addvar(VAR_VALUE, "DISCHRG_noro", "NO/NC configuration for DISCHRG state"); + addvar(VAR_VALUE, "FSD_noro", "NO/NC configuration for FSD state"); + addvar(VAR_VALUE, "FSD_pulse_duration", "FSD pulse duration"); } /* close modbus connection and free modbus context allocated memory */ void upsdrv_cleanup(void) { - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } } /* @@ -404,678 +404,678 @@ void upsdrv_cleanup(void) /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* write a modbus register */ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* returns the time elapsed since start in milliseconds */ long time_elapsed(struct timeval *start) { - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; } /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg) { - int rval; - int data; - struct timeval start; - long etime; - - if (!strcasecmp(cmd, "load.off")) { - if (sigar[FSD_T].addr != NOTUSED && - (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) - ) { - data = 1 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); - rval = STAT_INSTCMD_HANDLED; - } - - /* if pulse has been defined and rising edge was successful */ - if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for FSD_pulse_duration ms */ - while ((etime = time_elapsed(&start)) < FSD_pulse_duration); - - data = 0 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", - sigar[FSD_T].addr, - data, - etime - ); - rval = STAT_INSTCMD_HANDLED; - } - } - } else { - upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", - cmd, - arg - ); - rval = STAT_INSTCMD_FAILED; - } - } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; + int rval; + int data; + struct timeval start; + long etime; + + if (!strcasecmp(cmd, "load.off")) { + if (sigar[FSD_T].addr != NOTUSED && + (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) + ) { + data = 1 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); + rval = STAT_INSTCMD_HANDLED; + } + + /* if pulse has been defined and rising edge was successful */ + if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for FSD_pulse_duration ms */ + while ((etime = time_elapsed(&start)) < FSD_pulse_duration); + + data = 0 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", + sigar[FSD_T].addr, + data, + etime + ); + rval = STAT_INSTCMD_HANDLED; + } + } + } else { + upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", + cmd, + arg + ); + rval = STAT_INSTCMD_FAILED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; } /* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ int get_signal_state(devstate_t state) { - int rval = -1; - int reg_val; - regtype_t rtype = 0; /* register type */ - int addr = -1; /* register address */ - - /* assign register address and type */ - switch (state) { - case OL_T: - addr = sigar[OL_T].addr; - rtype = sigar[OL_T].type; - break; - case OB_T: - addr = sigar[OB_T].addr; - rtype = sigar[OB_T].type; - break; - case LB_T: - addr = sigar[LB_T].addr; - rtype = sigar[LB_T].type; - break; - case HB_T: - addr = sigar[HB_T].addr; - rtype = sigar[HB_T].type; - break; - case RB_T: - addr = sigar[RB_T].addr; - rtype = sigar[RB_T].type; - break; - case CHRG_T: - addr = sigar[CHRG_T].addr; - rtype = sigar[CHRG_T].type; - break; - case DISCHRG_T: - addr = sigar[DISCHRG_T].addr; - rtype = sigar[DISCHRG_T].type; - break; - - case BYPASS_T: - case CAL_T: - case FSD_T: - case OFF_T: - case OVER_T: - case TRIM_T: - case BOOST_T: + int rval = -1; + int reg_val; + regtype_t rtype = 0; /* register type */ + int addr = -1; /* register address */ + + /* assign register address and type */ + switch (state) { + case OL_T: + addr = sigar[OL_T].addr; + rtype = sigar[OL_T].type; + break; + case OB_T: + addr = sigar[OB_T].addr; + rtype = sigar[OB_T].type; + break; + case LB_T: + addr = sigar[LB_T].addr; + rtype = sigar[LB_T].type; + break; + case HB_T: + addr = sigar[HB_T].addr; + rtype = sigar[HB_T].type; + break; + case RB_T: + addr = sigar[RB_T].addr; + rtype = sigar[RB_T].type; + break; + case CHRG_T: + addr = sigar[CHRG_T].addr; + rtype = sigar[CHRG_T].type; + break; + case DISCHRG_T: + addr = sigar[DISCHRG_T].addr; + rtype = sigar[DISCHRG_T].type; + break; + + case BYPASS_T: + case CAL_T: + case FSD_T: + case OFF_T: + case OVER_T: + case TRIM_T: + case BOOST_T: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - break; - } - - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval > -1) { - rval = reg_val; - } - upsdebugx(3, "get_signal_state: state: %d", reg_val); - return rval; + break; + } + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval > -1) { + rval = reg_val; + } + upsdebugx(3, "get_signal_state: state: %d", reg_val); + return rval; } /* get driver configuration parameters */ void get_config_vars() { - int i; /* local index */ - - /* initialize sigar table */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - sigar[i].addr = NOTUSED; - sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ - } - - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); - - /* check if response time out (s) is set ang get the value */ - if (testvar("mod_resp_to_s")) { - mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); - } - upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); - - /* check if response time out (us) is set ang get the value */ - if (testvar("mod_resp_to_us")) { - mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); - if (mod_resp_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); - } - } - upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); - - /* check if byte time out (s) is set ang get the value */ - if (testvar("mod_byte_to_s")) { - mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); - } - upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); - - /* check if byte time out (us) is set ang get the value */ - if (testvar("mod_byte_to_us")) { - mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); - if (mod_byte_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); - } - } - upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); - - /* check if OL address is set and get the value */ - if (testvar("OL_addr")) { - sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); - if (testvar("OL_noro")) { - sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); - if (sigar[OL_T].noro != 1) { - sigar[OL_T].noro = 0; - } - } - } - - /* check if OL register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OL_regtype")) { - sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); - if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { - sigar[OL_T].type = INPUT_B; - } - } else { - sigar[OL_T].type = INPUT_B; - } - - /* check if OB address is set and get the value */ - if (testvar("OB_addr")) { - sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); - } - if (testvar("OB_noro")) { - sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); - if (sigar[OB_T].noro != 1) { - sigar[OB_T].noro = 0; - } - } - - /* check if OB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OB_regtype")) { - sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { - sigar[OB_T].type = INPUT_B; - } - } else { - sigar[OB_T].type = INPUT_B; - } - - /* check if LB address is set and get the value */ - if (testvar("LB_addr")) { - sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); - if (testvar("LB_noro")) { - sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); - if (sigar[LB_T].noro != 1) { - sigar[LB_T].noro = 0; - } - } - } - - /* check if LB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("LB_regtype")) { - sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { - sigar[LB_T].type = INPUT_B; - } - } else { - sigar[LB_T].type = INPUT_B; - } - - /* check if HB address is set and get the value */ - if (testvar("HB_addr")) { - sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); - if (testvar("HB_noro")) { - sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); - if (sigar[HB_T].noro != 1) { - sigar[HB_T].noro = 0; - } - } - } - - /* check if HB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("HB_regtype")) { - sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); - if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { - sigar[HB_T].type = INPUT_B; - } - } else { - sigar[HB_T].type = INPUT_B; - } - - /* check if RB address is set and get the value */ - if (testvar("RB_addr")) { - sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); - if (testvar("RB_noro")) { - sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); - if (sigar[RB_T].noro != 1) { - sigar[RB_T].noro = 0; - } - } - } - - /* check if RB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("RB_regtype")) { - sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); - if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { - sigar[RB_T].type = INPUT_B; - } - } else { - sigar[RB_T].type = INPUT_B; - } - - /* check if CHRG address is set and get the value */ - if (testvar("CHRG_addr")) { - sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); - if (testvar("CHRG_noro")) { - sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); - if (sigar[CHRG_T].noro != 1) { - sigar[CHRG_T].noro = 0; - } - } - } - - /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("CHRG_regtype")) { - sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); - if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { - sigar[CHRG_T].type = INPUT_B; - } - } else { - sigar[CHRG_T].type = INPUT_B; - } - - /* check if DISCHRG address is set and get the value */ - if (testvar("DISCHRG_addr")) { - sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); - if (testvar("DISCHRG_noro")) { - sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); - if (sigar[DISCHRG_T].noro != 1) { - sigar[DISCHRG_T].noro = 0; - } - } - } - - /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("DISCHRG_regtype")) { - sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); - if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { - sigar[DISCHRG_T].type = INPUT_B; - } - } else { - sigar[DISCHRG_T].type = INPUT_B; - } - - /* check if FSD address is set and get the value */ - if (testvar("FSD_addr")) { - sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); - if (testvar("FSD_noro")) { - sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); - if (sigar[FSD_T].noro != 1) { - sigar[FSD_T].noro = 0; - } - } - } - - /* check if FSD register type is set and get the value otherwise set to COIL */ - if (testvar("FSD_regtype")) { - sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); - if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { - sigar[FSD_T].type = COIL; - } - } else { - sigar[FSD_T].type = COIL; - } - - /* check if FSD pulse duration is set and get the value */ - if (testvar("FSD_pulse_duration")) { - FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); - } - upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); - - /* debug loop over signal array */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - if (sigar[i].addr != NOTUSED) { - char *signame; - switch (i) { - case OL_T: - signame = "OL"; - break; - case OB_T: - signame = "OB"; - break; - case LB_T: - signame = "LB"; - break; - case HB_T: - signame = "HB"; - break; - case RB_T: - signame = "RB"; - break; - case FSD_T: - signame = "FSD"; - break; - case CHRG_T: - signame = "CHRG"; - break; - case DISCHRG_T: - signame = "DISCHRG"; - break; - default: - signame = "NOTUSED"; - break; - } - upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); - } - } + int i; /* local index */ + + /* initialize sigar table */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + sigar[i].addr = NOTUSED; + sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ + } + + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); + + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); + + /* check if OL address is set and get the value */ + if (testvar("OL_addr")) { + sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); + if (testvar("OL_noro")) { + sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); + if (sigar[OL_T].noro != 1) { + sigar[OL_T].noro = 0; + } + } + } + + /* check if OL register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OL_regtype")) { + sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); + if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { + sigar[OL_T].type = INPUT_B; + } + } else { + sigar[OL_T].type = INPUT_B; + } + + /* check if OB address is set and get the value */ + if (testvar("OB_addr")) { + sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); + } + if (testvar("OB_noro")) { + sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); + if (sigar[OB_T].noro != 1) { + sigar[OB_T].noro = 0; + } + } + + /* check if OB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OB_regtype")) { + sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { + sigar[OB_T].type = INPUT_B; + } + } else { + sigar[OB_T].type = INPUT_B; + } + + /* check if LB address is set and get the value */ + if (testvar("LB_addr")) { + sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); + if (testvar("LB_noro")) { + sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); + if (sigar[LB_T].noro != 1) { + sigar[LB_T].noro = 0; + } + } + } + + /* check if LB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("LB_regtype")) { + sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { + sigar[LB_T].type = INPUT_B; + } + } else { + sigar[LB_T].type = INPUT_B; + } + + /* check if HB address is set and get the value */ + if (testvar("HB_addr")) { + sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); + if (testvar("HB_noro")) { + sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); + if (sigar[HB_T].noro != 1) { + sigar[HB_T].noro = 0; + } + } + } + + /* check if HB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("HB_regtype")) { + sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); + if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { + sigar[HB_T].type = INPUT_B; + } + } else { + sigar[HB_T].type = INPUT_B; + } + + /* check if RB address is set and get the value */ + if (testvar("RB_addr")) { + sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); + if (testvar("RB_noro")) { + sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); + if (sigar[RB_T].noro != 1) { + sigar[RB_T].noro = 0; + } + } + } + + /* check if RB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("RB_regtype")) { + sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); + if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { + sigar[RB_T].type = INPUT_B; + } + } else { + sigar[RB_T].type = INPUT_B; + } + + /* check if CHRG address is set and get the value */ + if (testvar("CHRG_addr")) { + sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); + if (testvar("CHRG_noro")) { + sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); + if (sigar[CHRG_T].noro != 1) { + sigar[CHRG_T].noro = 0; + } + } + } + + /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("CHRG_regtype")) { + sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); + if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { + sigar[CHRG_T].type = INPUT_B; + } + } else { + sigar[CHRG_T].type = INPUT_B; + } + + /* check if DISCHRG address is set and get the value */ + if (testvar("DISCHRG_addr")) { + sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); + if (testvar("DISCHRG_noro")) { + sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); + if (sigar[DISCHRG_T].noro != 1) { + sigar[DISCHRG_T].noro = 0; + } + } + } + + /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("DISCHRG_regtype")) { + sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); + if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { + sigar[DISCHRG_T].type = INPUT_B; + } + } else { + sigar[DISCHRG_T].type = INPUT_B; + } + + /* check if FSD address is set and get the value */ + if (testvar("FSD_addr")) { + sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); + if (testvar("FSD_noro")) { + sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); + if (sigar[FSD_T].noro != 1) { + sigar[FSD_T].noro = 0; + } + } + } + + /* check if FSD register type is set and get the value otherwise set to COIL */ + if (testvar("FSD_regtype")) { + sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); + if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { + sigar[FSD_T].type = COIL; + } + } else { + sigar[FSD_T].type = COIL; + } + + /* check if FSD pulse duration is set and get the value */ + if (testvar("FSD_pulse_duration")) { + FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); + } + upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); + + /* debug loop over signal array */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + if (sigar[i].addr != NOTUSED) { + char *signame; + switch (i) { + case OL_T: + signame = "OL"; + break; + case OB_T: + signame = "OB"; + break; + case LB_T: + signame = "LB"; + break; + case HB_T: + signame = "HB"; + break; + case RB_T: + signame = "RB"; + break; + case FSD_T: + signame = "FSD"; + break; + case CHRG_T: + signame = "CHRG"; + break; + case DISCHRG_T: + signame = "DISCHRG"; + break; + default: + signame = "NOTUSED"; + break; + } + upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); + } + } } /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port) { - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; } /* reconnect to modbus server upon connection error */ void modbus_reconnect() { - int rval; - - upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); - - /* clear current modbus context */ - modbus_close(mbctx); - modbus_free(mbctx); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); - } - - /* set modbus response timeout */ + int rval; + + upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ #if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) - { /* see comments above */ - struct timeval to = (struct timeval){0}; - to.tv_sec = mod_resp_to_s; - to.tv_usec = mod_resp_to_us; - /* void */ modbus_set_response_timeout(mbctx, &to); - } + { /* see comments above */ + struct timeval to = (struct timeval){0}; + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } /* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ #endif /* NUT_MODBUS_TIMEOUT_ARG_* */ - /* set modbus byte timeout */ + /* set modbus byte timeout */ #if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) - { /* see comments above */ - struct timeval to = (struct timeval){0}; - to.tv_sec = mod_byte_to_s; - to.tv_usec = mod_byte_to_us; - /* void */ modbus_set_byte_timeout(mbctx, &to); - } + { /* see comments above */ + struct timeval to = (struct timeval){0}; + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } /* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ #endif /* NUT_MODBUS_TIMEOUT_ARG_* */ } From 628960503f446b21b7878ecba0584944560d9e7b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 23 Sep 2021 19:55:13 +0200 Subject: [PATCH 16/18] drivers/generic_modbus.{c,h}: fix whitespace style (original fix from master branch) --- drivers/generic_modbus.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 590cf15d5c..04d0fa5c43 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -175,12 +175,12 @@ void upsdrv_initups(void) void upsdrv_updateinfo(void) { int rval; - int online = -1; /* keep online state */ + int online = -1; /* keep online state */ errcnt = 0; upsdebugx(2, "upsdrv_updateinfo"); - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ /* * update UPS status regarding MAINS state either via OL | OB. @@ -314,7 +314,7 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ struct timeval start; long etime; @@ -554,7 +554,7 @@ int upscmd(const char *cmd, const char *arg) if (!strcasecmp(cmd, "load.off")) { if (sigar[FSD_T].addr != NOTUSED && - (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) + (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) ) { data = 1 ^ sigar[FSD_T].noro; rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); From 2d1989107ffe032d049683d8749cf934df6c3927 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 17 Jan 2022 15:20:51 +0100 Subject: [PATCH 17/18] drivers/generic_modbus.c: address -Wstrict-prototypes complaints --- drivers/generic_modbus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index d1434fc587..216a3ff392 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -54,7 +54,7 @@ void get_config_vars(void); modbus_t *modbus_new(const char *port); /* reconnect upon communication error */ -void modbus_reconnect(); +void modbus_reconnect(void); /* modbus register read function */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); @@ -1030,7 +1030,7 @@ modbus_t *modbus_new(const char *port) } /* reconnect to modbus server upon connection error */ -void modbus_reconnect() +void modbus_reconnect(void) { int rval; From b9721bf153ce0b28cf8a3e7e36d68250d093b693 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 17 Jan 2022 15:21:40 +0100 Subject: [PATCH 18/18] drivers/generic_modbus.c: address -Wmissing-field-initializers complaints for timeval with timeouts (memset to 0 instead) --- drivers/generic_modbus.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 216a3ff392..44d99168cf 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -143,7 +143,8 @@ void upsdrv_initups(void) * but that is not guaranteed; for more details see * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html */ - struct timeval to = (struct timeval){0}; + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); to.tv_sec = mod_resp_to_s; to.tv_usec = mod_resp_to_us; /* void */ modbus_set_response_timeout(mbctx, &to); @@ -162,7 +163,8 @@ void upsdrv_initups(void) } #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) { /* see comments above */ - struct timeval to = (struct timeval){0}; + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); to.tv_sec = mod_byte_to_s; to.tv_usec = mod_byte_to_us; /* void */ modbus_set_byte_timeout(mbctx, &to); @@ -1068,7 +1070,8 @@ void modbus_reconnect(void) } #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) { /* see comments above */ - struct timeval to = (struct timeval){0}; + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); to.tv_sec = mod_resp_to_s; to.tv_usec = mod_resp_to_us; /* void */ modbus_set_response_timeout(mbctx, &to); @@ -1085,7 +1088,8 @@ void modbus_reconnect(void) } #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) { /* see comments above */ - struct timeval to = (struct timeval){0}; + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); to.tv_sec = mod_byte_to_s; to.tv_usec = mod_byte_to_us; /* void */ modbus_set_byte_timeout(mbctx, &to);