Continued refactoring of command processing

This commit is contained in:
Jordan Sherer 2018-07-31 14:34:36 -04:00
parent 0a7c4a68de
commit f0de2f2ba1
2 changed files with 540 additions and 442 deletions

View File

@ -222,6 +222,7 @@ namespace
}
}
#if 0
int round(int numToRound, int multiple)
{
if(multiple == 0)
@ -237,6 +238,7 @@ namespace
return roundDown;
}
#endif
int roundUp(int numToRound, int multiple)
{
@ -1121,6 +1123,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
QMenu * menu = new QMenu(ui->tableWidgetCalls);
QString selectedCall = callsignSelected();
bool isAllCall = isAllCallIncluded(selectedCall);
bool missingCallsign = selectedCall.isEmpty();
if(!missingCallsign && m_callActivity.contains(selectedCall)){
setFreqForRestore(m_callActivity[selectedCall].freq, true);
@ -1132,10 +1135,9 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
menu->addSeparator();
removeStation->setDisabled(missingCallsign || callsignSelected() == "ALLCALL");
removeStation->setDisabled(missingCallsign || isAllCall);
menu->addAction(removeStation);
menu->addSeparator();
menu->addAction(clearAction4);
menu->addAction(clearActionAll);
@ -4413,15 +4415,19 @@ void MainWindow::guiUpdate()
m_sec0=nsec;
// once per period
if(m_sec0 % m_TRperiod == 0){
// force rx dirty at least once pre period
m_rxDirty = true;
}
// once per second
// update the dial frequency once per second..
displayDialFrequency();
// process all received activity...
processActivity();
// once processed, lets update the display...
displayActivity();
}
@ -5855,6 +5861,9 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){
// once we find a directed call, data encode the rest of the line.
bool hasDirected = false;
// do the same for when we have sent data...
bool hasData = false;
// remove our callsign from the start of the line...
if(line.startsWith(mycall + ":")){
line = lstrip(line.mid(mycall.length() + 1));
@ -5889,21 +5898,22 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){
// if this parses to a standard FT8 free text message
// but it can be parsed as a directed message, then we
// should send the directed version. if we've already sent
// a directed message, we won't send any more but instead
// send it as a data message
// a directed message or a data frame, we will only follow it
// with more data frames.
if(isFree && !hasDirected && l > 0){
if(isFree && !hasDirected && !hasData && l > 0){
useBcn = true;
hasDirected = false;
frame = bcnFrame;
}
else if(isFree && !hasDirected && n > 0){
else if(isFree && !hasDirected && !hasData && n > 0){
useDir = true;
hasDirected = true;
frame = dirFrame;
}
else if ((isFree || hasDirected) && m > 0) {
useDat = true;
hasData = true;
frame = datFrame;
if(!datLineOut.isEmpty()){
line = datLineOut;
@ -7363,7 +7373,7 @@ void MainWindow::buildQueryMenu(QMenu * menu){
return;
}
bool isAllCall = call == "ALLCALL";
bool isAllCall = isAllCallIncluded(call);
auto sendReplyAction = menu->addAction("CALL - Send a message to selected callsign");
@ -8690,185 +8700,38 @@ bool MainWindow::isMyCallIncluded(const QString &text){
return text.contains(myCall);
}
bool MainWindow::isAllCallIncluded(const QString &text){
return text.contains("ALLCALL");
}
void MainWindow::displayActivity(bool force){
void MainWindow::processActivity(bool force) {
if (!m_rxDirty && !force) {
return;
}
// Is it ok to post spots to PSKReporter?
int nsec=QDateTime::currentMSecsSinceEpoch()/1000-m_secBandChanged;
bool okToPost=(nsec>(4*m_TRperiod)/5);
// Recent Rx Activity
processRxActivity();
// Selected Rows
int selectedOffset = -1;
auto selectedItems = ui->tableWidgetRXAll->selectedItems();
if(!selectedItems.isEmpty()){
selectedOffset = selectedItems.first()->text().toInt();
// Grouped Compound Activity
processCompoundActivity();
// Buffered Activity
processBufferedActivity();
// Command Activity
processCommandActivity();
// Process PSKReporter Spots
processSpots();
m_rxDirty = false;
}
// Selected callsign
QString selectedCall = callsignSelected();
// Band Activity
auto now = QDateTime::currentDateTimeUtc();
clearTableWidget(ui->tableWidgetRXAll);
QList<int> keys = m_bandActivity.keys();
// sort directed & recent messages first
qSort(keys.begin(), keys.end(), [this](const int left, int right){
if(m_rxDirectedCache.contains(left/10*10)){
return true;
}
if(m_rxDirectedCache.contains(right/10*10)){
return false;
}
if(m_rxRecentCache.contains(left/10*10)){
return true;
}
if(m_rxRecentCache.contains(right/10*10)){
return false;
}
return left < right;
});
foreach (int offset, keys) {
QList<ActivityDetail> items = m_bandActivity[offset];
if(items.length() > 0){
QStringList text;
QString age;
int snr = 0;
int activityAging = m_config.activity_aging();
foreach(ActivityDetail item, items){
if(activityAging && item.utcTimestamp.secsTo(now)/60 >= activityAging){
continue;
}
if(item.text.isEmpty()){
continue;
}
if(item.isLowConfidence){
item.text = QString("[%1]").arg(item.text);
}
if(item.bits == Varicode::FT8CallLast){
// can also use \u0004 \u2666 \u2404
item.text = QString("%1 \u2301 ").arg(item.text);
}
text.append(item.text);
snr = item.snr;
age = since(item.utcTimestamp);
void MainWindow::processRxActivity() {
if(m_rxFrameQueue.isEmpty()){
return;
}
auto joined = text.join(" ");
if(joined.isEmpty()){
continue;
}
ui->tableWidgetRXAll->insertRow(ui->tableWidgetRXAll->rowCount());
auto offsetItem = new QTableWidgetItem(QString("%1").arg(offset));
offsetItem->setData(Qt::UserRole, QVariant(offset));
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 0, offsetItem);
auto ageItem = new QTableWidgetItem(QString("(%1)").arg(age));
ageItem->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 1, ageItem);
auto snrItem = new QTableWidgetItem(QString("%1").arg(Varicode::formatSNR(snr)));
snrItem->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 2, snrItem);
// align right if eliding...
int colWidth = ui->tableWidgetRXAll->columnWidth(3);
auto textItem = new QTableWidgetItem(joined);
QFontMetrics fm(textItem->font());
auto elidedText = fm.elidedText(joined, Qt::ElideLeft, colWidth);
auto flag = Qt::AlignLeft|Qt::AlignVCenter;
if(elidedText != joined){
flag = Qt::AlignRight|Qt::AlignVCenter;
textItem->setText(joined);
}
textItem->setTextAlignment(flag);
if (text.last().contains(QRegularExpression {"^(CQ|QRZ|DE)\\s"})){
offsetItem->setBackground(QBrush(m_config.color_CQ()));
ageItem->setBackground(QBrush(m_config.color_CQ()));
snrItem->setBackground(QBrush(m_config.color_CQ()));
textItem->setBackground(QBrush(m_config.color_CQ()));
}
if(m_rxDirectedCache.contains(offset/10*10)){
offsetItem->setBackground(QBrush(m_config.color_MyCall()));
ageItem->setBackground(QBrush(m_config.color_MyCall()));
snrItem->setBackground(QBrush(m_config.color_MyCall()));
textItem->setBackground(QBrush(m_config.color_MyCall()));
}
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 3, textItem);
if(offset == selectedOffset){
ui->tableWidgetRXAll->selectRow(ui->tableWidgetRXAll->rowCount() - 1);
}
}
}
ui->tableWidgetRXAll->resizeColumnToContents(0);
ui->tableWidgetRXAll->resizeColumnToContents(1);
ui->tableWidgetRXAll->resizeColumnToContents(2);
// Call Activity
clearTableWidget(ui->tableWidgetCalls);
ui->tableWidgetCalls->insertRow(ui->tableWidgetCalls->rowCount());
auto item = new QTableWidgetItem("ALLCALL");
item->setData(Qt::UserRole, QVariant("ALLCALL"));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 0, item);
ui->tableWidgetCalls->setSpan(ui->tableWidgetCalls->rowCount() - 1, 0, 1, ui->tableWidgetCalls->columnCount());
if(selectedCall == "ALLCALL"){
ui->tableWidgetCalls->selectRow(ui->tableWidgetCalls->rowCount() - 1);
}
QList<QString> calls = m_callActivity.keys();
qSort(calls.begin(), calls.end());
int callsignAging = m_config.callsign_aging();
foreach(QString call, calls){
CallDetail d = m_callActivity[call];
if(callsignAging && d.utcTimestamp.secsTo(now)/60 >= callsignAging){
continue;
}
ui->tableWidgetCalls->insertRow(ui->tableWidgetCalls->rowCount());
QString displayCall = d.through.isEmpty() ? d.call : QString("%1 | %2").arg(d.through).arg(d.call);
auto displayItem = new QTableWidgetItem(displayCall);
displayItem->setData(Qt::UserRole, QVariant((d.call)));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 0, displayItem);
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 1, new QTableWidgetItem(QString("(%1)").arg(since(d.utcTimestamp))));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 2, new QTableWidgetItem(QString("%1").arg(Varicode::formatSNR(d.snr))));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 3, new QTableWidgetItem(QString("%1").arg(d.grid)));
auto distanceItem = new QTableWidgetItem(calculateDistance(d.grid));
distanceItem->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 4, distanceItem);
if(call == selectedCall){
ui->tableWidgetCalls->selectRow(ui->tableWidgetCalls->rowCount() - 1);
}
}
ui->tableWidgetCalls->resizeColumnToContents(0);
ui->tableWidgetCalls->resizeColumnToContents(1);
ui->tableWidgetCalls->resizeColumnToContents(2);
ui->tableWidgetCalls->resizeColumnToContents(3);
// Recently Directed Activity
while (!m_rxFrameQueue.isEmpty()) {
ActivityDetail d = m_rxFrameQueue.dequeue();
@ -8893,9 +8756,14 @@ void MainWindow::displayActivity(bool force){
m_rxFrameBlockNumbers.remove(freq);
}
}
}
// Grouped Compound Activity
// TODO: jsherer - group compound callsign and directed commands together.
void MainWindow::processCompoundActivity() {
if(m_messageBuffer.isEmpty()){
return;
}
// group compound callsign and directed commands together.
foreach(auto freq, m_messageBuffer.keys()) {
QMap < int, MessageBuffer > ::iterator i = m_messageBuffer.find(freq);
@ -8954,8 +8822,13 @@ void MainWindow::displayActivity(bool force){
m_rxCommandQueue.append(buffer.cmd);
m_messageBuffer.remove(freq);
}
}
void MainWindow::processBufferedActivity() {
if(m_messageBuffer.isEmpty()){
return;
}
// Buffered Activity
foreach(auto freq, m_messageBuffer.keys()) {
auto buffer = m_messageBuffer[freq];
@ -8988,24 +8861,34 @@ void MainWindow::displayActivity(bool force){
// regardless of valid or not, remove the "complete" buffered message from the buffer cache
m_messageBuffer.remove(freq);
}
}
// Command Activity
if(m_txFrameQueue.isEmpty() && !m_rxCommandQueue.isEmpty()){
int f = currentFreq();
void MainWindow::processCommandActivity() {
#if 0
if (!m_txFrameQueue.isEmpty()) {
return;
}
#endif
if (m_rxCommandQueue.isEmpty()) {
return;
}
#if 0
bool processed = false;
// TODO: jsherer - should we if we have _any_ directed messages, pause the beacon??
int f = currentFreq();
#endif
// TODO: jsherer - should we, if we have _any_ directed messages, pause the beacon??
// pauseBacon();
while (!m_rxCommandQueue.isEmpty()) {
auto d = m_rxCommandQueue.dequeue();
bool isAllCall = d.to == "ALLCALL";
bool isAllCall = isAllCallIncluded(d.to);
#if 1
qDebug() << "processing command" << d.from << d.to << d.cmd << d.freq;
#endif
qDebug() << "try processing command" << d.from << d.to << d.cmd << d.freq;
// if we need a compound callsign but never got one...skip
if (d.from == "<....>" || d.to == "<....>") {
@ -9022,16 +8905,19 @@ void MainWindow::displayActivity(bool force){
continue;
}
#if 0
// TODO: jsherer - check to make sure we haven't replied to their allcall recently (in the past beacon interval)
if (isAllCall && m_txAllcallCommandCache.contains(Radio::base_callsign(d.from)) && m_txAllcallCommandCache[Radio::base_callsign(d.from)]->secsTo(QDateTime::currentDateTimeUtc()) / 60 < m_config.beacon()) {
continue;
}
// TODO: jsherer - we need to queue these for later processing
// record the spot to PSKReporter
if (okToPost) {
pskSetLocal();
pskLogReport("FT8Call", d.freq, d.snr, d.from, "");
}
#endif
// construct a reply
QString reply;
@ -9070,7 +8956,9 @@ void MainWindow::displayActivity(bool force){
int i = 0;
int maxStations = 4;
auto calls = m_callActivity.keys();
qSort(calls.begin(), calls.end(), [this](QString const &a, QString const &b){
qSort(calls.begin(), calls.end(), [this](QString
const & a, QString
const & b) {
auto left = m_callActivity[a];
auto right = m_callActivity[b];
return right.snr < left.snr;
@ -9106,7 +8994,6 @@ void MainWindow::displayActivity(bool force){
msgBox->setText(header);
msgBox->setInformativeText(d.text);
auto ab = msgBox->addButton("ACK", QMessageBox::AcceptRole);
auto db = msgBox->addButton("Discard", QMessageBox::NoRole);
@ -9114,8 +9001,11 @@ void MainWindow::displayActivity(bool force){
if (btn != ab) {
return;
}
#if 0
addMessageText(QString("%1 ACK").arg(d.from));
toggleTx(true);
#endif
});
msgBox->show();
@ -9129,27 +9019,227 @@ void MainWindow::displayActivity(bool force){
continue;
}
#if 0
addMessageText(reply);
// use the last frequency
f = d.freq;
// if we're responding via allcall, pick a different frequency and mark it in the cache.
if(d.to == "ALLCALL"){
if (isAllCallIncluded(d.to)) {
f = findFreeFreqOffset(qMax(0, f - 100), qMin(f + 100, 2500), 50);
m_txAllcallCommandCache.insert(Radio::base_callsign(d.from), new QDateTime(QDateTime::currentDateTimeUtc()), 25);
}
processed = true;
#endif
// TODO: jsherer - queue the reply here to be sent when a free interval is available
}
#if 0
if (processed && ui->autoReplyButton->isChecked()) {
toggleTx(true);
}
#endif
}
void MainWindow::processSpots() {
// Is it ok to post spots to PSKReporter?
int nsec = QDateTime::currentMSecsSinceEpoch() / 1000 - m_secBandChanged;
bool okToPost = (nsec > (4 * m_TRperiod) / 5);
if (!okToPost) {
return;
}
m_rxDirty = false;
// Process spots to be sent...
}
void MainWindow::displayActivity(bool force) {
if (!m_rxDirty && !force) {
return;
}
// Band Activity
displayBandActivity();
// Call Activity
displayCallActivity();
}
void MainWindow::displayBandActivity() {
auto now = QDateTime::currentDateTimeUtc();
// Selected Offset
int selectedOffset = -1;
auto selectedItems = ui->tableWidgetRXAll->selectedItems();
if (!selectedItems.isEmpty()) {
selectedOffset = selectedItems.first()->text().toInt();
}
// Clear the table
clearTableWidget(ui->tableWidgetRXAll);
// Sort directed & recent messages first
QList < int > keys = m_bandActivity.keys();
qSort(keys.begin(), keys.end(), [this](const int left, int right) {
if (m_rxDirectedCache.contains(left / 10 * 10)) {
return true;
}
if (m_rxDirectedCache.contains(right / 10 * 10)) {
return false;
}
if (m_rxRecentCache.contains(left / 10 * 10)) {
return true;
}
if (m_rxRecentCache.contains(right / 10 * 10)) {
return false;
}
return left < right;
});
// Build the table
foreach(int offset, keys) {
QList < ActivityDetail > items = m_bandActivity[offset];
if (items.length() > 0) {
QStringList text;
QString age;
int snr = 0;
int activityAging = m_config.activity_aging();
foreach(ActivityDetail item, items) {
if (activityAging && item.utcTimestamp.secsTo(now) / 60 >= activityAging) {
continue;
}
if (item.text.isEmpty()) {
continue;
}
if (item.isLowConfidence) {
item.text = QString("[%1]").arg(item.text);
}
if (item.bits == Varicode::FT8CallLast) {
// can also use \u0004 \u2666 \u2404
item.text = QString("%1 \u2301 ").arg(item.text);
}
text.append(item.text);
snr = item.snr;
age = since(item.utcTimestamp);
}
auto joined = text.join(" ");
if (joined.isEmpty()) {
continue;
}
ui->tableWidgetRXAll->insertRow(ui->tableWidgetRXAll->rowCount());
auto offsetItem = new QTableWidgetItem(QString("%1").arg(offset));
offsetItem->setData(Qt::UserRole, QVariant(offset));
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 0, offsetItem);
auto ageItem = new QTableWidgetItem(QString("(%1)").arg(age));
ageItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter);
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 1, ageItem);
auto snrItem = new QTableWidgetItem(QString("%1").arg(Varicode::formatSNR(snr)));
snrItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter);
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 2, snrItem);
// align right if eliding...
int colWidth = ui->tableWidgetRXAll->columnWidth(3);
auto textItem = new QTableWidgetItem(joined);
QFontMetrics fm(textItem->font());
auto elidedText = fm.elidedText(joined, Qt::ElideLeft, colWidth);
auto flag = Qt::AlignLeft | Qt::AlignVCenter;
if (elidedText != joined) {
flag = Qt::AlignRight | Qt::AlignVCenter;
textItem->setText(joined);
}
textItem->setTextAlignment(flag);
if (text.last().contains(QRegularExpression {
"^(CQ|QRZ|DE)\\s"
})) {
offsetItem->setBackground(QBrush(m_config.color_CQ()));
ageItem->setBackground(QBrush(m_config.color_CQ()));
snrItem->setBackground(QBrush(m_config.color_CQ()));
textItem->setBackground(QBrush(m_config.color_CQ()));
}
if (m_rxDirectedCache.contains(offset / 10 * 10)) {
offsetItem->setBackground(QBrush(m_config.color_MyCall()));
ageItem->setBackground(QBrush(m_config.color_MyCall()));
snrItem->setBackground(QBrush(m_config.color_MyCall()));
textItem->setBackground(QBrush(m_config.color_MyCall()));
}
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 3, textItem);
if (offset == selectedOffset) {
ui->tableWidgetRXAll->selectRow(ui->tableWidgetRXAll->rowCount() - 1);
}
}
}
// Resize the table columns
ui->tableWidgetRXAll->resizeColumnToContents(0);
ui->tableWidgetRXAll->resizeColumnToContents(1);
ui->tableWidgetRXAll->resizeColumnToContents(2);
}
void MainWindow::displayCallActivity() {
auto now = QDateTime::currentDateTimeUtc();
// Selected callsign
QString selectedCall = callsignSelected();
// Clear the table
clearTableWidget(ui->tableWidgetCalls);
// Create the ALLCALL item
auto item = new QTableWidgetItem("ALLCALL");
ui->tableWidgetCalls->insertRow(ui->tableWidgetCalls->rowCount());
item->setData(Qt::UserRole, QVariant("ALLCALL"));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 0, item);
ui->tableWidgetCalls->setSpan(ui->tableWidgetCalls->rowCount() - 1, 0, 1, ui->tableWidgetCalls->columnCount());
if (isAllCallIncluded(selectedCall)) {
ui->tableWidgetCalls->selectRow(ui->tableWidgetCalls->rowCount() - 1);
}
// Build the table
QList < QString > calls = m_callActivity.keys();
qSort(calls.begin(), calls.end());
int callsignAging = m_config.callsign_aging();
foreach(QString call, calls) {
CallDetail d = m_callActivity[call];
if (callsignAging && d.utcTimestamp.secsTo(now) / 60 >= callsignAging) {
continue;
}
ui->tableWidgetCalls->insertRow(ui->tableWidgetCalls->rowCount());
QString displayCall = d.through.isEmpty() ? d.call : QString("%1 | %2").arg(d.through).arg(d.call);
auto displayItem = new QTableWidgetItem(displayCall);
displayItem->setData(Qt::UserRole, QVariant((d.call)));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 0, displayItem);
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 1, new QTableWidgetItem(QString("(%1)").arg(since(d.utcTimestamp))));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 2, new QTableWidgetItem(QString("%1").arg(Varicode::formatSNR(d.snr))));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 3, new QTableWidgetItem(QString("%1").arg(d.grid)));
auto distanceItem = new QTableWidgetItem(calculateDistance(d.grid));
distanceItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 4, distanceItem);
if (call == selectedCall) {
ui->tableWidgetCalls->selectRow(ui->tableWidgetCalls->rowCount() - 1);
}
}
// Resize the table columns
ui->tableWidgetCalls->resizeColumnToContents(0);
ui->tableWidgetCalls->resizeColumnToContents(1);
ui->tableWidgetCalls->resizeColumnToContents(2);
ui->tableWidgetCalls->resizeColumnToContents(3);
}
void MainWindow::postWSPRDecode (bool is_new, QStringList parts)

View File

@ -795,7 +795,15 @@ private:
QString callsignSelected();
bool isRecentOffset(int offset);
bool isDirectedOffset(int offset);
void processActivity(bool force=false);
void processRxActivity();
void processCompoundActivity();
void processBufferedActivity();
void processCommandActivity();
void processSpots();
void displayActivity(bool force=false);
void displayBandActivity();
void displayCallActivity();
void postWSPRDecode (bool is_new, QStringList message_parts);
void enable_DXCC_entity (bool on);
void switch_mode (Mode);