Mike Black W9MDB 2023-05-08 17:15:26 -05:00
rodzic a4c10c3206
commit ca763d4ff1
2 zmienionych plików z 225 dodań i 218 usunięć

Wyświetl plik

@ -86,27 +86,21 @@ static int multicast_status_changed(RIG *rig)
return 0; return 0;
} }
void json_add_string(char *msg, const char *key, const char *value) void json_add_string(char *msg, const char *key, const char *value,
int addComma)
{ {
if (strlen(msg) != 0)
{
strcat(msg, ",\n");
}
strcat(msg, "\""); strcat(msg, "\"");
strcat(msg, key); strcat(msg, key);
strcat(msg, "\": "); strcat(msg, "\": ");
strcat(msg, "\""); strcat(msg, "\"");
strcat(msg, value); strcat(msg, value);
strcat(msg, "\""); strcat(msg, "\"");
if (addComma) { strcat(msg, ",\n"); }
} }
void json_add_int(char *msg, const char *key, const int number) void json_add_int(char *msg, const char *key, const int number, int addComma)
{ {
if (strlen(msg) != 0)
{
strcat(msg, ",\n");
}
strcat(msg, "\""); strcat(msg, "\"");
strcat(msg, key); strcat(msg, key);
@ -114,6 +108,11 @@ void json_add_int(char *msg, const char *key, const int number)
char tmp[64]; char tmp[64];
sprintf(tmp, "%d", number); sprintf(tmp, "%d", number);
strcat(msg, tmp); strcat(msg, tmp);
if (addComma)
{
strcat(msg, ",\n");
}
} }
void json_add_double(char *msg, const char *key, const double value) void json_add_double(char *msg, const char *key, const double value)
@ -131,49 +130,52 @@ void json_add_double(char *msg, const char *key, const double value)
strcat(msg, tmp); strcat(msg, tmp);
} }
void json_add_boolean(char *msg, const char *key, const int value) void json_add_boolean(char *msg, const char *key, const int value, int addComma)
{ {
if (strlen(msg) != 0)
{
strcat(msg, ",\n");
}
strcat(msg, "\""); strcat(msg, "\"");
strcat(msg, key); strcat(msg, key);
strcat(msg, "\": "); strcat(msg, "\": ");
char tmp[64]; char tmp[64];
sprintf(tmp, "%s", value == 0 ? "False" : "True"); sprintf(tmp, "%s", value == 0 ? "false" : "true");
strcat(msg, tmp); strcat(msg, tmp);
if (addComma)
{
strcat(msg, ",\n");
}
} }
void json_add_time(char *msg) void json_add_time(char *msg, int addComma)
{ {
char mydate[256]; char mydate[256];
date_strget(mydate, sizeof(mydate), 0); date_strget(mydate, sizeof(mydate), 0);
if (strlen(msg) != 0)
strcat(msg, "\"Time\": \"");
strcat(msg, mydate);
strcat(msg, "\"");
if (addComma)
{ {
strcat(msg, ",\n"); strcat(msg, ",\n");
} }
strcat(msg, "\"Time\": ");
strcat(msg, mydate);
} }
void json_add_vfoA(RIG *rig, char *msg) void json_add_vfoA(RIG *rig, char *msg)
{ {
strcat(msg, "{\n"); strcat(msg, "{\n");
json_add_string(msg, "Name", "VFOA"); json_add_string(msg, "Name", "VFOA", 1);
json_add_int(msg, "Freq", rig->state.cache.freqMainA); json_add_int(msg, "Freq", rig->state.cache.freqMainA, 1);
if (strlen(rig_strrmode(rig->state.cache.modeMainA)) > 0) if (strlen(rig_strrmode(rig->state.cache.modeMainA)) > 0)
{ {
json_add_string(msg, "Mode", rig_strrmode(rig->state.cache.modeMainA)); json_add_string(msg, "Mode", rig_strrmode(rig->state.cache.modeMainA), 1);
} }
if (rig->state.cache.widthMainA > 0) if (rig->state.cache.widthMainA > 0)
{ {
json_add_int(msg, "Width", rig->state.cache.widthMainA); json_add_int(msg, "Width", rig->state.cache.widthMainA, 1);
} }
// what about full duplex? rx_vfo would be in rx all the time? // what about full duplex? rx_vfo would be in rx all the time?
@ -181,70 +183,70 @@ void json_add_vfoA(RIG *rig, char *msg)
{ {
if (rig->state.tx_vfo && (RIG_VFO_B | RIG_VFO_MAIN_B)) if (rig->state.tx_vfo && (RIG_VFO_B | RIG_VFO_MAIN_B))
{ {
json_add_boolean(msg, "RX", !rig->state.cache.ptt); json_add_boolean(msg, "RX", !rig->state.cache.ptt, 1);
json_add_boolean(msg, "TX", 0); json_add_boolean(msg, "TX", 0, 0);
} }
else // we must be in reverse split else // we must be in reverse split
{ {
json_add_boolean(msg, "RX", 0); json_add_boolean(msg, "RX", 0, 1);
json_add_boolean(msg, "TX", rig->state.cache.ptt); json_add_boolean(msg, "TX", rig->state.cache.ptt, 0);
} }
} }
else if (rig->state.current_vfo && (RIG_VFO_A | RIG_VFO_MAIN_A)) else if (rig->state.current_vfo && (RIG_VFO_A | RIG_VFO_MAIN_A))
{ {
json_add_boolean(msg, "RX", !rig->state.cache.ptt); json_add_boolean(msg, "RX", !rig->state.cache.ptt, 1);
json_add_boolean(msg, "TX", rig->state.cache.ptt); json_add_boolean(msg, "TX", rig->state.cache.ptt, 0);
} }
else // VFOB must be active so never RX or TX else // VFOB must be active so never RX or TX
{ {
json_add_boolean(msg, "RX", 0); json_add_boolean(msg, "RX", 0, 1);
json_add_boolean(msg, "TX", 0); json_add_boolean(msg, "TX", 0, 0);
} }
strcat(msg, "\n}\n"); strcat(msg, "\n}");
} }
void json_add_vfoB(RIG *rig, char *msg) void json_add_vfoB(RIG *rig, char *msg)
{ {
strcat(msg, "{\n"); strcat(msg, ",\n{\n");
json_add_string(msg, "Name", "VFOB"); json_add_string(msg, "Name", "VFOB", 1);
json_add_int(msg, "Freq", rig->state.cache.freqMainB); json_add_int(msg, "Freq", rig->state.cache.freqMainB, 1);
if (strlen(rig_strrmode(rig->state.cache.modeMainB)) > 0) if (strlen(rig_strrmode(rig->state.cache.modeMainB)) > 0)
{ {
json_add_string(msg, "Mode", rig_strrmode(rig->state.cache.modeMainB)); json_add_string(msg, "Mode", rig_strrmode(rig->state.cache.modeMainB), 1);
} }
if (rig->state.cache.widthMainB > 0) if (rig->state.cache.widthMainB > 0)
{ {
json_add_int(msg, "Width", rig->state.cache.widthMainB); json_add_int(msg, "Width", rig->state.cache.widthMainB, 1);
} }
if (rig->state.rx_vfo != rig->state.tx_vfo && rig->state.cache.split) if (rig->state.rx_vfo != rig->state.tx_vfo && rig->state.cache.split)
{ {
if (rig->state.tx_vfo && (RIG_VFO_B | RIG_VFO_MAIN_B)) if (rig->state.tx_vfo && (RIG_VFO_B | RIG_VFO_MAIN_B))
{ {
json_add_boolean(msg, "RX", 0); json_add_boolean(msg, "RX", 0, 1);
json_add_boolean(msg, "TX", rig->state.cache.ptt); json_add_boolean(msg, "TX", rig->state.cache.ptt, 0);
} }
else // we must be in reverse split else // we must be in reverse split
{ {
json_add_boolean(msg, "RX", rig->state.cache.ptt); json_add_boolean(msg, "RX", rig->state.cache.ptt, 1);
json_add_boolean(msg, "TX", 0); json_add_boolean(msg, "TX", 0, 0);
} }
} }
else if (rig->state.current_vfo && (RIG_VFO_A | RIG_VFO_MAIN_A)) else if (rig->state.current_vfo && (RIG_VFO_A | RIG_VFO_MAIN_A))
{ {
json_add_boolean(msg, "RX", !rig->state.cache.ptt); json_add_boolean(msg, "RX", !rig->state.cache.ptt, 1);
json_add_boolean(msg, "TX", rig->state.cache.ptt); json_add_boolean(msg, "TX", rig->state.cache.ptt, 0);
} }
else // VFOB must be active so always RX or TX else // VFOB must be active so always RX or TX
{ {
json_add_boolean(msg, "RX", 1); json_add_boolean(msg, "RX", 1, 1);
json_add_boolean(msg, "TX", 1); json_add_boolean(msg, "TX", 1, 0);
} }
strcat(msg, "\n},\n"); strcat(msg, "\n}\n]\n");
} }
static int multicast_send_json(RIG *rig) static int multicast_send_json(RIG *rig)
@ -255,13 +257,16 @@ static int multicast_send_json(RIG *rig)
msg[0] = 0; msg[0] = 0;
snprintf(buf, sizeof(buf), "%s:%s", rig->caps->model_name, snprintf(buf, sizeof(buf), "%s:%s", rig->caps->model_name,
rig->state.rigport.pathname); rig->state.rigport.pathname);
json_add_string(msg, "ID", buf); strcat(msg, "{\n");
json_add_time(msg); json_add_string(msg, "ID", buf, 1);
json_add_int(msg, "Sequence", rig->state.multicast->seqnumber++); json_add_time(msg, 1);
json_add_string(msg, "VFOCurr", rig_strvfo(rig->state.current_vfo)); json_add_int(msg, "Sequence", rig->state.multicast->seqnumber++, 1);
json_add_int(msg, "Split", rig->state.cache.split); json_add_string(msg, "VFOCurr", rig_strvfo(rig->state.current_vfo), 1);
strcat(msg, ",\n\"VFOs\": [\n{\n"); json_add_int(msg, "Split", rig->state.cache.split, 1);
strcat(msg, "\"VFOs\": [\n");
json_add_vfoA(rig, msg); json_add_vfoA(rig, msg);
json_add_vfoB(rig, msg);
strcat(msg, "}\n");
// send the thing // send the thing
multicast_send(rig, msg, strlen(msg)); multicast_send(rig, msg, strlen(msg));
@ -322,196 +327,198 @@ void *multicast_thread(void *vrig)
#ifdef WIN32 #ifdef WIN32
static char *GetWinsockLastError(char *errorBuffer, DWORD errorBufferSize) static char *GetWinsockLastError(char *errorBuffer, DWORD errorBufferSize)
{ {
void GetWinsockErrorString(char *errorBuffer, DWORD errorBufferSize) { void GetWinsockErrorString(char *errorBuffer, DWORD errorBufferSize)
int errorCode = WSAGetLastError(); {
DWORD charsWritten; int errorCode = WSAGetLastError();
DWORD charsWritten;
FormatMessage( FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, NULL,
errorCode, errorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
errorBuffer, errorBuffer,
errorBufferSize, errorBufferSize,
NULL NULL
); );
} }
#endif #endif
int multicast_init(RIG *rig, char *addr, int port) int multicast_init(RIG * rig, char *addr, int port)
{
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{ {
char errorMessage[1024]; #ifdef _WIN32
fprintf(stderr, "WSAStartup failed: %s\n", GetWinsockLastError(errorMessage, sizeof(errorMessage))); WSADATA wsaData;
return 1;
} if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
char errorMessage[1024];
fprintf(stderr, "WSAStartup failed: %s\n", GetWinsockLastError(errorMessage,
sizeof(errorMessage)));
return 1;
}
#endif #endif
if (rig->state.multicast == NULL) if (rig->state.multicast == NULL)
{ {
rig->state.multicast = calloc(1, sizeof(struct multicast_s)); rig->state.multicast = calloc(1, sizeof(struct multicast_s));
} }
else if (rig->state.multicast->multicast_running) { return RIG_OK; } // we only need one port else if (rig->state.multicast->multicast_running) { return RIG_OK; } // we only need one port
//rig->state.multicast->mreq = {0}; //rig->state.multicast->mreq = {0};
if (addr == NULL) { addr = RIG_MULTICAST_ADDR; } if (addr == NULL) { addr = RIG_MULTICAST_ADDR; }
if (port == 0) { port = RIG_MULTICAST_PORT; } if (port == 0) { port = RIG_MULTICAST_PORT; }
// Create a UDP socket // Create a UDP socket
rig->state.multicast->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); rig->state.multicast->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (rig->state.multicast->sock < 0) if (rig->state.multicast->sock < 0)
{ {
#ifdef _WIN32 #ifdef _WIN32
int err = WSAGetLastError(); int err = WSAGetLastError();
rig_debug(RIG_DEBUG_ERR, "%s: socket: WSAGetLastError=%d\n", __func__, err); rig_debug(RIG_DEBUG_ERR, "%s: socket: WSAGetLastError=%d\n", __func__, err);
#else #else
rig_debug(RIG_DEBUG_ERR, "%s: socket: %s\n", __func__, strerror(errno)); rig_debug(RIG_DEBUG_ERR, "%s: socket: %s\n", __func__, strerror(errno));
#endif #endif
return -RIG_EIO; return -RIG_EIO;
}
// Set the SO_REUSEADDR option to allow multiple processes to use the same address
int optval = 1;
if (setsockopt(rig->state.multicast->sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&optval,
sizeof(optval)) < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
return -RIG_EIO;
}
// Bind the socket to any available local address and the specified port
struct sockaddr_in saddr = {0};
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(port);
if (bind(rig->state.multicast->sock, (struct sockaddr *)&saddr,
sizeof(saddr)) < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: bind: %s\n", __func__, strerror(errno));
return -RIG_EIO;
}
// Construct the multicast group address
// struct ip_mreq mreq = {0};
rig->state.multicast->mreq.imr_multiaddr.s_addr = inet_addr(addr);
rig->state.multicast->mreq.imr_interface.s_addr = htonl(INADDR_ANY);
// Set the multicast TTL (time-to-live) to limit the scope of the packets
char ttl = 1;
if (setsockopt(rig->state.multicast->sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
sizeof(ttl)) < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
return -RIG_EIO;
}
// Join the multicast group
if (setsockopt(rig->state.multicast->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&rig->state.multicast->mreq, sizeof(rig->state.multicast->mreq)) < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
return -RIG_EIO;
}
// prime the dest_addr for the send routine
rig->state.multicast->dest_addr.sin_family = AF_INET;
rig->state.multicast->dest_addr.sin_addr.s_addr = inet_addr(addr);
rig->state.multicast->dest_addr.sin_port = htons(port);
printf("starting thread\n");
rig->state.multicast->runflag = 1;
pthread_create(&rig->state.multicast->threadid, NULL, multicast_thread,
(void *)rig);
//printf("threadid=%ld\n", rig->state.multicast->threadid);
rig->state.multicast->multicast_running = 1;
return RIG_OK;
} }
// Set the SO_REUSEADDR option to allow multiple processes to use the same address void multicast_close(RIG * rig)
int optval = 1;
if (setsockopt(rig->state.multicast->sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&optval,
sizeof(optval)) < 0)
{ {
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno)); int retval;
return -RIG_EIO;
// Leave the multicast group
if ((retval = setsockopt(rig->state.multicast->sock, IPPROTO_IP,
IP_DROP_MEMBERSHIP, (char *)&rig->state.multicast->mreq,
sizeof(rig->state.multicast->mreq))) < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
return;
}
// Close the socket
if ((retval = close(rig->state.multicast->sock)))
{
rig_debug(RIG_DEBUG_ERR, "%s: close: %s\n", __func__, strerror(errno));
}
} }
// Bind the socket to any available local address and the specified port
struct sockaddr_in saddr = {0};
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(port);
if (bind(rig->state.multicast->sock, (struct sockaddr *)&saddr,
sizeof(saddr)) < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: bind: %s\n", __func__, strerror(errno));
return -RIG_EIO;
}
// Construct the multicast group address
// struct ip_mreq mreq = {0};
rig->state.multicast->mreq.imr_multiaddr.s_addr = inet_addr(addr);
rig->state.multicast->mreq.imr_interface.s_addr = htonl(INADDR_ANY);
// Set the multicast TTL (time-to-live) to limit the scope of the packets
char ttl = 1;
if (setsockopt(rig->state.multicast->sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
sizeof(ttl)) < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
return -RIG_EIO;
}
// Join the multicast group
if (setsockopt(rig->state.multicast->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&rig->state.multicast->mreq, sizeof(rig->state.multicast->mreq)) < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
return -RIG_EIO;
}
// prime the dest_addr for the send routine
rig->state.multicast->dest_addr.sin_family = AF_INET;
rig->state.multicast->dest_addr.sin_addr.s_addr = inet_addr(addr);
rig->state.multicast->dest_addr.sin_port = htons(port);
printf("starting thread\n");
rig->state.multicast->runflag = 1;
pthread_create(&rig->state.multicast->threadid, NULL, multicast_thread,
(void *)rig);
//printf("threadid=%ld\n", rig->state.multicast->threadid);
rig->state.multicast->multicast_running = 1;
return RIG_OK;
}
void multicast_close(RIG *rig)
{
int retval;
// Leave the multicast group
if ((retval = setsockopt(rig->state.multicast->sock, IPPROTO_IP,
IP_DROP_MEMBERSHIP, (char *)&rig->state.multicast->mreq,
sizeof(rig->state.multicast->mreq))) < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
return;
}
// Close the socket
if ((retval = close(rig->state.multicast->sock)))
{
rig_debug(RIG_DEBUG_ERR, "%s: close: %s\n", __func__, strerror(errno));
}
}
// if msglen=0 msg is assumed to be a string // if msglen=0 msg is assumed to be a string
int multicast_send(RIG *rig, const char *msg, int msglen) int multicast_send(RIG * rig, const char *msg, int msglen)
{
// Construct the message to send
if (msglen == 0) { msglen = strlen((char *)msg); }
// Send the message to the multicast group
ssize_t num_bytes = sendto(rig->state.multicast->sock, msg, msglen, 0,
(struct sockaddr *)&rig->state.multicast->dest_addr,
sizeof(rig->state.multicast->dest_addr));
if (num_bytes < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: sendto: %s\n", __func__, strerror(errno));
return -RIG_EIO;
}
else
{ {
// Construct the message to send
if (msglen == 0) { msglen = strlen((char *)msg); }
// Send the message to the multicast group
ssize_t num_bytes = sendto(rig->state.multicast->sock, msg, msglen, 0,
(struct sockaddr *)&rig->state.multicast->dest_addr,
sizeof(rig->state.multicast->dest_addr));
if (num_bytes < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: sendto: %s\n", __func__, strerror(errno));
return -RIG_EIO;
}
else
{
// printf("Sent %zd bytes to multicast group %s:%d\n", num_bytes, MULTICAST_ADDR, // printf("Sent %zd bytes to multicast group %s:%d\n", num_bytes, MULTICAST_ADDR,
// PORT); // PORT);
} }
return num_bytes; return num_bytes;
} }
//#define TEST //#define TEST
#ifdef TEST #ifdef TEST
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{
RIG *rig;
rig_model_t myrig_model;
rig_set_debug_level(RIG_DEBUG_NONE);
if (argc > 1) { myrig_model = atoi(argv[1]); }
else
{ {
myrig_model = 1035; RIG *rig;
rig_model_t myrig_model;
rig_set_debug_level(RIG_DEBUG_NONE);
if (argc > 1) { myrig_model = atoi(argv[1]); }
else
{
myrig_model = 1035;
}
rig = rig_init(myrig_model);
if (rig == NULL)
{
}
strncpy(rig->state.rigport.pathname, "/dev/ttyUSB0", HAMLIB_FILPATHLEN - 1);
rig->state.rigport.parm.serial.rate = 38400;
rig_open(rig);
multicast_init(rig, "224.0.0.1", 4532);
pthread_join(rig->state.multicast->threadid, NULL);
pthread_exit(NULL);
return 0;
} }
rig = rig_init(myrig_model);
if (rig == NULL)
{
}
strncpy(rig->state.rigport.pathname, "/dev/ttyUSB0", HAMLIB_FILPATHLEN - 1);
rig->state.rigport.parm.serial.rate = 38400;
rig_open(rig);
multicast_init(rig, "224.0.0.1", 4532);
pthread_join(rig->state.multicast->threadid, NULL);
pthread_exit(NULL);
return 0;
}
#endif #endif

Wyświetl plik

@ -90,7 +90,7 @@ int main()
} }
buffer[bytes_received] = '\0'; buffer[bytes_received] = '\0';
printf("Received: %s\n", buffer); printf("%s\n", buffer);
} }
// Drop membership before closing the socket // Drop membership before closing the socket