| 192 | // ── TCP Connection ───────────────────────────────────────────────────────── |
| 193 | |
| 194 | void AntennaGeniusModel::connectToDevice(const AgDeviceInfo& info) |
| 195 | { |
| 196 | // Always clean up any existing socket — connected or still pending. |
| 197 | // Multiple auto-connect triggers (radio connect + UDP beacon) can fire |
| 198 | // in quick succession; without this the R4 accepts the first socket and |
| 199 | // sends its greeting there while AetherSDR's active socket is a later one. |
| 200 | if (m_tcpSocket) { |
| 201 | m_tcpSocket->abort(); |
| 202 | m_tcpSocket->deleteLater(); |
| 203 | m_tcpSocket = nullptr; |
| 204 | } |
| 205 | if (m_connected) { |
| 206 | m_connected = false; |
| 207 | emit disconnected(); |
| 208 | } |
| 209 | |
| 210 | m_device = info; |
| 211 | m_gotPrologue = false; |
| 212 | m_lineBuffer.clear(); |
| 213 | m_nextSeq = 1; |
| 214 | m_pending.clear(); |
| 215 | |
| 216 | m_tcpSocket = new QTcpSocket(this); |
| 217 | connect(m_tcpSocket, &QTcpSocket::connected, |
| 218 | this, &AntennaGeniusModel::onTcpConnected); |
| 219 | connect(m_tcpSocket, &QTcpSocket::disconnected, |
| 220 | this, &AntennaGeniusModel::onTcpDisconnected); |
| 221 | connect(m_tcpSocket, &QTcpSocket::readyRead, |
| 222 | this, &AntennaGeniusModel::onTcpReadyRead); |
| 223 | connect(m_tcpSocket, &QTcpSocket::errorOccurred, |
| 224 | this, [this]() { onTcpError(); }); |
| 225 | |
| 226 | qCDebug(lcTuner) << "AntennaGenius: connecting to" << info.ip.toString() << ":" << info.port; |
| 227 | m_tcpSocket->connectToHost(info.ip, info.port); |
| 228 | } |
| 229 | |
| 230 | void AntennaGeniusModel::connectToAddress(const QHostAddress& ip, quint16 port) |
| 231 | { |
no test coverage detected