From c8c8869a3c19661a73e321079d35a3cc1ce58262 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Wed, 1 Nov 2023 00:26:37 +0200 Subject: [PATCH 1/8] Refactor multicast publisher and rig poll routine. Create skeleton for multicast command server. --- include/hamlib/rig.h | 8 +- src/conf.c | 54 +++++++- src/event.c | 116 +++++++++++++++- src/network.c | 319 ++++++++++++++++++++++++++++++------------- src/network.h | 2 + src/rig.c | 42 ++++-- src/token.h | 9 ++ tests/rigctld.c | 47 ------- 8 files changed, 433 insertions(+), 164 deletions(-) diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index 3816bbab0..6a0647518 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -2758,7 +2758,13 @@ struct rig_state { volatile int morse_data_handler_thread_run; void *morse_data_handler_priv_data; FIFO_RIG *fifo_morse; - int port_multicast; /*!< May be different so this is initially a copy of rigctl'd port selection */ + char *multicast_data_addr; /*!< Multicast data UDP address for publishing rig data and state */ + int multicast_data_port; /*!< Multicast data UDP port for publishing rig data and state */ + char *multicast_cmd_addr; /*!< Multicast command server UDP address for sending commands to rig */ + int multicast_cmd_port; /*!< Multicast command server UDP port for sending commands to rig */ + + volatile int multicast_receiver_run; + void *multicast_receiver_priv_data; }; /** diff --git a/src/conf.c b/src/conf.c index cc9291e3a..3c17ecb11 100644 --- a/src/conf.c +++ b/src/conf.c @@ -92,8 +92,8 @@ static const struct confparams frontend_cfg_params[] = }, { TOK_POLL_INTERVAL, "poll_interval", "Rig state poll interval in milliseconds", - "Polling interval in milliseconds for transceive emulation, value of 0 disables polling", - "0", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + "Polling interval in milliseconds for transceive emulation, defaults to 1000, value of 0 disables polling", + "1000", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } }, { TOK_PTT_TYPE, "ptt_type", "PTT type", @@ -190,6 +190,26 @@ static const struct confparams frontend_cfg_params[] = "Add Hz to VFOB/Sub frequency set", "0", RIG_CONF_NUMERIC, { .n = {0, 1e12, 1}} }, + { + TOK_MULTICAST_DATA_ADDR, "multicast_data_addr", "Multicast data UDP address", + "Multicast data UDP address for publishing rig data and state, value of 0.0.0.0 disables multicast data publishing", + "224.0.0.1", RIG_CONF_STRING, + }, + { + TOK_MULTICAST_DATA_PORT, "multicast_data_port", "Multicast data UDP port", + "Multicast data UDP port for publishing rig data and state", + "4532", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + }, + { + TOK_MULTICAST_CMD_ADDR, "multicast_cmd_addr", "Multicast command server UDP address", + "Multicast command UDP address for sending commands to rig, value of 0.0.0.0 disables multicast command server", + "224.0.0.2", RIG_CONF_STRING, + }, + { + TOK_MULTICAST_CMD_PORT, "multicast_cmd_port", "Multicast command server UDP port", + "Multicast data UDP port for sending commands to rig", + "4532", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + }, { RIG_CONF_END, NULL, } }; @@ -608,7 +628,11 @@ static int frontend_set_conf(RIG *rig, token_t token, const char *val) break; case TOK_POLL_INTERVAL: - rs->poll_interval = atof(val); + if (1 != sscanf(val, "%ld", &val_i)) + { + return -RIG_EINVAL; + } + rs->poll_interval = val_i; // Make sure cache times out before next poll cycle rig_set_cache_timeout_ms(rig, HAMLIB_CACHE_ALL, atol(val)); break; @@ -735,7 +759,31 @@ static int frontend_set_conf(RIG *rig, token_t token, const char *val) rig_debug(RIG_DEBUG_VERBOSE, "%s: offset_vfob=%ld\n", __func__, val_i); break; + case TOK_MULTICAST_DATA_ADDR: + rs->multicast_data_addr = strdup(val); + break; + case TOK_MULTICAST_DATA_PORT: + if (1 != sscanf(val, "%ld", &val_i)) + { + return -RIG_EINVAL; + } + + rs->multicast_data_port = val_i; + break; + + case TOK_MULTICAST_CMD_ADDR: + rs->multicast_cmd_addr = strdup(val); + break; + + case TOK_MULTICAST_CMD_PORT: + if (1 != sscanf(val, "%ld", &val_i)) + { + return -RIG_EINVAL; + } + + rs->multicast_cmd_port = val_i; + break; default: return -RIG_EINVAL; diff --git a/src/event.c b/src/event.c index e6f2ea51e..5cdedc43c 100644 --- a/src/event.c +++ b/src/event.c @@ -75,12 +75,11 @@ void *rig_poll_routine(void *arg) int update_occurred; vfo_t vfo = RIG_VFO_NONE, vfo_prev = RIG_VFO_NONE; - freq_t freq_main = 0, freq_sub = 0, freq_main_prev = 0, freq_sub_prev = 0; - rmode_t mode_main = RIG_MODE_NONE, mode_sub = RIG_MODE_NONE, - mode_main_prev = RIG_MODE_NONE, mode_sub_prev = RIG_MODE_NONE; - pbwidth_t width_main = 0, width_sub = 0, width_main_prev = 0, - width_sub_prev = 0; - split_t split, split_prev = -1; + freq_t freq_main_a = 0, freq_main_b = 0, freq_main_c = 0, freq_sub_a = 0, freq_sub_b = 0, freq_sub_c = 0; + rmode_t mode_main_a = 0, mode_main_b = 0, mode_main_c = 0, mode_sub_a = 0, mode_sub_b = 0, mode_sub_c = 0; + pbwidth_t width_main_a = 0, width_main_b = 0, width_main_c = 0, width_sub_a = 0, width_sub_b = 0, width_sub_c = 0; + ptt_t ptt = RIG_PTT_OFF; + split_t split = RIG_SPLIT_OFF; rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting rig poll routine thread\n", __FILE__, __LINE__); @@ -92,6 +91,109 @@ void *rig_poll_routine(void *arg) while (rs->poll_routine_thread_run) { + if (rig->state.cache.freqMainA != freq_main_a) + { + freq_main_a = rig->state.cache.freqMainA; + update_occurred = 1; + } + else if (rig->state.cache.freqMainB != freq_main_b) + { + freq_main_b = rig->state.cache.freqMainB; + update_occurred = 1; + } + else if (rig->state.cache.freqMainC != freq_main_c) + { + freq_main_b = rig->state.cache.freqMainC; + update_occurred = 1; + } + else if (rig->state.cache.freqSubA != freq_sub_a) + { + freq_sub_a = rig->state.cache.freqSubA; + update_occurred = 1; + } + else if (rig->state.cache.freqSubB != freq_sub_b) + { + freq_sub_b = rig->state.cache.freqSubB; + update_occurred = 1; + } + else if (rig->state.cache.freqSubC != freq_sub_c) + { + freq_sub_c = rig->state.cache.freqSubC; + update_occurred = 1; + } + else if (rig->state.cache.ptt != ptt) + { + ptt = rig->state.cache.ptt; + update_occurred = 1; + } + else if (rig->state.cache.split != split) + { + split = rig->state.cache.split; + update_occurred = 1; + } + else if (rig->state.cache.modeMainA != mode_main_a) + { + mode_main_a = rig->state.cache.modeMainA; + update_occurred = 1; + } + else if (rig->state.cache.modeMainB != mode_main_b) + { + mode_main_b = rig->state.cache.modeMainB; + update_occurred = 1; + } + else if (rig->state.cache.modeMainC != mode_main_c) + { + mode_main_c = rig->state.cache.modeMainC; + update_occurred = 1; + } + else if (rig->state.cache.modeSubA != mode_sub_a) + { + mode_sub_a = rig->state.cache.modeSubA; + update_occurred = 1; + } + else if (rig->state.cache.modeSubB != mode_sub_b) + { + mode_sub_b = rig->state.cache.modeSubB; + update_occurred = 1; + } + else if (rig->state.cache.modeSubC != mode_sub_c) + { + mode_sub_c = rig->state.cache.modeSubC; + update_occurred = 1; + } + else if (rig->state.cache.widthMainA != width_main_a) + { + width_main_a = rig->state.cache.widthMainA; + update_occurred = 1; + } + else if (rig->state.cache.widthMainB != width_main_b) + { + width_main_b = rig->state.cache.widthMainB; + update_occurred = 1; + } + else if (rig->state.cache.widthMainC != width_main_c) + { + width_main_c = rig->state.cache.widthMainC; + update_occurred = 1; + } + else if (rig->state.cache.widthSubA != width_sub_a) + { + width_sub_a = rig->state.cache.widthSubA; + update_occurred = 1; + } + else if (rig->state.cache.widthSubB != width_sub_b) + { + width_sub_b = rig->state.cache.widthSubB; + update_occurred = 1; + } + else if (rig->state.cache.widthSubC != width_sub_c) + { + width_sub_c = rig->state.cache.widthSubC; + update_occurred = 1; + } + +// The original code here actively reads rig state, which can be too intensive and intrusive +#if 0 if (rig->caps->get_vfo) { result = rig_get_vfo(rig, &vfo); @@ -225,10 +327,12 @@ void *rig_poll_routine(void *arg) split_prev = split; } } +#endif if (update_occurred) { network_publish_rig_poll_data(rig); + update_occurred = 0; } hl_usleep(rs->poll_interval * 1000); diff --git a/src/network.c b/src/network.c index 9b4b5126f..cde6e473a 100644 --- a/src/network.c +++ b/src/network.c @@ -1,6 +1,6 @@ /* * Hamlib Interface - network communication low-level support - * Copyright (c) 2021 by Mikael Nousiainen + * Copyright (c) 2021-2023 by Mikael Nousiainen * Copyright (c) 2000-2012 by Stephane Fillod * * This library is free software; you can redistribute it and/or @@ -126,6 +126,20 @@ typedef struct multicast_publisher_priv_data_s multicast_publisher_args args; } multicast_publisher_priv_data; +typedef struct multicast_receiver_args_s +{ + RIG *rig; + int socket_fd; + const char *multicast_addr; + int multicast_port; +} multicast_receiver_args; + +typedef struct multicast_receiver_priv_data_s +{ + pthread_t thread_id; + multicast_receiver_args args; +} multicast_receiver_priv_data; + static void handle_error(enum rig_debug_level_e lvl, const char *msg) { int e; @@ -515,7 +529,6 @@ static int multicast_publisher_write_data(multicast_publisher_args return (RIG_OK); } -#if 0 // disable until we figure out what to do about Windows poor performance static int multicast_publisher_read_data(multicast_publisher_args const *mcast_publisher_args, size_t length, unsigned char *data) { @@ -558,7 +571,6 @@ static int multicast_publisher_read_data(multicast_publisher_args return (RIG_OK); } -#endif #else @@ -638,7 +650,6 @@ static int multicast_publisher_write_data(const multicast_publisher_args return (RIG_OK); } -#if 0 static int multicast_publisher_read_data(const multicast_publisher_args *mcast_publisher_args, size_t length, unsigned char *data) { @@ -703,7 +714,6 @@ static int multicast_publisher_read_data(const multicast_publisher_args return (RIG_OK); } -#endif #endif @@ -777,7 +787,6 @@ int network_publish_rig_transceive_data(RIG *rig) return multicast_publisher_write_packet_header(rig, &packet); } -// cppcheck-suppress unusedFunction int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line) { int result; @@ -827,7 +836,6 @@ int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line) RETURNFUNC2(RIG_OK); } -#if 0 static int multicast_publisher_read_packet(multicast_publisher_args const *mcast_publisher_args, uint8_t *type, struct rig_spectrum_line *spectrum_line, @@ -893,11 +901,10 @@ static int multicast_publisher_read_packet(multicast_publisher_args return (RIG_OK); } -#endif void *multicast_publisher(void *arg) { - //unsigned char spectrum_data[HAMLIB_MAX_SPECTRUM_DATA]; + unsigned char spectrum_data[HAMLIB_MAX_SPECTRUM_DATA]; char snapshot_buffer[HAMLIB_MAX_SNAPSHOT_PACKET_SIZE]; struct multicast_publisher_args_s *args = (struct multicast_publisher_args_s *) @@ -923,29 +930,20 @@ void *multicast_publisher(void *arg) while (rs->multicast_publisher_run == 1) { - int i; int result; - static freq_t freqA, freqB, freqC; - static mode_t modeA, modeB, modeC; - static pbwidth_t widthA, widthB, widthC; - static ptt_t ptt; - static split_t split; -#if 0 result = multicast_publisher_read_packet(args, &packet_type, &spectrum_line, spectrum_data); -#endif if (result != RIG_OK) { if (result == -RIG_ETIMEOUT) { - // continue; + continue; } // TODO: how to detect closing of pipe, indicate with error code // TODO: error handling, flush pipe in case of error? - //hl_usleep(500 * 1000); -// continue; + continue; } result = snapshot_serialize(sizeof(snapshot_buffer), snapshot_buffer, rig, @@ -959,10 +957,8 @@ void *multicast_publisher(void *arg) continue; } -#if 0 rig_debug(RIG_DEBUG_CACHE, "%s: sending rig snapshot data: %s\n", __func__, snapshot_buffer); -#endif send_result = sendto( socket_fd, @@ -978,84 +974,73 @@ void *multicast_publisher(void *arg) rig_debug(RIG_DEBUG_ERR, "%s: error sending UDP packet: %s\n", __func__, strerror(errno)); } - - struct sockaddr_in client_addr; - char buf[4096]; - socklen_t client_len = sizeof(client_addr); - int n = recvfrom(socket_fd, buf, sizeof(buf), 0, (struct sockaddr*)&client_addr, &client_len); - if (n > 0) - { - // To-do handle commands from multicast clients - rig_debug(RIG_DEBUG_ERR, "%s: received %d bytes=%s\n", __func__, n, buf); - } - for(i=0;i<5;++i) - { - hl_usleep(200*1000); - if (rig->state.cache.freqMainA != freqA) - { - freqA = rig->state.cache.freqMainA; - break; - } - if (rig->state.cache.freqMainB != freqB) - { - freqB = rig->state.cache.freqMainB; - break; - } - if (rig->state.cache.freqMainC != freqC) - { - freqC = rig->state.cache.freqMainC; - break; - } - if (rig->state.cache.ptt != ptt) - { - ptt = rig->state.cache.ptt; - break; - } - if (rig->state.cache.split != split) - { - split = rig->state.cache.split; - break; - } - if (rig->state.cache.modeMainA != modeA) - { - modeA = rig->state.cache.modeMainA; - break; - } - if (rig->state.cache.modeMainB != modeB) - { - modeB = rig->state.cache.modeMainB; - break; - } - if (rig->state.cache.modeMainC != modeC) - { - modeC = rig->state.cache.modeMainC; - break; - } - if (rig->state.cache.widthMainA != widthA) - { - widthA = rig->state.cache.widthMainA; - break; - } - if (rig->state.cache.widthMainB != widthB) - { - widthB = rig->state.cache.widthMainB; - break; - } - if (rig->state.cache.widthMainC != widthC) - { - widthC = rig->state.cache.widthMainC; - break; - } - } - } - rs->multicast_publisher_run = 2; // stop value rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping multicast publisher\n", __FILE__, __LINE__); return NULL; } +void *multicast_receiver(void *arg) +{ + char data[4096]; + + struct multicast_receiver_args_s *args = (struct multicast_receiver_args_s *) + arg; + RIG *rig = args->rig; + struct rig_state *rs = &rig->state; + + struct sockaddr_in dest_addr; + int socket_fd = args->socket_fd; + + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, + __LINE__); + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.sin_family = AF_INET; + dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr); + dest_addr.sin_port = htons(args->multicast_port); + + if ((bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr))) < 0) { + rig_debug(RIG_DEBUG_ERR, "%s: error binding UDP socket to %s:%d: %s\n", __func__, + args->multicast_addr, args->multicast_port, strerror(errno)); + return NULL; + } + + rs->multicast_receiver_run = 1; + + while (rs->multicast_receiver_run == 1) + { + ssize_t result; + struct sockaddr_in client_addr; + socklen_t client_len = sizeof(client_addr); + + result = recvfrom(socket_fd, data, sizeof(data), 0, (struct sockaddr *) &client_addr, &client_len); + + rig_debug(RIG_DEBUG_ERR, "%s: received multicast packet with result %ld\n", __func__, result); + + if (result <= 0) + { + if (result < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error receiving from UDP socket %s:%d: %s\n", __func__, + args->multicast_addr, args->multicast_port, strerror(errno)); + } + break; + } + + // TODO: handle commands from multicast clients + rig_debug(RIG_DEBUG_ERR, "%s: received %ld bytes=%s\n", __func__, result, data); + + // TODO: if a new snapshot needs to be sent, call network_publish_rig_poll_data() and the publisher routine will send out a snapshot + // TODO: new logic in publisher needs to be written for other types of responses + } + + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping multicast receiver\n", __FILE__, + __LINE__); + return NULL; +} + //! @endcond /** @@ -1077,11 +1062,11 @@ int network_multicast_publisher_start(RIG *rig, const char *multicast_addr, ENTERFUNC; - rig_debug(RIG_DEBUG_VERBOSE, "%s(%d):multicast address=%s, port=%d\n", __FILE__, + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): multicast publisher address=%s, port=%d\n", __FILE__, __LINE__, multicast_addr, multicast_port); - if (strcmp(multicast_addr, "0.0.0.0") == 0) + if (multicast_addr == NULL || strcmp(multicast_addr, "0.0.0.0") == 0) { rig_debug(RIG_DEBUG_TRACE, "%s(%d): not starting multicast publisher\n", __FILE__, __LINE__); @@ -1231,5 +1216,149 @@ int network_multicast_publisher_stop(RIG *rig) RETURNFUNC(RIG_OK); } + + +/** + * \brief Start multicast receiver + * + * Start multicast receiver. + * + * \param multicast_addr UDP address + * \param multicast_port UDP socket port + * \return RIG_OK or < 0 if error + */ +int network_multicast_receiver_start(RIG *rig, const char *multicast_addr, int multicast_port) +{ + struct rig_state *rs = &rig->state; + multicast_receiver_priv_data *mcast_receiver_priv; + int socket_fd; + int status; + + ENTERFUNC; + + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): multicast receiver address=%s, port=%d\n", __FILE__, + __LINE__, + multicast_addr, multicast_port); + + if (multicast_addr == NULL || strcmp(multicast_addr, "0.0.0.0") == 0) + { + rig_debug(RIG_DEBUG_TRACE, "%s(%d): not starting multicast receiver\n", + __FILE__, __LINE__); + return RIG_OK; + } + + if (rs->multicast_receiver_priv_data != NULL) + { + rig_debug(RIG_DEBUG_ERR, "%s(%d): multicast receiver already running\n", + __FILE__, + __LINE__); + RETURNFUNC(-RIG_EINVAL); + } + + status = network_init(); + + if (status != RIG_OK) + { + RETURNFUNC(status); + } + + socket_fd = socket(AF_INET, SOCK_DGRAM, 0); + + if (socket_fd < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error opening new UDP socket: %s", __func__, + strerror(errno)); + RETURNFUNC(-RIG_EIO); + } + + rs->multicast_receiver_run = 1; + rs->multicast_receiver_priv_data = calloc(1, + sizeof(multicast_receiver_priv_data)); + + if (rs->multicast_receiver_priv_data == NULL) + { + close(socket_fd); + RETURNFUNC(-RIG_ENOMEM); + } + + mcast_receiver_priv = (multicast_receiver_priv_data *) + rs->multicast_receiver_priv_data; + mcast_receiver_priv->args.socket_fd = socket_fd; + mcast_receiver_priv->args.multicast_addr = multicast_addr; + mcast_receiver_priv->args.multicast_port = multicast_port; + mcast_receiver_priv->args.rig = rig; + + int err = pthread_create(&mcast_receiver_priv->thread_id, NULL, + multicast_receiver, + &mcast_receiver_priv->args); + + if (err) + { + rig_debug(RIG_DEBUG_ERR, "%s(%d) pthread_create error %s\n", __FILE__, __LINE__, + strerror(errno)); + free(mcast_receiver_priv); + rs->multicast_receiver_priv_data = NULL; + close(socket_fd); + RETURNFUNC(-RIG_EINTERNAL); + } + + RETURNFUNC(RIG_OK); +} + +/** + * \brief Stop multicast receiver + * + * Stop multicast receiver + * + * \return RIG_OK or < 0 if error + */ +int network_multicast_receiver_stop(RIG *rig) +{ + struct rig_state *rs = &rig->state; + multicast_receiver_priv_data *mcast_receiver_priv; + + ENTERFUNC; + + rs->multicast_receiver_run = 0; + + mcast_receiver_priv = (multicast_receiver_priv_data *) + rs->multicast_receiver_priv_data; + + if (mcast_receiver_priv == NULL) + { + RETURNFUNC(RIG_OK); + } + + // Close the socket first to stop the routine + if (mcast_receiver_priv->args.socket_fd >= 0) + { + close(mcast_receiver_priv->args.socket_fd); + } + + if (mcast_receiver_priv->thread_id != 0) + { + int err = pthread_join(mcast_receiver_priv->thread_id, NULL); + + if (err) + { + rig_debug(RIG_DEBUG_ERR, "%s(%d): pthread_join error %s\n", __FILE__, __LINE__, + strerror(errno)); + // just ignore it + } + + mcast_receiver_priv->thread_id = 0; + } + + if (mcast_receiver_priv->args.socket_fd >= 0) + { + mcast_receiver_priv->args.socket_fd = -1; + } + + free(rs->multicast_receiver_priv_data); + rs->multicast_receiver_priv_data = NULL; + + RETURNFUNC(RIG_OK); +} + #endif /** @} */ diff --git a/src/network.h b/src/network.h index c46c7b22c..997b8874c 100644 --- a/src/network.h +++ b/src/network.h @@ -36,6 +36,8 @@ int network_publish_rig_transceive_data(RIG *rig); int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line); 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); +HAMLIB_EXPORT(int) network_multicast_receiver_stop(RIG *rig); __END_DECLS diff --git a/src/rig.c b/src/rig.c index 3c1d496b3..1165c8c70 100644 --- a/src/rig.c +++ b/src/rig.c @@ -670,7 +670,11 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model) rs->vfo_comp = 0.0; /* override it with preferences */ rs->current_vfo = RIG_VFO_CURR; /* we don't know yet! */ rs->tx_vfo = RIG_VFO_CURR; /* we don't know yet! */ - rs->poll_interval = 0; // disable polling by default + rs->poll_interval = 1000; // enable polling by default + rs->multicast_data_addr = "224.0.0.1"; // enable multicast data publishing by default + rs->multicast_data_port = 4532; + rs->multicast_cmd_addr = "224.0.0.2"; // enable multicast command server by default + rs->multicast_cmd_port = 4532; rs->lo_freq = 0; rs->cache.timeout_ms = 500; // 500ms cache timeout by default rs->cache.ptt = 0; @@ -1484,22 +1488,36 @@ int HAMLIB_API rig_open(RIG *rig) memcpy(&rs->pttport_deprecated, &rs->pttport, sizeof(hamlib_port_t_deprecated)); memcpy(&rs->dcdport_deprecated, &rs->dcdport, sizeof(hamlib_port_t_deprecated)); rig_flush_force(&rs->rigport, 1); -// if (rig->caps->rig_model != RIG_MODEL_NETRIGCTL) multicast_init(rig, "224.0.0.1", 4532); -// multicast_init(rig, "224.0.0.1", 4532); - char *multicast_addr = "224.0.0.1"; - int multicast_port = 4532; - enum multicast_item_e items = RIG_MULTICAST_POLL | RIG_MULTICAST_TRANSCEIVE; -// | RIG_MULTICAST_SPECTRUM; - retval = network_multicast_publisher_start(rig, multicast_addr, - multicast_port, items); + + enum multicast_item_e items = RIG_MULTICAST_POLL | RIG_MULTICAST_TRANSCEIVE + | RIG_MULTICAST_SPECTRUM; + retval = network_multicast_publisher_start(rig, rs->multicast_data_addr, + rs->multicast_data_port, items); if (retval != RIG_OK) { - rig_debug(RIG_DEBUG_ERR, "%s: network_multicast_server failed: %s\n", __FILE__, + rig_debug(RIG_DEBUG_ERR, "%s: network_multicast_publisher_start failed: %s\n", __FILE__, rigerror(retval)); // we will consider this non-fatal for now } + retval = network_multicast_receiver_start(rig, rs->multicast_cmd_addr, rs->multicast_cmd_port); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: network_multicast_receiver_start failed: %s\n", __FILE__, + rigerror(retval)); + // we will consider this non-fatal for now + } + + retval = rig_poll_routine_start(rig); + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: rig_poll_routine_start failed: %s\n", __FILE__, + rigerror(retval)); + // we will consider this non-fatal for now + } + RETURNFUNC2(RIG_OK); } @@ -1541,9 +1559,9 @@ int HAMLIB_API rig_close(RIG *rig) morse_data_handler_stop(rig); async_data_handler_stop(rig); + rig_poll_routine_stop(rig); + network_multicast_receiver_stop(rig); network_multicast_publisher_stop(rig); - //while(rs->multicast_publisher_run != 2) hl_usleep(10*1000); - //multicast_stop(rig); /* * Let the backend say 73s to the rig. diff --git a/src/token.h b/src/token.h index f3c4ad648..53d568798 100644 --- a/src/token.h +++ b/src/token.h @@ -131,6 +131,15 @@ #define TOK_OFFSET_VFOA TOKEN_FRONTEND(130) /** \brief rig: Add Hz to VFOB/Sub frequency set */ #define TOK_OFFSET_VFOB TOKEN_FRONTEND(131) +/** \brief rig: Multicast data UDP address for publishing rig data and state, default 224.0.0.1, value of 0.0.0.0 disables multicast data publishing */ +#define TOK_MULTICAST_DATA_ADDR TOKEN_FRONTEND(132) +/** \brief rig: Multicast data UDP port, default 4532 */ +#define TOK_MULTICAST_DATA_PORT TOKEN_FRONTEND(133) +/** \brief rig: Multicast command server UDP address for sending commands to rig, default 224.0.0.2, value of 0.0.0.0 disables multicast command server */ +#define TOK_MULTICAST_CMD_ADDR TOKEN_FRONTEND(134) +/** \brief rig: Multicast command server UDP port, default 4532 */ +#define TOK_MULTICAST_CMD_PORT TOKEN_FRONTEND(135) + /* * rotator specific tokens * (strictly, should be documented as rotator_internal) diff --git a/tests/rigctld.c b/tests/rigctld.c index dbf648ddc..33d0d6110 100644 --- a/tests/rigctld.c +++ b/tests/rigctld.c @@ -105,8 +105,6 @@ static struct option long_options[] = {"twiddle_rit", 1, 0, 'w'}, {"uplink", 1, 0, 'x'}, {"debug-time-stamps", 0, 0, 'Z'}, - {"multicast-addr", 1, 0, 'M'}, - {"multicast-port", 1, 0, 'n'}, {"password", 1, 0, 'A'}, {"rigctld-idle", 0, 0, 'R'}, {"bind-all", 0, 0, 'b'}, @@ -145,8 +143,6 @@ static int volatile ctrl_c; const char *portno = "4532"; const char *src_addr = NULL; /* INADDR_ANY */ -const char *multicast_addr = "0.0.0.0"; -int multicast_port = 4532; extern char rigctld_password[65]; char resp_sep = '\n'; extern int lock_mode; @@ -618,33 +614,6 @@ int main(int argc, char *argv[]) rig_set_debug_time_stamp(1); break; - case 'M': - if (!optarg) - { - usage(); /* wrong arg count */ - exit(1); - } - - multicast_addr = optarg; - break; - - case 'n': - if (!optarg) - { - usage(); /* wrong arg count */ - exit(1); - } - - multicast_port = atoi(optarg); - - if (multicast_port == 0) - { - fprintf(stderr, "Invalid multicast port: %s\n", optarg); - exit(1); - } - - break; - default: usage(); /* unknown option? */ exit(1); @@ -867,18 +836,6 @@ int main(int argc, char *argv[]) saved_result = result; - enum multicast_item_e items = RIG_MULTICAST_POLL | RIG_MULTICAST_TRANSCEIVE | - RIG_MULTICAST_SPECTRUM; - retcode = network_multicast_publisher_start(my_rig, multicast_addr, - multicast_port, items); - - if (retcode != RIG_OK) - { - rig_debug(RIG_DEBUG_ERR, "%s: network_multicast_server failed: %s\n", __FILE__, - rigerror(retcode)); - // we will consider this non-fatal for now - } - do { sock_listen = socket(result->ai_family, @@ -1148,8 +1105,6 @@ int main(int argc, char *argv[]) rig_close(my_rig); /* close port */ #endif - network_multicast_publisher_stop(my_rig); - rig_cleanup(my_rig); /* if you care about memory */ #ifdef __MINGW32__ @@ -1464,8 +1419,6 @@ void usage(void) " -w, --twiddle_rit suppress VFOB getfreq so RIT can be twiddled\n" " -x, --uplink set uplink get_freq ignore, 1=Sub, 2=Main\n" " -Z, --debug-time-stamps enable time stamps for debug messages\n" - " -M, --multicast-addr=addr set multicast UDP address, default 0.0.0.0 (off), recommend 224.0.1.1\n" - " -n, --multicast-port=port set multicast UDP port, default 4532\n" " -A, --password set password for rigctld access\n" " -R, --rigctld-idle make rigctld close the rig when no clients are connected\n" " -h, --help display this help and exit\n" From f618540f227d634c05dfa69d2237fb1350a0eb77 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 00:11:57 +0200 Subject: [PATCH 2/8] Join multicast group to enable UDP packet reception --- src/network.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/network.c b/src/network.c index cde6e473a..0c57ba2b9 100644 --- a/src/network.c +++ b/src/network.c @@ -728,7 +728,7 @@ static int multicast_publisher_write_packet_header(RIG *rig, if (rs->multicast_publisher_priv_data == NULL) { // Silently ignore if multicast publisher is not enabled - RETURNFUNC2(RIG_OK); + return RIG_OK; } mcast_publisher_priv = (multicast_publisher_priv_data *) @@ -741,10 +741,10 @@ static int multicast_publisher_write_packet_header(RIG *rig, if (result != RIG_OK) { - RETURNFUNC2(result); + return result; } - RETURNFUNC2(RIG_OK); + return RIG_OK; } // cppcheck-suppress unusedFunction @@ -996,17 +996,38 @@ void *multicast_receiver(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, __LINE__); + int optval = 1; + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP port reuse: %s\n", __func__, + strerror(errno)); + return NULL; + } + memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr); dest_addr.sin_port = htons(args->multicast_port); - if ((bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr))) < 0) { + if ((bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr))) < 0) + { rig_debug(RIG_DEBUG_ERR, "%s: error binding UDP socket to %s:%d: %s\n", __func__, args->multicast_addr, args->multicast_port, strerror(errno)); return NULL; } + struct ip_mreq mreq; + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = inet_addr(args->multicast_addr); + mreq.imr_interface.s_addr = INADDR_ANY; + + if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error joining multicast group %s:%d: %s\n", __func__, + args->multicast_addr, args->multicast_port, strerror(errno)); + return NULL; + } + rs->multicast_receiver_run = 1; while (rs->multicast_receiver_run == 1) @@ -1017,12 +1038,15 @@ void *multicast_receiver(void *arg) result = recvfrom(socket_fd, data, sizeof(data), 0, (struct sockaddr *) &client_addr, &client_len); - rig_debug(RIG_DEBUG_ERR, "%s: received multicast packet with result %ld\n", __func__, result); - if (result <= 0) { if (result < 0) { + if (errno == EAGAIN) + { + hl_usleep(100 * 1000); + continue; + } rig_debug(RIG_DEBUG_ERR, "%s: error receiving from UDP socket %s:%d: %s\n", __func__, args->multicast_addr, args->multicast_port, strerror(errno)); } @@ -1030,7 +1054,7 @@ void *multicast_receiver(void *arg) } // TODO: handle commands from multicast clients - rig_debug(RIG_DEBUG_ERR, "%s: received %ld bytes=%s\n", __func__, result, data); + rig_debug(RIG_DEBUG_VERBOSE, "%s: received %ld bytes of data: %.*s\n", __func__, result, (int) result, data); // TODO: if a new snapshot needs to be sent, call network_publish_rig_poll_data() and the publisher routine will send out a snapshot // TODO: new logic in publisher needs to be written for other types of responses From f36822ac20b43d7257ded7ba25b8162b4c5eda31 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 00:18:27 +0200 Subject: [PATCH 3/8] Fix socket option --- src/network.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/network.c b/src/network.c index 0c57ba2b9..a1208fba9 100644 --- a/src/network.c +++ b/src/network.c @@ -996,10 +996,10 @@ void *multicast_receiver(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, __LINE__); - int optval = 1; - if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) + char optval = 1; + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { - rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP port reuse: %s\n", __func__, + rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP address reuse: %s\n", __func__, strerror(errno)); return NULL; } From 5fb2e68e07c8b972d82b68dc4b0a5b2d79045623 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 00:20:15 +0200 Subject: [PATCH 4/8] Fix warning --- src/network.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network.c b/src/network.c index a1208fba9..de3687873 100644 --- a/src/network.c +++ b/src/network.c @@ -1054,7 +1054,7 @@ void *multicast_receiver(void *arg) } // TODO: handle commands from multicast clients - rig_debug(RIG_DEBUG_VERBOSE, "%s: received %ld bytes of data: %.*s\n", __func__, result, (int) result, data); + rig_debug(RIG_DEBUG_VERBOSE, "%s: received %ld bytes of data: %.*s\n", __func__, (long) result, (int) result, data); // TODO: if a new snapshot needs to be sent, call network_publish_rig_poll_data() and the publisher routine will send out a snapshot // TODO: new logic in publisher needs to be written for other types of responses From 53ff60d98156b277c369fbdd63ce3da2893594d2 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 00:32:05 +0200 Subject: [PATCH 5/8] Fix socket option again --- src/network.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network.c b/src/network.c index de3687873..e07e2f4fe 100644 --- a/src/network.c +++ b/src/network.c @@ -996,7 +996,7 @@ void *multicast_receiver(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, __LINE__); - char optval = 1; + int optval = 1; if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP address reuse: %s\n", __func__, @@ -1009,7 +1009,7 @@ void *multicast_receiver(void *arg) dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr); dest_addr.sin_port = htons(args->multicast_port); - if ((bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr))) < 0) + if (bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr)) < 0) { rig_debug(RIG_DEBUG_ERR, "%s: error binding UDP socket to %s:%d: %s\n", __func__, args->multicast_addr, args->multicast_port, strerror(errno)); From 88e86fb62a17a14dc681a1ed4336d6268af709cc Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 00:35:47 +0200 Subject: [PATCH 6/8] Comment out SO_REUSEADDR for now --- src/network.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/network.c b/src/network.c index e07e2f4fe..3b590fb7a 100644 --- a/src/network.c +++ b/src/network.c @@ -996,6 +996,8 @@ void *multicast_receiver(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, __LINE__); +// Not working right now +#if 0 int optval = 1; if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { @@ -1003,6 +1005,7 @@ void *multicast_receiver(void *arg) strerror(errno)); return NULL; } +#endif memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; From 5b86d4efffe86b9893093790bcb0f74c86e11a9e Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 18:00:16 +0200 Subject: [PATCH 7/8] Bind multicast receiver to INADDR_ANY on Windows, because binding to multicast group IPs doesn't seem to work. Use a different port number for (4351) multicast receiver commands to avoid multicast publisher packets ending up in the receiver (loopback issue). --- src/conf.c | 2 +- src/network.c | 18 +++++++++++++++--- src/rig.c | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/conf.c b/src/conf.c index 3c17ecb11..349456d35 100644 --- a/src/conf.c +++ b/src/conf.c @@ -208,7 +208,7 @@ static const struct confparams frontend_cfg_params[] = { TOK_MULTICAST_CMD_PORT, "multicast_cmd_port", "Multicast command server UDP port", "Multicast data UDP port for sending commands to rig", - "4532", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + "4531", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } }, { RIG_CONF_END, NULL, } diff --git a/src/network.c b/src/network.c index 3b590fb7a..877ab510a 100644 --- a/src/network.c +++ b/src/network.c @@ -996,8 +996,6 @@ void *multicast_receiver(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, __LINE__); -// Not working right now -#if 0 int optval = 1; if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { @@ -1005,11 +1003,25 @@ void *multicast_receiver(void *arg) strerror(errno)); return NULL; } + +#if defined(SO_REUSEPORT) + // Windows does not have SO_REUSEPORT. However, SO_REUSEADDR works in a similar way. + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP port reuse: %s\n", __func__, + strerror(errno)); + return NULL; + } #endif memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; +#ifdef __MINGW32__ + // Windows cannot bind to multicast group addresses for some unknown reason + dest_addr.sin_addr.s_addr = htonl(INADDR_ANY); +#else dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr); +#endif dest_addr.sin_port = htons(args->multicast_port); if (bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr)) < 0) @@ -1022,7 +1034,7 @@ void *multicast_receiver(void *arg) struct ip_mreq mreq; memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr.s_addr = inet_addr(args->multicast_addr); - mreq.imr_interface.s_addr = INADDR_ANY; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { diff --git a/src/rig.c b/src/rig.c index 1165c8c70..1998ddfc6 100644 --- a/src/rig.c +++ b/src/rig.c @@ -674,7 +674,7 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model) rs->multicast_data_addr = "224.0.0.1"; // enable multicast data publishing by default rs->multicast_data_port = 4532; rs->multicast_cmd_addr = "224.0.0.2"; // enable multicast command server by default - rs->multicast_cmd_port = 4532; + rs->multicast_cmd_port = 4531; rs->lo_freq = 0; rs->cache.timeout_ms = 500; // 500ms cache timeout by default rs->cache.ptt = 0; From 41c891251f4641d2aeb292e76654ed88b9f85a90 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Fri, 3 Nov 2023 17:18:49 +0200 Subject: [PATCH 8/8] Fix multicast receiver shutdown by using non-blocking sockets and select(). Use the same port number 4532 for both state snapshots and commands -- it is enough to have different multicast groups for them. Publish multicast state snapshots more often (change detection interval now set to 50ms), but at least at interval defined by poll_interval. --- src/conf.c | 2 +- src/event.c | 15 ++++++++- src/network.c | 87 +++++++++++++++++++++++++++++++++++++++++++++------ src/rig.c | 2 +- 4 files changed, 93 insertions(+), 13 deletions(-) diff --git a/src/conf.c b/src/conf.c index 349456d35..3c17ecb11 100644 --- a/src/conf.c +++ b/src/conf.c @@ -208,7 +208,7 @@ static const struct confparams frontend_cfg_params[] = { TOK_MULTICAST_CMD_PORT, "multicast_cmd_port", "Multicast command server UDP port", "Multicast data UDP port for sending commands to rig", - "4531", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + "4532", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } }, { RIG_CONF_END, NULL, } diff --git a/src/event.c b/src/event.c index 5cdedc43c..c9f6ca9de 100644 --- a/src/event.c +++ b/src/event.c @@ -87,6 +87,10 @@ void *rig_poll_routine(void *arg) // Rig cache time should be equal to rig poll interval (should be set automatically by rigctld at least) rig_set_cache_timeout_ms(rig, HAMLIB_CACHE_ALL, rs->poll_interval); + // Attempt to detect changes with the interval below (in milliseconds) + int change_detection_interval = 50; + int interval_count = 0; + update_occurred = 0; while (rs->poll_routine_thread_run) @@ -333,9 +337,18 @@ void *rig_poll_routine(void *arg) { network_publish_rig_poll_data(rig); update_occurred = 0; + interval_count = 0; } - hl_usleep(rs->poll_interval * 1000); + hl_usleep(change_detection_interval * 1000); + interval_count++; + + // Publish updates every poll_interval if no changes have been detected + if (interval_count >= (rs->poll_interval / change_detection_interval)) + { + interval_count = 0; + network_publish_rig_poll_data(rig); + } } rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping rig poll routine thread\n", diff --git a/src/network.c b/src/network.c index 877ab510a..4d34527a9 100644 --- a/src/network.c +++ b/src/network.c @@ -1047,9 +1047,43 @@ void *multicast_receiver(void *arg) while (rs->multicast_receiver_run == 1) { - ssize_t result; struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); + fd_set rfds, efds; + struct timeval timeout; + int select_result; + ssize_t result; + + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(socket_fd, &rfds); + efds = rfds; + + select_result = select(socket_fd + 1, &rfds, NULL, &efds, &timeout); + if (select_result == 0) + { + // Select timed out + continue; + } + + if (select_result < 0) + { + rig_debug(RIG_DEBUG_ERR, + "%s(): select() failed when reading UDP multicast socket data: %s\n", + __func__, + strerror(errno)); + + break; + } + + if (FD_ISSET(socket_fd, &efds)) + { + rig_debug(RIG_DEBUG_ERR, + "%s(): fd error when reading UDP multicast socket data\n", __func__); + break; + } result = recvfrom(socket_fd, data, sizeof(data), 0, (struct sockaddr *) &client_addr, &client_len); @@ -1057,9 +1091,8 @@ void *multicast_receiver(void *arg) { if (result < 0) { - if (errno == EAGAIN) + if (errno == 0 || errno == EAGAIN || errno == EWOULDBLOCK) { - hl_usleep(100 * 1000); continue; } rig_debug(RIG_DEBUG_ERR, "%s: error receiving from UDP socket %s:%d: %s\n", __func__, @@ -1128,13 +1161,6 @@ int network_multicast_publisher_start(RIG *rig, const char *multicast_addr, } socket_fd = socket(AF_INET, SOCK_DGRAM, 0); - u_long mode = 1; // Enable non-blocking mode -#ifdef __MINGW32__ - ioctlsocket(socket_fd, FIONBIO, &mode); -#else - ioctl(socket_fd, FIONBIO, &mode); -#endif - if (socket_fd < 0) { @@ -1143,6 +1169,24 @@ int network_multicast_publisher_start(RIG *rig, const char *multicast_addr, RETURNFUNC(-RIG_EIO); } + // Enable non-blocking mode + u_long mode = 1; +#ifdef __MINGW32__ + if (ioctlsocket(socket_fd, FIONBIO, &mode) == SOCKET_ERROR) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s", __func__, + strerror(errno)); + RETURNFUNC(-RIG_EIO); + } +#else + if (ioctl(socket_fd, FIONBIO, &mode) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s", __func__, + strerror(errno)); + RETURNFUNC(-RIG_EIO); + } +#endif + if (items & RIG_MULTICAST_TRANSCEIVE) { rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) MULTICAST_TRANSCEIVE enabled\n", __FILE__, @@ -1310,6 +1354,24 @@ int network_multicast_receiver_start(RIG *rig, const char *multicast_addr, int m RETURNFUNC(-RIG_EIO); } + // Enable non-blocking mode + u_long mode = 1; +#ifdef __MINGW32__ + if (ioctlsocket(socket_fd, FIONBIO, &mode) == SOCKET_ERROR) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s", __func__, + strerror(errno)); + RETURNFUNC(-RIG_EIO); + } +#else + if (ioctl(socket_fd, FIONBIO, &mode) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s", __func__, + strerror(errno)); + RETURNFUNC(-RIG_EIO); + } +#endif + rs->multicast_receiver_run = 1; rs->multicast_receiver_priv_data = calloc(1, sizeof(multicast_receiver_priv_data)); @@ -1371,6 +1433,11 @@ int network_multicast_receiver_stop(RIG *rig) // Close the socket first to stop the routine if (mcast_receiver_priv->args.socket_fd >= 0) { +#ifdef __MINGW32__ + shutdown(mcast_receiver_priv->args.socket_fd, SD_BOTH); +#else + shutdown(mcast_receiver_priv->args.socket_fd, SHUT_RDWR); +#endif close(mcast_receiver_priv->args.socket_fd); } diff --git a/src/rig.c b/src/rig.c index 1998ddfc6..1165c8c70 100644 --- a/src/rig.c +++ b/src/rig.c @@ -674,7 +674,7 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model) rs->multicast_data_addr = "224.0.0.1"; // enable multicast data publishing by default rs->multicast_data_port = 4532; rs->multicast_cmd_addr = "224.0.0.2"; // enable multicast command server by default - rs->multicast_cmd_port = 4531; + rs->multicast_cmd_port = 4532; rs->lo_freq = 0; rs->cache.timeout_ms = 500; // 500ms cache timeout by default rs->cache.ptt = 0;