From 15ee95360f1d0a80ce6eb60836b29da33430a474 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Sat, 8 Sep 2018 23:36:12 -0400 Subject: [PATCH] Initial cut of relay --- mainwindow.cpp | 143 ++++++++++++++++++++++++++++++++++++++----------- mainwindow.h | 1 + varicode.cpp | 9 ++-- 3 files changed, 117 insertions(+), 36 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 79422f1..3ef5f18 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -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"((?\b(?[A-Z0-9]{1,4}\/)?(?([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?\/[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(?\b(?[A-Z0-9]{1,4}\/)?(?([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?\/[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... Hello Julian... 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(); diff --git a/mainwindow.h b/mainwindow.h index bb4218a..5898518 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -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); diff --git a/varicode.cpp b/varicode.cpp index 14366ee..340a0a1 100644 --- a/varicode.cpp +++ b/varicode.cpp @@ -45,7 +45,7 @@ QMap 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 directed_cmds = { {" ", 31 }, // send freetext }; -QSet 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 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 buffered_cmds = {6, 7, 8, 13, 14, 15}; +QSet buffered_cmds = {5, 6, 7, 8, 13, 14, 15}; QMap checksum_cmds = { + { 5, 16 }, { 6, 16 }, { 7, 16 }, { 8, 32 }, @@ -92,7 +93,7 @@ QMap checksum_cmds = { }; QString callsign_pattern = QString("(?[A-Z0-9/]+)"); -QString optional_cmd_pattern = QString("(?\\s?(?:AGN[?]|ACK|73|YES|NO|SNR|QSL[?]?|RR|HEARING|HW CPY[?]|FB|QTH|QTC|GRID|APRS[:]|QSO|[?@&$%|!#^ ]))?"); +QString optional_cmd_pattern = QString("(?\\s?(?:AGN[?]|ACK|73|YES|NO|SNR|QSL[?]?|RR|HEARING|HW CPY[?]|FB|QTH|QTC|GRID|APRS[:]|QSO|[?@&$%|!#^> ]))?"); QString optional_grid_pattern = QString("(?\\s?[A-R]{2}[0-9]{2})?"); QString optional_extended_grid_pattern = QString("^(?\\s?(?:[A-R]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*))?"); QString optional_num_pattern = QString("(?(?<=SNR|HEARING)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?");