From 43d1fbb3230c74e62f47dc510aeafb072443aca5 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Mon, 6 Nov 2023 00:42:02 +0200 Subject: [PATCH] Add more detailed rig communication status to rig_state and multicast state data packets. Handle SIGINT and SIGTERM in a consistent way in both rigctl and rigctld. --- include/hamlib/rig.h | 12 ++++ src/conf.c | 29 ++++++++ src/event.c | 10 ++- src/misc.c | 36 ++++++++++ src/network.c | 2 + src/network.h | 1 + src/rig.c | 19 ++++-- src/snapshot_data.c | 29 +++----- src/snapshot_data.h | 1 + src/token.h | 1 + tests/rigctl.c | 154 ++++++++++++++++++++++++++++++++++++++++++- tests/rigctld.c | 26 +++++++- 12 files changed, 291 insertions(+), 29 deletions(-) diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index dc62832e7..a9080cee2 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -2577,6 +2577,15 @@ struct multicast_s //#endif }; +typedef unsigned int rig_comm_status_t; + +#define RIG_COMM_STATUS_OK 0x00 +#define RIG_COMM_STATUS_CONNECTING 0x01 +#define RIG_COMM_STATUS_DISCONNECTED 0x02 +#define RIG_COMM_STATUS_TERMINATED 0x03 +#define RIG_COMM_STATUS_WARNING 0x04 +#define RIG_COMM_STATUS_ERROR 0x05 + /** * \brief Rig state containing live data and customized fields. * @@ -2766,6 +2775,8 @@ struct rig_state { int multicast_cmd_port; /*!< Multicast command server UDP port for sending commands to rig */ volatile int multicast_receiver_run; void *multicast_receiver_priv_data; + rig_comm_status_t comm_status; /*!< Detailed rig control status */ + char device_id[HAMLIB_RIGNAMSIZ]; }; /** @@ -3710,6 +3721,7 @@ extern HAMLIB_EXPORT(const char *) rig_strscan(scan_t scan); extern HAMLIB_EXPORT(const char *) rig_strstatus(enum rig_status_e status); extern HAMLIB_EXPORT(const char *) rig_strmtype(chan_type_t mtype); extern HAMLIB_EXPORT(const char *) rig_strspectrummode(enum rig_spectrum_mode_e mode); +extern HAMLIB_EXPORT(const char *) rig_strcommstatus(rig_comm_status_t vfo); extern HAMLIB_EXPORT(rmode_t) rig_parse_mode(const char *s); extern HAMLIB_EXPORT(vfo_t) rig_parse_vfo(const char *s); diff --git a/src/conf.c b/src/conf.c index 24ea22d78..99fad9154 100644 --- a/src/conf.c +++ b/src/conf.c @@ -89,6 +89,11 @@ static const struct confparams frontend_cfg_params[] = "The tx/rx range list name", "Default", RIG_CONF_STRING }, + { + TOK_DEVICE_ID, "device_id", "Device ID", + "User-specified device ID for multicast state data and commands", + "", RIG_CONF_STRING, + }, { TOK_VFO_COMP, "vfo_comp", "VFO compensation", @@ -627,6 +632,10 @@ static int frontend_set_conf(RIG *rig, token_t token, const char *val) strncpy(rs->dcdport_deprecated.pathname, val, HAMLIB_FILPATHLEN - 1); break; + case TOK_DEVICE_ID: + strncpy(rs->device_id, val, HAMLIB_RIGNAMSIZ - 1); + break; + case TOK_VFO_COMP: rs->vfo_comp = atof(val); @@ -989,6 +998,10 @@ static int frontend_get_conf2(RIG *rig, token_t token, char *val, int val_len) strcpy(val, s); break; + case TOK_DEVICE_ID: + SNPRINTF(val, val_len, "%s", rs->device_id); + break; + case TOK_VFO_COMP: SNPRINTF(val, val_len, "%f", rs->vfo_comp); break; @@ -1149,6 +1162,22 @@ static int frontend_get_conf2(RIG *rig, token_t token, char *val, int val_len) SNPRINTF(val, val_len, "%d", rs->rigport.timeout_retry); break; + case TOK_MULTICAST_DATA_ADDR: + SNPRINTF(val, val_len, "%s", rs->multicast_data_addr); + break; + + case TOK_MULTICAST_DATA_PORT: + SNPRINTF(val, val_len, "%d", rs->multicast_data_port); + break; + + case TOK_MULTICAST_CMD_ADDR: + SNPRINTF(val, val_len, "%s", rs->multicast_cmd_addr); + break; + + case TOK_MULTICAST_CMD_PORT: + SNPRINTF(val, val_len, "%d", rs->multicast_cmd_port); + break; + default: return -RIG_EINVAL; } diff --git a/src/event.c b/src/event.c index c9f6ca9de..6f70e37af 100644 --- a/src/event.c +++ b/src/event.c @@ -64,8 +64,6 @@ typedef struct rig_poll_routine_priv_data_s rig_poll_routine_args args; } rig_poll_routine_priv_data; -// TODO: Where to start/stop rig poll routine? - void *rig_poll_routine(void *arg) { rig_poll_routine_args *args = (rig_poll_routine_args *)arg; @@ -93,6 +91,8 @@ void *rig_poll_routine(void *arg) update_occurred = 0; + network_publish_rig_poll_data(rig); + while (rs->poll_routine_thread_run) { if (rig->state.cache.freqMainA != freq_main_a) @@ -351,6 +351,8 @@ void *rig_poll_routine(void *arg) } } + network_publish_rig_poll_data(rig); + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping rig poll routine thread\n", __FILE__, __LINE__); @@ -408,6 +410,8 @@ int rig_poll_routine_start(RIG *rig) RETURNFUNC(-RIG_EINTERNAL); } + network_publish_rig_poll_data(rig); + RETURNFUNC(RIG_OK); } @@ -453,6 +457,8 @@ int rig_poll_routine_stop(RIG *rig) poll_routine_priv->thread_id = 0; } + network_publish_rig_poll_data(rig); + free(rs->poll_routine_priv_data); rs->poll_routine_priv_data = NULL; diff --git a/src/misc.c b/src/misc.c index 2a13709dd..67a96205f 100644 --- a/src/misc.c +++ b/src/misc.c @@ -2707,6 +2707,42 @@ const char *HAMLIB_API rig_get_caps_cptr(rig_model_t rig_model, } } +static const struct +{ + rig_comm_status_t status; + const char *str; +} comm_status_str[] = +{ + { RIG_COMM_STATUS_OK, "OK" }, + { RIG_COMM_STATUS_CONNECTING, "CONNECTING" }, + { RIG_COMM_STATUS_DISCONNECTED, "DISCONNECTED" }, + { RIG_COMM_STATUS_TERMINATED, "TERMINATIED" }, + { RIG_COMM_STATUS_WARNING, "WARNING" }, + { RIG_COMM_STATUS_ERROR, "ERROR" }, + { 0xffffffff, "" }, +}; + +/** + * \brief Convert enum RIG_COMM_STATUS... to alpha string + * \param vfo RIG_COMM_STATUS_... + * \return alpha string + */ +const char *HAMLIB_API rig_strcommstatus(rig_comm_status_t status) +{ + int i; + + for (i = 0; comm_status_str[i].str[0] != '\0'; i++) + { + if (status == comm_status_str[i].status) + { + return comm_status_str[i].str; + } + } + + return ""; +} + + void errmsg(int err, char *s, const char *func, const char *file, int line) { rig_debug(RIG_DEBUG_ERR, "%s(%s:%d): %s: %s\b", __func__, file, line, s, diff --git a/src/network.c b/src/network.c index 4d34527a9..c4754cbd1 100644 --- a/src/network.c +++ b/src/network.c @@ -921,6 +921,8 @@ void *multicast_publisher(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast publisher\n", __FILE__, __LINE__); + snapshot_init(); + memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr); diff --git a/src/network.h b/src/network.h index 997b8874c..7dc9c517c 100644 --- a/src/network.h +++ b/src/network.h @@ -34,6 +34,7 @@ void network_flush(hamlib_port_t *rp); int network_publish_rig_poll_data(RIG *rig); int network_publish_rig_transceive_data(RIG *rig); int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line); +int network_publish_rig_status_change(RIG *rig, int32_t status); HAMLIB_EXPORT(int) network_multicast_publisher_start(RIG *rig, const char *multicast_addr, int multicast_port, enum multicast_item_e items); HAMLIB_EXPORT(int) network_multicast_publisher_stop(RIG *rig); HAMLIB_EXPORT(int) network_multicast_receiver_start(RIG *rig, const char *multicast_addr, int multicast_port); diff --git a/src/rig.c b/src/rig.c index 57d6a951d..a080cd7d8 100644 --- a/src/rig.c +++ b/src/rig.c @@ -593,6 +593,7 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model) #if defined(HAVE_PTHREAD) rs->rigport.asyncio = 0; #endif + rig->state.comm_status = RIG_COMM_STATUS_CONNECTING; rs->tuner_control_pathname = DEFAULT_TUNER_CONTROL_PATHNAME; @@ -1046,6 +1047,8 @@ int HAMLIB_API rig_open(RIG *rig) RETURNFUNC2(-RIG_EINVAL); } + rs->comm_status = RIG_COMM_STATUS_CONNECTING; + rs->rigport.fd = -1; if (rs->rigport.type.rig == RIG_PORT_SERIAL) @@ -1094,6 +1097,7 @@ int HAMLIB_API rig_open(RIG *rig) rig_debug(RIG_DEBUG_VERBOSE, "%s: rs->comm_state==0?=%d\n", __func__, rs->comm_state); rs->comm_state = 0; + rig->state.comm_status = RIG_COMM_STATUS_ERROR; RETURNFUNC2(status); } @@ -1312,6 +1316,7 @@ int HAMLIB_API rig_open(RIG *rig) if (status < 0) { port_close(&rs->rigport, rs->rigport.type.rig); + rig->state.comm_status = RIG_COMM_STATUS_ERROR; RETURNFUNC2(status); } @@ -1320,11 +1325,10 @@ int HAMLIB_API rig_open(RIG *rig) if (status < 0) { port_close(&rs->rigport, rs->rigport.type.rig); + rig->state.comm_status = RIG_COMM_STATUS_ERROR; RETURNFUNC2(status); } - add_opened_rig(rig); - rs->comm_state = 1; rig_debug(RIG_DEBUG_VERBOSE, "%s: %p rs->comm_state==1?=%d\n", __func__, &rs->comm_state, @@ -1377,6 +1381,7 @@ int HAMLIB_API rig_open(RIG *rig) port_close(&rs->rigport, rs->rigport.type.rig); memcpy(&rs->rigport_deprecated, &rs->rigport, sizeof(hamlib_port_t_deprecated)); rs->comm_state = 0; + rig->state.comm_status = RIG_COMM_STATUS_ERROR; RETURNFUNC2(status); } } @@ -1518,6 +1523,10 @@ int HAMLIB_API rig_open(RIG *rig) // we will consider this non-fatal for now } + rig->state.comm_status = RIG_COMM_STATUS_OK; + + add_opened_rig(rig); + RETURNFUNC2(RIG_OK); } @@ -1557,6 +1566,10 @@ int HAMLIB_API rig_close(RIG *rig) RETURNFUNC(-RIG_EINVAL); } + remove_opened_rig(rig); + + rig->state.comm_status = RIG_COMM_STATUS_DISCONNECTED; + morse_data_handler_stop(rig); async_data_handler_stop(rig); rig_poll_routine_stop(rig); @@ -1676,8 +1689,6 @@ int HAMLIB_API rig_close(RIG *rig) port_close(&rs->rigport, rs->rigport.type.rig); - remove_opened_rig(rig); - // zero split so it will allow it to be set again on open for rigctld rig->state.cache.split = 0; rs->comm_state = 0; diff --git a/src/snapshot_data.c b/src/snapshot_data.c index 2465eee4f..8d99af7e9 100644 --- a/src/snapshot_data.c +++ b/src/snapshot_data.c @@ -15,35 +15,21 @@ #define SPECTRUM_MODE_FIXED "FIXED" #define SPECTRUM_MODE_CENTER "CENTER" +char snapshot_data_pid[20]; + static int snapshot_serialize_rig(cJSON *rig_node, RIG *rig) { cJSON *node; char buf[1024]; -#if 0 - // TODO: need to assign rig an ID, e.g. from command line - snprintf(buf, sizeof(buf), "%s:%s:%d", rig->caps->model_name, - rig->state.rigport.pathname, getpid()); - - node = cJSON_AddStringToObject(rig_node, "id", buf); - if (node == NULL) - { - goto error; - } - -#else cJSON *id_node = cJSON_CreateObject(); cJSON_AddStringToObject(id_node, "model", rig->caps->model_name); cJSON_AddStringToObject(id_node, "endpoint", rig->state.rigport.pathname); - char pid[16]; - sprintf(pid,"%d",getpid()); - cJSON_AddStringToObject(id_node, "process", pid); + cJSON_AddStringToObject(id_node, "process", snapshot_data_pid); + cJSON_AddStringToObject(id_node, "deviceId", rig->state.device_id); cJSON_AddItemToObject(rig_node, "id", id_node); -#endif - // TODO: what kind of status should this reflect? - node = cJSON_AddStringToObject(rig_node, "status", - rig->state.comm_state ? "OK" : "CLOSED"); + node = cJSON_AddStringToObject(rig_node, "status", rig_strcommstatus(rig->state.comm_status)); if (node == NULL) { @@ -330,6 +316,11 @@ error: RETURNFUNC2(-RIG_EINTERNAL); } +void snapshot_init() +{ + snprintf(snapshot_data_pid, sizeof(snapshot_data_pid), "%d", getpid()); +} + int snapshot_serialize(size_t buffer_length, char *buffer, RIG *rig, struct rig_spectrum_line *spectrum_line) { diff --git a/src/snapshot_data.h b/src/snapshot_data.h index ab1536429..cbe4a1ab8 100644 --- a/src/snapshot_data.h +++ b/src/snapshot_data.h @@ -1,6 +1,7 @@ #ifndef _SNAPSHOT_DATA_H #define _SNAPSHOT_DATA_H +void snapshot_init(); int snapshot_serialize(size_t buffer_length, char *buffer, RIG *rig, struct rig_spectrum_line *spectrum_line); #endif diff --git a/src/token.h b/src/token.h index 58b1f6aaa..b2118ac18 100644 --- a/src/token.h +++ b/src/token.h @@ -98,6 +98,7 @@ /** \brief Number of retries permitted in case of read timeouts */ #define TOK_TIMEOUT_RETRY TOKEN_FRONTEND(39) #define TOK_POST_PTT_DELAY TOKEN_FRONTEND(40) +#define TOK_DEVICE_ID TOKEN_FRONTEND(41) /* * rig specific tokens diff --git a/tests/rigctl.c b/tests/rigctl.c index f388fb00e..6ae1bd9e1 100644 --- a/tests/rigctl.c +++ b/tests/rigctl.c @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef HAVE_LIBREADLINE # if defined(HAVE_READLINE_READLINE_H) @@ -113,11 +114,86 @@ static struct option long_options[] = extern char rig_resp_sep; extern powerstat_t rig_powerstat; +static RIG *my_rig; /* handle to rig (instance) */ + +#ifdef HAVE_SIG_ATOMIC_T +static sig_atomic_t volatile ctrl_c = 0; +#else +static int volatile ctrl_c = 0; +#endif + #define MAXCONFLEN 2048 +#ifdef WIN32 +static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s: called\n", __func__); + + switch (fdwCtrlType) + { + case CTRL_C_EVENT: + case CTRL_CLOSE_EVENT: + ctrl_c = 1; + return TRUE; + + default: + return FALSE; + } +} +#else +static void signal_handler(int sig) +{ + switch (sig) + { + case SIGINT: + case SIGTERM: + fprintf(stderr, "\nTerminating application, caught signal %d\n", sig); + // Close stdin to stop reading input + fclose(stdin); + ctrl_c = 1; + break; + + default: + /* do nothing */ + break; + } +} +#endif + +static void handle_error(enum rig_debug_level_e lvl, const char *msg) +{ + int e; +#ifdef __MINGW32__ + LPVOID lpMsgBuf; + + lpMsgBuf = (LPVOID)"Unknown error"; + e = WSAGetLastError(); + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, e, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + // Default language + (LPTSTR)&lpMsgBuf, 0, NULL)) + { + + rig_debug(lvl, "%s: Network error %d: %s\n", msg, e, (char *)lpMsgBuf); + LocalFree(lpMsgBuf); + } + else + { + rig_debug(lvl, "%s: Network error %d\n", msg, e); + } + +#else + e = errno; + rig_debug(lvl, "%s: Network error %d: %s\n", msg, e, strerror(e)); +#endif +} + int main(int argc, char *argv[]) { - RIG *my_rig; /* handle to rig (instance) */ rig_model_t my_model = RIG_MODEL_DUMMY; int retcode; /* generic return code from functions */ @@ -151,6 +227,9 @@ int main(int argc, char *argv[]) char rigstartup[1024]; char vbuf[1024]; rig_powerstat = RIG_POWER_ON; // defaults to power on +#if HAVE_SIGACTION + struct sigaction act; +#endif int err = setvbuf(stderr, vbuf, _IOFBF, sizeof(vbuf)); @@ -648,6 +727,77 @@ int main(int argc, char *argv[]) #endif /* HAVE_LIBREADLINE */ int rig_opened = 1; // our rig is already open +#if HAVE_SIGACTION + +#ifdef SIGPIPE + /* Ignore SIGPIPE as we will handle it at the write()/send() calls + that will consequently fail with EPIPE. All child threads will + inherit this disposition which is what we want. */ + memset(&act, 0, sizeof act); + act.sa_handler = SIG_IGN; + act.sa_flags = SA_RESTART; + + if (sigaction(SIGPIPE, &act, NULL)) + { + handle_error(RIG_DEBUG_ERR, "sigaction SIGPIPE"); + } + +#endif + +#ifdef SIGINT + memset(&act, 0, sizeof act); + act.sa_handler = signal_handler; + + if (sigaction(SIGINT, &act, NULL)) + { + handle_error(RIG_DEBUG_ERR, "sigaction SIGINT"); + } + +#endif +#ifdef SIGTERM + memset(&act, 0, sizeof act); + act.sa_handler = signal_handler; + + if (sigaction(SIGTERM, &act, NULL)) + { + handle_error(RIG_DEBUG_ERR, "sigaction SIGTERM"); + } + +#endif +#elif defined (WIN32) + + if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) + { + handle_error(RIG_DEBUG_ERR, "SetConsoleCtrlHandler"); + } + +#elif HAVE_SIGNAL +#ifdef SIGPIPE + + if (SIG_ERR == signal(SIGPIPE, SIG_IGN)) + { + handle_error(RIG_DEBUG_ERR, "signal SIGPIPE"); + } + +#endif +#ifdef SIGINT + + if (SIG_ERR == signal(SIGINT, signal_handler)) + { + handle_error(RIG_DEBUG_ERR, "signal SIGINT"); + } + +#endif +#ifdef SIGTERM + + if (SIG_ERR == signal(SIGTERM, signal_handler)) + { + handle_error(RIG_DEBUG_ERR, "signal SIGTERM"); + } + +#endif +#endif + do { if (!rig_opened) @@ -695,7 +845,7 @@ int main(int argc, char *argv[]) while (retry-- > 0 && retcode != RIG_OK); } } - while (retcode == RIG_OK || RIG_IS_SOFT_ERRCODE(-retcode)); + while (!ctrl_c && (retcode == RIG_OK || RIG_IS_SOFT_ERRCODE(-retcode))); if (interactive && prompt) { diff --git a/tests/rigctld.c b/tests/rigctld.c index 33d0d6110..dda18b891 100644 --- a/tests/rigctld.c +++ b/tests/rigctld.c @@ -136,9 +136,9 @@ static volatile int rig_opened = 0; static int verbose; #ifdef HAVE_SIG_ATOMIC_T -static sig_atomic_t volatile ctrl_c; +static sig_atomic_t volatile ctrl_c = 0; #else -static int volatile ctrl_c; +static int volatile ctrl_c = 0; #endif const char *portno = "4532"; @@ -196,6 +196,10 @@ static void signal_handler(int sig) switch (sig) { case SIGINT: + case SIGTERM: + fprintf(stderr, "\nTerminating application, caught signal %d\n", sig); + // Close stdin to stop reading input + fclose(stdin); ctrl_c = 1; break; @@ -949,6 +953,16 @@ int main(int argc, char *argv[]) handle_error(RIG_DEBUG_ERR, "sigaction SIGINT"); } +#endif +#ifdef SIGTERM + memset(&act, 0, sizeof act); + act.sa_handler = signal_handler; + + if (sigaction(SIGTERM, &act, NULL)) + { + handle_error(RIG_DEBUG_ERR, "sigaction SIGTERM"); + } + #endif #elif defined (WIN32) @@ -973,6 +987,14 @@ int main(int argc, char *argv[]) handle_error(RIG_DEBUG_ERR, "signal SIGINT"); } +#endif +#ifdef SIGTERM + + if (SIG_ERR == signal(SIGTERM, signal_handler)) + { + handle_error(RIG_DEBUG_ERR, "signal SIGTERM"); + } + #endif #endif