LibreVNA/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnatcpdriver.cpp

370 lines
12 KiB
C++

#include "librevnatcpdriver.h"
#include "CustomWidgets/informationbox.h"
#include "devicepacketlog.h"
#include "Util/util.h"
#include <QTimer>
#include <QNetworkInterface>
using namespace std;
static const QString service_name = "urn:schemas-upnp-org:device:LibreVNA:1";
static constexpr int DataPort = 19544;
static constexpr int LogPort = 19545;
static auto SSDPaddress = QHostAddress("239.255.255.250");
static constexpr int SSDPport = 1900;
LibreVNATCPDriver::LibreVNATCPDriver()
: LibreVNADriver()
{
connected = false;
m_receiveThread = nullptr;
auto interfaces = QNetworkInterface::allInterfaces();
for(auto i : interfaces) {
switch(i.type()) {
case QNetworkInterface::Ethernet:
case QNetworkInterface::Wifi:
case QNetworkInterface::Virtual:
case QNetworkInterface::Unknown:
break;
default:
// skip all other interface types
continue;
}
auto socket = new QUdpSocket();
socket->bind(QHostAddress::AnyIPv4, 0, QUdpSocket::ShareAddress);
socket->setMulticastInterface(i);
socket->joinMulticastGroup(SSDPaddress, i);
connect(socket, &QUdpSocket::readyRead, this, [=](){
SSDPreceived(socket);
});
ssdpSockets.push_back(socket);
}
specificSettings.push_back(Savable::SettingDescription(&captureRawReceiverValues, "LibreVNATCPDriver.captureRawReceiverValues", false));
specificSettings.push_back(Savable::SettingDescription(&harmonicMixing, "LibreVNATCPDriver.harmonicMixing", false));
specificSettings.push_back(Savable::SettingDescription(&SASignalID, "LibreVNATCPDriver.signalID", true));
specificSettings.push_back(Savable::SettingDescription(&VNASuppressInvalidPeaks, "LibreVNATCPDriver.suppressInvalidPeaks", true));
specificSettings.push_back(Savable::SettingDescription(&VNAAdjustPowerLevel, "LibreVNATCPDriver.adjustPowerLevel", false));
specificSettings.push_back(Savable::SettingDescription(&SAUseDFT, "LibreVNATCPDriver.useDFT", true));
specificSettings.push_back(Savable::SettingDescription(&SARBWLimitForDFT, "LibreVNATCPDriver.RBWlimitDFT", 3000));
}
QString LibreVNATCPDriver::getDriverName()
{
return "LibreVNA/TCP";
}
std::set<QString> LibreVNATCPDriver::GetAvailableDevices()
{
QByteArray data;
data.append("M-SEARCH * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"MAN: \"ssdp:discover\"\r\n"
"MX: 1\r\n"
"ST: ");
data.append(service_name.toUtf8());
data.append("\r\n"
"\r\n");
// just delete everything instead of keeping old entries (they will answer again if they are still available)
detectedDevices.clear();
// pruneDetectedDevices();
for(auto s : ssdpSockets) {
s->writeDatagram(data.data(), SSDPaddress, SSDPport);
}
// need delay here while still processing events
SynSleep::sleep(100);
std::set<QString> serials;
for(auto d : detectedDevices) {
serials.insert(d.serial);
}
return serials;
}
bool LibreVNATCPDriver::connectTo(QString serial)
{
if(connected) {
disconnect();
}
// check if this device is actually available
DetectedDevice devInfo;
bool available = false;
for(auto d : detectedDevices) {
if(d.serial == serial) {
devInfo = d;
available = true;
break;
}
}
if(!available) {
// no location information about this device available
return false;
}
// attempt to connect to the device
dataSocket.connectToHost(devInfo.address, DataPort);
logSocket.connectToHost(devInfo.address, LogPort);
// check if connection succeeds
if(!dataSocket.waitForConnected(1000) || !logSocket.waitForConnected(1000)) {
// at least one socket failed
dataSocket.close();
logSocket.close();
InformationBox::ShowError("Error", "TCP connection timed out");
return false;
}
// sockets are connected now
dataBuffer.clear();
logBuffer.clear();
connect(&dataSocket, qOverload<QAbstractSocket::SocketError>(&QTcpSocket::errorOccurred), this, &LibreVNATCPDriver::ConnectionLost, Qt::QueuedConnection);
connect(&logSocket, qOverload<QAbstractSocket::SocketError>(&QTcpSocket::errorOccurred), this, &LibreVNATCPDriver::ConnectionLost, Qt::QueuedConnection);
qInfo() << "TCP connection established" << Qt::flush;
this->serial = serial;
connected = true;
connect(&dataSocket, &QTcpSocket::readyRead, this, &LibreVNATCPDriver::ReceivedData, Qt::UniqueConnection);
connect(&logSocket, &QTcpSocket::readyRead, this, &LibreVNATCPDriver::ReceivedLog, Qt::UniqueConnection);
connect(&transmissionTimer, &QTimer::timeout, this, &LibreVNATCPDriver::transmissionTimeout, Qt::UniqueConnection);
connect(this, &LibreVNATCPDriver::receivedAnswer, this, &LibreVNATCPDriver::transmissionFinished, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
connect(this, &LibreVNATCPDriver::receivedPacket, this, &LibreVNATCPDriver::handleReceivedPacket, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
transmissionTimer.setSingleShot(true);
transmissionActive = false;
sendWithoutPayload(Protocol::PacketType::RequestDeviceInfo);
sendWithoutPayload(Protocol::PacketType::RequestDeviceStatus);
// updateIFFrequencies();
return true;
}
void LibreVNATCPDriver::disconnect()
{
if(connected) {
setIdle();
transmissionTimer.stop();
transmissionQueue.clear();
transmissionActive = false;
dataSocket.flush();
dataSocket.close();
logSocket.close();
// delete dataSocket;
// delete logSocket;
// dataSocket = nullptr;
// logSocket = nullptr;
connected = false;
serial = "";
}
}
void LibreVNATCPDriver::registerTypes()
{
qDebug() << "Registering meta type: " << qRegisterMetaType<Protocol::PacketInfo>();
qDebug() << "Registering meta type: " << qRegisterMetaType<TransmissionResult>();
}
void LibreVNATCPDriver::SSDPreceived(QUdpSocket *sock)
{
while(sock->hasPendingDatagrams()) {
QHostAddress sender;
quint16 senderPort;
QByteArray buf(sock->pendingDatagramSize(), Qt::Uninitialized);
QDataStream str(&buf, QIODevice::ReadOnly);
sock->readDatagram(buf.data(), buf.size(), &sender, &senderPort);
QString ssdp_string = QString(buf);
auto lines = ssdp_string.split("\r\n");
QString location, st, serial, max_age;
if(lines[0] != "HTTP/1.1 200 OK") {
continue;
}
for(QString l : lines) {
if(l.startsWith("LOCATION:")) {
location = l.split(" ")[1];
} else if(l.startsWith("ST:")) {
st = l.split(" ")[1];
} else if(l.startsWith("LibreVNA-serial:")) {
serial = l.split(" ")[1];
} else if(l.startsWith("CACHE-CONTROL:")) {
max_age = l.split("=")[1];
}
}
if(location.isEmpty() || st.isEmpty() || serial.isEmpty() || max_age.isEmpty()) {
// some required field is missing
continue;
}
// new device
DetectedDevice d;
d.address = QHostAddress(location);
d.maxAgeSeconds = max_age.toUInt();
d.responseTime = QDateTime::currentDateTime();
d.serial = serial;
addDetectedDevice(d);
}
}
void LibreVNATCPDriver::ReceivedData()
{
dataBuffer.append(dataSocket.readAll());
Protocol::PacketInfo packet;
uint16_t handled_len;
// qDebug() << "Received data";
do {
// qDebug() << "Decoding" << dataBuffer->getReceived() << "Bytes";
handled_len = Protocol::DecodeBuffer((uint8_t*) dataBuffer.data(), dataBuffer.size(), &packet);
// qDebug() << "Handled" << handled_len << "Bytes, type:" << (int) packet.type;
if(handled_len > 0) {
auto &log = DevicePacketLog::getInstance();
if(packet.type != Protocol::PacketType::None) {
log.addPacket(packet, serial);
} else {
log.addInvalidBytes((uint8_t*) dataBuffer.data(), handled_len, serial);
}
}
dataBuffer.remove(0, handled_len);
switch(packet.type) {
case Protocol::PacketType::Ack:
emit receivedAnswer(TransmissionResult::Ack);
break;
case Protocol::PacketType::Nack:
emit receivedAnswer(TransmissionResult::Nack);
break;
default:
// pass on to LibreVNADriver class
emit receivedPacket(packet);
break;
}
} while (handled_len > 0);
}
void LibreVNATCPDriver::ReceivedLog()
{
logBuffer.append(logSocket.readAll());
uint16_t handled_len;
do {
handled_len = 0;
auto firstLinebreak = (uint8_t*) memchr((uint8_t*) logBuffer.data(), '\n', logBuffer.size());
if(firstLinebreak) {
handled_len = firstLinebreak - (uint8_t*) logBuffer.data();
auto line = QString::fromLatin1(logBuffer.data(), handled_len - 1);
emit LogLineReceived(line);
logBuffer.remove(0, handled_len + 1);
}
} while(handled_len > 0);
}
void LibreVNATCPDriver::transmissionFinished(LibreVNADriver::TransmissionResult result)
{
lock_guard<mutex> lock(transmissionMutex);
// remove transmitted packet
// qDebug() << "Transmission finsished (" << result << "), queue at " << transmissionQueue.size() << " Outstanding ACKs:"<<outstandingAckCount;
if(transmissionQueue.empty()) {
qWarning() << "transmissionFinished with empty transmission queue, stray Ack? Result:" << result;
return;
}
auto t = transmissionQueue.dequeue();
if(result == TransmissionResult::Timeout) {
qWarning() << "transmissionFinished with timeout, packettype:" << (int) t.packet.type << "Device:" << serial;
}
if(result == TransmissionResult::Nack) {
qWarning() << "transmissionFinished with NACK";
}
if(t.callback) {
t.callback(result);
}
transmissionTimer.stop();
bool success = false;
while(!transmissionQueue.isEmpty() && !success) {
success = startNextTransmission();
if(!success) {
// failed to send this packet
auto t = transmissionQueue.dequeue();
if(t.callback) {
t.callback(TransmissionResult::InternalError);
}
}
}
if(transmissionQueue.isEmpty()) {
transmissionActive = false;
}
}
bool LibreVNATCPDriver::SendPacket(const Protocol::PacketInfo &packet, std::function<void (LibreVNADriver::TransmissionResult)> cb, unsigned int timeout)
{
Transmission t;
t.packet = packet;
t.timeout = timeout;
t.callback = cb;
lock_guard<mutex> lock(transmissionMutex);
transmissionQueue.enqueue(t);
// qDebug() << "Enqueued packet, queue at " << transmissionQueue.size();
if(!transmissionActive) {
startNextTransmission();
}
return true;
}
void LibreVNATCPDriver::addDetectedDevice(const LibreVNATCPDriver::DetectedDevice &d)
{
for(auto &e : detectedDevices) {
if(e.serial == d.serial) {
// replace entry
e = d;
return;
}
}
// serial not already present, add
detectedDevices.push_back(d);
}
void LibreVNATCPDriver::pruneDetectedDevices()
{
for(unsigned int i=0;i<detectedDevices.size();i++) {
auto d = detectedDevices[i];
if(d.responseTime.secsTo(QDateTime::currentDateTime()) > d.maxAgeSeconds) {
// too old, remove
detectedDevices.erase(detectedDevices.begin()+i);
i--;
}
}
}
bool LibreVNATCPDriver::startNextTransmission()
{
if(transmissionQueue.isEmpty() || !connected) {
// nothing more to transmit
transmissionActive = false;
return false;
}
transmissionActive = true;
auto t = transmissionQueue.head();
unsigned char buffer[1024];
unsigned int length = Protocol::EncodePacket(t.packet, buffer, sizeof(buffer));
if(!length) {
qCritical() << "Failed to encode packet";
return false;
}
auto &log = DevicePacketLog::getInstance();
log.addPacket(t.packet);
auto ret = dataSocket.write((char*) buffer, length);
if(ret < 0) {
qCritical() << "Error sending TCP data";
return false;
}
transmissionTimer.start(t.timeout);
// qDebug() << "Transmission started, queue at " << transmissionQueue.size();
return true;
}