candleLight, CANable, CANnectivity and cantact adapters speak gs_usb. On Linux and macOS the kernel driver exposes them as SocketCAN interfaces, so SavvyCAN already reached them through Qt SerialBus. Windows has no such driver, which left those adapters unusable there. Add a GSUSBConnection that talks to the hardware directly over WinUSB: - one connection per physical device, one SavvyCAN bus per CAN channel - CAN FD including BRS, plus RTR, extended IDs and error frames - known good bit timings for 16/48/80/160/170 MHz device clocks, with a generic solver for clocks the tables do not cover - hardware timestamps, unwrapped across the 32 bit rollover and anchored to the host clock so they line up with the rest of SavvyCAN gs_usb multiplexes every channel over a single USB bulk IN endpoint, so a single reader thread drains it and hands frames to the connection thread in batches, coalescing the wakeups. That keeps one producer on the lock free queue, which the tx echo path already writes to. Device scans skip adapters that a live connection holds. candle_dev_open() shares the file handle and queues read URBs immediately, so probing a device in use would consume frames the open connection is waiting for. connections/candle_api is an unmodified copy of the candle Windows API, LGPL 3.0 rather than MIT and only compiled into Windows builds. See its README for provenance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XZnuZJ7zc3e8hk6C8bGDZN
510 lines
15 KiB
C++
510 lines
15 KiB
C++
#include <QCanBus>
|
|
#include "newconnectiondialog.h"
|
|
#include "ui_newconnectiondialog.h"
|
|
|
|
#ifdef Q_OS_WIN
|
|
#include "gs_usb.h"
|
|
#endif
|
|
|
|
NewConnectionDialog::NewConnectionDialog(QVector<QString>* gvretips, QVector<QString>* kayakhosts, QWidget *parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::NewConnectionDialog),
|
|
remoteDeviceIPGVRET(gvretips),
|
|
remoteBusKayak(kayakhosts)
|
|
{
|
|
ui->setupUi(this);
|
|
if (isSerialBusAvailable())
|
|
{
|
|
ui->rbSocketCAN->setEnabled(true);
|
|
}
|
|
else
|
|
{
|
|
ui->rbSocketCAN->setEnabled(false);
|
|
QString errorString;
|
|
const QList<QCanBusDeviceInfo> devices = QCanBus::instance()->availableDevices(QStringLiteral("socketcan"), &errorString);
|
|
if (!errorString.isEmpty()) ui->rbSocketCAN->setToolTip(errorString);
|
|
}
|
|
|
|
|
|
connect(ui->rbGVRET, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged);
|
|
connect(ui->rbSocketCAN, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged);
|
|
connect(ui->rbRemote, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged);
|
|
connect(ui->rbKayak, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged);
|
|
connect(ui->rbMQTT, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged);
|
|
connect(ui->rbLawicel, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged);
|
|
connect(ui->rbCANserver, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged);
|
|
connect(ui->rbCanlogserver, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged);
|
|
connect(ui->rbGSUSB, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged);
|
|
|
|
#ifndef Q_OS_WIN
|
|
//on Linux and macOS the kernel gs_usb driver already exposes these devices as SocketCAN
|
|
ui->rbGSUSB->setEnabled(false);
|
|
ui->rbGSUSB->setToolTip("Connect gs_usb devices through SocketCAN on this platform");
|
|
#endif
|
|
|
|
connect(ui->cbDeviceType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &NewConnectionDialog::handleDeviceTypeChanged);
|
|
connect(ui->btnOK, &QPushButton::clicked, this, &NewConnectionDialog::handleCreateButton);
|
|
|
|
ui->lblDeviceType->setHidden(true);
|
|
ui->cbDeviceType->setHidden(true);
|
|
ui->cbCANSpeed->setHidden(true);
|
|
ui->cbSerialSpeed->setHidden(true);
|
|
ui->lblCANSpeed->setHidden(true);
|
|
ui->lblSerialSpeed->setHidden(true);
|
|
ui->cbCanFd->setHidden(true);
|
|
ui->cbDataRate->setHidden(true);
|
|
ui->lblDataRate->setHidden(true);
|
|
|
|
selectSerial();
|
|
|
|
qDebug() << "Was passed " << remoteDeviceIPGVRET->count() << " remote GVRET IPs";
|
|
qDebug() << "Was passed " << remoteBusKayak->count() << " remote Kayak Busses";
|
|
}
|
|
|
|
NewConnectionDialog::~NewConnectionDialog()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void NewConnectionDialog::handleCreateButton()
|
|
{
|
|
accept();
|
|
}
|
|
|
|
void NewConnectionDialog::handleConnTypeChanged()
|
|
{
|
|
if (ui->rbGVRET->isChecked()) selectSerial();
|
|
if (ui->rbSocketCAN->isChecked()) selectSocketCan();
|
|
if (ui->rbLawicel->isChecked()) selectLawicel();
|
|
if (ui->rbRemote->isChecked()) selectRemote();
|
|
if (ui->rbKayak->isChecked()) selectKayak();
|
|
if (ui->rbMQTT->isChecked()) selectMQTT();
|
|
if (ui->rbCANserver->isChecked()) selectCANserver();
|
|
if (ui->rbCanlogserver->isChecked()) selectCANlogserver();
|
|
if (ui->rbGSUSB->isChecked()) selectGSUSB();
|
|
}
|
|
|
|
void NewConnectionDialog::handleDeviceTypeChanged()
|
|
{
|
|
|
|
ui->cbPort->clear();
|
|
canDevices = QCanBus::instance()->availableDevices(ui->cbDeviceType->currentText());
|
|
|
|
for (int i = 0; i < canDevices.count(); i++)
|
|
ui->cbPort->addItem(canDevices[i].name());
|
|
}
|
|
|
|
void NewConnectionDialog::selectLawicel()
|
|
{
|
|
ui->lPort->setText("Serial Port:");
|
|
|
|
ui->lblDeviceType->setHidden(true);
|
|
ui->cbDeviceType->setHidden(true);
|
|
|
|
ui->cbCANSpeed->setHidden(false);
|
|
ui->cbSerialSpeed->setHidden(false);
|
|
ui->lblCANSpeed->setHidden(false);
|
|
ui->lblSerialSpeed->setHidden(false);
|
|
ui->cbCanFd->setHidden(false);
|
|
ui->cbDataRate->setHidden(false);
|
|
ui->lblDataRate->setHidden(false);
|
|
|
|
ui->cbPort->clear();
|
|
ports = QSerialPortInfo::availablePorts();
|
|
|
|
for (int i = 0; i < ports.count(); i++)
|
|
ui->cbPort->addItem(ports[i].portName());
|
|
|
|
if (ui->cbCANSpeed->count() == 0)
|
|
{
|
|
ui->cbCANSpeed->addItem("10000");
|
|
ui->cbCANSpeed->addItem("20000");
|
|
ui->cbCANSpeed->addItem("50000");
|
|
ui->cbCANSpeed->addItem("83333");
|
|
ui->cbCANSpeed->addItem("100000");
|
|
ui->cbCANSpeed->addItem("125000");
|
|
ui->cbCANSpeed->addItem("250000");
|
|
ui->cbCANSpeed->addItem("500000");
|
|
ui->cbCANSpeed->addItem("1000000");
|
|
}
|
|
if (ui->cbDataRate->count() == 0)
|
|
{
|
|
ui->cbDataRate->addItem("1000000");
|
|
ui->cbDataRate->addItem("2000000");
|
|
ui->cbDataRate->addItem("4000000");
|
|
ui->cbDataRate->addItem("5000000");
|
|
}
|
|
if (ui->cbSerialSpeed->count() == 0)
|
|
{
|
|
ui->cbSerialSpeed->addItem("115200");
|
|
ui->cbSerialSpeed->addItem("150000");
|
|
ui->cbSerialSpeed->addItem("250000");
|
|
ui->cbSerialSpeed->addItem("500000");
|
|
ui->cbSerialSpeed->addItem("1000000");
|
|
ui->cbSerialSpeed->addItem("2000000");
|
|
ui->cbSerialSpeed->addItem("3000000");
|
|
}
|
|
|
|
}
|
|
|
|
void NewConnectionDialog::selectSerial()
|
|
{
|
|
ui->lPort->setText("Serial Port:");
|
|
|
|
ui->lblDeviceType->setHidden(true);
|
|
ui->cbDeviceType->setHidden(true);
|
|
ui->cbCANSpeed->setHidden(true);
|
|
ui->cbSerialSpeed->setHidden(true);
|
|
ui->lblCANSpeed->setHidden(true);
|
|
ui->lblSerialSpeed->setHidden(true);
|
|
ui->cbCanFd->setHidden(true);
|
|
ui->cbDataRate->setHidden(true);
|
|
ui->lblDataRate->setHidden(true);
|
|
|
|
ui->cbPort->clear();
|
|
ports = QSerialPortInfo::availablePorts();
|
|
|
|
for (int i = 0; i < ports.count(); i++)
|
|
ui->cbPort->addItem(ports[i].portName());
|
|
}
|
|
|
|
void NewConnectionDialog::selectSocketCan()
|
|
{
|
|
ui->lPort->setText("Port:");
|
|
ui->lblDeviceType->setHidden(false);
|
|
ui->cbDeviceType->setHidden(false);
|
|
ui->cbCANSpeed->setHidden(true);
|
|
ui->cbSerialSpeed->setHidden(true);
|
|
ui->lblCANSpeed->setHidden(true);
|
|
ui->lblSerialSpeed->setHidden(true);
|
|
ui->cbCanFd->setHidden(true);
|
|
ui->cbDataRate->setHidden(true);
|
|
ui->lblDataRate->setHidden(true);
|
|
|
|
ui->cbDeviceType->clear();
|
|
QStringList plugins;
|
|
plugins = QCanBus::instance()->plugins();
|
|
for (int i = 0; i < plugins.count(); i++)
|
|
ui->cbDeviceType->addItem(plugins[i]);
|
|
|
|
}
|
|
|
|
void NewConnectionDialog::selectRemote()
|
|
{
|
|
ui->lPort->setText("IP Address:");
|
|
|
|
ui->lblDeviceType->setHidden(true);
|
|
ui->cbDeviceType->setHidden(true);
|
|
ui->cbCANSpeed->setHidden(true);
|
|
ui->cbSerialSpeed->setHidden(true);
|
|
ui->lblCANSpeed->setHidden(true);
|
|
ui->lblSerialSpeed->setHidden(true);
|
|
ui->cbCanFd->setHidden(true);
|
|
ui->cbDataRate->setHidden(true);
|
|
ui->lblDataRate->setHidden(true);
|
|
|
|
ui->cbPort->clear();
|
|
foreach(QString pName, *remoteDeviceIPGVRET)
|
|
{
|
|
ui->cbPort->addItem(pName);
|
|
}
|
|
}
|
|
|
|
void NewConnectionDialog::selectKayak()
|
|
{
|
|
ui->lPort->setText("Available Bus(ses):");
|
|
|
|
ui->lblDeviceType->setHidden(true);
|
|
ui->cbDeviceType->setHidden(true);
|
|
ui->cbCANSpeed->setHidden(true);
|
|
ui->cbSerialSpeed->setHidden(true);
|
|
ui->lblCANSpeed->setHidden(true);
|
|
ui->lblSerialSpeed->setHidden(true);
|
|
ui->cbCanFd->setHidden(true);
|
|
ui->cbDataRate->setHidden(true);
|
|
ui->lblDataRate->setHidden(true);
|
|
|
|
ui->cbPort->clear();
|
|
foreach(QString pName, *remoteBusKayak)
|
|
{
|
|
ui->cbPort->addItem(pName);
|
|
}
|
|
}
|
|
|
|
void NewConnectionDialog::selectMQTT()
|
|
{
|
|
ui->lPort->setText("Topic Name:");
|
|
|
|
ui->lblDeviceType->setHidden(true);
|
|
ui->cbDeviceType->setHidden(true);
|
|
ui->cbCANSpeed->setHidden(true);
|
|
ui->cbSerialSpeed->setHidden(true);
|
|
ui->lblCANSpeed->setHidden(true);
|
|
ui->lblSerialSpeed->setHidden(true);
|
|
ui->cbCanFd->setHidden(true);
|
|
ui->cbDataRate->setHidden(true);
|
|
ui->lblDataRate->setHidden(true);
|
|
|
|
ui->cbPort->clear();
|
|
}
|
|
|
|
void NewConnectionDialog::selectCANserver()
|
|
{
|
|
ui->lPort->setText("CANserver IP Address:");
|
|
|
|
ui->lblDeviceType->setHidden(true);
|
|
ui->cbDeviceType->setHidden(true);
|
|
ui->cbCANSpeed->setHidden(true);
|
|
ui->cbSerialSpeed->setHidden(true);
|
|
ui->lblCANSpeed->setHidden(true);
|
|
ui->lblSerialSpeed->setHidden(true);
|
|
ui->cbCanFd->setHidden(true);
|
|
ui->cbDataRate->setHidden(true);
|
|
ui->lblDataRate->setHidden(true);
|
|
|
|
ui->cbPort->clear();
|
|
}
|
|
|
|
void NewConnectionDialog::selectCANlogserver()
|
|
{
|
|
ui->lPort->setText("CANlogserver IP Address:");
|
|
|
|
ui->lblDeviceType->setHidden(true);
|
|
ui->cbDeviceType->setHidden(true);
|
|
ui->cbCANSpeed->setHidden(true);
|
|
ui->cbSerialSpeed->setHidden(true);
|
|
ui->lblCANSpeed->setHidden(true);
|
|
ui->lblSerialSpeed->setHidden(true);
|
|
ui->cbCanFd->setHidden(true);
|
|
ui->cbDataRate->setHidden(true);
|
|
ui->lblDataRate->setHidden(true);
|
|
|
|
ui->cbPort->clear();
|
|
}
|
|
|
|
void NewConnectionDialog::selectGSUSB()
|
|
{
|
|
ui->lPort->setText("gs_usb Device:");
|
|
|
|
ui->lblDeviceType->setHidden(true);
|
|
ui->cbDeviceType->setHidden(true);
|
|
|
|
ui->cbCANSpeed->setHidden(false);
|
|
ui->lblCANSpeed->setHidden(false);
|
|
ui->cbSerialSpeed->setHidden(true);
|
|
ui->lblSerialSpeed->setHidden(true);
|
|
ui->cbCanFd->setHidden(false);
|
|
ui->cbDataRate->setHidden(false);
|
|
ui->lblDataRate->setHidden(false);
|
|
|
|
ui->cbPort->clear();
|
|
#ifdef Q_OS_WIN
|
|
foreach (const GSUSBDeviceInfo &dev, GSUSBConnection::enumerateDevices())
|
|
ui->cbPort->addItem(dev.name);
|
|
#endif
|
|
|
|
if (ui->cbCANSpeed->count() == 0)
|
|
{
|
|
ui->cbCANSpeed->addItem("10000");
|
|
ui->cbCANSpeed->addItem("20000");
|
|
ui->cbCANSpeed->addItem("50000");
|
|
ui->cbCANSpeed->addItem("83333");
|
|
ui->cbCANSpeed->addItem("100000");
|
|
ui->cbCANSpeed->addItem("125000");
|
|
ui->cbCANSpeed->addItem("250000");
|
|
ui->cbCANSpeed->addItem("500000");
|
|
ui->cbCANSpeed->addItem("1000000");
|
|
}
|
|
if (ui->cbDataRate->count() == 0)
|
|
{
|
|
ui->cbDataRate->addItem("1000000");
|
|
ui->cbDataRate->addItem("2000000");
|
|
ui->cbDataRate->addItem("4000000");
|
|
ui->cbDataRate->addItem("5000000");
|
|
}
|
|
|
|
//default to the most common bus speed rather than the lowest one in the list
|
|
int idx = ui->cbCANSpeed->findText("500000");
|
|
if (idx > -1) ui->cbCANSpeed->setCurrentIndex(idx);
|
|
}
|
|
|
|
void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QString pDriver)
|
|
{
|
|
|
|
switch(pType)
|
|
{
|
|
case CANCon::GVRET_SERIAL:
|
|
ui->rbGVRET->setChecked(true);
|
|
break;
|
|
case CANCon::SERIALBUS:
|
|
ui->rbSocketCAN->setChecked(true);
|
|
break;
|
|
case CANCon::REMOTE:
|
|
ui->rbRemote->setChecked(true);
|
|
break;
|
|
case CANCon::KAYAK:
|
|
ui->rbKayak->setChecked(true);
|
|
break;
|
|
case CANCon::MQTT:
|
|
ui->rbMQTT->setChecked(true);
|
|
break;
|
|
case CANCon::LAWICEL:
|
|
ui->rbLawicel->setChecked(true);
|
|
break;
|
|
case CANCon::CANSERVER:
|
|
ui->rbCANserver->setChecked(true);
|
|
break;
|
|
case CANCon::CANLOGSERVER:
|
|
ui->rbCanlogserver->setChecked(true);
|
|
break;
|
|
case CANCon::GS_USB:
|
|
ui->rbGSUSB->setChecked(true);
|
|
break;
|
|
default: {}
|
|
}
|
|
|
|
/* refresh names whenever needed */
|
|
//handleConnTypeChanged();
|
|
|
|
switch(pType)
|
|
{
|
|
case CANCon::GVRET_SERIAL:
|
|
case CANCon::LAWICEL:
|
|
case CANCon::GS_USB:
|
|
{
|
|
int idx = ui->cbPort->findText(pPortName);
|
|
if( idx<0 ) idx=0;
|
|
ui->cbPort->setCurrentIndex(idx);
|
|
break;
|
|
}
|
|
case CANCon::SERIALBUS:
|
|
{
|
|
int idx = ui->cbDeviceType->findText(pDriver);
|
|
if (idx < 0) idx = 0;
|
|
ui->cbDeviceType->setCurrentIndex(idx);
|
|
idx = ui->cbPort->findText(pPortName);
|
|
if( idx < 0 ) idx = 0;
|
|
ui->cbPort->setCurrentIndex(idx);
|
|
break;
|
|
}
|
|
case CANCon::REMOTE:
|
|
{
|
|
int idx = ui->cbPort->findText(pPortName);
|
|
if (idx > -1) ui->cbPort->setCurrentIndex(idx);
|
|
else ui->cbPort->addItem(pPortName);
|
|
break;
|
|
}
|
|
case CANCon::KAYAK:
|
|
{
|
|
int idx = ui->cbPort->findText(pPortName);
|
|
if (idx > -1) ui->cbPort->setCurrentIndex(idx);
|
|
else ui->cbPort->addItem(pPortName);
|
|
break;
|
|
}
|
|
case CANCon::MQTT:
|
|
ui->cbPort->setCurrentText(pPortName);
|
|
break;
|
|
case CANCon::CANSERVER:
|
|
case CANCon::CANLOGSERVER:
|
|
{
|
|
ui->cbPort->setCurrentText(pPortName);
|
|
break;
|
|
}
|
|
default: {}
|
|
}
|
|
}
|
|
|
|
QString NewConnectionDialog::getPortName()
|
|
{
|
|
switch( getConnectionType() ) {
|
|
case CANCon::GVRET_SERIAL:
|
|
case CANCon::SERIALBUS:
|
|
case CANCon::REMOTE:
|
|
case CANCon::MQTT:
|
|
case CANCon::LAWICEL:
|
|
case CANCon::GS_USB:
|
|
return ui->cbPort->currentText();
|
|
case CANCon::KAYAK:
|
|
return ui->cbPort->currentText();
|
|
case CANCon::CANSERVER:
|
|
case CANCon::CANLOGSERVER:
|
|
return ui->cbPort->currentText();
|
|
|
|
default:
|
|
qDebug() << "getPortName: can't get port";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
QString NewConnectionDialog::getDriverName()
|
|
{
|
|
if (getConnectionType() == CANCon::SERIALBUS)
|
|
{
|
|
return ui->cbDeviceType->currentText();
|
|
}
|
|
return "N/A";
|
|
}
|
|
|
|
int NewConnectionDialog::getSerialSpeed()
|
|
{
|
|
if (getConnectionType() == CANCon::LAWICEL)
|
|
{
|
|
return ui->cbSerialSpeed->currentText().toInt();
|
|
}
|
|
else return 0;
|
|
}
|
|
|
|
int NewConnectionDialog::getBusSpeed()
|
|
{
|
|
if (getConnectionType() == CANCon::LAWICEL || getConnectionType() == CANCon::GS_USB)
|
|
{
|
|
return ui->cbCANSpeed->currentText().toInt();
|
|
}
|
|
else return 0;
|
|
}
|
|
|
|
CANCon::type NewConnectionDialog::getConnectionType()
|
|
{
|
|
if (ui->rbGVRET->isChecked()) return CANCon::GVRET_SERIAL;
|
|
if (ui->rbSocketCAN->isChecked()) return CANCon::SERIALBUS;
|
|
if (ui->rbRemote->isChecked()) return CANCon::REMOTE;
|
|
if (ui->rbKayak->isChecked()) return CANCon::KAYAK;
|
|
if (ui->rbMQTT->isChecked()) return CANCon::MQTT;
|
|
if (ui->rbLawicel->isChecked()) return CANCon::LAWICEL;
|
|
if (ui->rbCANserver->isChecked()) return CANCon::CANSERVER;
|
|
if (ui->rbCanlogserver->isChecked()) return CANCon::CANLOGSERVER;
|
|
if (ui->rbGSUSB->isChecked()) return CANCon::GS_USB;
|
|
qDebug() << "getConnectionType: error";
|
|
|
|
return CANCon::NONE;
|
|
}
|
|
|
|
bool NewConnectionDialog::isSerialBusAvailable()
|
|
{
|
|
if (QCanBus::instance()->plugins().count() > 0) return true;
|
|
return false;
|
|
}
|
|
|
|
int NewConnectionDialog::getDataRate()
|
|
{
|
|
if (getConnectionType() == CANCon::LAWICEL || getConnectionType() == CANCon::GS_USB)
|
|
{
|
|
return ui->cbDataRate->currentText().toInt();
|
|
}
|
|
else return 0;
|
|
}
|
|
|
|
bool NewConnectionDialog::isCanFd()
|
|
{
|
|
if (getConnectionType() == CANCon::GS_USB)
|
|
{
|
|
return ui->cbCanFd->isChecked();
|
|
}
|
|
if (getConnectionType() == CANCon::LAWICEL)
|
|
{
|
|
return ui->cbCanFd;
|
|
}
|
|
else return 0;
|
|
}
|