| 465 | } |
| 466 | |
| 467 | void TciServer::onClientDisconnected() |
| 468 | { |
| 469 | auto* ws = qobject_cast<QWebSocket*>(sender()); |
| 470 | if (!ws) return; |
| 471 | |
| 472 | for (int i = 0; i < m_clients.size(); ++i) { |
| 473 | if (m_clients[i].socket == ws) { |
| 474 | // If this client was driving TX_CHRONO, stop and unkey |
| 475 | if (ws == m_txChronoClient) { |
| 476 | stopTxChrono(); |
| 477 | if (m_model) { |
| 478 | QMetaObject::invokeMethod(m_model, [this]() { |
| 479 | m_model->setTransmit(false); |
| 480 | }, Qt::QueuedConnection); |
| 481 | } |
| 482 | } |
| 483 | // Clean up IQ stream if this client started one |
| 484 | if (m_clients[i].iqEnabled && m_model) { |
| 485 | int ch = m_clients[i].iqChannel + 1; // TRX 0 → DAX channel 1 |
| 486 | // Only remove if no other client uses the same IQ channel |
| 487 | bool otherUsing = false; |
| 488 | for (int j = 0; j < m_clients.size(); ++j) { |
| 489 | if (j != i && m_clients[j].iqEnabled && |
| 490 | m_clients[j].iqChannel == m_clients[i].iqChannel) { |
| 491 | otherUsing = true; |
| 492 | break; |
| 493 | } |
| 494 | } |
| 495 | if (!otherUsing) { |
| 496 | QMetaObject::invokeMethod(m_model, [this, ch]() { |
| 497 | m_model->daxIqModel().removeStream(ch); |
| 498 | }, Qt::QueuedConnection); |
| 499 | } |
| 500 | } |
| 501 | delete m_clients[i].protocol; |
| 502 | qDeleteAll(m_clients[i].resamplers); |
| 503 | m_clients.removeAt(i); |
| 504 | |
| 505 | // Release DAX if no remaining clients want audio (#1331) |
| 506 | bool anyAudio = false; |
| 507 | for (const auto& cs : m_clients) { |
| 508 | if (cs.audioEnabled) { anyAudio = true; break; } |
| 509 | } |
| 510 | if (!anyAudio) scheduleDaxRelease(); // debounce: survive transient WSJT-X reconnects |
| 511 | break; |
| 512 | } |
| 513 | } |
| 514 | |
| 515 | ws->deleteLater(); |
| 516 | // DIAG: qCWarning — a TCP-level client drop (WSJT-X threw a rig-control |
| 517 | // error in do_stop()) is the trigger for the DAX RX teardown above. Always |
| 518 | // log it so the cause of mid-session RX loss is visible. |
| 519 | qCWarning(lcCat) << "TciServer: client disconnected (TCP drop)," |
| 520 | << m_clients.size() << "remaining"; |
| 521 | emit clientCountChanged(m_clients.size()); |
| 522 | emit clientsChanged(); |
| 523 | } |
| 524 |
nothing calls this directly
no test coverage detected