Initial cut of relay

This commit is contained in:
Jordan Sherer 2018-09-08 23:36:12 -04:00
parent 308a45efe0
commit 15ee95360f
3 changed files with 117 additions and 36 deletions

View File

@ -7,6 +7,7 @@
#include <fstream>
#include <iterator>
#include <fftw3.h>
#include <QInputDialog>
#include <QLineEdit>
#include <QRegExpValidator>
#include <QRegExp>
@ -9349,6 +9350,7 @@ void MainWindow::processCommandActivity() {
if (d.cmd == "?") {
reply = QString("%1 SNR %2").arg(d.from).arg(Varicode::formatSNR(d.snr));
}
// QUERIED QTH
else if (d.cmd == "@" && !isAllCall) {
QString qth = m_config.my_qth();
@ -9358,6 +9360,7 @@ void MainWindow::processCommandActivity() {
reply = QString("%1 QTH %2").arg(d.from).arg(qth);
}
// QUERIED GRID
else if (d.cmd == "^" && !isAllCall) {
QString grid = m_config.my_grid();
@ -9367,10 +9370,12 @@ void MainWindow::processCommandActivity() {
reply = QString("%1 GRID %2").arg(d.from).arg(grid);
}
// QUERIED STATION MESSAGE
else if (d.cmd == "&" && !isAllCall) {
reply = QString("%1 QTC %2").arg(d.from).arg(m_config.my_station());
}
// QUERIED STATIONS HEARD
else if (d.cmd == "$" && !isAllCall) {
int i = 0;
@ -9406,14 +9411,62 @@ void MainWindow::processCommandActivity() {
lines.prepend(QString("<%1 HEARING>").arg(m_config.my_callsign()));
reply = lines.join('\n');
}
// PROCESS RETRANSMIT
else if (d.cmd == "|" && !isAllCall) {
// TODO: jsherer - perhaps parse d.text and ensure it is a valid message as well as prefix it with our call...
reply = QString("%1 ACK\n%2 DE %1").arg(d.from).arg(d.text);
}
// PROCESS RELAY
else if (d.cmd == ">" && !isAllCall) {
// 1. see if there are any more hops to process
// 2. if so, forward
// 3. otherwise, display alert
QString callToPattern = {R"((?<callsign>\b(?<prefix>[A-Z0-9]{1,4}\/)?(?<base>([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?<suffix>\/[A-Z0-9]{1,4})?[>])\b)"};
QRegularExpression re(callToPattern);
auto match = re.match(d.text);
// if the text starts with a callsign, relay.
if(match.hasMatch()){
reply = QString("%1 DE %2").arg(d.text).arg(d.from);
// otherwise, as long as we're not an ACK...alert the user and either send an ACK or Message
} else if(!d.text.startsWith("ACK DE")) {
QStringList calls;
QString callDePattern = {R"(\sDE\s(?<callsign>\b(?<prefix>[A-Z0-9]{1,4}\/)?(?<base>([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?<suffix>\/[A-Z0-9]{1,4})?)\b)"};
QRegularExpression re(callDePattern);
auto iter = re.globalMatch(d.text);
while(iter.hasNext()){
auto match = iter.next();
calls.prepend(match.captured("callsign"));
}
calls.prepend(d.from);
processAlertReplyForCommand(d, calls.join('>'), ">");
//reply = QString("%1>ACK").arg(calls.join('>'));
//bool ok = false;
//reply = QString("%1>%2").arg(calls.join('>')).arg(ok && !text.isEmpty() ? text : "ACK");
}
#if 0
KN4CRD: W0FW>N0JDS>OH8STN> Hello Julian...
W0FW: N0JDS>OH8STN> Hello Julian... <KN4CRD
N0JDS: OH8STN> Hello Julian... <KN4CRD<W0FW
OH8STN: N0JDS>W0FW>KN4CRD> ACK
#endif
}
// PROCESS BUFFERED MESSAGE
else if (d.cmd == "#" && !isAllCall) {
// open file /save/messages/[callsign].txt and append a message log entry...
QFile f(QDir::toNativeSeparators(m_config.writeable_data_dir ().absolutePath()) + QString("/save/messages/%1.txt").arg(Radio::base_callsign(d.from)));
if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
@ -9431,10 +9484,12 @@ void MainWindow::processCommandActivity() {
reply = QString("%1 ACK").arg(d.from);
}
// PROCESS AGN
else if (d.cmd == " AGN?" && !isAllCall && !m_lastTxMessage.isEmpty()) {
reply = m_lastTxMessage;
}
// PROCESS BUFFERED QSO QUERY
else if (d.cmd == " QSO"){
auto who = d.text;
@ -9462,6 +9517,15 @@ void MainWindow::processCommandActivity() {
}
reply = replies.join("\n");
}
// PROCESS BUFFERED APRS:
else if(d.cmd == " APRS:" && m_config.spot_to_reporting_networks() && m_aprsClient->isPasscodeValid()){
m_aprsClient->enqueueThirdParty(Radio::base_callsign(d.from), d.text);
// make sure this is explicit
continue;
}
// PROCESS BUFFERED QTH
else if (d.cmd == " GRID"){
// 1. parse grids
@ -9478,42 +9542,15 @@ void MainWindow::processCommandActivity() {
logCallActivity(cd, true);
}
// make sure this is explicit
continue;
}
// PROCESS APRS
else if(d.cmd == " APRS:" && m_config.spot_to_reporting_networks() && m_aprsClient->isPasscodeValid()){
m_aprsClient->enqueueThirdParty(Radio::base_callsign(d.from), d.text);
reply = QString("%1 ACK").arg(d.from);
}
// PROCESS ALERT
else if (d.cmd == "!" && !isAllCall) {
QMessageBox * msgBox = new QMessageBox(this);
msgBox->setIcon(QMessageBox::Information);
auto header = QString("Message from %3 at %1 (%2):");
header = header.arg(d.utcTimestamp.time().toString());
header = header.arg(d.freq);
header = header.arg(d.from);
msgBox->setText(header);
msgBox->setInformativeText(d.text);
auto ab = msgBox->addButton("ACK", QMessageBox::AcceptRole);
auto db = msgBox->addButton("Discard", QMessageBox::NoRole);
connect(msgBox, & QMessageBox::buttonClicked, this, [this, d, db, ab](QAbstractButton * btn) {
if (btn != ab) {
return;
}
enqueueMessage(PriorityHigh, QString("%1 ACK").arg(d.from), d.freq, nullptr);
});
auto wav = m_config.sound_am_path();
if(!wav.isEmpty()){
QSound::play(wav);
}
msgBox->show();
// create alert dialog
processAlertReplyForCommand(d, d.from, " ");
// make sure this is explicit
continue;
@ -9536,6 +9573,48 @@ void MainWindow::processCommandActivity() {
}
}
void MainWindow::processAlertReplyForCommand(CommandDetail d, QString from, QString cmd){
QMessageBox * msgBox = new QMessageBox(this);
msgBox->setIcon(QMessageBox::Information);
auto header = QString("Message from %3 at %1 (%2):");
header = header.arg(d.utcTimestamp.time().toString());
header = header.arg(d.freq);
header = header.arg(d.from);
msgBox->setText(header);
msgBox->setInformativeText(d.text);
auto ab = msgBox->addButton("ACK", QMessageBox::AcceptRole);
auto rb = msgBox->addButton("Reply", QMessageBox::AcceptRole);
auto db = msgBox->addButton("Discard", QMessageBox::NoRole);
connect(msgBox, & QMessageBox::buttonClicked, this, [this, cmd, from, d, db, rb, ab](QAbstractButton * btn) {
if (btn == db) {
return;
}
if (btn == ab){
enqueueMessage(PriorityHigh, QString("%1%2ACK").arg(from).arg(cmd), d.freq, nullptr);
}
if(btn == rb){
bool ok = false;
QString text = QInputDialog::getMultiLineText(this, "Message Reply", QString("Message to send to %1:").arg(from), "", &ok);
if(ok && !text.isEmpty()){
enqueueMessage(PriorityHigh, QString("%1%2%3").arg(from).arg(cmd).arg(text), d.freq, nullptr);
}
}
});
auto wav = m_config.sound_am_path();
if(!wav.isEmpty()){
QSound::play(wav);
}
msgBox->show();
}
void MainWindow::processSpots() {
if(!ui->spotButton->isChecked()){
m_rxCallQueue.clear();

View File

@ -872,6 +872,7 @@ private:
void processCompoundActivity();
void processBufferedActivity();
void processCommandActivity();
void processAlertReplyForCommand(CommandDetail d, QString from, QString cmd);
void processSpots();
void processTxQueue();
void displayActivity(bool force=false);

View File

@ -45,7 +45,7 @@ QMap<QString, int> directed_cmds = {
{"&", 2 }, // query station message
{"$", 3 }, // query station(s) heard
{"^", 4 }, // query grid
// {"%" 5 }, // unused
{">", 5 }, // relay message
{"|", 6 }, // retransmit message
{"!", 7 }, // alert message
{"#", 8 }, // all or nothing message
@ -78,11 +78,12 @@ QMap<QString, int> directed_cmds = {
{" ", 31 }, // send freetext
};
QSet<int> allowed_cmds = {0, 1, 2, 3, 4, /*5,*/ 6, 7, 8, /*...*/ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, /*24,*/ 25, 26, 27, 28, 29, 30, 31};
QSet<int> allowed_cmds = {0, 1, 2, 3, 4, 5, 6, 7, 8, /*...*/ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, /*24,*/ 25, 26, 27, 28, 29, 30, 31};
QSet<int> buffered_cmds = {6, 7, 8, 13, 14, 15};
QSet<int> buffered_cmds = {5, 6, 7, 8, 13, 14, 15};
QMap<int, int> checksum_cmds = {
{ 5, 16 },
{ 6, 16 },
{ 7, 16 },
{ 8, 32 },
@ -92,7 +93,7 @@ QMap<int, int> checksum_cmds = {
};
QString callsign_pattern = QString("(?<callsign>[A-Z0-9/]+)");
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|ACK|73|YES|NO|SNR|QSL[?]?|RR|HEARING|HW CPY[?]|FB|QTH|QTC|GRID|APRS[:]|QSO|[?@&$%|!#^ ]))?");
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|ACK|73|YES|NO|SNR|QSL[?]?|RR|HEARING|HW CPY[?]|FB|QTH|QTC|GRID|APRS[:]|QSO|[?@&$%|!#^> ]))?");
QString optional_grid_pattern = QString("(?<grid>\\s?[A-R]{2}[0-9]{2})?");
QString optional_extended_grid_pattern = QString("^(?<grid>\\s?(?:[A-R]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*))?");
QString optional_num_pattern = QString("(?<num>(?<=SNR|HEARING)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?");