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
173 lines
4.7 KiB
C++
173 lines
4.7 KiB
C++
#include "canconnectionmodel.h"
|
|
#include "connections/canconnection.h"
|
|
#include "connections/canconmanager.h"
|
|
|
|
CANConnectionModel::CANConnectionModel(QObject *parent)
|
|
: QAbstractTableModel(parent)
|
|
{
|
|
}
|
|
|
|
CANConnectionModel::~CANConnectionModel()
|
|
{
|
|
}
|
|
|
|
enum class Column {
|
|
Type = 0, ///< The CAN driver/backend type, e.g. GVRET, peakcan, or socketcan
|
|
Subtype = 1, ///< Mostly used by SerialBus devices to pick the sub type
|
|
Port = 2, ///< The CAN hardware port, e.g. can0 for socketcan
|
|
NumBuses = 3, ///< Number of buses exposed by this device. Usually non-GVRET devices will just have one
|
|
Status = 4 ///< The bus status as text message
|
|
};
|
|
|
|
QVariant CANConnectionModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (role != Qt::DisplayRole)
|
|
return QVariant();
|
|
|
|
if (orientation == Qt::Horizontal)
|
|
{
|
|
switch (Column(section))
|
|
{
|
|
case Column::Type:
|
|
return QString(tr("Type"));
|
|
case Column::Subtype:
|
|
return QString(tr("Subtype"));
|
|
case Column::Port:
|
|
return QString(tr("Port"));
|
|
case Column::NumBuses:
|
|
return QString(tr("Buses"));
|
|
case Column::Status:
|
|
return QString(tr("Status"));
|
|
}
|
|
}
|
|
|
|
else
|
|
return QString::number(section + 1);
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
int CANConnectionModel::columnCount(const QModelIndex &parent) const
|
|
{
|
|
Q_UNUSED(parent);
|
|
return 5;
|
|
}
|
|
|
|
|
|
int CANConnectionModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
Q_UNUSED(parent);
|
|
|
|
QList<CANConnection*>& conns = CANConManager::getInstance()->getConnections();
|
|
|
|
return conns.count();
|
|
}
|
|
|
|
QVariant CANConnectionModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (!index.isValid())
|
|
return QVariant();
|
|
//qDebug() << "Row: " << index.row();
|
|
|
|
CANConnection *conn_p = getAtIdx(index.row());
|
|
|
|
if (!conn_p) return QVariant();
|
|
|
|
//bool isSocketCAN = (conn_p->getType() == CANCon::SERIALBUS) ? true: false;
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
|
|
switch (Column(index.column()))
|
|
{
|
|
case Column::Type:
|
|
if (conn_p)
|
|
switch (conn_p->getType()) {
|
|
case CANCon::KVASER: return "KVASER";
|
|
case CANCon::SERIALBUS: return "SerialBus";
|
|
case CANCon::GVRET_SERIAL: return "GVRET";
|
|
case CANCon::KAYAK: return "socketcand";
|
|
case CANCon::LAWICEL: return "LAWICEL";
|
|
case CANCon::CANSERVER: return "CANserver";
|
|
case CANCon::CANLOGSERVER: return "CanLogServer";
|
|
case CANCon::GS_USB: return "gs_usb";
|
|
default: {}
|
|
}
|
|
else qDebug() << "Tried to show connection type but connection was nullptr";
|
|
break;
|
|
case Column::Port:
|
|
if (conn_p) return conn_p->getPort();
|
|
else qDebug() << "Tried to show connection port but connection was nullptr";
|
|
break;
|
|
case Column::Subtype:
|
|
return conn_p->getDriver();
|
|
break;
|
|
case Column::NumBuses:
|
|
return conn_p->getNumBuses();
|
|
break;
|
|
case Column::Status:
|
|
return (conn_p->getStatus()==CANCon::CONNECTED) ? "Connected" : "Not Connected";
|
|
}
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
|
|
void CANConnectionModel::add(CANConnection* pConn_p)
|
|
{
|
|
CANConManager* manager = CANConManager::getInstance();
|
|
|
|
beginResetModel();
|
|
manager->add(pConn_p);
|
|
endResetModel();
|
|
}
|
|
|
|
|
|
void CANConnectionModel::remove(CANConnection* pConn_p)
|
|
{
|
|
CANConManager* manager = CANConManager::getInstance();
|
|
|
|
beginResetModel();
|
|
manager->remove(pConn_p);
|
|
endResetModel();
|
|
}
|
|
|
|
void CANConnectionModel::replace(int idx , CANConnection* pConn_p)
|
|
{
|
|
CANConManager* manager = CANConManager::getInstance();
|
|
|
|
beginResetModel();
|
|
manager->replace(idx, pConn_p);
|
|
endResetModel();
|
|
}
|
|
|
|
CANConnection* CANConnectionModel::getAtIdx(int pIdx) const
|
|
{
|
|
if (pIdx < 0)
|
|
return nullptr;
|
|
|
|
QList<CANConnection*>& conns = CANConManager::getInstance()->getConnections();
|
|
|
|
return conns.at(pIdx);
|
|
}
|
|
|
|
void CANConnectionModel::refresh(int pIndex)
|
|
{
|
|
Q_UNUSED(pIndex)
|
|
|
|
beginResetModel();
|
|
endResetModel();
|
|
/*
|
|
QModelIndex begin;
|
|
QModelIndex end;
|
|
|
|
if(pIndex>=0) {
|
|
begin = createIndex(pIndex, 0);
|
|
end = createIndex(pIndex, columnCount()-1);
|
|
}
|
|
else {
|
|
begin = createIndex(0, 0);
|
|
end = createIndex(rowCount()-1, columnCount()-1);
|
|
}
|
|
dataChanged(begin, end, QVector<int>(Qt::DisplayRole)); */
|
|
}
|