More complete monitoring messages to AGW client app.

This commit is contained in:
wb2osz 2020-04-13 21:07:10 -04:00
parent c15903edb9
commit 65d8d265cd
3 changed files with 172 additions and 51 deletions

View File

@ -1,7 +1,7 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017 John Langner, WB2OSZ
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2020 John Langner, WB2OSZ
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -45,9 +45,9 @@
* 'k' Ask to start receiving RAW AX25 frames.
*
* 'm' Ask to start receiving Monitor AX25 frames.
* Enables sending of U, I, S, and T messages to client app.
*
* 'V' Transmit UI data frame.
* Generate audio for transmission.
*
* 'H' Report recently heard stations. Not implemented yet.
*
@ -89,7 +89,16 @@
* 'K' Received AX.25 frame in raw format.
* (Enabled with 'k' command.)
*
* 'U' Received AX.25 frame in monitor format.
* 'U' Received AX.25 "UI" frames in monitor format.
* (Enabled with 'm' command.)
*
* 'I' Received AX.25 "I" frames in monitor format. (new in 1.6)
* (Enabled with 'm' command.)
*
* 'S' Received AX.25 "S" and "U" (other than UI) frames in monitor format. (new in 1.6)
* (Enabled with 'm' command.)
*
* 'T' Own Transmitted AX.25 frames in monitor format. (new in 1.6)
* (Enabled with 'm' command.)
*
* 'y' Outstanding frames waiting on a Port (new in 1.2)
@ -768,10 +777,13 @@ static THREAD_F connect_listen_thread (void *arg)
*
* There are two different formats:
* RAW - the original received frame.
* MONITOR - just the information part.
* MONITOR - human readable monitoring format.
*
*--------------------------------------------------------------------*/
static void mon_addrs (int chan, packet_t pp, char *result, int result_size);
static char mon_desc (packet_t pp, char *result, int result_size);
void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int flen)
{
@ -781,15 +793,11 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
} agwpe_msg;
int err;
int info_len;
unsigned char *pinfo;
int client;
/*
* RAW format
*/
for (client=0; client<MAX_NET_CLIENTS; client++) {
for (int client=0; client<MAX_NET_CLIENTS; client++) {
if (enable_send_raw_to_client[client] && client_sock[client] > 0){
@ -839,33 +847,38 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
}
}
// Application might want more human readable format.
/* MONITOR format - only for UI frames. */
server_send_monitored (chan, pp, 0);
for (client=0; client<MAX_NET_CLIENTS; client++) {
} /* end server_send_rec_packet */
if (enable_send_monitor_to_client[client] && client_sock[client] > 0
&& ax25_get_control(pp) == AX25_UI_FRAME){
time_t clock;
struct tm *tm;
int num_digi;
clock = time(NULL);
tm = localtime(&clock); // TODO: should use localtime_r
void server_send_monitored (int chan, packet_t pp, int own_xmit)
{
/*
* MONITOR format - 'I' for information frames.
* 'U' for unnumbered information.
* 'S' for supervisory and other unnumbered.
*/
struct {
struct agwpe_s hdr;
char data[1+AX25_MAX_PACKET_LEN];
} agwpe_msg;
int err;
for (int client=0; client<MAX_NET_CLIENTS; client++) {
if (enable_send_monitor_to_client[client] && client_sock[client] > 0) {
memset (&agwpe_msg.hdr, 0, sizeof(agwpe_msg.hdr));
agwpe_msg.hdr.portx = chan;
agwpe_msg.hdr.datakind = 'U';
agwpe_msg.hdr.portx = chan; // datakind is added later.
ax25_get_addr_with_ssid (pp, AX25_SOURCE, agwpe_msg.hdr.call_from);
ax25_get_addr_with_ssid (pp, AX25_DESTINATION, agwpe_msg.hdr.call_to);
info_len = ax25_get_info (pp, &pinfo);
/* http://uz7ho.org.ua/includes/agwpeapi.htm#_Toc500723812 */
/* Description mentions one CR character after timestamp but example has two. */
@ -883,34 +896,34 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
// AGWPE:
// [AGWE-IN] 1:Fm ZL4FOX-8 To Q7P2U2 Via WIDE3-3 [08:32:14]`I0*l V>/"98}[:Barts Tracker 3.83V X
num_digi = ax25_get_num_repeaters(pp);
// Format the channel and addresses, with leading and trailing space.
if (num_digi > 0) {
mon_addrs (chan, pp, (char*)(agwpe_msg.data), sizeof(agwpe_msg.data));
char via[AX25_MAX_REPEATERS*(AX25_MAX_ADDR_LEN+1)];
char stemp[AX25_MAX_ADDR_LEN+1];
int j;
// Add the description with <... >
ax25_get_addr_with_ssid (pp, AX25_REPEATER_1, via);
for (j = 1; j < num_digi; j++) {
ax25_get_addr_with_ssid (pp, AX25_REPEATER_1 + j, stemp);
strlcat (via, ",", sizeof(via));
strlcat (via, stemp, sizeof(via));
}
snprintf (agwpe_msg.data, sizeof(agwpe_msg.data), " %d:Fm %s To %s Via %s <UI pid=%02X Len=%d >[%02d:%02d:%02d]\r%s\r\r",
chan+1, agwpe_msg.hdr.call_from, agwpe_msg.hdr.call_to, via,
ax25_get_pid(pp), info_len,
tm->tm_hour, tm->tm_min, tm->tm_sec,
pinfo);
char desc[80];
agwpe_msg.hdr.datakind = mon_desc (pp, desc, sizeof(desc));
if (own_xmit) {
agwpe_msg.hdr.datakind = 'T';
}
else {
strlcat ((char*)(agwpe_msg.data), desc, sizeof(agwpe_msg.data));
snprintf (agwpe_msg.data, sizeof(agwpe_msg.data), " %d:Fm %s To %s <UI pid=%02X Len=%d >[%02d:%02d:%02d]\r%s\r\r",
chan+1, agwpe_msg.hdr.call_from, agwpe_msg.hdr.call_to,
ax25_get_pid(pp), info_len,
tm->tm_hour, tm->tm_min, tm->tm_sec,
pinfo);
// Timestamp with [...]\r
time_t clock = time(NULL);
struct tm *tm = localtime(&clock); // TODO: use localtime_r ?
char ts[32];
snprintf (ts, sizeof(ts), "[%02d:%02d:%02d]\r", tm->tm_hour, tm->tm_min, tm->tm_sec);
strlcat ((char*)(agwpe_msg.data), ts, sizeof(agwpe_msg.data));
// Information if any with \r\r.
unsigned char *pinfo = NULL;
int info_len = ax25_get_info (pp, &pinfo);
if (info_len > 0 && pinfo != NULL) {
strlcat ((char*)(agwpe_msg.data), (char*)pinfo, sizeof(agwpe_msg.data));
strlcat ((char*)(agwpe_msg.data), "\r", sizeof(agwpe_msg.data));
}
agwpe_msg.hdr.data_len_NETLE = host2netle(strlen(agwpe_msg.data) + 1) /* +1 to include terminating null */ ;
@ -944,9 +957,103 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
}
}
} /* server_send_rec_packet */
} /* server_send_monitored */
// Next two are broken out in case they can be reused elsewhere.
// Format addresses in AGWPR monitoring format such as:
// 1:Fm ZL4FOX-8 To Q7P2U2 Via WIDE3-3
static void mon_addrs (int chan, packet_t pp, char *result, int result_size)
{
char src[AX25_MAX_ADDR_LEN];
char dst[AX25_MAX_ADDR_LEN];
ax25_get_addr_with_ssid (pp, AX25_SOURCE, src);
ax25_get_addr_with_ssid (pp, AX25_DESTINATION, dst);
int num_digi = ax25_get_num_repeaters(pp);
if (num_digi > 0) {
char via[AX25_MAX_REPEATERS*(AX25_MAX_ADDR_LEN+1)];
char stemp[AX25_MAX_ADDR_LEN+1];
int j;
ax25_get_addr_with_ssid (pp, AX25_REPEATER_1, via);
for (j = 1; j < num_digi; j++) {
ax25_get_addr_with_ssid (pp, AX25_REPEATER_1 + j, stemp);
strlcat (via, ",", sizeof(via));
strlcat (via, stemp, sizeof(via));
}
snprintf (result, result_size, " %d:Fm %s To %s Via %s ",
chan+1, src, dst, via);
}
else {
snprintf (result, result_size, " %d:Fm %s To %s ",
chan+1, src, dst);
}
}
// Generate frame description in AGWPE monitoring format such as
// <UI pid=F0 Len=123 >
// <I R1 S3 pid=F0 Len=123 >
// <RR P1 R5 >
//
// Returns:
// 'I' for information frame.
// 'U' for unnumbered information frame.
// 'S' for supervisory and other unnumbered frames.
static char mon_desc (packet_t pp, char *result, int result_size)
{
cmdres_t cr; // command/response.
char ignore[80]; // direwolf description. not used here.
int pf; // poll/final bit.
int ns; // N(S) Send sequence number.
int nr; // N(R) Received sequence number.
char pf_text[4]; // P or F depending on whether command or response.
ax25_frame_type_t ftype = ax25_frame_type (pp, &cr, ignore, &pf, &nr, &ns);
switch (cr) {
case cr_cmd: strcpy(pf_text, "P"); break; // P only: I, SABME, SABM, DISC
case cr_res: strcpy(pf_text, "F"); break; // F only: DM, UA, FRMR
// Either: RR, RNR, REJ, SREJ, UI, XID, TEST
default: strcpy(pf_text, "PF"); break; // Not AX.25 version >= 2.0
// APRS is often sloppy about this but it
// is essential for connected mode.
}
unsigned char *pinfo = NULL; // I, UI, XID, SREJ, TEST can have information part.
int info_len = ax25_get_info (pp, &pinfo);
switch (ftype) {
case frame_type_I: snprintf (result, result_size, "<I S%d R%d pid=0x%02X Len=%d %s=%d >", ns, nr, ax25_get_pid(pp), info_len, pf_text, pf); return ('I');
case frame_type_U_UI: snprintf (result, result_size, "<UI pid=%02X Len=%d %s=%d >", ax25_get_pid(pp), info_len, pf_text, pf); return ('U'); break;
case frame_type_S_RR: snprintf (result, result_size, "<RR R%d %s=%d >", nr, pf_text, pf); return ('S'); break;
case frame_type_S_RNR: snprintf (result, result_size, "<RNR R%d %s=%d >", nr, pf_text, pf); return ('S'); break;
case frame_type_S_REJ: snprintf (result, result_size, "<REJ R%d %s=%d >", nr, pf_text, pf); return ('S'); break;
case frame_type_S_SREJ: snprintf (result, result_size, "<SREJ R%d %s=%d Len=%d >", nr, pf_text, pf, info_len); return ('S'); break;
case frame_type_U_SABME: snprintf (result, result_size, "<SABME %s=%d >", pf_text, pf); return ('S'); break;
case frame_type_U_SABM: snprintf (result, result_size, "<SABM %s=%d >", pf_text, pf); return ('S'); break;
case frame_type_U_DISC: snprintf (result, result_size, "<DISC %s=%d >", pf_text, pf); return ('S'); break;
case frame_type_U_DM: snprintf (result, result_size, "<DM %s=%d >", pf_text, pf); return ('S'); break;
case frame_type_U_UA: snprintf (result, result_size, "<UA %s=%d >", pf_text, pf); return ('S'); break;
case frame_type_U_FRMR: snprintf (result, result_size, "<FRMR %s=%d >", pf_text, pf); return ('S'); break;
case frame_type_U_XID: snprintf (result, result_size, "<XID %s=%d Len=%d >", pf_text, pf, info_len); return ('S'); break;
case frame_type_U_TEST: snprintf (result, result_size, "<TEST %s=%d Len=%d >", pf_text, pf, info_len); return ('S'); break;
default:
case frame_type_U: snprintf (result, result_size, "<U other??? >"); return ('S'); break;
}
}
/*-------------------------------------------------------------------
*

View File

@ -15,6 +15,8 @@ void server_init (struct audio_s *audio_config_p, struct misc_config_s *misc_con
void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int flen);
void server_send_monitored (int chan, packet_t pp, int own_xmit);
int server_callsign_registered_by_client (char *callsign);

View File

@ -76,7 +76,7 @@
#include "dtmf.h"
#include "xid.h"
#include "dlq.h"
#include "server.h"
/*
@ -1002,7 +1002,14 @@ static int send_one_frame (int c, int p, packet_t pp)
ax25_format_addrs (pp, stemp);
info_len = ax25_get_info (pp, &pinfo);
text_color_set(DW_COLOR_XMIT);
#if 0
dw_printf ("[%d%c%s%s] ", c,
p==TQ_PRIO_0_HI ? 'H' : 'L',
save_audio_config_p->fx25_xmit_enable ? "F" : "",
ts);
#else
dw_printf ("[%d%c%s] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L', ts);
#endif
dw_printf ("%s", stemp); /* stations followed by : */
/* Demystify non-APRS. Use same format for received frames in direwolf.c. */
@ -1068,6 +1075,11 @@ static int send_one_frame (int c, int p, packet_t pp)
}
nb = hdlc_send_frame (c, fbuf, flen, send_invalid_fcs2, save_audio_config_p->fx25_xmit_enable);
// Optionally send confirmation to AGW client app if monitoring enabled.
server_send_monitored (c, pp, 1);
return (nb);
} /* end send_one_frame */