Merge pull request #1413 from mikaelnousiainen/hamlib-multicast-1

Refactor multicast publisher/server code
pull/1415/head
Michael Black 2023-11-05 15:26:55 -06:00 zatwierdzone przez GitHub
commit 3107a060f0
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
8 zmienionych plików z 562 dodań i 175 usunięć

Wyświetl plik

@ -2759,8 +2759,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 */
int doppler; /*!< True if doppler changing detected */
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;
};
/**

Wyświetl plik

@ -97,8 +97,8 @@ static const struct confparams frontend_cfg_params[] =
},
{
TOK_POLL_INTERVAL, "poll_interval", "Rig state poll interval in ms",
"Polling interval in ms for transceive emulation, value of 0 disables polling",
"0", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } }
"Polling interval in ms 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",
@ -195,6 +195,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, }
};
@ -613,7 +633,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;
@ -740,7 +764,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;

Wyświetl plik

@ -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__);
@ -88,10 +87,117 @@ 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)
{
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,13 +331,24 @@ void *rig_poll_routine(void *arg)
split_prev = split;
}
}
#endif
if (update_occurred)
{
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",

Wyświetl plik

@ -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
@ -718,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 *)
@ -731,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
@ -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,145 @@ 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__);
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__,
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)
{
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 = htonl(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)
{
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);
if (result <= 0)
{
if (result < 0)
{
if (errno == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
{
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));
}
break;
}
// TODO: handle commands from multicast clients
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
}
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping multicast receiver\n", __FILE__,
__LINE__);
return NULL;
}
//! @endcond
/**
@ -1077,11 +1134,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__);
@ -1104,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)
{
@ -1119,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__,
@ -1231,5 +1299,172 @@ 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);
}
// 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));
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)
{
#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);
}
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
/** @} */

Wyświetl plik

@ -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

Wyświetl plik

@ -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.

Wyświetl plik

@ -132,6 +132,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)

Wyświetl plik

@ -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"