diff --git a/.gitignore b/.gitignore index fd8a706..6cb0bf6 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ Makefile # Editor files *.code-workspace SavvyCAN.pro.qtds +.xcode +SavvyCAN.xcodeproj diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 822ab10..57d34d2 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -20,6 +20,7 @@ QMAKE_INFO_PLIST = Info.plist.template ICON = icons/SavvyIcon.icns SOURCES += main.cpp\ + connections/canserver.cpp \ connections/lawicel_serial.cpp \ connections/mqtt_bus.cpp \ dbc/dbcnodeduplicateeditor.cpp \ @@ -101,6 +102,7 @@ SOURCES += main.cpp\ HEADERS += mainwindow.h \ can_structs.h \ canframemodel.h \ + connections/canserver.h \ connections/lawicel_serial.h \ connections/socketcand.h \ connections/mqtt_bus.h \ diff --git a/connections/canconconst.h b/connections/canconconst.h index 2cf53cc..88fa31e 100644 --- a/connections/canconconst.h +++ b/connections/canconconst.h @@ -21,6 +21,7 @@ namespace CANCon { KAYAK, MQTT, LAWICEL, + CANSERVER, NONE }; } diff --git a/connections/canconfactory.cpp b/connections/canconfactory.cpp index 9ff5bd8..0b25904 100644 --- a/connections/canconfactory.cpp +++ b/connections/canconfactory.cpp @@ -5,6 +5,7 @@ #include "mqtt_bus.h" #include "socketcand.h" #include "lawicel_serial.h" +#include "canserver.h" using namespace CANCon; @@ -26,6 +27,8 @@ CANConnection* CanConFactory::create(type pType, QString pPortName, QString pDri return new SocketCANd(pPortName); case MQTT: return new MQTT_BUS(pPortName); + case CANSERVER: + return new CANserver(pPortName); default: {} } diff --git a/connections/canconnectionmodel.cpp b/connections/canconnectionmodel.cpp index 32a277f..b3a5e09 100644 --- a/connections/canconnectionmodel.cpp +++ b/connections/canconnectionmodel.cpp @@ -87,6 +87,7 @@ QVariant CANConnectionModel::data(const QModelIndex &index, int role) const case CANCon::GVRET_SERIAL: return "GVRET"; case CANCon::KAYAK: return "socketcand"; case CANCon::LAWICEL: return "LAWICEL"; + case CANCon::CANSERVER: return "CANserver"; default: {} } else qDebug() << "Tried to show connection type but connection was nullptr"; diff --git a/connections/canserver.cpp b/connections/canserver.cpp new file mode 100644 index 0000000..e304f83 --- /dev/null +++ b/connections/canserver.cpp @@ -0,0 +1,238 @@ +// +// canserver.cpp +// SavvyCAN +// +// Created by Chris Whiteford on 2022-01-21. +// + +#include +#include +#include +#include +#include +#include + +#include "utility.h" +#include "canserver.h" + +CANserver::CANserver(QString serverAddressString) : + CANConnection(serverAddressString, "CANserver", CANCon::CANSERVER, 0, 0, 3, 4000, true), + _udpClient(new QUdpSocket(this)), + _heartbeatTimer(new QTimer(this)) +{ + + qDebug() << "CANserver: " << "Constructing new Connection..."; + + CANBus bus_info; + bus_info.setActive(true); + bus_info.setListenOnly(true); + bus_info.setSpeed(500000); + + setBusConfig(0, bus_info); + setBusConfig(1, bus_info); + setBusConfig(2, bus_info); + + // setup udp client + this->_canserverAddress = QHostAddress(serverAddressString); + connect(_udpClient, SIGNAL(readyRead()), this, SLOT(readNetworkData())); + + // setup signal and slot + connect(_heartbeatTimer, SIGNAL(timeout()), this, SLOT(heartbeatTimerSlot())); + _heartbeatTimer->stop(); +} + + +CANserver::~CANserver() +{ + qDebug() << "CANserver: " << "Deconstructing Connection..."; + + stop(); + + delete _heartbeatTimer; _heartbeatTimer = NULL; + delete _udpClient; _udpClient = NULL; +} + +void CANserver::piStarted() +{ + this->connectToDevice(); +} + +void CANserver::piSuspend(bool pSuspend) +{ + /* update capSuspended */ + setCapSuspended(pSuspend); + + /* flush queue if we are suspended */ + if(isCapSuspended()) + getQueue().flush(); +} + + +void CANserver::piStop() +{ + this->disconnectFromDevice(); +} + + +bool CANserver::piGetBusSettings(int pBusIdx, CANBus& pBus) +{ + return getBusConfig(pBusIdx, pBus); +} + + +void CANserver::piSetBusSettings(int pBusIdx, CANBus bus) +{ + /* sanity checks */ + if( (pBusIdx < 0) || pBusIdx >= getNumBuses()) + return; + + /* copy bus config */ + setBusConfig(pBusIdx, bus); +} + + +bool CANserver::piSendFrame(const CANFrame& ) +{ + //We don't support sending frames right now + return true; +} + +void CANserver::connectToDevice() +{ + qDebug() << "CANserver: " << "Establishing UDP connection to a CANserver device..."; + + bool bindResult = _udpClient->bind(1338); + qDebug() << "CANserver: " << "UDP Bind result: " << bindResult; + + heartbeat(); + + qDebug() << "CANserver: " << "Scheduling additional heartbeats..."; + _heartbeatTimer->start(3500); + + setStatus(CANCon::CONNECTED); + + CANConStatus stats; + stats.conStatus = getStatus(); + stats.numHardwareBuses = 3; + emit status(stats); +} + +void CANserver::disconnectFromDevice() +{ + _heartbeatTimer->stop(); + + QByteArray Data; + Data.append("bye"); + QNetworkDatagram datagram(Data, _canserverAddress, 1338); + + qDebug() << "CANserver: " << "Sending bye..."; + _udpClient->writeDatagram(datagram); + + + _udpClient->close(); +} + +void CANserver::heartbeat() +{ + //Send the PANDA hello packet (in this case we are using the v1 version of the protocol because we just want to get ALL the data) + QByteArray Data; + Data.append("hello"); + QNetworkDatagram datagram(Data, _canserverAddress, 1338); + + qDebug() << "CANserver: " << "Sending a heartbeat..."; + _udpClient->writeDatagram(datagram); +} + +void CANserver::readNetworkData() +{ + QByteArray datagram; + do { + datagram.resize(_udpClient->pendingDatagramSize()); + _udpClient->readDatagram(datagram.data(), datagram.size()); + } while (_udpClient->hasPendingDatagrams()); + + + // If capture is suspended bail out after reading the bytes from the network. No need to parse them + if(isCapSuspended()) + return; + + + uint16_t packetCount = datagram.length() / 16; + //qDebug() << "Processing " << packetCount << " packets"; + + //Process this chunk of data + for (int i = 0 ; i < packetCount; i++) + { + uint16_t headerByteLocation = (i*16); + uint16_t dataByteLocation = (i*16) + 8; + + + uint32_t headerData1Byte1 = datagram.at(headerByteLocation + 0) & 0xFF; + uint32_t headerData1Byte2 = datagram.at(headerByteLocation + 1) & 0xFF; + uint32_t headerData1Byte3 = datagram.at(headerByteLocation + 2) & 0xFF; + uint32_t headerData1Byte4 = datagram.at(headerByteLocation + 3) & 0xFF; + + uint32_t headerData2Byte1 = datagram.at(headerByteLocation + 4) & 0xFF; + uint32_t headerData2Byte2 = datagram.at(headerByteLocation + 5) & 0xFF; + uint32_t headerData2Byte3 = datagram.at(headerByteLocation + 6) & 0xFF; + uint32_t headerData2Byte4 = datagram.at(headerByteLocation + 7) & 0xFF; + + + //printf("HB1: %02X, %02X, %02X, %02X\n", headerData1Byte1, headerData1Byte2, headerData1Byte3, headerData1Byte4); + //printf("HB2: %02X, %02X, %02X, %02X\n", headerData2Byte1, headerData2Byte2, headerData2Byte3, headerData2Byte4); + + uint32_t headerData1 = headerData1Byte1; + headerData1 += headerData1Byte2 << 8; + headerData1 += headerData1Byte3 << 16; + headerData1 += headerData1Byte4 << 24; + + uint32_t headerData2 = headerData2Byte1; + headerData2 += headerData2Byte2 << 8; + headerData2 += headerData2Byte3 << 16; + headerData2 += headerData2Byte4 << 24; + + uint32_t frameId = headerData1 >> 21; + uint32_t length = (headerData2 & 0x0F); + uint32_t busId = (headerData2 >> 4); + + //printf("frameId: %02X, busId: %d, length: %d\n", frameId, busId, length); + + CANFrame* frame_p = getQueue().get(); + if(frame_p) + { + frame_p->setFrameId(frameId); + frame_p->setExtendedFrameFormat(0); + + // We need to change the bus id if it is the special CANserver bus id. + // This keeps us from needing to define 15 busses just to get access to our special one + if (busId == 15) + { + busId = 2; + } + frame_p->bus = busId; + + frame_p->setFrameType(QCanBusFrame::DataFrame); + frame_p->isReceived = true; + + frame_p->setTimeStamp(QCanBusFrame::TimeStamp(0, QDateTime::currentMSecsSinceEpoch() * 1000ul)); + + frame_p->setPayload(datagram.mid(dataByteLocation, length)); + + checkTargettedFrame(*frame_p); + + /* enqueue frame */ + getQueue().queue(); + } + } + +} + +void CANserver::heartbeatTimerSlot() +{ + heartbeat(); +} + +void CANserver::readSettings() +{ + QSettings settings; +} diff --git a/connections/canserver.h b/connections/canserver.h new file mode 100644 index 0000000..c7e8f1c --- /dev/null +++ b/connections/canserver.h @@ -0,0 +1,64 @@ +// +// canserver.h +// SavvyCAN +// +// Created by Chris Whiteford on 2022-01-21. +// + +#ifndef canserver_h +#define canserver_h + +#include + +#include +#include +#include +#include + +/*************/ +#include +/*************/ + +#include "canframemodel.h" +#include "canconnection.h" +#include "canconmanager.h" + +class CANserver : public CANConnection +{ + Q_OBJECT + +public: + CANserver(QString serverAddress); + virtual ~CANserver(); + +protected: + + virtual void piStarted(); + virtual void piStop(); + virtual void piSetBusSettings(int pBusIdx, CANBus pBus); + virtual bool piGetBusSettings(int pBusIdx, CANBus& pBus); + virtual void piSuspend(bool pSuspend); + virtual bool piSendFrame(const CANFrame&); + +private slots: + void readNetworkData(); + void heartbeatTimerSlot(); + + +private: + void readSettings(); + + void connectToDevice(); + void disconnectFromDevice(); + + void heartbeat(); + +protected: + QHostAddress _canserverAddress; + + QUdpSocket *_udpClient; + + QTimer *_heartbeatTimer; +}; + +#endif /* canserver_h */ diff --git a/connections/newconnectiondialog.cpp b/connections/newconnectiondialog.cpp index e6b1b24..9533848 100644 --- a/connections/newconnectiondialog.cpp +++ b/connections/newconnectiondialog.cpp @@ -28,6 +28,7 @@ NewConnectionDialog::NewConnectionDialog(QVector* gvretips, QVectorrbKayak, &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->cbDeviceType, QOverload::of(&QComboBox::currentIndexChanged), this, &NewConnectionDialog::handleDeviceTypeChanged); connect(ui->btnOK, &QPushButton::clicked, this, &NewConnectionDialog::handleCreateButton); @@ -62,6 +63,7 @@ void NewConnectionDialog::handleConnTypeChanged() if (ui->rbRemote->isChecked()) selectRemote(); if (ui->rbKayak->isChecked()) selectKayak(); if (ui->rbMQTT->isChecked()) selectMQTT(); + if (ui->rbCANserver->isChecked()) selectCANserver(); } void NewConnectionDialog::handleDeviceTypeChanged() @@ -202,6 +204,20 @@ void NewConnectionDialog::selectMQTT() 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->cbPort->clear(); +} + void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QString pDriver) { @@ -224,6 +240,10 @@ void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QSt break; case CANCon::LAWICEL: ui->rbLawicel->setChecked(true); + break; + case CANCon::CANSERVER: + ui->rbCANserver->setChecked(true); + break; default: {} } @@ -267,6 +287,11 @@ void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QSt case CANCon::MQTT: ui->cbPort->setCurrentText(pPortName); break; + case CANCon::CANSERVER: + { + ui->cbPort->setCurrentText(pPortName); + break; + } default: {} } } @@ -282,6 +307,9 @@ QString NewConnectionDialog::getPortName() return ui->cbPort->currentText(); case CANCon::KAYAK: return ui->cbPort->currentText(); + case CANCon::CANSERVER: + return ui->cbPort->currentText(); + default: qDebug() << "getPortName: can't get port"; } @@ -324,6 +352,7 @@ CANCon::type NewConnectionDialog::getConnectionType() 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; qDebug() << "getConnectionType: error"; return CANCon::NONE; diff --git a/connections/newconnectiondialog.h b/connections/newconnectiondialog.h index 938d2d2..9901b83 100644 --- a/connections/newconnectiondialog.h +++ b/connections/newconnectiondialog.h @@ -46,6 +46,7 @@ private: void selectKayak(); void selectMQTT(); void selectLawicel(); + void selectCANserver(); bool isSerialBusAvailable(); void setPortName(CANCon::type pType, QString pPortName, QString pDriver); }; diff --git a/ui/newconnectiondialog.ui b/ui/newconnectiondialog.ui index a257907..990467e 100644 --- a/ui/newconnectiondialog.ui +++ b/ui/newconnectiondialog.ui @@ -71,6 +71,13 @@ + + + + CANserver + + +