@@ -1,7 +1,8 @@
|
||||
language: cpp
|
||||
|
||||
# Any time a commit is done to either master or WIP we do a CI build on all three platforms to make sure it really builds. The linux version will be saved to the continuous tag
|
||||
# and thus available for testing. All three platforms will get a proper release upon a tagged commit.
|
||||
# Any time a commit is done to either master or WIP we do a CI build on all three platforms to make
|
||||
# sure it really builds. The linux version will be saved to the continuous tag and thus available
|
||||
# for testing. All three platforms will get a proper release upon a tagged commit.
|
||||
|
||||
matrix:
|
||||
include:
|
||||
@@ -10,11 +11,11 @@ matrix:
|
||||
compiler: gcc
|
||||
sudo: require
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:beineri/opt-qt-5.12.3-xenial -y
|
||||
- sudo add-apt-repository ppa:beineri/opt-qt-5.14.1-xenial -y
|
||||
- sudo apt-get update -qq
|
||||
|
||||
install:
|
||||
- sudo apt-get -y install qt512base qt512connectivity qt512imageformats qt512serialport qt512serialbus qt512doc qt512tools libgl1-mesa-dev
|
||||
- sudo apt-get -y install qt514base qt514connectivity qt514imageformats qt514serialport qt514serialbus qt514doc qt514tools libgl1-mesa-dev
|
||||
- source /opt/qt*/bin/qt*-env.sh
|
||||
|
||||
script:
|
||||
@@ -42,56 +43,27 @@ matrix:
|
||||
tags: true #deploy is only done on tagged builds
|
||||
draft: false
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode10
|
||||
before_install:
|
||||
- brew install qt5
|
||||
- brew link qt5 --force
|
||||
script:
|
||||
- qmake CONFIG+=release -spec macx-xcode SavvyCAN.pro
|
||||
- xcodebuild
|
||||
- mkdir Release/SavvyCAN.app/Contents/MacOS/help
|
||||
- cp -R help/*.* Release/SavvyCAN.app/Contents/MacOS/help
|
||||
- cd Release
|
||||
- ls SavvyCAN.app/Contents/MacOS
|
||||
- macdeployqt SavvyCAN.app -dmg
|
||||
deploy:
|
||||
provider: releases
|
||||
skip_cleanup: true
|
||||
api_key: $GITHUB_TOKEN
|
||||
file: SavvyCAN.dmg
|
||||
on:
|
||||
tags: true
|
||||
draft: false
|
||||
|
||||
- os: windows
|
||||
before_install:
|
||||
- cmd.exe /C 'cd && "C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/VC/Auxiliary/Build/vcvars32.bat"'
|
||||
- choco install zip
|
||||
- curl -vLO http://download.qt.io/official_releases/online_installers/qt-unified-windows-x86-online.exe
|
||||
- "./qt-unified-windows-x86-online.exe --verbose --script qt-installer-windows.qs"
|
||||
script:
|
||||
- cmd.exe /C 'cd && "C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/VC/Auxiliary/Build/vcvars32.bat" && cd && "C:/Qt/5.12.3/msvc2017/bin/qmake" CONFIG+=RELEASE SavvyCAN.pro && cd && nmake'
|
||||
- ls
|
||||
- cmd.exe /C 'cd release && "C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/VC/Auxiliary/Build/vcvars32.bat" && "C:/Qt/5.12.3/msvc2017/bin/windeployqt.exe" ./SavvyCAN.exe'
|
||||
- cd release
|
||||
- ls
|
||||
- rm *.cpp
|
||||
- rm *.h
|
||||
- rm *.obj
|
||||
- mkdir help
|
||||
- cp -R ../help/*.* ./help
|
||||
- cp -r ../examples .
|
||||
- 7z a -tzip SavvyCAN32.zip -r *
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
skip_cleanup: true
|
||||
api_key: $GITHUB_TOKEN
|
||||
file: SavvyCAN32.zip
|
||||
on:
|
||||
tags: true
|
||||
draft: false
|
||||
# - os: osx
|
||||
# osx_image: xcode10
|
||||
# before_install:
|
||||
# - brew install qt5
|
||||
# - brew link qt5 --force
|
||||
# script:
|
||||
# - qmake CONFIG+=release -spec macx-xcode SavvyCAN.pro
|
||||
# - xcodebuild
|
||||
# - mkdir Release/SavvyCAN.app/Contents/MacOS/help
|
||||
# - cp -R help/*.* Release/SavvyCAN.app/Contents/MacOS/help
|
||||
# - cd Release
|
||||
# - ls SavvyCAN.app/Contents/MacOS
|
||||
# - macdeployqt SavvyCAN.app -dmg
|
||||
# deploy:
|
||||
# provider: releases
|
||||
# skip_cleanup: true
|
||||
# api_key: $GITHUB_TOKEN
|
||||
# file: SavvyCAN.dmg
|
||||
# on:
|
||||
# tags: true
|
||||
# draft: false
|
||||
|
||||
branches:
|
||||
except:
|
||||
|
||||
@@ -97,3 +97,18 @@ when you installed Qt.
|
||||
### What to do if `qmake` fails with error `Project ERROR: Unknown module(s) in QT: qml serialbus help` on Ubuntu? :
|
||||
|
||||
`sudo apt install libqt5serialbus5-dev qtdeclarative5-dev qttools5-dev`
|
||||
|
||||
### Used Items Requiring Attribution
|
||||
|
||||
nodes by Adrien Coquet from the Noun Project
|
||||
|
||||
message by Vectorstall from the Noun Project
|
||||
|
||||
signal by shashank singh from the Noun Project
|
||||
|
||||
signal by juli from the Noun Project
|
||||
|
||||
signal by yudi from the Noun Project
|
||||
|
||||
Death by Adrien Coquet from the Noun Project
|
||||
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT = core gui printsupport qml serialbus serialport widgets help
|
||||
QT = core gui printsupport qml serialbus serialport widgets help network
|
||||
|
||||
CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT
|
||||
|
||||
CONFIG += c++11
|
||||
CONFIG += NO_UNIT_TESTS
|
||||
|
||||
DEFINES += QCUSTOMPLOT_USE_OPENGL
|
||||
|
||||
@@ -16,8 +17,23 @@ TARGET = SavvyCAN
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += main.cpp\
|
||||
connections/mqtt_bus.cpp \
|
||||
mqtt/qmqtt_client.cpp \
|
||||
mqtt/qmqtt_client_p.cpp \
|
||||
mqtt/qmqtt_frame.cpp \
|
||||
mqtt/qmqtt_message.cpp \
|
||||
mqtt/qmqtt_network.cpp \
|
||||
mqtt/qmqtt_router.cpp \
|
||||
mqtt/qmqtt_routesubscription.cpp \
|
||||
mqtt/qmqtt_socket.cpp \
|
||||
mqtt/qmqtt_ssl_socket.cpp \
|
||||
mqtt/qmqtt_timer.cpp \
|
||||
mqtt/qmqtt_websocket.cpp \
|
||||
mqtt/qmqtt_websocketiodevice.cpp \
|
||||
re/dbccomparatorwindow.cpp \
|
||||
mainwindow.cpp \
|
||||
canframemodel.cpp \
|
||||
simplecrypt.cpp \
|
||||
utility.cpp \
|
||||
qcustomplot.cpp \
|
||||
frameplaybackwindow.cpp \
|
||||
@@ -39,10 +55,12 @@ SOURCES += main.cpp\
|
||||
re/sniffer/snifferitem.cpp \
|
||||
re/sniffer/sniffermodel.cpp \
|
||||
re/sniffer/snifferwindow.cpp \
|
||||
dbc/dbcmessageeditor.cpp \
|
||||
dbc/dbc_classes.cpp \
|
||||
dbc/dbchandler.cpp \
|
||||
dbc/dbcloadsavewindow.cpp \
|
||||
dbc/dbcmaineditor.cpp \
|
||||
dbc/dbcnodeeditor.cpp \
|
||||
dbc/dbcsignaleditor.cpp \
|
||||
re/discretestatewindow.cpp \
|
||||
re/filecomparatorwindow.cpp \
|
||||
@@ -73,7 +91,29 @@ SOURCES += main.cpp\
|
||||
|
||||
HEADERS += mainwindow.h \
|
||||
can_structs.h \
|
||||
canframemodel.h \
|
||||
canframemodel.h \ \
|
||||
connections/mqtt_bus.h \
|
||||
mqtt/qmqtt.h \
|
||||
mqtt/qmqtt_client.h \
|
||||
mqtt/qmqtt_client_p.h \
|
||||
mqtt/qmqtt_frame.h \
|
||||
mqtt/qmqtt_global.h \
|
||||
mqtt/qmqtt_message.h \
|
||||
mqtt/qmqtt_message_p.h \
|
||||
mqtt/qmqtt_network_p.h \
|
||||
mqtt/qmqtt_networkinterface.h \
|
||||
mqtt/qmqtt_routedmessage.h \
|
||||
mqtt/qmqtt_router.h \
|
||||
mqtt/qmqtt_routesubscription.h \
|
||||
mqtt/qmqtt_socket_p.h \
|
||||
mqtt/qmqtt_socketinterface.h \
|
||||
mqtt/qmqtt_ssl_socket_p.h \
|
||||
mqtt/qmqtt_timer_p.h \
|
||||
mqtt/qmqtt_timerinterface.h \
|
||||
mqtt/qmqtt_websocket_p.h \
|
||||
mqtt/qmqtt_websocketiodevice_p.h \
|
||||
re/dbccomparatorwindow.h \
|
||||
simplecrypt.h \
|
||||
utility.h \
|
||||
qcustomplot.h \
|
||||
frameplaybackwindow.h \
|
||||
@@ -103,6 +143,8 @@ HEADERS += mainwindow.h \
|
||||
dbc/dbcloadsavewindow.h \
|
||||
dbc/dbcmaineditor.h \
|
||||
dbc/dbcsignaleditor.h \
|
||||
dbc/dbcmessageeditor.h \
|
||||
dbc/dbcnodeeditor.h \
|
||||
re/discretestatewindow.h \
|
||||
re/filecomparatorwindow.h \
|
||||
re/flowviewwindow.h \
|
||||
@@ -132,10 +174,13 @@ HEADERS += mainwindow.h \
|
||||
filterutility.h
|
||||
|
||||
FORMS += ui/candatagrid.ui \
|
||||
ui/dbccomparatorwindow.ui \
|
||||
ui/dbcmessageeditor.ui \
|
||||
ui/connectionwindow.ui \
|
||||
ui/dbcloadsavewindow.ui \
|
||||
ui/dbcmaineditor.ui \
|
||||
ui/dbcsignaleditor.ui \
|
||||
ui/dbcnodeeditor.ui \
|
||||
ui/discretestatewindow.ui \
|
||||
ui/filecomparatorwindow.ui \
|
||||
ui/firmwareuploaderwindow.ui \
|
||||
|
||||
@@ -64,9 +64,12 @@ bool BisectWindow::eventFilter(QObject *obj, QEvent *event)
|
||||
void BisectWindow::refreshIDList()
|
||||
{
|
||||
int id;
|
||||
foundID.clear();
|
||||
ui->cbIDLower->clear();
|
||||
ui->cbIDUpper->clear();
|
||||
for (int i = 0; i < modelFrames->count(); i++)
|
||||
{
|
||||
id = modelFrames->at(i).ID;
|
||||
id = modelFrames->at(i).frameId();
|
||||
if (!foundID.contains(id))
|
||||
{
|
||||
foundID.append(id);
|
||||
@@ -129,7 +132,7 @@ void BisectWindow::handleCalculateButton()
|
||||
uint32_t upperID = Utility::ParseStringToNum2(ui->cbIDUpper->currentText());
|
||||
for (int i = 0; i < modelFrames->count(); i++)
|
||||
{
|
||||
if (modelFrames->at(i).ID >= lowerID && modelFrames->at(i).ID <= upperID) splitFrames.append(modelFrames->at(i));
|
||||
if (modelFrames->at(i).frameId() >= lowerID && modelFrames->at(i).frameId() <= upperID) splitFrames.append(modelFrames->at(i));
|
||||
}
|
||||
}
|
||||
refreshFrameNumbers();
|
||||
@@ -137,7 +140,12 @@ void BisectWindow::handleCalculateButton()
|
||||
|
||||
void BisectWindow::handleReplaceButton()
|
||||
{
|
||||
|
||||
CANFrameModel *model;
|
||||
model = MainWindow::getReference()->getCANFrameModel();
|
||||
model->clearFrames();
|
||||
model->insertFrames(splitFrames);
|
||||
refreshFrameNumbers();
|
||||
refreshIDList();
|
||||
}
|
||||
|
||||
void BisectWindow::handleSaveButton()
|
||||
|
||||
@@ -67,7 +67,7 @@ bool BLFHandler::loadBLF(QString filename, QVector<CANFrame>* frames)
|
||||
qDebug() << "Container is not compressed";
|
||||
uncompressedData = fileData;
|
||||
}
|
||||
if (objHeader.containerObj.compressionMethod == BLF_CONT_ZLIB_COMPRESSION)
|
||||
else if (objHeader.containerObj.compressionMethod == BLF_CONT_ZLIB_COMPRESSION)
|
||||
{
|
||||
qDebug() << "Compressed container. Unpacking it.";
|
||||
fileData.prepend(objHeader.containerObj.uncompressedSize & 0xFF);
|
||||
@@ -76,9 +76,14 @@ bool BLFHandler::loadBLF(QString filename, QVector<CANFrame>* frames)
|
||||
fileData.prepend((objHeader.containerObj.uncompressedSize >> 24) & 0xFF);
|
||||
uncompressedData += qUncompress(fileData);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Dunno what this is... " << objHeader.containerObj.compressionMethod;
|
||||
}
|
||||
qDebug() << "Uncompressed size: " << uncompressedData.count();
|
||||
qDebug() << "Currently loaded frames at this point: " << frames->count();
|
||||
pos = 0;
|
||||
bool foundHeader = false;
|
||||
//bool foundHeader = false;
|
||||
//first skip forward to find a header signature - usually not necessary
|
||||
while ( (int)(pos + sizeof(BLF_OBJ_HEADER)) < uncompressedData.count())
|
||||
{
|
||||
@@ -91,7 +96,8 @@ bool BLFHandler::loadBLF(QString filename, QVector<CANFrame>* frames)
|
||||
{
|
||||
memcpy(&obj.header.base, (uncompressedData.constData() + pos), sizeof(BLF_OBJ_HEADER_BASE));
|
||||
memcpy(&obj.header.v1Obj, (uncompressedData.constData() + pos) + sizeof(BLF_OBJ_HEADER_BASE), sizeof(BLF_OBJ_HEADER_V1));
|
||||
qDebug() << "Pos: " << pos << " Type: " << obj.header.base.objType << "Obj Size: " << obj.header.base.objSize;
|
||||
//if (obj.header.base.objType != 1)
|
||||
//qDebug() << "Pos: " << pos << " Type: " << obj.header.base.objType << "Obj Size: " << obj.header.base.objSize;
|
||||
if (qFromLittleEndian(objHeader.base.sig) == 0x4A424F4C)
|
||||
{
|
||||
fileData = uncompressedData.mid(pos + sizeof(BLF_OBJ_HEADER_BASE) + sizeof(BLF_OBJ_HEADER_V1), obj.header.base.objSize - sizeof(BLF_OBJ_HEADER_BASE) - sizeof(BLF_OBJ_HEADER_V1));
|
||||
@@ -100,19 +106,20 @@ bool BLFHandler::loadBLF(QString filename, QVector<CANFrame>* frames)
|
||||
memcpy(&canObject, fileData.constData(), sizeof(BLF_CAN_OBJ));
|
||||
CANFrame frame;
|
||||
frame.bus = canObject.channel;
|
||||
frame.extended = (canObject.id & 0x80000000ull)?true:false;
|
||||
frame.ID = canObject.id & 0x1FFFFFFFull;
|
||||
frame.setExtendedFrameFormat((canObject.id & 0x80000000ull)?true:false);
|
||||
frame.setFrameId(canObject.id & 0x1FFFFFFFull);
|
||||
frame.isReceived = true;
|
||||
frame.len = canObject.dlc;
|
||||
QByteArray bytes(canObject.dlc, 0);
|
||||
|
||||
if (canObject.flags & BLF_REMOTE_FLAG) {
|
||||
frame.remote = true;
|
||||
frame.setFrameType(QCanBusFrame::RemoteRequestFrame);
|
||||
} else {
|
||||
frame.remote = false;
|
||||
for (int i = 0; i < 8; i++) frame.data[i] = canObject.data[i];
|
||||
frame.setFrameType(QCanBusFrame::DataFrame);
|
||||
for (int i = 0; i < canObject.dlc; i++) bytes[i] = canObject.data[i];
|
||||
}
|
||||
frame.setPayload(bytes);
|
||||
//Should we divide by a thousand or a million? Unsure here. It appears some logs are stamped in microseconds and some in milliseconds?
|
||||
frame.timestamp = obj.header.v1Obj.uncompSize / 1000.0; //uncompsize field also used for timestamp oddly enough
|
||||
frame.setTimeStamp(QCanBusFrame::TimeStamp(0, obj.header.v1Obj.uncompSize / 1000.0)); //uncompsize field also used for timestamp oddly enough
|
||||
frames->append(frame);
|
||||
}
|
||||
else if (obj.header.base.objType == BLF_CAN_MSG2)
|
||||
@@ -120,24 +127,24 @@ bool BLFHandler::loadBLF(QString filename, QVector<CANFrame>* frames)
|
||||
memcpy(&canObject2, fileData.constData(), sizeof(BLF_CAN_OBJ2));
|
||||
CANFrame frame;
|
||||
frame.bus = canObject2.channel;
|
||||
frame.extended = (canObject2.id & 0x80000000ull)?true:false;
|
||||
frame.ID = canObject2.id & 0x1FFFFFFFull;
|
||||
frame.setExtendedFrameFormat((canObject2.id & 0x80000000ull)?true:false);
|
||||
frame.setFrameId(canObject2.id & 0x1FFFFFFFull);
|
||||
frame.isReceived = true;
|
||||
frame.len = canObject2.dlc;
|
||||
QByteArray bytes(canObject2.dlc, 0);
|
||||
|
||||
if (canObject2.flags & BLF_REMOTE_FLAG) {
|
||||
frame.remote = true;
|
||||
frame.setFrameType(QCanBusFrame::RemoteRequestFrame);
|
||||
} else {
|
||||
frame.remote = false;
|
||||
for (int i = 0; i < 8; i++) frame.data[i] = canObject2.data[i];
|
||||
frame.setFrameType(QCanBusFrame::DataFrame);
|
||||
for (int i = 0; i < canObject2.dlc; i++) bytes[i] = canObject2.data[i];
|
||||
}
|
||||
//Should we divide by a thousand or a million? Unsure here. It appears some logs are stamped in microseconds and some in milliseconds?
|
||||
frame.timestamp = obj.header.v1Obj.uncompSize / 1000.0; //uncompsize field also used for timestamp oddly enough
|
||||
frame.setTimeStamp(QCanBusFrame::TimeStamp(0, obj.header.v1Obj.uncompSize / 1000.0)); //uncompsize field also used for timestamp oddly enough
|
||||
frames->append(frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
//qDebug() << "Not a can frame! ObjType: " << obj.header.objType;
|
||||
qDebug() << "Not a can frame! ObjType: " << obj.header.base.objType;
|
||||
if (obj.header.base.objType > 0xFFFF)
|
||||
return false;
|
||||
}
|
||||
@@ -162,5 +169,7 @@ bool BLFHandler::loadBLF(QString filename, QVector<CANFrame>* frames)
|
||||
|
||||
bool BLFHandler::saveBLF(QString filename, QVector<CANFrame> *frames)
|
||||
{
|
||||
Q_UNUSED(filename)
|
||||
Q_UNUSED(frames)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ ISOTP_HANDLER::ISOTP_HANDLER()
|
||||
isReceiving = false;
|
||||
issueFlowMsgs = false;
|
||||
processAll = false;
|
||||
sendPartialMessages = false;
|
||||
lastSenderBus = 0;
|
||||
lastSenderID = 0;
|
||||
|
||||
@@ -47,10 +48,10 @@ void ISOTP_HANDLER::setReception(bool mode)
|
||||
}
|
||||
}
|
||||
|
||||
void ISOTP_HANDLER::sendISOTPFrame(int bus, int ID, QVector<unsigned char> data)
|
||||
void ISOTP_HANDLER::sendISOTPFrame(int bus, int ID, QByteArray data)
|
||||
{
|
||||
CANFrame frame;
|
||||
frame.remote = false;
|
||||
frame.setFrameType(QCanBusFrame::DataFrame);
|
||||
int currByte = 0;
|
||||
int index = 0;
|
||||
if (bus < 0) return;
|
||||
@@ -59,29 +60,27 @@ void ISOTP_HANDLER::sendISOTPFrame(int bus, int ID, QVector<unsigned char> data)
|
||||
lastSenderID = ID;
|
||||
lastSenderBus = bus;
|
||||
|
||||
frame.bus = bus;
|
||||
frame.setFrameId(ID);
|
||||
if (ID > 0x7FF) frame.setExtendedFrameFormat(true);
|
||||
else frame.setExtendedFrameFormat(false);
|
||||
|
||||
if (data.length() < 8)
|
||||
{
|
||||
frame.bus = bus;
|
||||
frame.ID = ID;
|
||||
if (ID > 0x7FF) frame.extended = true;
|
||||
else frame.extended = false;
|
||||
frame.len = 8;
|
||||
for (int b = 0; b < 8; b++) frame.data[b] = 0x00;
|
||||
frame.data[0] = data.length();
|
||||
for (int i = 0; i < frame.data[0]; i++) frame.data[i + 1] = data[i];
|
||||
QByteArray bytes(8,0);
|
||||
bytes.resize(8);
|
||||
bytes[0] = data.length();
|
||||
for (int i = 0; i < data.length(); i++) bytes[i + 1] = data[i];
|
||||
frame.setPayload(bytes);
|
||||
CANConManager::getInstance()->sendFrame(frame);
|
||||
}
|
||||
else //need to send a multi-part ISO_TP message - Respects timing and frame number based flow control
|
||||
{
|
||||
frame.bus = bus;
|
||||
frame.ID = ID;
|
||||
if (ID > 0x7FF) frame.extended = true;
|
||||
else frame.extended = false;
|
||||
frame.len = 8;
|
||||
for (int b = 0; b < 8; b++) frame.data[b] = 0x00;
|
||||
frame.data[0] = 0x10 + (data.length() / 256);
|
||||
frame.data[1] = data.length() & 0xFF;
|
||||
for (int i = 0; i < 6; i++) frame.data[2 + i] = data[currByte++];
|
||||
QByteArray bytes(8, 0);
|
||||
bytes[0] = 0x10 + (data.length() / 256);
|
||||
bytes[1] = data.length() & 0xFF;
|
||||
for (int i = 0; i < 6; i++) bytes[2 + i] = data[currByte++];
|
||||
frame.setPayload(bytes);
|
||||
CANConManager::getInstance()->sendFrame(frame);
|
||||
//Queue up the rest of the frames
|
||||
waitingForFlow = true;
|
||||
@@ -90,13 +89,13 @@ void ISOTP_HANDLER::sendISOTPFrame(int bus, int ID, QVector<unsigned char> data)
|
||||
frameTimer.start();
|
||||
while (currByte < data.length())
|
||||
{
|
||||
for (int b = 0; b < 8; b++) frame.data[b] = 0x00;
|
||||
frame.data[0] = 0x20 + index;
|
||||
for (int b = 0; b < 8; b++) bytes[b] = 0x00;
|
||||
bytes[0] = 0x20 + index;
|
||||
index = (index + 1) & 0xF;
|
||||
int bytesToGo = data.length() - currByte;
|
||||
if (bytesToGo > 7) bytesToGo = 7;
|
||||
for (int i = 0; i < bytesToGo; i++) frame.data[1 + i] = data[currByte++];
|
||||
frame.len = 8;
|
||||
for (int i = 0; i < bytesToGo; i++) bytes[1 + i] = data[currByte++];
|
||||
frame.setPayload(bytes);
|
||||
sendingFrames.append(frame);
|
||||
//CANConManager::getInstance()->sendFrame(frame);
|
||||
}
|
||||
@@ -108,9 +107,11 @@ void ISOTP_HANDLER::updatedFrames(int numFrames)
|
||||
{
|
||||
if (numFrames == -1) //all frames deleted. Kill the display
|
||||
{
|
||||
messageBuffer.clear();
|
||||
}
|
||||
else if (numFrames == -2) //all new set of frames. Reset
|
||||
{
|
||||
messageBuffer.clear();
|
||||
for (int i = 0; i < modelFrames->length(); i++) processFrame(modelFrames->at(i));
|
||||
}
|
||||
else //just got some new frames. See if they are relevant.
|
||||
@@ -127,7 +128,7 @@ void ISOTP_HANDLER::rapidFrames(const CANConnection* conn, const QVector<CANFram
|
||||
Q_UNUSED(conn)
|
||||
if (pFrames.length() <= 0) return;
|
||||
|
||||
//qDebug() << "received messages in ISOTP handler";
|
||||
qDebug() << "received " << QString::number(pFrames.count()) << " messages in ISOTP handler";
|
||||
|
||||
foreach(const CANFrame& thisFrame, pFrames)
|
||||
{
|
||||
@@ -138,7 +139,7 @@ void ISOTP_HANDLER::rapidFrames(const CANConnection* conn, const QVector<CANFram
|
||||
{
|
||||
for (int i = 0; i < filters.count(); i++)
|
||||
{
|
||||
if ((thisFrame.bus == filters[i].bus) && ((thisFrame.ID & filters[i].mask) == filters[i].ID))
|
||||
if ((thisFrame.bus == filters[i].bus) && ((thisFrame.frameId() & filters[i].mask) == filters[i].ID))
|
||||
{
|
||||
processFrame(thisFrame);
|
||||
break;
|
||||
@@ -151,13 +152,17 @@ void ISOTP_HANDLER::rapidFrames(const CANConnection* conn, const QVector<CANFram
|
||||
|
||||
void ISOTP_HANDLER::processFrame(const CANFrame &frame)
|
||||
{
|
||||
uint64_t ID = frame.ID;
|
||||
uint64_t ID = frame.frameId();
|
||||
int frameType;
|
||||
int frameLen;
|
||||
int ln;
|
||||
//int offset;
|
||||
ISOTP_MESSAGE msg;
|
||||
ISOTP_MESSAGE *pMsg;
|
||||
QByteArray dataBytes;
|
||||
const unsigned char *data = reinterpret_cast<const unsigned char *>(frame.payload().constData());
|
||||
//qDebug() << frame.payload().count();
|
||||
//int dataLen = frame.payload().count();
|
||||
|
||||
frameType = 0;
|
||||
frameLen = 0;
|
||||
@@ -165,14 +170,14 @@ void ISOTP_HANDLER::processFrame(const CANFrame &frame)
|
||||
if (useExtendedAddressing)
|
||||
{
|
||||
ID = ID << 8;
|
||||
ID += frame.data[0];
|
||||
frameType = frame.data[1] >> 4;
|
||||
frameLen = frame.data[1] & 0xF;
|
||||
ID += data[0];
|
||||
frameType = data[1] >> 4;
|
||||
frameLen = data[1] & 0xF;
|
||||
}
|
||||
else
|
||||
{
|
||||
frameType = frame.data[0] >> 4;
|
||||
frameLen = frame.data[0] & 0xF;
|
||||
frameType = data[0] >> 4;
|
||||
frameLen = data[0] & 0xF;
|
||||
}
|
||||
|
||||
switch(frameType)
|
||||
@@ -185,89 +190,106 @@ void ISOTP_HANDLER::processFrame(const CANFrame &frame)
|
||||
if (frameLen > 7) return;
|
||||
|
||||
msg.bus = frame.bus;
|
||||
msg.extended = frame.extended;
|
||||
msg.ID = ID;
|
||||
msg.setFrameType(QCanBusFrame::FrameType::DataFrame);
|
||||
msg.setExtendedFrameFormat( frame.hasExtendedFrameFormat() );
|
||||
msg.setFrameId(ID);
|
||||
msg.isReceived = frame.isReceived;
|
||||
msg.len = frameLen;
|
||||
msg.data.reserve(frameLen);
|
||||
msg.timestamp = frame.timestamp;
|
||||
dataBytes.reserve(frameLen);
|
||||
msg.reportedLength = frameLen;
|
||||
msg.setTimeStamp(frame.timeStamp());
|
||||
msg.isMultiframe = false;
|
||||
if (useExtendedAddressing) for (int j = 0; j < frameLen; j++) msg.data.append(frame.data[j+2]);
|
||||
else for (int j = 0; j < frameLen; j++) msg.data.append(frame.data[j+1]);
|
||||
//qDebug() << "Emitting single frame ISOTP message";
|
||||
if (useExtendedAddressing)
|
||||
{
|
||||
for (int j = 0; j < frameLen; j++)
|
||||
{
|
||||
if (frame.payload().count() > (j+2))
|
||||
dataBytes.append(data[j+2]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < frameLen; j++)
|
||||
{
|
||||
if (frame.payload().count() > (j+1))
|
||||
dataBytes.append(data[j+1]);
|
||||
}
|
||||
}
|
||||
qDebug() << "Emitting single frame ISOTP message";
|
||||
msg.setPayload(dataBytes);
|
||||
emit newISOMessage(msg);
|
||||
break;
|
||||
case 1: //first frame of a multi-frame message
|
||||
checkNeedFlush(ID);
|
||||
msg.bus = frame.bus;
|
||||
msg.extended = frame.extended;
|
||||
msg.ID = ID;
|
||||
msg.timestamp = frame.timestamp;
|
||||
if (frame.payload().count() < 8) return; //MUST have all 8 data bytes in this first frame.
|
||||
msg.setExtendedFrameFormat( frame.hasExtendedFrameFormat() );
|
||||
msg.setFrameId(ID);
|
||||
msg.setTimeStamp(frame.timeStamp());
|
||||
msg.isReceived = frame.isReceived;
|
||||
msg.isMultiframe = true;
|
||||
frameLen = frameLen << 8;
|
||||
if (useExtendedAddressing)
|
||||
{
|
||||
frameLen += frame.data[2];
|
||||
frameLen += data[2];
|
||||
frameLen = frameLen & 0xFFF;
|
||||
msg.len = frameLen;
|
||||
msg.data.reserve(frameLen);
|
||||
for (int j = 0; j < 5; j++) msg.data.append(frame.data[3 + j]);
|
||||
dataBytes.reserve(frameLen);
|
||||
msg.reportedLength = frameLen;
|
||||
for (int j = 0; j < 5; j++) dataBytes.append(frame.payload()[3 + j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
frameLen += frame.data[1];
|
||||
frameLen += data[1];
|
||||
frameLen = frameLen & 0xFFF;
|
||||
msg.len = frameLen;
|
||||
msg.data.reserve(frameLen);
|
||||
for (int j = 0; j < 6; j++) msg.data.append(frame.data[2 + j]);
|
||||
msg.payload().reserve(frameLen);
|
||||
msg.reportedLength = frameLen;
|
||||
for (int j = 0; j < 6; j++) dataBytes.append(frame.payload()[2 + j]);
|
||||
}
|
||||
msg.lastSequence = -1;
|
||||
messageBuffer.append(msg);
|
||||
msg.setPayload(dataBytes);
|
||||
messageBuffer.insert(msg.frameId(), msg);
|
||||
//The sending ID is set to the last ID we used to send from this class which is
|
||||
//very likely to be correct. But, caution, there is a chance that it isn't. Beware.
|
||||
if (issueFlowMsgs && lastSenderID > 0)
|
||||
{
|
||||
CANFrame outFrame;
|
||||
outFrame.bus = lastSenderBus;
|
||||
outFrame.extended = false;
|
||||
outFrame.ID = lastSenderID;
|
||||
outFrame.len = 8;
|
||||
for (int b = 0; b < 8; b++) outFrame.data[b] = 0x00;
|
||||
outFrame.data[0] = 0x30; //flow control, go ahead and send
|
||||
outFrame.data[1] = 0; //dont ask again about flow control
|
||||
outFrame.data[2] = 3; //separation time in milliseconds between messages.
|
||||
outFrame.setExtendedFrameFormat(false);
|
||||
outFrame.setFrameId(lastSenderID);
|
||||
QByteArray bytes(8, 0);
|
||||
bytes[0] = 0x30; //flow control, go ahead and send
|
||||
bytes[1] = 0; //dont ask again about flow control
|
||||
bytes[2] = 3; //separation time in milliseconds between messages.
|
||||
outFrame.setPayload(bytes);
|
||||
CANConManager::getInstance()->sendFrame(outFrame);
|
||||
}
|
||||
break;
|
||||
case 2: //subsequent frames for multi-frame messages
|
||||
pMsg = nullptr;
|
||||
for (int i = 0; i < messageBuffer.length(); i++)
|
||||
if (messageBuffer.contains(ID))
|
||||
{
|
||||
if (messageBuffer[i].ID == ID)
|
||||
{
|
||||
pMsg = &messageBuffer[i];
|
||||
break;
|
||||
}
|
||||
pMsg = &messageBuffer[ID];
|
||||
}
|
||||
if (!pMsg) return;
|
||||
if (!pMsg->isMultiframe) return; //if we didn't get a frame type 1 (start of multiframe) first then ignore this frame.
|
||||
ln = pMsg->len - pMsg->data.count();
|
||||
dataBytes.clear();
|
||||
dataBytes.append(pMsg->payload());
|
||||
ln = pMsg->reportedLength - pMsg->payload().count();
|
||||
//offset = pMsg->data.count();
|
||||
if (useExtendedAddressing)
|
||||
{
|
||||
if (ln > 6) ln = 6;
|
||||
for (int j = 0; j < ln; j++) pMsg->data.append(frame.data[j+2]);
|
||||
for (int j = 0; j < ln; j++) dataBytes.append(frame.payload()[j+2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ln > 7) ln = 7;
|
||||
for (int j = 0; j < ln; j++) pMsg->data.append(frame.data[j+1]);
|
||||
for (int j = 0; j < ln; j++) dataBytes.append(frame.payload()[j+1]);
|
||||
}
|
||||
if (pMsg->len <= pMsg->data.count())
|
||||
pMsg->setPayload(dataBytes);
|
||||
if (pMsg->reportedLength <= pMsg->payload().count())
|
||||
{
|
||||
//qDebug() << "Emitting multiframe ISOTP message";
|
||||
checkNeedFlush(pMsg->ID);
|
||||
qDebug() << "Emitting multiframe ISOTP message";
|
||||
checkNeedFlush(pMsg->frameId());
|
||||
}
|
||||
break;
|
||||
case 3: //flow control messages
|
||||
@@ -276,10 +298,10 @@ void ISOTP_HANDLER::processFrame(const CANFrame &frame)
|
||||
case 0: //continue to send frames but maybe change inter-frame delay
|
||||
waitingForFlow = false;
|
||||
//data[1] contains number of frames to send before waiting for next flow control
|
||||
framesUntilFlow = frame.data[1];
|
||||
framesUntilFlow = data[1];
|
||||
if (framesUntilFlow == 0) framesUntilFlow = -1; //-1 means don't count frames and just keep going
|
||||
//data[2] contains the interframe delay to use (0xF1 through 0xF9 are special through)
|
||||
if (frame.data[2] < 0xF1) frameTimer.start(frame.data[2]); //set proper delay between frames
|
||||
if (data[2] < 0xF1) frameTimer.start(data[2]); //set proper delay between frames
|
||||
else frameTimer.start(1); //can't do sub-millisecond sending with this code so just use 1ms timing
|
||||
break;
|
||||
case 1: //wait - do not send any more frames until other side says so
|
||||
@@ -300,26 +322,25 @@ void ISOTP_HANDLER::processFrame(const CANFrame &frame)
|
||||
|
||||
void ISOTP_HANDLER::checkNeedFlush(uint64_t ID)
|
||||
{
|
||||
for (int i = 0; i < messageBuffer.length(); i++)
|
||||
ISOTP_MESSAGE *msg;
|
||||
if (messageBuffer.contains(ID))
|
||||
{
|
||||
if (messageBuffer[i].ID == ID)
|
||||
msg = &messageBuffer[ID];
|
||||
if (msg->reportedLength <= msg->payload().count())
|
||||
{
|
||||
//used to pass by reference but now newISOMessage should pass by value which makes it easier to use cross thread
|
||||
if (messageBuffer[i].ID > 0x600 && messageBuffer[i].ID < 0x630)
|
||||
{
|
||||
if (messageBuffer[i].len <= messageBuffer[i].data.count())
|
||||
{
|
||||
qDebug() << "Flushing full frame" << QString::number(messageBuffer[i].ID, 16) << " " << messageBuffer[i].len << " " << messageBuffer[i].data.count();
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Flushing a partial frame " << QString::number(messageBuffer[i].ID, 16) << " " << messageBuffer[i].len << " " << messageBuffer[i].data.count();
|
||||
}
|
||||
}
|
||||
emit newISOMessage(messageBuffer[i]);
|
||||
messageBuffer.removeAt(i);
|
||||
return;
|
||||
qDebug() << "Flushing full frame" << QString::number(msg->frameId(), 16) << " " << msg->reportedLength << " " << msg->payload().count();
|
||||
if (msg->reportedLength > 0) emit newISOMessage(*msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sendPartialMessages)
|
||||
{
|
||||
qDebug() << "Flushing a partial frame " << QString::number(msg->frameId(), 16) << " " << msg->reportedLength << " " << msg->payload().count();
|
||||
if (msg->reportedLength > 0) emit newISOMessage(*msg);
|
||||
}
|
||||
else qDebug() << "Have a partial message but sending of such is disabled. Throwing it away";
|
||||
}
|
||||
messageBuffer.remove(ID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +377,7 @@ void ISOTP_HANDLER::setProcessAll(bool state)
|
||||
processAll = state;
|
||||
}
|
||||
|
||||
void ISOTP_HANDLER::addFilter(uint32_t pBusId, uint32_t ID, uint32_t mask)
|
||||
void ISOTP_HANDLER::addFilter(int pBusId, uint32_t ID, uint32_t mask)
|
||||
{
|
||||
CANFilter filt;
|
||||
filt.ID = ID;
|
||||
@@ -366,7 +387,7 @@ void ISOTP_HANDLER::addFilter(uint32_t pBusId, uint32_t ID, uint32_t mask)
|
||||
filters.append(filt);
|
||||
}
|
||||
|
||||
void ISOTP_HANDLER::removeFilter(uint32_t pBusId, uint32_t ID, uint32_t mask)
|
||||
void ISOTP_HANDLER::removeFilter(int pBusId, uint32_t ID, uint32_t mask)
|
||||
{
|
||||
for (int i = 0; i < filters.count(); i++)
|
||||
{
|
||||
@@ -379,4 +400,4 @@ void ISOTP_HANDLER::clearAllFilters()
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
|
||||
void setEmitPartials(bool mode);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#ifndef ISOTP_HANDLER_H
|
||||
#define ISOTP_HANDLER_H
|
||||
#pragma once
|
||||
|
||||
#include <Qt>
|
||||
#include <QObject>
|
||||
@@ -20,11 +19,12 @@ public:
|
||||
~ISOTP_HANDLER();
|
||||
void setExtendedAddressing(bool mode);
|
||||
void setReception(bool mode); //set whether to accept and forward frames or not
|
||||
void sendISOTPFrame(int bus, int ID, QVector<unsigned char> data);
|
||||
void setEmitPartials(bool mode);
|
||||
void sendISOTPFrame(int bus, int ID, QByteArray data);
|
||||
void setProcessAll(bool state);
|
||||
void setFlowCtrl(bool state);
|
||||
void addFilter(uint32_t pBusId, uint32_t ID, uint32_t mask);
|
||||
void removeFilter(uint32_t pBusId, uint32_t ID, uint32_t mask);
|
||||
void addFilter(int pBusId, uint32_t ID, uint32_t mask);
|
||||
void removeFilter(int pBusId, uint32_t ID, uint32_t mask);
|
||||
void clearAllFilters();
|
||||
|
||||
public slots:
|
||||
@@ -36,7 +36,7 @@ signals:
|
||||
void newISOMessage(ISOTP_MESSAGE msg);
|
||||
|
||||
private:
|
||||
QList<ISOTP_MESSAGE> messageBuffer;
|
||||
QHash<uint32_t, ISOTP_MESSAGE> messageBuffer;
|
||||
QList<CANFrame> sendingFrames;
|
||||
QList<CANFilter> filters;
|
||||
const QVector<CANFrame> *modelFrames;
|
||||
@@ -46,6 +46,7 @@ private:
|
||||
int framesUntilFlow;
|
||||
bool processAll;
|
||||
bool issueFlowMsgs;
|
||||
bool sendPartialMessages;
|
||||
QTimer frameTimer;
|
||||
uint32_t lastSenderID;
|
||||
uint32_t lastSenderBus;
|
||||
@@ -53,5 +54,3 @@ private:
|
||||
void processFrame(const CANFrame &frame);
|
||||
void checkNeedFlush(uint64_t ID);
|
||||
};
|
||||
|
||||
#endif // ISOTP_HANDLER_H
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
#ifndef ISOTP_MESSAGE_H
|
||||
#define ISOTP_MESSAGE_H
|
||||
|
||||
//the same as the CANFrame struct but with arbitrary data size.
|
||||
class ISOTP_MESSAGE
|
||||
#include <Qt>
|
||||
#include <QVector>
|
||||
#include <can_structs.h>
|
||||
|
||||
//Now a child class of CANFrame. We just add the ability to track how long it was supposed to be and other
|
||||
//ISOTP related details. But, mostly just CANFrame.
|
||||
class ISOTP_MESSAGE : public CANFrame
|
||||
{
|
||||
public:
|
||||
uint32_t ID;
|
||||
int bus;
|
||||
bool extended;
|
||||
bool isReceived;
|
||||
int len; //# of bytes this message should have (as reported)
|
||||
int actualSize; //# we actually got
|
||||
int reportedLength;
|
||||
int lastSequence;
|
||||
bool isMultiframe;
|
||||
QVector<unsigned char> data;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
#endif // ISOTP_MESSAGE_H
|
||||
|
||||
@@ -33,6 +33,14 @@ static QVector<CODE_STRUCT> UDS_ROUTINE_SUB = {
|
||||
{3,"GET_ROUTINE_RESULTS", "Get results from routine specified by ID"},
|
||||
};
|
||||
|
||||
static QVector<CODE_STRUCT> UDS_FILE_MODEOFOP = {
|
||||
{1, "ADDFILE", "Add file to file system"},
|
||||
{2, "DELETEFILE", "Add file to file system"},
|
||||
{3, "REPLACEFILE", "Add file to file system"},
|
||||
{4, "READFILE", "Add file to file system"},
|
||||
{5, "READDIR", "Add file to file system"}
|
||||
};
|
||||
|
||||
static QVector<CODE_STRUCT> UDS_SERVICE_DESC = {
|
||||
{1, "OBDII_SHOW_CURRENT", "OBDII - Show current data"},
|
||||
{2, "OBDII_SHOW_FREEZE", "OBDII - Show freeze data"},
|
||||
@@ -158,7 +166,6 @@ UDS_MESSAGE::UDS_MESSAGE()
|
||||
subFunc = 0;
|
||||
service = 0;
|
||||
subFuncLen = 1;
|
||||
extended = false;
|
||||
isErrorReply = false;
|
||||
}
|
||||
|
||||
@@ -178,39 +185,39 @@ UDS_HANDLER::~UDS_HANDLER()
|
||||
void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE msg)
|
||||
{
|
||||
qDebug() << "UDS handler got ISOTP frame";
|
||||
const unsigned char *data = reinterpret_cast<const unsigned char *>(msg.payload().constData());
|
||||
int dataLen = msg.payload().count();
|
||||
UDS_MESSAGE udsMsg;
|
||||
udsMsg.bus = msg.bus;
|
||||
udsMsg.extended = msg.extended;
|
||||
udsMsg.ID = msg.ID;
|
||||
udsMsg.setExtendedFrameFormat(msg.hasExtendedFrameFormat());
|
||||
udsMsg.setFrameId(msg.frameId());
|
||||
udsMsg.isReceived = msg.isReceived;
|
||||
udsMsg.timestamp = msg.timestamp;
|
||||
udsMsg.actualSize = msg.actualSize;
|
||||
udsMsg.setTimeStamp(msg.timeStamp());
|
||||
udsMsg.reportedLength = msg.reportedLength;
|
||||
udsMsg.service = 0;
|
||||
udsMsg.subFunc = 0;
|
||||
udsMsg.subFuncLen = 0;
|
||||
udsMsg.isErrorReply = false;
|
||||
udsMsg.len = msg.len;
|
||||
if (msg.data.length() > 0) {
|
||||
udsMsg.service = msg.data.at(0);
|
||||
udsMsg.setPayload(msg.payload());
|
||||
if (dataLen > 0) {
|
||||
udsMsg.service = data[0];
|
||||
if (udsMsg.service == 0x7F)
|
||||
{
|
||||
udsMsg.isErrorReply = true;
|
||||
if (msg.data.length() > 1)
|
||||
if (dataLen > 1)
|
||||
{
|
||||
udsMsg.service = msg.data.at(1);
|
||||
if (msg.data.length() > 2) udsMsg.subFunc = msg.data.at(2);
|
||||
udsMsg.service = data[1];
|
||||
if (dataLen > 2) udsMsg.subFunc = data[2];
|
||||
else return;
|
||||
}
|
||||
else return;
|
||||
udsMsg.data = msg.data.mid(2, -1); //don't copy error byte nor service byte
|
||||
udsMsg.len -= 2; //and remove those bytes from the length reported too.
|
||||
udsMsg.payload().remove(0, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
udsMsg.isErrorReply = false;
|
||||
if (msg.data.length() > 1) udsMsg.subFunc = msg.data.at(1);
|
||||
udsMsg.data = msg.data.mid(1, -1); //don't copy service byte
|
||||
udsMsg.len -= 1; //and remove service byte from length too.
|
||||
if (dataLen > 1) udsMsg.subFunc = data[1];
|
||||
udsMsg.payload().remove(0, 1);
|
||||
}
|
||||
}
|
||||
else return;
|
||||
@@ -245,7 +252,7 @@ void UDS_HANDLER::setReception(bool mode)
|
||||
|
||||
void UDS_HANDLER::sendUDSFrame(const UDS_MESSAGE &msg)
|
||||
{
|
||||
QVector<unsigned char> data;
|
||||
QByteArray data;
|
||||
if (msg.bus < 0) return;
|
||||
if (msg.bus >= CANConManager::getInstance()->getNumBuses()) return;
|
||||
if (msg.service > 0xFF) return;
|
||||
@@ -256,8 +263,10 @@ void UDS_HANDLER::sendUDSFrame(const UDS_MESSAGE &msg)
|
||||
data.append((msg.subFunc >> (8 * b)) & 0xFF);
|
||||
}
|
||||
|
||||
data.append(msg.data);
|
||||
isoHandler->sendISOTPFrame(msg.bus, msg.ID, data);
|
||||
data.append(msg.payload());
|
||||
isoHandler->sendISOTPFrame(msg.bus, msg.frameId(), data);
|
||||
|
||||
//qDebug() << "Data sending: " << data;
|
||||
|
||||
qDebug() << "Sent UDS service: " << getServiceShortDesc(msg.service) << " on bus " << msg.bus;
|
||||
}
|
||||
@@ -325,24 +334,23 @@ QString UDS_HANDLER::getNegativeResponseLong(int respCode)
|
||||
QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg)
|
||||
{
|
||||
QString buildString;
|
||||
bool isResponse = true;
|
||||
int dataSize;
|
||||
int addrSize;
|
||||
int compType, encType;
|
||||
const unsigned char *data = reinterpret_cast<const unsigned char *>(msg.payload().constData());
|
||||
int dataLen = msg.payload().length();
|
||||
|
||||
if (msg.isErrorReply)
|
||||
{
|
||||
isResponse = true;
|
||||
buildString.append("UDS ERROR Response\n");
|
||||
buildString.append("Service: " + getServiceLongDesc(msg.service) + "\n");
|
||||
}
|
||||
else if (msg.service < 0x3F || (msg.service > 0x7F && msg.service < 0xAF)) {
|
||||
isResponse = false;
|
||||
buildString.append("UDS Request\n");
|
||||
buildString.append("Service: " + getServiceLongDesc(msg.service) + "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
isResponse = true;
|
||||
buildString.append("UDS Positive Response\n");
|
||||
buildString.append("Service: " + getServiceLongDesc(msg.service - 0x40) + "\n");
|
||||
}
|
||||
@@ -363,15 +371,15 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg)
|
||||
case UDS_SERVICES::DIAG_CONTROL + 0x40: //positive response
|
||||
buildString.append("Session Request: " + getLongDesc(UDS_DIAG_CTRL_SUB, msg.subFunc));
|
||||
//there should be four extra bytes now
|
||||
if (msg.data.length() < 5) //5 because subfunc codes are left in data so it starts with one subfunc byte
|
||||
if (dataLen < 5) //5 because subfunc codes are left in data so it starts with one subfunc byte
|
||||
{
|
||||
//buildString.append("\nReturned data payload wasn't at least \n4 bytes like it should have been");
|
||||
}
|
||||
else
|
||||
{
|
||||
int p2 = msg.data[1] * 256 + msg.data[2];
|
||||
int p2 = data[1] * 256 + data[2];
|
||||
buildString.append("\nP2MAX (Max Wait / Resp Time): " + QString::number(p2) + "ms");
|
||||
p2 = (msg.data[3] * 256 + msg.data[4]) * 10;
|
||||
p2 = (data[3] * 256 + data[4]) * 10;
|
||||
buildString.append("\nP2 Ext MAX: " + QString::number(p2) + "ms");
|
||||
}
|
||||
break;
|
||||
@@ -382,11 +390,11 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg)
|
||||
case UDS_SERVICES::ECU_RESET + 0x40:
|
||||
buildString.append("Reset Type: " + getLongDesc(UDS_ECU_RESET_SUB, msg.subFunc));
|
||||
//There should be one additional byte which encodes power down time
|
||||
if (msg.data.length() > 1)
|
||||
if (dataLen > 1)
|
||||
{
|
||||
if (msg.data[1] < 0xFF)
|
||||
if (data[1] < 0xFF)
|
||||
{
|
||||
buildString.append("\nMinimum powered down time: " + QString::number(msg.data[1]));
|
||||
buildString.append("\nMinimum powered down time: " + QString::number(data[1]));
|
||||
}
|
||||
else buildString.append("\nPowerdown time not available");
|
||||
}
|
||||
@@ -395,11 +403,11 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg)
|
||||
case UDS_SERVICES::COMM_CTRL:
|
||||
//Comm control has potentially a lot of parameters. control type, comm type, nodeID
|
||||
buildString.append("Control type: " + getLongDesc(UDS_COMM_CTRL_SUB, msg.subFunc));
|
||||
if (msg.data.length() > 1)
|
||||
buildString.append("\nComm Type: " + QString::number(msg.data[1])); //TODO: no attempt to interpret yet
|
||||
if (msg.data.length() > 3)
|
||||
if (dataLen > 1)
|
||||
buildString.append("\nComm Type: " + QString::number(data[1])); //TODO: no attempt to interpret yet
|
||||
if (dataLen > 3)
|
||||
{
|
||||
int nodeID = (msg.data[2] * 256 + msg.data[3]);
|
||||
int nodeID = (data[2] * 256 + data[3]);
|
||||
buildString.append("\nNode ID: " + Utility::formatHexNum(nodeID));
|
||||
}
|
||||
break;
|
||||
@@ -407,19 +415,19 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg)
|
||||
if ((msg.subFunc % 2) == 1)
|
||||
{
|
||||
buildString.append("Seed request for security level: " + QString::number(msg.subFunc) + "\n");
|
||||
if (msg.data.length()> 1)
|
||||
if (dataLen > 1)
|
||||
{
|
||||
buildString.append("Data payload: ");
|
||||
for (int j = 1; j < msg.data.length(); j++) buildString.append(Utility::formatHexNum(msg.data[j]) + " ");
|
||||
for (int j = 2; j < dataLen; j++) buildString.append(Utility::formatHexNum(data[j]) + " ");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buildString.append("Key sending for security level: " + QString::number(msg.subFunc - 1));
|
||||
if (msg.data.length()> 1) //and it sure as hell should be!
|
||||
if (dataLen > 1) //and it sure as hell should be!
|
||||
{
|
||||
buildString.append("KEY: ");
|
||||
for (int j = 1; j < msg.data.length(); j++) buildString.append(Utility::formatHexNum(msg.data[j]) + " ");
|
||||
buildString.append(" KEY: ");
|
||||
for (int j = 2; j < dataLen; j++) buildString.append(Utility::formatHexNum(data[j]) + " ");
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -427,10 +435,10 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg)
|
||||
if ((msg.subFunc % 2) == 1)
|
||||
{
|
||||
buildString.append("Seed response for security level: " + QString::number(msg.subFunc) + "\n");
|
||||
if (msg.data.length()> 1) //be kinda pointless if it weren't
|
||||
if (dataLen > 1) //be kinda pointless if it weren't
|
||||
{
|
||||
buildString.append("SEED: ");
|
||||
for (int j = 1; j < msg.data.length(); j++) buildString.append(Utility::formatHexNum(msg.data[j]) + " ");
|
||||
for (int j = 2; j < dataLen; j++) buildString.append(Utility::formatHexNum(data[j]) + " ");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -441,33 +449,33 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg)
|
||||
break;
|
||||
case UDS_SERVICES::READ_BY_ID:
|
||||
//parameter is groups of two bytes, each of which specify an ID to read
|
||||
if (msg.data.length() > 2)
|
||||
if (dataLen > 2)
|
||||
{
|
||||
uint32_t id;
|
||||
for (int i = 1; i < msg.data.length(); i = i + 2)
|
||||
for (int i = 1; i < dataLen; i = i + 2)
|
||||
{
|
||||
id = (msg.data[i] * 256) + msg.data[i+1];
|
||||
id = (data[i] * 256) + data[i+1];
|
||||
buildString.append("\nID to read: " + Utility::formatHexNum(id));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UDS_SERVICES::READ_BY_ID + 0x40: //reply
|
||||
buildString.append("Reply is non-standard and so no decoding is done. The format is (ID) followed by how ever much data that ID returns, followed by more ID/data pairs if applicable.\nPayload: ");
|
||||
for (int i = 1; i < msg.data.length(); i++)
|
||||
for (int i = 1; i < dataLen; i++)
|
||||
{
|
||||
buildString.append(Utility::formatHexNum(msg.data[i]) + " ");
|
||||
buildString.append(Utility::formatHexNum(data[i]) + " ");
|
||||
}
|
||||
break;
|
||||
case UDS_SERVICES::READ_BY_ADDR:
|
||||
//subfunc byte specifies address and length format, then address, then size
|
||||
dataSize = msg.subFunc >> 4;
|
||||
addrSize = msg.subFunc & 0xF;
|
||||
if (msg.data.length() > (dataSize + addrSize))
|
||||
if (dataLen > (dataSize + addrSize))
|
||||
{
|
||||
buildString.append("Address: 0x");
|
||||
for (int i = 0; i < addrSize; i++) buildString.append(QString::number(msg.data[1+i], 16).toUpper().rightJustified(2,'0'));
|
||||
for (int i = 0; i < addrSize; i++) buildString.append(QString::number(data[1+i], 16).toUpper().rightJustified(2,'0'));
|
||||
buildString.append("\nSize: 0x");
|
||||
for (int i = 0; i < dataSize; i++) buildString.append(QString::number(msg.data[1+i+addrSize], 16).toUpper().rightJustified(2,'0'));
|
||||
for (int i = 0; i < dataSize; i++) buildString.append(QString::number(data[1+i+addrSize], 16).toUpper().rightJustified(2,'0'));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -476,54 +484,109 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg)
|
||||
break;
|
||||
case UDS_SERVICES::READ_BY_ADDR + 0x40:
|
||||
buildString.append("Reply is a raw packet of data of the size requested.\nPayload: ");
|
||||
for (int i = 1; i < msg.data.length(); i++)
|
||||
for (int i = 1; i < dataLen; i++)
|
||||
{
|
||||
buildString.append(Utility::formatHexNum(msg.data[i]) + " ");
|
||||
buildString.append(Utility::formatHexNum(data[i]) + " ");
|
||||
}
|
||||
break;
|
||||
case UDS_SERVICES::WRITE_BY_ID:
|
||||
case UDS_SERVICES::WRITE_BY_ID:
|
||||
if (dataLen > 3)
|
||||
{
|
||||
int writeID = (data[1] * 256 + data[2]);
|
||||
buildString.append("ID to write to: " + Utility::formatHexNum(writeID) + "\nPayload: ");
|
||||
for (int i = 3; i < dataLen; i++)
|
||||
{
|
||||
buildString.append(Utility::formatHexNum(data[i]) + " ");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UDS_SERVICES::WRITE_BY_ID + 0x40:
|
||||
if (dataLen > 2)
|
||||
{
|
||||
int writeID = (data[1] * 256 + data[2]);
|
||||
buildString.append("ID written to: " + Utility::formatHexNum(writeID));
|
||||
}
|
||||
break;
|
||||
case UDS_SERVICES::ROUTINE_CTRL:
|
||||
buildString.append("Routine Control: " + getLongDesc(UDS_ROUTINE_SUB, msg.subFunc));
|
||||
if (msg.data.length() > 2)
|
||||
if (dataLen > 3)
|
||||
{
|
||||
int routineID;
|
||||
routineID = (msg.data[1] * 256 + msg.data[2]);
|
||||
routineID = (data[2] * 256 + data[3]);
|
||||
buildString.append("\nRoutine ID: " + Utility::formatHexNum(routineID));
|
||||
}
|
||||
if (msg.data.length() > 3)
|
||||
if (dataLen > 4)
|
||||
{
|
||||
buildString.append("\nParameter bytes to routine: ");
|
||||
for (int i = 4; i < msg.data.length(); i++)
|
||||
for (int i = 4; i < dataLen; i++)
|
||||
{
|
||||
buildString.append(Utility::formatHexNum(msg.data[i]) + " ");
|
||||
buildString.append(Utility::formatHexNum(data[i]) + " ");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UDS_SERVICES::ROUTINE_CTRL + 0x40:
|
||||
buildString.append("Routine Control: " + getLongDesc(UDS_ROUTINE_SUB, msg.subFunc));
|
||||
if (msg.data.length() > 2)
|
||||
if (dataLen > 2)
|
||||
{
|
||||
int routineID;
|
||||
routineID = (msg.data[1] * 256 + msg.data[2]);
|
||||
routineID = (data[2] * 256 + data[3]);
|
||||
buildString.append("\nRoutine ID: " + Utility::formatHexNum(routineID));
|
||||
}
|
||||
if (msg.data.length() > 3)
|
||||
if (dataLen > 4)
|
||||
{
|
||||
buildString.append("\nBytes returned by routine: ");
|
||||
for (int i = 4; i < msg.data.length(); i++)
|
||||
for (int i = 4; i < dataLen; i++)
|
||||
{
|
||||
buildString.append(Utility::formatHexNum(msg.data[i]) + " ");
|
||||
buildString.append(Utility::formatHexNum(data[i]) + " ");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UDS_SERVICES::REQUEST_DOWNLOAD:
|
||||
break;
|
||||
case UDS_SERVICES::REQUEST_UPLOAD:
|
||||
compType = data[1] >> 4;
|
||||
encType = data[1] & 0xF;
|
||||
buildString.append("Compression Type: " + QString::number(compType));
|
||||
if (compType == 0) buildString.append(" (none)");
|
||||
buildString.append("\n");
|
||||
buildString.append("Encryption Type: " + QString::number(encType));
|
||||
if (encType == 0) buildString.append(" (none)");
|
||||
buildString.append("\n");
|
||||
//subfunc byte specifies address and length format, then address, then size
|
||||
dataSize = data[2] >> 4;
|
||||
addrSize = data[2] & 0xF;
|
||||
if (dataLen > (dataSize + addrSize))
|
||||
{
|
||||
buildString.append("Address: 0x");
|
||||
for (int i = 0; i < addrSize; i++) buildString.append(QString::number(data[3 + i], 16).toUpper().rightJustified(2,'0'));
|
||||
buildString.append("\nSize: 0x");
|
||||
for (int i = 0; i < dataSize; i++) buildString.append(QString::number(data[3 + i + addrSize], 16).toUpper().rightJustified(2,'0'));
|
||||
}
|
||||
else
|
||||
{
|
||||
buildString.append("Message has insufficient bytes to properly decode address and size!");
|
||||
}
|
||||
break;
|
||||
case UDS_SERVICES::REQUEST_DOWNLOAD + 0x40:
|
||||
case UDS_SERVICES::REQUEST_UPLOAD + 0x40:
|
||||
dataSize = data[1] >> 4;
|
||||
buildString.append("\nMax Size of data block: 0x");
|
||||
for (int i = 0; i < dataSize; i++) buildString.append(QString::number(data[2 + i], 16).toUpper().rightJustified(2,'0'));
|
||||
break;
|
||||
case UDS_SERVICES::TRANSFER_DATA:
|
||||
case UDS_SERVICES::TRANSFER_DATA + 0x40:
|
||||
buildString.append("\nBlock Sequence: " + QString::number(data[1], 16) + "\nPayload: ");
|
||||
for (int i = 2; i < dataLen; i++)
|
||||
{
|
||||
buildString.append(Utility::formatHexNum(data[i]) + " ");
|
||||
}
|
||||
break;
|
||||
case UDS_SERVICES::REQ_TRANS_EXIT:
|
||||
case UDS_SERVICES::REQ_TRANS_EXIT + 0x40:
|
||||
buildString.append("\nPayload: ");
|
||||
for (int i = 1; i < dataLen; i++)
|
||||
{
|
||||
buildString.append(Utility::formatHexNum(data[i]) + " ");
|
||||
}
|
||||
break;
|
||||
case UDS_SERVICES::REQ_FILE_TRANS:
|
||||
break;
|
||||
|
||||
@@ -4,35 +4,32 @@
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
#include <stdint.h>
|
||||
#include <QCanBusFrame>
|
||||
|
||||
struct CANFrame
|
||||
//Now inherits from the built-in CAN frame class from Qt. This should be more future proof and easier to integrate with other code
|
||||
|
||||
struct CANFrame : public QCanBusFrame
|
||||
{
|
||||
public:
|
||||
uint32_t ID;
|
||||
int bus;
|
||||
bool extended;
|
||||
bool remote;
|
||||
bool isReceived; //did we receive this or send it?
|
||||
int len;
|
||||
unsigned char data[8];
|
||||
uint64_t timestamp;
|
||||
uint64_t timedelta;
|
||||
uint32_t frameCount; //used in overwrite mode
|
||||
|
||||
friend bool operator<(const CANFrame& l, const CANFrame& r)
|
||||
{
|
||||
return l.timestamp < r.timestamp;
|
||||
qint64 lStamp = l.timeStamp().seconds() * 1000000 + l.timeStamp().microSeconds();
|
||||
qint64 rStamp = r.timeStamp().seconds() * 1000000 + r.timeStamp().microSeconds();
|
||||
return lStamp < rStamp;
|
||||
}
|
||||
|
||||
CANFrame()
|
||||
{
|
||||
ID = 0;
|
||||
setFrameId(0);
|
||||
bus = 0;
|
||||
extended = false;
|
||||
remote = false;
|
||||
setExtendedFrameFormat(false);
|
||||
setFrameType(QCanBusFrame::DataFrame);
|
||||
isReceived = true;
|
||||
len = 0;
|
||||
timestamp = 0;
|
||||
timedelta = 0;
|
||||
frameCount = 1;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ CANDataGrid::CANDataGrid(QWidget *parent) :
|
||||
memset(data, 0, 8);
|
||||
memset(refData, 0, 8);
|
||||
memset(usedData, 0, 8);
|
||||
memset(usedSignalNum, 0, 64);
|
||||
for (int x = 0; x < 8; x++)
|
||||
for (int y = 0; y < 8; y++)
|
||||
textStates[x][y] = GridTextState::NORMAL;
|
||||
@@ -53,7 +54,7 @@ void CANDataGrid::setCellTextState(int x, int y, GridTextState state)
|
||||
this->update();
|
||||
}
|
||||
|
||||
GridTextState CANDataGrid::geCellTextState(int x, int y)
|
||||
GridTextState CANDataGrid::getCellTextState(int x, int y)
|
||||
{
|
||||
if (x < 0) return GridTextState::NORMAL;
|
||||
if (x > 7) return GridTextState::NORMAL;
|
||||
@@ -62,6 +63,21 @@ GridTextState CANDataGrid::geCellTextState(int x, int y)
|
||||
return textStates[x][y];
|
||||
}
|
||||
|
||||
void CANDataGrid::setUsedSignalNum(int bit, unsigned char signal)
|
||||
{
|
||||
if (bit < 0) return;
|
||||
if (bit > 63) return;
|
||||
usedSignalNum[bit] = signal;
|
||||
}
|
||||
|
||||
unsigned char CANDataGrid::getUsedSignalNum(int bit)
|
||||
{
|
||||
if (bit < 0) return 0;
|
||||
if (bit > 63) return 0;
|
||||
return usedSignalNum[bit];
|
||||
}
|
||||
|
||||
|
||||
void CANDataGrid::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
@@ -161,7 +177,11 @@ void CANDataGrid::paintEvent(QPaintEvent *event)
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((usedData[y] & (1 << (7-x))) == (1 << (7-x))) painter.setBrush(grayBrush);
|
||||
if ((usedData[y] & (1 << (7-x))) == (1 << (7-x)))
|
||||
{
|
||||
grayBrush = QBrush(QColor(0xB6, 0xB6, 0xB6), Qt::BDiagPattern);
|
||||
painter.setBrush(grayBrush);
|
||||
}
|
||||
else painter.setBrush(whiteBrush);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ class CANDataGrid;
|
||||
* Then, support was added for modifying the text color of each cell. This can be used for whatever the calling code wants. An example is
|
||||
* FlowView that uses all of the above functionality plus this functionality in order to show which bits are set as triggers for stopping
|
||||
* the flowview playback.
|
||||
*
|
||||
* Now the control also tracks which signal is using which bit. Currently this is not used for graphical output. I tried but couldn't make it look
|
||||
* like I wanted. So, it's un-used as of yet.
|
||||
*/
|
||||
|
||||
enum GridTextState
|
||||
@@ -41,7 +44,9 @@ public:
|
||||
void setUsed(unsigned char *, bool);
|
||||
void saveImage(QString filename, int width, int height);
|
||||
void setCellTextState(int x, int y, GridTextState state);
|
||||
GridTextState geCellTextState(int x, int y);
|
||||
GridTextState getCellTextState(int x, int y);
|
||||
void setUsedSignalNum(int bit, unsigned char signal);
|
||||
unsigned char getUsedSignalNum(int bit);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||
@@ -54,6 +59,7 @@ private:
|
||||
unsigned char refData[8];
|
||||
unsigned char data[8];
|
||||
unsigned char usedData[8];
|
||||
unsigned char usedSignalNum[64]; //allows a full char per 64 bits we track so we can specify which signal claims this bit
|
||||
GridTextState textStates[8][8];
|
||||
QPoint upperLeft, gridSize;
|
||||
};
|
||||
|
||||
@@ -133,23 +133,23 @@ void CANFrameModel::normalizeTiming()
|
||||
{
|
||||
mutex.lock();
|
||||
if (frames.count() == 0) return;
|
||||
timeOffset = frames[0].timestamp;
|
||||
timeOffset = frames[0].timeStamp().microSeconds();
|
||||
|
||||
//find the absolute lowest timestamp in the whole time. Needed because maybe timestamp was reset in the middle.
|
||||
for (int j = 0; j < frames.count(); j++)
|
||||
{
|
||||
if (frames[j].timestamp < timeOffset) timeOffset = frames[j].timestamp;
|
||||
if (frames[j].timeStamp().microSeconds() < timeOffset) timeOffset = frames[j].timeStamp().microSeconds();
|
||||
}
|
||||
|
||||
for (int i = 0; i < frames.count(); i++)
|
||||
{
|
||||
frames[i].timestamp -= timeOffset;
|
||||
frames[i].setTimeStamp(QCanBusFrame::TimeStamp(0, frames[i].timeStamp().microSeconds() - timeOffset));
|
||||
}
|
||||
|
||||
this->beginResetModel();
|
||||
for (int i = 0; i < filteredFrames.count(); i++)
|
||||
{
|
||||
filteredFrames[i].timestamp -= timeOffset;
|
||||
filteredFrames[i].setTimeStamp(QCanBusFrame::TimeStamp(0, filteredFrames[i].timeStamp().microSeconds() - timeOffset));
|
||||
}
|
||||
this->endResetModel();
|
||||
|
||||
@@ -195,15 +195,15 @@ uint64_t CANFrameModel::getCANFrameVal(int row, Column col)
|
||||
{
|
||||
case Column::TimeStamp:
|
||||
if (overwriteDups) return frame.timedelta;
|
||||
return frame.timestamp;
|
||||
return frame.timeStamp().microSeconds();
|
||||
case Column::FrameId:
|
||||
return frame.ID;
|
||||
return frame.frameId();
|
||||
case Column::Extended:
|
||||
if (frame.extended) return 1;
|
||||
if (frame.hasExtendedFrameFormat()) return 1;
|
||||
return 0;
|
||||
case Column::Remote:
|
||||
if (overwriteDups) return frame.frameCount;
|
||||
if (frame.remote) return 1;
|
||||
if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) return 1;
|
||||
return 0;
|
||||
case Column::Direction:
|
||||
if (frame.isReceived) return 1;
|
||||
@@ -211,10 +211,10 @@ uint64_t CANFrameModel::getCANFrameVal(int row, Column col)
|
||||
case Column::Bus:
|
||||
return static_cast<uint64_t>(frame.bus);
|
||||
case Column::Length:
|
||||
return static_cast<uint64_t>(frame.len);
|
||||
return static_cast<uint64_t>(frame.payload().length());
|
||||
case Column::ASCII: //sort both the same for now
|
||||
case Column::Data:
|
||||
for (int i = 0; i < frame.len; i++) temp += (static_cast<uint64_t>(frame.data[i]) << (56 - (8 * i)));
|
||||
for (int i = 0; i < frame.payload().length(); i++) temp += (static_cast<uint64_t>(frame.payload()[i]) << (56 - (8 * i)));
|
||||
//qDebug() << temp;
|
||||
return temp;
|
||||
case Column::NUM_COLUMN:
|
||||
@@ -311,7 +311,7 @@ void CANFrameModel::recalcOverwrite()
|
||||
uint64_t idAugmented; //id in lower 29 bits, bus number shifted up 29 bits
|
||||
foreach(CANFrame frame, frames)
|
||||
{
|
||||
idAugmented = frame.ID;
|
||||
idAugmented = frame.frameId();
|
||||
idAugmented = idAugmented + (frame.bus << 29ull);
|
||||
if (!overWriteFrames.contains(idAugmented))
|
||||
{
|
||||
@@ -321,7 +321,7 @@ void CANFrameModel::recalcOverwrite()
|
||||
}
|
||||
else
|
||||
{
|
||||
frame.timedelta = frame.timestamp - overWriteFrames[idAugmented].timestamp;
|
||||
frame.timedelta = frame.timeStamp().microSeconds() - overWriteFrames[idAugmented].timeStamp().microSeconds();
|
||||
frame.frameCount = overWriteFrames[idAugmented].frameCount + 1;
|
||||
overWriteFrames[idAugmented] = frame;
|
||||
}
|
||||
@@ -335,7 +335,7 @@ void CANFrameModel::recalcOverwrite()
|
||||
|
||||
for (int i = 0; i < frames.count(); i++)
|
||||
{
|
||||
if (filters[frames[i].ID])
|
||||
if (filters[frames[i].frameId()])
|
||||
{
|
||||
filteredFrames.append(frames[i]);
|
||||
}
|
||||
@@ -347,7 +347,6 @@ void CANFrameModel::recalcOverwrite()
|
||||
|
||||
QVariant CANFrameModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
int dLen;
|
||||
QString tempString;
|
||||
CANFrame thisFrame;
|
||||
static bool rowFlip = false;
|
||||
@@ -361,6 +360,9 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
thisFrame = filteredFrames.at(index.row());
|
||||
|
||||
const unsigned char *data = reinterpret_cast<const unsigned char *>(thisFrame.payload().constData());
|
||||
int dataLen = thisFrame.payload().count();
|
||||
|
||||
if (role == Qt::BackgroundColorRole)
|
||||
{
|
||||
if (dbcHandler != nullptr && interpretFrames)
|
||||
@@ -404,17 +406,17 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const
|
||||
if (timeSeconds) return QString::number(thisFrame.timedelta / 1000000.0, 'f', 5);
|
||||
return QString::number(thisFrame.timedelta);
|
||||
}
|
||||
else ts = Utility::formatTimestamp(thisFrame.timestamp);
|
||||
else ts = Utility::formatTimestamp(thisFrame.timeStamp().microSeconds());
|
||||
if (ts.type() == QVariant::Double) return QString::number(ts.toDouble(), 'f', 5); //never scientific notation, 5 decimal places
|
||||
if (ts.type() == QVariant::LongLong) return QString::number(ts.toLongLong()); //never scientific notion, all digits shown
|
||||
if (ts.type() == QVariant::DateTime) return ts.toDateTime().toString(timeFormat); //custom set format for dates and times
|
||||
return Utility::formatTimestamp(thisFrame.timestamp);
|
||||
return Utility::formatTimestamp(thisFrame.timeStamp().microSeconds());
|
||||
case Column::FrameId:
|
||||
return Utility::formatCANID(thisFrame.ID, thisFrame.extended);
|
||||
return Utility::formatCANID(thisFrame.frameId(), thisFrame.hasExtendedFrameFormat());
|
||||
case Column::Extended:
|
||||
return QString::number(thisFrame.extended);
|
||||
return QString::number(thisFrame.hasExtendedFrameFormat());
|
||||
case Column::Remote:
|
||||
if (!overwriteDups) return QString::number(thisFrame.remote);
|
||||
if (!overwriteDups) return QString::number(thisFrame.frameType() == QCanBusFrame::RemoteRequestFrame);
|
||||
return QString::number(thisFrame.frameCount);
|
||||
case Column::Direction:
|
||||
if (thisFrame.isReceived) return QString(tr("Rx"));
|
||||
@@ -422,40 +424,37 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const
|
||||
case Column::Bus:
|
||||
return QString::number(thisFrame.bus);
|
||||
case Column::Length:
|
||||
return QString::number(thisFrame.len);
|
||||
return QString::number(dataLen);
|
||||
case Column::ASCII:
|
||||
if (thisFrame.ID >= 0x7FFFFFF0ull)
|
||||
if (thisFrame.frameId() >= 0x7FFFFFF0ull)
|
||||
{
|
||||
tempString.append("MARK ");
|
||||
tempString.append(QString::number(thisFrame.ID & 0x7));
|
||||
tempString.append(QString::number(thisFrame.frameId() & 0x7));
|
||||
return tempString;
|
||||
}
|
||||
dLen = thisFrame.len;
|
||||
if (!thisFrame.remote) {
|
||||
if (dLen < 0) dLen = 0;
|
||||
if (dLen > 8) dLen = 8;
|
||||
for (int i = 0; i < dLen; i++)
|
||||
if (thisFrame.frameType() != QCanBusFrame::RemoteRequestFrame) {
|
||||
if (dataLen < 0) dataLen = 0;
|
||||
//if (dLen > 8) dLen = 8;
|
||||
for (int i = 0; i < dataLen; i++)
|
||||
{
|
||||
quint8 byt = thisFrame.data[i];
|
||||
char byt = thisFrame.payload()[i];
|
||||
//0x20 through 0x7E are printable characters. Outside of that range they aren't. So use dots instead
|
||||
if (byt < 0x20) byt = 0x2E; //dot character
|
||||
if (byt > 0x7E) byt = 0x2E;
|
||||
tempString.append(QString::fromUtf8((char *)&byt, 1));
|
||||
tempString.append(QString::fromUtf8(&byt, 1));
|
||||
}
|
||||
}
|
||||
return tempString;
|
||||
case Column::Data:
|
||||
dLen = thisFrame.len;
|
||||
if (dLen < 0) dLen = 0;
|
||||
if (dLen > 8) dLen = 8;
|
||||
if (dataLen < 0) dataLen = 0;
|
||||
//if (useHexMode) tempString.append("0x ");
|
||||
if (thisFrame.remote) {
|
||||
if (thisFrame.frameType() == QCanBusFrame::RemoteRequestFrame) {
|
||||
return tempString;
|
||||
}
|
||||
for (int i = 0; i < dLen; i++)
|
||||
for (int i = 0; i < dataLen; i++)
|
||||
{
|
||||
if (useHexMode) tempString.append( QString::number(thisFrame.data[i], 16).toUpper().rightJustified(2, '0'));
|
||||
else tempString.append(QString::number(thisFrame.data[i], 10));
|
||||
if (useHexMode) tempString.append( QString::number(data[i], 16).toUpper().rightJustified(2, '0'));
|
||||
else tempString.append(QString::number(data[i], 10));
|
||||
tempString.append(" ");
|
||||
}
|
||||
//now, if we're supposed to interpret the data and the DBC handler is loaded then use it
|
||||
@@ -464,8 +463,8 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const
|
||||
DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame);
|
||||
if (msg != nullptr)
|
||||
{
|
||||
tempString.append(" <Interpreted>\n");
|
||||
tempString.append(msg->name + "\n" + msg->comment + "\n");
|
||||
tempString.append(" <" + msg->name + ">\n");
|
||||
if (msg->comment.length() > 1) tempString.append(msg->comment + "\n");
|
||||
for (int j = 0; j < msg->sigHandler->getCount(); j++)
|
||||
{
|
||||
QString sigString;
|
||||
@@ -552,25 +551,26 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false)
|
||||
mutex.lock();
|
||||
CANFrame tempFrame;
|
||||
tempFrame = frame;
|
||||
tempFrame.timestamp -= timeOffset;
|
||||
|
||||
tempFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, tempFrame.timeStamp().microSeconds() - timeOffset));
|
||||
|
||||
lastUpdateNumFrames++;
|
||||
|
||||
//if this ID isn't found in the filters list then add it and show it by default
|
||||
if (!filters.contains(tempFrame.ID))
|
||||
if (!filters.contains(tempFrame.frameId()))
|
||||
{
|
||||
// if there are any filters already configured, leave the new filter disabled
|
||||
if (any_filters_are_configured())
|
||||
filters.insert(tempFrame.ID, false);
|
||||
filters.insert(tempFrame.frameId(), false);
|
||||
else
|
||||
filters.insert(tempFrame.ID, true);
|
||||
filters.insert(tempFrame.frameId(), true);
|
||||
needFilterRefresh = true;
|
||||
}
|
||||
|
||||
if (!overwriteDups)
|
||||
{
|
||||
frames.append(tempFrame);
|
||||
if (filters[tempFrame.ID])
|
||||
if (filters[tempFrame.frameId()])
|
||||
{
|
||||
if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count(), filteredFrames.count());
|
||||
tempFrame.frameCount = 1;
|
||||
@@ -583,10 +583,10 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false)
|
||||
bool found = false;
|
||||
for (int i = 0; i < frames.count(); i++)
|
||||
{
|
||||
if ( (frames[i].ID == tempFrame.ID) && (frames[i].bus == tempFrame.bus) )
|
||||
if ( (frames[i].frameId() == tempFrame.frameId()) && (frames[i].bus == tempFrame.bus) )
|
||||
{
|
||||
tempFrame.frameCount = frames[i].frameCount + 1;
|
||||
tempFrame.timedelta = tempFrame.timestamp - frames[i].timestamp;
|
||||
tempFrame.timedelta = tempFrame.timeStamp().microSeconds() - frames[i].timeStamp().microSeconds();
|
||||
frames.replace(i, tempFrame);
|
||||
found = true;
|
||||
break;
|
||||
@@ -595,7 +595,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false)
|
||||
if (!found)
|
||||
{
|
||||
frames.append(tempFrame);
|
||||
if (filters[tempFrame.ID])
|
||||
if (filters[tempFrame.frameId()])
|
||||
{
|
||||
if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count(), filteredFrames.count());
|
||||
tempFrame.frameCount = 1;
|
||||
@@ -608,7 +608,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false)
|
||||
{
|
||||
for (int j = 0; j < filteredFrames.count(); j++)
|
||||
{
|
||||
if ( (filteredFrames[j].ID == tempFrame.ID) && (filteredFrames[j].bus == tempFrame.bus) )
|
||||
if ( (filteredFrames[j].frameId() == tempFrame.frameId()) && (filteredFrames[j].bus == tempFrame.bus) )
|
||||
{
|
||||
if (autoRefresh) beginResetModel();
|
||||
filteredFrames.replace(j, tempFrame);
|
||||
@@ -642,7 +642,7 @@ void CANFrameModel::sendRefresh()
|
||||
int count = frames.count();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (filters[frames[i].ID])
|
||||
if (filters[frames[i].frameId()])
|
||||
{
|
||||
tempContainer.append(frames[i]);
|
||||
}
|
||||
@@ -718,12 +718,12 @@ void CANFrameModel::insertFrames(const QVector<CANFrame> &newFrames)
|
||||
for (int i = 0; i < newFrames.count(); i++)
|
||||
{
|
||||
frames.append(newFrames[i]);
|
||||
if (!filters.contains(newFrames[i].ID))
|
||||
if (!filters.contains(newFrames[i].frameId()))
|
||||
{
|
||||
filters.insert(newFrames[i].ID, true);
|
||||
filters.insert(newFrames[i].frameId(), true);
|
||||
needFilterRefresh = true;
|
||||
}
|
||||
if (filters[newFrames[i].ID])
|
||||
if (filters[newFrames[i].frameId()])
|
||||
{
|
||||
insertedFiltered++;
|
||||
filteredFrames.append(newFrames[i]);
|
||||
@@ -740,12 +740,12 @@ void CANFrameModel::insertFrames(const QVector<CANFrame> &newFrames)
|
||||
int CANFrameModel::getIndexFromTimeID(unsigned int ID, double timestamp)
|
||||
{
|
||||
int bestIndex = -1;
|
||||
uint64_t intTimeStamp = timestamp * 1000000l;
|
||||
int64_t intTimeStamp = static_cast<int64_t> (timestamp * 1000000l);
|
||||
for (int i = 0; i < frames.count(); i++)
|
||||
{
|
||||
if ((frames[i].ID == ID))
|
||||
if ((frames[i].frameId() == ID))
|
||||
{
|
||||
if (frames[i].timestamp <= intTimeStamp) bestIndex = i;
|
||||
if (frames[i].timeStamp().microSeconds() <= intTimeStamp) bestIndex = i;
|
||||
else break; //drop out of loop as soon as we pass the proper timestamp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ private:
|
||||
bool timeSeconds;
|
||||
bool useSystemTime;
|
||||
bool needFilterRefresh;
|
||||
uint64_t timeOffset;
|
||||
int64_t timeOffset;
|
||||
int lastUpdateNumFrames;
|
||||
uint32_t preallocSize;
|
||||
bool sortDirAsc;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef CONFIG
|
||||
#define CONFIG
|
||||
|
||||
#define VERSION 199
|
||||
#define VERSION 200
|
||||
|
||||
//try to keep this in sync.
|
||||
//SavvyCAN will complain if you connect a GVRET board with a revision
|
||||
|
||||
@@ -9,7 +9,8 @@ public:
|
||||
CANBus();
|
||||
CANBus(const CANBus&);
|
||||
bool operator==(const CANBus&) const;
|
||||
virtual ~CANBus(){};
|
||||
CANBus& operator=(const CANBus& other) = default;
|
||||
//virtual ~CANBus(){}
|
||||
|
||||
int speed;
|
||||
bool listenOnly;
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace CANCon {
|
||||
KVASER,
|
||||
SERIALBUS,
|
||||
REMOTE,
|
||||
MQTT,
|
||||
NONE
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "canconfactory.h"
|
||||
#include "serialbusconnection.h"
|
||||
#include "gvretserial.h"
|
||||
#include "mqtt_bus.h"
|
||||
|
||||
using namespace CANCon;
|
||||
|
||||
@@ -14,6 +15,8 @@ CANConnection* CanConFactory::create(type pType, QString pPortName, QString pDri
|
||||
return new GVRetSerial(pPortName, false);
|
||||
case REMOTE:
|
||||
return new GVRetSerial(pPortName, true); //it's a special case of GVRET connected over TCP/IP so it uses the same class
|
||||
case MQTT:
|
||||
return new MQTT_BUS(pPortName);
|
||||
default: {}
|
||||
}
|
||||
|
||||
|
||||
@@ -214,17 +214,17 @@ bool CANConManager::sendFrame(const CANFrame& pFrame)
|
||||
foreach (CANConnection* conn, mConns)
|
||||
{
|
||||
//check if this CAN connection is supposed to handle the requested bus
|
||||
if (pFrame.bus < (uint32_t)(busBase + conn->getNumBuses()))
|
||||
if (pFrame.bus < (busBase + conn->getNumBuses()))
|
||||
{
|
||||
workingFrame.bus -= busBase;
|
||||
workingFrame.isReceived = false;
|
||||
if (useSystemTime)
|
||||
{
|
||||
workingFrame.timestamp = (QDateTime::currentMSecsSinceEpoch() * 1000);
|
||||
workingFrame.setTimeStamp(QCanBusFrame::TimeStamp(0,QDateTime::currentMSecsSinceEpoch() * 1000));
|
||||
}
|
||||
else
|
||||
{
|
||||
workingFrame.timestamp = mElapsedTimer.nsecsElapsed() / 1000;
|
||||
workingFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, mElapsedTimer.nsecsElapsed() / 1000));
|
||||
//workingFrame.timestamp -= mTimestampBasis;
|
||||
}
|
||||
txFrame = conn->getQueue().get();
|
||||
|
||||
@@ -8,8 +8,8 @@ CANConnection::CANConnection(QString pPort,
|
||||
int pNumBuses,
|
||||
int pQueueLen,
|
||||
bool pUseThread) :
|
||||
mQueue(),
|
||||
mNumBuses(pNumBuses),
|
||||
mQueue(),
|
||||
mPort(pPort),
|
||||
mDriver(pDriver),
|
||||
mType(pType),
|
||||
@@ -343,7 +343,7 @@ void CANConnection::checkTargettedFrame(CANFrame &frame)
|
||||
foreach (const CANFltObserver filt, mBusData[frame.bus].mTargettedFrames)
|
||||
{
|
||||
//qDebug() << "Checking filter with id " << filt.id << " mask " << filt.mask;
|
||||
maskedID = frame.ID & filt.mask;
|
||||
maskedID = frame.frameId() & filt.mask;
|
||||
if (maskedID == filt.id) {
|
||||
qDebug() << "In connection object I got a targetted frame. Forwarding it.";
|
||||
QMetaObject::invokeMethod(filt.observer, "gotTargettedFrame",Qt::QueuedConnection, Q_ARG(CANFrame, frame));
|
||||
|
||||
@@ -77,6 +77,12 @@ public:
|
||||
*/
|
||||
CANCon::status getStatus();
|
||||
|
||||
/**
|
||||
* @brief setConsoleOutput
|
||||
* @param state - set whether to send debugging info to the console or not
|
||||
*/
|
||||
void setConsoleOutput(bool state);
|
||||
|
||||
|
||||
signals:
|
||||
/*not implemented yet */
|
||||
@@ -194,6 +200,7 @@ public slots:
|
||||
protected:
|
||||
int mNumBuses; //protected to allow connected device to figure out how many buses are available
|
||||
QVector<BusData> mBusData;
|
||||
bool mConsoleOutput; //send debugging info to the console?
|
||||
|
||||
//determine if the passed frame is part of a filter or not.
|
||||
void checkTargettedFrame(CANFrame &frame);
|
||||
|
||||
@@ -70,10 +70,10 @@ QVariant CANConnectionModel::data(const QModelIndex &index, int role) const
|
||||
//qDebug() << "Row: " << index.row();
|
||||
|
||||
CANConnection *conn_p = getAtIdx(index.row());
|
||||
bool ret;
|
||||
|
||||
if (!conn_p) return QVariant();
|
||||
|
||||
bool isSocketCAN = (conn_p->getType() == CANCon::SERIALBUS) ? true: false;
|
||||
//bool isSocketCAN = (conn_p->getType() == CANCon::SERIALBUS) ? true: false;
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
|
||||
@@ -147,6 +147,8 @@ CANConnection* CANConnectionModel::getAtIdx(int pIdx) const
|
||||
|
||||
void CANConnectionModel::refresh(int pIndex)
|
||||
{
|
||||
Q_UNUSED(pIndex)
|
||||
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
/*
|
||||
|
||||
@@ -70,7 +70,7 @@ ConnectionWindow::ConnectionWindow(QWidget *parent) :
|
||||
rxBroadcast = new QUdpSocket(this);
|
||||
//Need to make sure it tries to share the address in case there are
|
||||
//multiple instances of SavvyCAN running.
|
||||
rxBroadcast->bind(QHostAddress::AnyIPv4, 17222, QAbstractSocket::ShareAddress);
|
||||
rxBroadcast->bind(QHostAddress::Any, 17222, QAbstractSocket::ShareAddress);
|
||||
|
||||
connect(rxBroadcast, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
|
||||
|
||||
@@ -85,7 +85,7 @@ void ConnectionWindow::readPendingDatagrams()
|
||||
if (!remoteDeviceIP.contains(datagram.senderAddress().toString()))
|
||||
{
|
||||
remoteDeviceIP.append(datagram.senderAddress().toString());
|
||||
//qDebug() << "Add new remote IP " << datagram.senderAddress().toString();
|
||||
qDebug() << "Add new remote IP " << datagram.senderAddress().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,8 +312,8 @@ void ConnectionWindow::populateBusDetails(int offset)
|
||||
}
|
||||
else
|
||||
{
|
||||
bool ret;
|
||||
int numBuses;
|
||||
//bool ret;
|
||||
//int numBuses;
|
||||
|
||||
CANConnection* conn_p = connModel->getAtIdx(selIdx);
|
||||
CANBus bus;
|
||||
@@ -365,7 +365,7 @@ void ConnectionWindow::currentRowChanged(const QModelIndex ¤t, const QMode
|
||||
}
|
||||
else
|
||||
{
|
||||
bool ret;
|
||||
//bool ret;
|
||||
ui->groupBus->setEnabled(true);
|
||||
int numBuses;
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#include "gvretserial.h"
|
||||
|
||||
GVRetSerial::GVRetSerial(QString portName, bool useTcp) :
|
||||
CANConnection(portName, "gvret", CANCon::GVRET_SERIAL, 3, 4000, true),
|
||||
useTcp(useTcp),
|
||||
mTimer(this) /*NB: set this as parent of timer to manage it from working thread */
|
||||
CANConnection(portName, "gvret", CANCon::GVRET_SERIAL, 3, 4000, true),
|
||||
mTimer(this), /*NB: set this as parent of timer to manage it from working thread */
|
||||
useTcp(useTcp)
|
||||
{
|
||||
sendDebug("GVRetSerial()");
|
||||
|
||||
@@ -192,14 +192,14 @@ void GVRetSerial::piSetBusSettings(int pBusIdx, CANBus bus)
|
||||
sendDebug("Got signal to update bauds. 1: " + QString::number((can0Baud & 0xFFFFFFF)) + " 2: " + QString::number((can1Baud & 0xFFFFFFF)));
|
||||
buffer[0] = (char)0xF1; //start of a command over serial
|
||||
buffer[1] = 5; //setup canbus
|
||||
buffer[2] = (unsigned char)(can0Baud & 0xFF); //four bytes of ID LSB first
|
||||
buffer[3] = (unsigned char)(can0Baud >> 8);
|
||||
buffer[4] = (unsigned char)(can0Baud >> 16);
|
||||
buffer[5] = (unsigned char)(can0Baud >> 24);
|
||||
buffer[6] = (unsigned char)(can1Baud & 0xFF); //four bytes of ID LSB first
|
||||
buffer[7] = (unsigned char)(can1Baud >> 8);
|
||||
buffer[8] = (unsigned char)(can1Baud >> 16);
|
||||
buffer[9] = (unsigned char)(can1Baud >> 24);
|
||||
buffer[2] = (char)(can0Baud & 0xFF); //four bytes of ID LSB first
|
||||
buffer[3] = (char)(can0Baud >> 8);
|
||||
buffer[4] = (char)(can0Baud >> 16);
|
||||
buffer[5] = (char)(can0Baud >> 24);
|
||||
buffer[6] = (char)(can1Baud & 0xFF); //four bytes of ID LSB first
|
||||
buffer[7] = (char)(can1Baud >> 8);
|
||||
buffer[8] = (char)(can1Baud >> 16);
|
||||
buffer[9] = (char)(can1Baud >> 24);
|
||||
buffer[10] = 0;
|
||||
sendToSerial(buffer);
|
||||
}
|
||||
@@ -210,18 +210,18 @@ void GVRetSerial::piSetBusSettings(int pBusIdx, CANBus bus)
|
||||
sendDebug("Got signal to update extended bus speeds SWCAN: " + QString::number(swcanBaud) + " LIN1: " + QString::number(lin1Baud) + " LIN2: " + QString::number(lin2Baud));
|
||||
buffer[0] = (char)0xF1; //start of a command over serial
|
||||
buffer[1] = 14; //setup extended buses
|
||||
buffer[2] = (unsigned char)(swcanBaud & 0xFF); //four bytes of ID LSB first
|
||||
buffer[3] = (unsigned char)(swcanBaud >> 8);
|
||||
buffer[4] = (unsigned char)(swcanBaud >> 16);
|
||||
buffer[5] = (unsigned char)(swcanBaud >> 24);
|
||||
buffer[6] = (unsigned char)(lin1Baud & 0xFF); //four bytes of ID LSB first
|
||||
buffer[7] = (unsigned char)(lin1Baud >> 8);
|
||||
buffer[8] = (unsigned char)(lin1Baud >> 16);
|
||||
buffer[9] = (unsigned char)(lin1Baud >> 24);
|
||||
buffer[10] = (unsigned char)(lin2Baud & 0xFF); //four bytes of ID LSB first
|
||||
buffer[11] = (unsigned char)(lin2Baud >> 8);
|
||||
buffer[12] = (unsigned char)(lin2Baud >> 16);
|
||||
buffer[13] = (unsigned char)(lin2Baud >> 24);
|
||||
buffer[2] = (char)(swcanBaud & 0xFF); //four bytes of ID LSB first
|
||||
buffer[3] = (char)(swcanBaud >> 8);
|
||||
buffer[4] = (char)(swcanBaud >> 16);
|
||||
buffer[5] = (char)(swcanBaud >> 24);
|
||||
buffer[6] = (char)(lin1Baud & 0xFF); //four bytes of ID LSB first
|
||||
buffer[7] = (char)(lin1Baud >> 8);
|
||||
buffer[8] = (char)(lin1Baud >> 16);
|
||||
buffer[9] = (char)(lin1Baud >> 24);
|
||||
buffer[10] = (char)(lin2Baud & 0xFF); //four bytes of ID LSB first
|
||||
buffer[11] = (char)(lin2Baud >> 8);
|
||||
buffer[12] = (char)(lin2Baud >> 16);
|
||||
buffer[13] = (char)(lin2Baud >> 24);
|
||||
buffer[14] = 0;
|
||||
sendToSerial(buffer);
|
||||
}
|
||||
@@ -231,8 +231,8 @@ void GVRetSerial::piSetBusSettings(int pBusIdx, CANBus bus)
|
||||
bool GVRetSerial::piSendFrame(const CANFrame& frame)
|
||||
{
|
||||
QByteArray buffer;
|
||||
unsigned int c;
|
||||
int ID;
|
||||
int c;
|
||||
quint32 ID;
|
||||
|
||||
//qDebug() << "Sending out GVRET frame with id " << frame.ID << " on bus " << frame.bus;
|
||||
|
||||
@@ -246,25 +246,25 @@ bool GVRetSerial::piSendFrame(const CANFrame& frame)
|
||||
|
||||
// Doesn't make sense to send an error frame
|
||||
// to an adapter
|
||||
if (frame.ID & 0x20000000) {
|
||||
if (frame.frameId() & 0x20000000) {
|
||||
return true;
|
||||
}
|
||||
ID = frame.ID;
|
||||
if (frame.extended) ID |= 1 << 31;
|
||||
ID = frame.frameId();
|
||||
if (frame.hasExtendedFrameFormat()) ID |= 1u << 31;
|
||||
|
||||
buffer[0] = (unsigned char)0xF1; //start of a command over serial
|
||||
buffer[0] = (char)0xF1; //start of a command over serial
|
||||
buffer[1] = 0; //command ID for sending a CANBUS frame
|
||||
buffer[2] = (unsigned char)(ID & 0xFF); //four bytes of ID LSB first
|
||||
buffer[3] = (unsigned char)(ID >> 8);
|
||||
buffer[4] = (unsigned char)(ID >> 16);
|
||||
buffer[5] = (unsigned char)(ID >> 24);
|
||||
buffer[6] = (unsigned char)((frame.bus) & 3);
|
||||
buffer[7] = (unsigned char)frame.len;
|
||||
for (c = 0; c < frame.len; c++)
|
||||
buffer[2] = (char)(ID & 0xFF); //four bytes of ID LSB first
|
||||
buffer[3] = (char)(ID >> 8);
|
||||
buffer[4] = (char)(ID >> 16);
|
||||
buffer[5] = (char)(ID >> 24);
|
||||
buffer[6] = (char)((frame.bus) & 3);
|
||||
buffer[7] = (char)frame.payload().length();
|
||||
for (c = 0; c < frame.payload().length(); c++)
|
||||
{
|
||||
buffer[8 + c] = frame.data[c];
|
||||
buffer[8 + c] = frame.payload()[c];
|
||||
}
|
||||
buffer[8 + frame.len] = 0;
|
||||
buffer[8 + frame.payload().length()] = 0;
|
||||
|
||||
sendToSerial(buffer);
|
||||
|
||||
@@ -375,18 +375,18 @@ void GVRetSerial::deviceConnected()
|
||||
{
|
||||
sendDebug("Connecting to GVRET Device!");
|
||||
QByteArray output;
|
||||
output.append((unsigned char)0xE7); //this puts the device into binary comm mode
|
||||
output.append((unsigned char)0xE7);
|
||||
output.append((char)0xE7); //this puts the device into binary comm mode
|
||||
output.append((char)0xE7);
|
||||
|
||||
output.append((unsigned char)0xF1);
|
||||
output.append((unsigned char)0x0C); //get number of actually implemented buses. Not implemented except on M2RET
|
||||
output.append((char)0xF1);
|
||||
output.append((char)0x0C); //get number of actually implemented buses. Not implemented except on M2RET
|
||||
mNumBuses = 2; //the proper number if C/12 is not implemented
|
||||
|
||||
output.append((unsigned char)0xF1); //signal we want to issue a command
|
||||
output.append((unsigned char)0x06); //request canbus stats from the board
|
||||
output.append((char)0xF1); //signal we want to issue a command
|
||||
output.append((char)0x06); //request canbus stats from the board
|
||||
|
||||
output.append((unsigned char)0xF1); //another command to the GVRET
|
||||
output.append((unsigned char)0x07); //request device information
|
||||
output.append((char)0xF1); //another command to the GVRET
|
||||
output.append((char)0x07); //request device information
|
||||
|
||||
/*output.append((char)0xF1);
|
||||
output.append((char)0x08); //setting singlewire mode
|
||||
@@ -399,11 +399,11 @@ void GVRetSerial::deviceConnected()
|
||||
output.append((char)0xFF); //signal we don't want single wire mode
|
||||
}*/
|
||||
|
||||
output.append((unsigned char)0xF1); //and another command
|
||||
output.append((unsigned char)0x01); //Time Sync - Not implemented until 333 but we can try
|
||||
output.append((char)0xF1); //and another command
|
||||
output.append((char)0x01); //Time Sync - Not implemented until 333 but we can try
|
||||
|
||||
output.append((unsigned char)0xF1); //yet another command
|
||||
output.append((unsigned char)0x09); //comm validation command
|
||||
output.append((char)0xF1); //yet another command
|
||||
output.append((char)0x09); //comm validation command
|
||||
|
||||
continuousTimeSync = true;
|
||||
|
||||
@@ -659,57 +659,59 @@ void GVRetSerial::procRXChar(unsigned char c)
|
||||
switch (rx_step)
|
||||
{
|
||||
case 0:
|
||||
buildFrame.timestamp = c;
|
||||
buildTimestamp = c;
|
||||
break;
|
||||
case 1:
|
||||
buildFrame.timestamp |= (uint)(c << 8);
|
||||
buildTimestamp |= (uint)(c << 8);
|
||||
break;
|
||||
case 2:
|
||||
buildFrame.timestamp |= (uint)c << 16;
|
||||
buildTimestamp |= (uint)c << 16;
|
||||
break;
|
||||
case 3:
|
||||
buildFrame.timestamp |= (uint)c << 24;
|
||||
buildTimestamp |= (uint)c << 24;
|
||||
|
||||
buildFrame.timestamp += timeBasis;
|
||||
buildTimestamp += timeBasis;
|
||||
if (useSystemTime)
|
||||
{
|
||||
buildFrame.timestamp = QDateTime::currentMSecsSinceEpoch() * 1000l;
|
||||
buildTimestamp = QDateTime::currentMSecsSinceEpoch() * 1000l;
|
||||
}
|
||||
buildFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, buildTimestamp));
|
||||
break;
|
||||
case 4:
|
||||
buildFrame.ID = c;
|
||||
buildId = c;
|
||||
break;
|
||||
case 5:
|
||||
buildFrame.ID |= c << 8;
|
||||
buildId |= c << 8;
|
||||
break;
|
||||
case 6:
|
||||
buildFrame.ID |= c << 16;
|
||||
buildId |= c << 16;
|
||||
break;
|
||||
case 7:
|
||||
buildFrame.ID |= c << 24;
|
||||
if ((buildFrame.ID & 1 << 31) == 1u << 31)
|
||||
buildId |= c << 24;
|
||||
if ((buildId & 1 << 31) == 1u << 31)
|
||||
{
|
||||
buildFrame.ID &= 0x7FFFFFFF;
|
||||
buildFrame.extended = true;
|
||||
buildId &= 0x7FFFFFFF;
|
||||
buildFrame.setExtendedFrameFormat(true);
|
||||
}
|
||||
else buildFrame.extended = false;
|
||||
else buildFrame.setExtendedFrameFormat(false);
|
||||
buildFrame.setFrameId(buildId);
|
||||
break;
|
||||
case 8:
|
||||
buildFrame.len = c & 0xF;
|
||||
if (buildFrame.len > 8) buildFrame.len = 8;
|
||||
buildData.resize(c & 0xF);
|
||||
buildFrame.bus = (c & 0xF0) >> 4;
|
||||
break;
|
||||
default:
|
||||
if (rx_step < buildFrame.len + 9)
|
||||
if (rx_step < buildData.length() + 9)
|
||||
{
|
||||
buildFrame.data[rx_step - 9] = c;
|
||||
buildData[rx_step - 9] = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_state = IDLE;
|
||||
rx_step = 0;
|
||||
buildFrame.isReceived = true;
|
||||
|
||||
buildFrame.setPayload(buildData);
|
||||
buildFrame.setFrameType(QCanBusFrame::FrameType::DataFrame);
|
||||
if (!isCapSuspended())
|
||||
{
|
||||
/* get frame from queue */
|
||||
@@ -717,8 +719,7 @@ void GVRetSerial::procRXChar(unsigned char c)
|
||||
if(frame_p) {
|
||||
//qDebug() << "GVRET got frame on bus " << frame_p->bus;
|
||||
/* copy frame */
|
||||
*frame_p = buildFrame;
|
||||
frame_p->remote = false;
|
||||
*frame_p = buildFrame;
|
||||
checkTargettedFrame(buildFrame);
|
||||
/* enqueue frame */
|
||||
getQueue().queue();
|
||||
@@ -818,11 +819,14 @@ void GVRetSerial::procRXChar(unsigned char c)
|
||||
qDebug() << "Baud 0 = " << can0Baud;
|
||||
qDebug() << "Baud 1 = " << can1Baud;
|
||||
mBusData[0].mBus.setSpeed(can0Baud);
|
||||
mBusData[1].mBus.setSpeed(can1Baud);
|
||||
mBusData[0].mBus.setActive(can0Enabled);
|
||||
mBusData[1].mBus.setActive(can1Enabled);
|
||||
mBusData[0].mConfigured = true;
|
||||
mBusData[1].mConfigured = true;
|
||||
if (mBusData.count() > 1)
|
||||
{
|
||||
mBusData[1].mBus.setSpeed(can1Baud);
|
||||
mBusData[1].mBus.setActive(can1Enabled);
|
||||
mBusData[1].mConfigured = true;
|
||||
}
|
||||
|
||||
can0Baud |= 0x80000000;
|
||||
if (can0Enabled) can0Baud |= 0x40000000;
|
||||
|
||||
@@ -91,8 +91,11 @@ protected:
|
||||
QUdpSocket *udpClient;
|
||||
int framesRapid;
|
||||
STATE rx_state;
|
||||
uint32_t rx_step;
|
||||
int rx_step;
|
||||
CANFrame buildFrame;
|
||||
qint64 buildTimestamp;
|
||||
quint32 buildId;
|
||||
QByteArray buildData;
|
||||
int can0Baud, can1Baud, swcanBaud, lin1Baud, lin2Baud;
|
||||
bool can0Enabled, can1Enabled, swcanEnabled, lin1Enabled, lin2Enabled;
|
||||
bool can0ListenOnly, can1ListenOnly, swcanListenOnly;
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
#include <QCanBusFrame>
|
||||
#include <QSettings>
|
||||
#include <QStringBuilder>
|
||||
#include <QtNetwork>
|
||||
|
||||
#include "utility.h"
|
||||
#include "mqtt_bus.h"
|
||||
|
||||
MQTT_BUS::MQTT_BUS(QString topicName) :
|
||||
CANConnection(topicName, "mqtt_client", CANCon::MQTT, 1, 4000, true),
|
||||
mTimer(this) /*NB: set this as parent of timer to manage it from working thread */
|
||||
{
|
||||
sendDebug("MQTT_BUS()");
|
||||
|
||||
crypto = new SimpleCrypt(Q_UINT64_C(0xdeadbeefface6285));
|
||||
|
||||
isAutoRestart = false;
|
||||
this->topicName = topicName;
|
||||
|
||||
timeBasis = 0;
|
||||
lastSystemTimeBasis = 0;
|
||||
|
||||
readSettings();
|
||||
}
|
||||
|
||||
|
||||
MQTT_BUS::~MQTT_BUS()
|
||||
{
|
||||
delete crypto;
|
||||
stop();
|
||||
sendDebug("~MQTT_BUS");
|
||||
}
|
||||
|
||||
void MQTT_BUS::sendDebug(const QString debugText)
|
||||
{
|
||||
qDebug() << debugText;
|
||||
debugOutput(debugText);
|
||||
}
|
||||
|
||||
void MQTT_BUS::piStarted()
|
||||
{
|
||||
QSettings settings;
|
||||
QString userName = settings.value("Remote/User", "Anonymous").toString();
|
||||
QString host = settings.value("Remote/Host", "api.savvycan.com").toString();
|
||||
int port = settings.value("Remote/Port", 8333).toInt();
|
||||
QByteArray encPass = settings.value("Remote/Pass", "").toByteArray();
|
||||
QByteArray password = crypto->decryptToByteArray(encPass);
|
||||
|
||||
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||
mqttClient = new QMQTT::Client(host, port, sslConfig);
|
||||
|
||||
connect(mqttClient, &QMQTT::Client::connected, this, &MQTT_BUS::clientConnected);
|
||||
connect(mqttClient, &QMQTT::Client::error, this, &MQTT_BUS::clientErrored);
|
||||
|
||||
//qDebug() << "User: " << userName << " Pass: " << password;
|
||||
mqttClient->setClientId(genRandomClientID());
|
||||
mqttClient->setUsername(userName);
|
||||
mqttClient->setPassword(password);
|
||||
mqttClient->connectToHost();
|
||||
}
|
||||
|
||||
//MQTT required a client ID and they cannot be the same for any two clients. But, they really aren't super exciting
|
||||
//or important to be named explicitly. Perhaps it might be nice to be able to see it for debugging though.
|
||||
QString MQTT_BUS::genRandomClientID()
|
||||
{
|
||||
QString output;
|
||||
output.reserve(12);
|
||||
QRandomGenerator gen;
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
int val = gen.bounded(0, 62);
|
||||
if (val < 26) output.append(QChar('A'+val));
|
||||
else if (val < 52) output.append(QChar('a'+val-26));
|
||||
else output.append(QChar('0'+val-52));
|
||||
}
|
||||
qDebug() << "Client ID: " << output;
|
||||
return output;
|
||||
}
|
||||
|
||||
void MQTT_BUS::clientErrored(const QMQTT::ClientError error)
|
||||
{
|
||||
qDebug() << QString::number(error);
|
||||
}
|
||||
|
||||
void MQTT_BUS::piSuspend(bool pSuspend)
|
||||
{
|
||||
/* update capSuspended */
|
||||
setCapSuspended(pSuspend);
|
||||
|
||||
/* flush queue if we are suspended */
|
||||
if(isCapSuspended())
|
||||
getQueue().flush();
|
||||
}
|
||||
|
||||
|
||||
void MQTT_BUS::piStop()
|
||||
{
|
||||
mTimer.stop();
|
||||
disconnectDevice();
|
||||
}
|
||||
|
||||
|
||||
bool MQTT_BUS::piGetBusSettings(int pBusIdx, CANBus& pBus)
|
||||
{
|
||||
return getBusConfig(pBusIdx, pBus);
|
||||
}
|
||||
|
||||
|
||||
void MQTT_BUS::piSetBusSettings(int pBusIdx, CANBus bus)
|
||||
{
|
||||
/* sanity checks */
|
||||
if( (pBusIdx < 0) || pBusIdx >= getNumBuses())
|
||||
return;
|
||||
|
||||
/* copy bus config */
|
||||
setBusConfig(pBusIdx, bus);
|
||||
//we don't really update anything. We're just here to listen and perhaps send frames.
|
||||
}
|
||||
|
||||
|
||||
bool MQTT_BUS::piSendFrame(const CANFrame& frame)
|
||||
{
|
||||
QByteArray buffer;
|
||||
int c;
|
||||
quint32 ID;
|
||||
|
||||
//qDebug() << "Sending out GVRET frame with id " << frame.ID << " on bus " << frame.bus;
|
||||
|
||||
framesRapid++;
|
||||
|
||||
// Doesn't make sense to send an error frame
|
||||
// to an adapter
|
||||
if (frame.frameId() & 0x20000000) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QMQTT::Message msg;
|
||||
QByteArray bytes;
|
||||
|
||||
msg.setTopic(topicName + "/s/" + QString::number(frame.frameId()));
|
||||
uint8_t flags = 0;
|
||||
if (frame.hasExtendedFrameFormat()) flags += 1;
|
||||
if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) flags += 2;
|
||||
if (frame.hasFlexibleDataRateFormat()) flags += 4;
|
||||
if (frame.frameType() == QCanBusFrame::ErrorFrame) flags += 8;
|
||||
|
||||
uint64_t micros = QDateTime::currentMSecsSinceEpoch() * 1000ull;
|
||||
for (int x = 0; x < 8; x++)
|
||||
{
|
||||
bytes.append(micros & 0xFF);
|
||||
micros = micros / 256;
|
||||
}
|
||||
bytes.append(flags);
|
||||
bytes.append(frame.payload());
|
||||
|
||||
msg.setPayload(bytes);
|
||||
mqttClient->publish(msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
|
||||
void MQTT_BUS::readSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
}
|
||||
|
||||
void MQTT_BUS::clientMessageReceived(const QMQTT::Message& message)
|
||||
{
|
||||
uint64_t timeBasis = CANConManager::getInstance()->getTimeBasis();
|
||||
|
||||
|
||||
/* drop frame if capture is suspended */
|
||||
if(isCapSuspended())
|
||||
return;
|
||||
|
||||
CANFrame* frame_p = getQueue().get();
|
||||
if(frame_p)
|
||||
{
|
||||
uint32_t frameID = message.topic().split("/")[1].toInt();
|
||||
uint64_t timeStamp = message.payload()[0] + (message.payload()[1] << 8) + (message.payload()[2] << 16) + (message.payload()[3] << 24)
|
||||
+ ((uint64_t)message.payload()[4] << 32ull) + ((uint64_t)message.payload()[5] << 40ull)
|
||||
+ ((uint64_t)message.payload()[6] << 48ull) + ((uint64_t)message.payload()[7] << 56ull);
|
||||
int flags = message.payload()[8];
|
||||
frame_p->setPayload(message.payload().right(message.payload().count() - 9));
|
||||
frame_p->bus = 0;
|
||||
frame_p->setExtendedFrameFormat(flags & 1);
|
||||
frame_p->setFrameId(frameID);
|
||||
frame_p->setFrameType(QCanBusFrame::DataFrame);
|
||||
frame_p->isReceived = true;
|
||||
if (useSystemTime)
|
||||
{
|
||||
frame_p->setTimeStamp(QCanBusFrame::TimeStamp(0, QDateTime::currentMSecsSinceEpoch() * 1000ul));
|
||||
}
|
||||
else frame_p->setTimeStamp(QCanBusFrame::TimeStamp(0, timeStamp));
|
||||
|
||||
checkTargettedFrame(*frame_p);
|
||||
|
||||
/* enqueue frame */
|
||||
getQueue().queue();
|
||||
}
|
||||
}
|
||||
|
||||
void MQTT_BUS::clientConnected()
|
||||
{
|
||||
sendDebug("Connecting to MQTT Broker!");
|
||||
|
||||
mqttClient->subscribe(topicName + "/+", 0); //subscribe to all sub topics to grab the frames.
|
||||
connect(mqttClient, &QMQTT::Client::received, this, &MQTT_BUS::clientMessageReceived);
|
||||
|
||||
setStatus(CANCon::CONNECTED);
|
||||
CANConStatus stats;
|
||||
stats.conStatus = getStatus();
|
||||
stats.numHardwareBuses = 1;//mNumBuses;
|
||||
emit status(stats);
|
||||
}
|
||||
|
||||
void MQTT_BUS::disconnectDevice() {
|
||||
|
||||
setStatus(CANCon::NOT_CONNECTED);
|
||||
CANConStatus stats;
|
||||
stats.conStatus = getStatus();
|
||||
stats.numHardwareBuses = mNumBuses;
|
||||
emit status(stats);
|
||||
}
|
||||
|
||||
void MQTT_BUS::rebuildLocalTimeBasis()
|
||||
{
|
||||
|
||||
//qDebug() << "Rebuilding GVRET time base. GVRET local base = " << buildTimeBasis;
|
||||
|
||||
/*
|
||||
our time basis is the value we have to modulate the main system basis by in order
|
||||
to sync the GVRET timestamps to the rest of the system.
|
||||
The rest of the system uses CANConManager::getInstance()->getTimeBasis as the basis.
|
||||
GVRET returns to us the current time since boot up in microseconds.
|
||||
timeAtGVRETSync stores the "system" timestamp when the GVRET timestamp was retrieved.
|
||||
*/
|
||||
/*
|
||||
lastSystemTimeBasis = CANConManager::getInstance()->getTimeBasis();
|
||||
int64_t systemDelta = timeAtGVRETSync - lastSystemTimeBasis;
|
||||
int32_t localDelta = buildTimeBasis - systemDelta;
|
||||
timeBasis = -localDelta;
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef MQTTBUS_H
|
||||
#define MQTTBUS_H
|
||||
|
||||
#include <QCanBusDevice>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include "mqtt/qmqtt.h"
|
||||
|
||||
/*************/
|
||||
#include <QDateTime>
|
||||
/*************/
|
||||
|
||||
#include "canframemodel.h"
|
||||
#include "canconnection.h"
|
||||
#include "canconmanager.h"
|
||||
#include "simplecrypt.h"
|
||||
|
||||
class MQTT_BUS : public CANConnection
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MQTT_BUS(QString topicName);
|
||||
virtual ~MQTT_BUS();
|
||||
|
||||
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&) ;
|
||||
|
||||
void disconnectDevice();
|
||||
|
||||
private slots:
|
||||
void clientConnected();
|
||||
void clientErrored(const QMQTT::ClientError error);
|
||||
void clientMessageReceived(const QMQTT::Message& message);
|
||||
|
||||
private:
|
||||
void readSettings();
|
||||
void rebuildLocalTimeBasis();
|
||||
void sendDebug(const QString debugText);
|
||||
QString genRandomClientID();
|
||||
SimpleCrypt *crypto;
|
||||
|
||||
protected:
|
||||
QTimer mTimer;
|
||||
QThread mThread;
|
||||
|
||||
QMQTT::Client *mqttClient;
|
||||
QString topicName;
|
||||
|
||||
bool isAutoRestart;
|
||||
int framesRapid;
|
||||
CANFrame buildFrame;
|
||||
qint64 buildTimestamp;
|
||||
quint32 buildId;
|
||||
QByteArray buildData;
|
||||
uint32_t buildTimeBasis;
|
||||
int32_t timeBasis;
|
||||
uint64_t lastSystemTimeBasis;
|
||||
};
|
||||
|
||||
#endif // MQTT_BUS_H
|
||||
@@ -25,6 +25,8 @@ NewConnectionDialog::NewConnectionDialog(QVector<QString>* ips, QWidget *parent)
|
||||
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->rbMQTT, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged);
|
||||
|
||||
connect(ui->cbDeviceType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &NewConnectionDialog::handleDeviceTypeChanged);
|
||||
connect(ui->btnOK, &QPushButton::clicked, this, &NewConnectionDialog::handleCreateButton);
|
||||
|
||||
@@ -50,6 +52,7 @@ void NewConnectionDialog::handleConnTypeChanged()
|
||||
if (ui->rbGVRET->isChecked()) selectSerial();
|
||||
if (ui->rbSocketCAN->isChecked()) selectSocketCan();
|
||||
if (ui->rbRemote->isChecked()) selectRemote();
|
||||
if (ui->rbMQTT->isChecked()) selectMQTT();
|
||||
}
|
||||
|
||||
void NewConnectionDialog::handleDeviceTypeChanged()
|
||||
@@ -102,6 +105,14 @@ void NewConnectionDialog::selectRemote()
|
||||
}
|
||||
}
|
||||
|
||||
void NewConnectionDialog::selectMQTT()
|
||||
{
|
||||
ui->lPort->setText("Topic Name:");
|
||||
ui->lblDeviceType->setHidden(true);
|
||||
ui->cbDeviceType->setHidden(true);
|
||||
ui->cbPort->clear();
|
||||
}
|
||||
|
||||
void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QString pDriver)
|
||||
{
|
||||
|
||||
@@ -112,7 +123,12 @@ void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QSt
|
||||
break;
|
||||
case CANCon::SERIALBUS:
|
||||
ui->rbSocketCAN->setChecked(true);
|
||||
//you can't configure any of the below three with socketcan so dim them out
|
||||
break;
|
||||
case CANCon::REMOTE:
|
||||
ui->rbRemote->setChecked(true);
|
||||
break;
|
||||
case CANCon::MQTT:
|
||||
ui->rbMQTT->setChecked(true);
|
||||
break;
|
||||
default: {}
|
||||
}
|
||||
@@ -146,6 +162,9 @@ void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QSt
|
||||
else ui->cbPort->addItem(pPortName);
|
||||
break;
|
||||
}
|
||||
case CANCon::MQTT:
|
||||
ui->cbPort->setCurrentText(pPortName);
|
||||
break;
|
||||
default: {}
|
||||
}
|
||||
}
|
||||
@@ -156,6 +175,7 @@ QString NewConnectionDialog::getPortName()
|
||||
case CANCon::GVRET_SERIAL:
|
||||
case CANCon::SERIALBUS:
|
||||
case CANCon::REMOTE:
|
||||
case CANCon::MQTT:
|
||||
return ui->cbPort->currentText();
|
||||
default:
|
||||
qDebug() << "getPortName: can't get port";
|
||||
@@ -178,6 +198,7 @@ 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->rbMQTT->isChecked()) return CANCon::MQTT;
|
||||
qDebug() << "getConnectionType: error";
|
||||
|
||||
return CANCon::NONE;
|
||||
|
||||
@@ -40,6 +40,7 @@ private:
|
||||
void selectKvaser();
|
||||
void selectSocketCan();
|
||||
void selectRemote();
|
||||
void selectMQTT();
|
||||
bool isSerialBusAvailable();
|
||||
void setPortName(CANCon::type pType, QString pPortName, QString pDriver);
|
||||
};
|
||||
|
||||
@@ -115,22 +115,11 @@ void SerialBusConnection::piSetBusSettings(int pBusIdx, CANBus bus)
|
||||
bool SerialBusConnection::piSendFrame(const CANFrame& pFrame)
|
||||
{
|
||||
/* sanity checks */
|
||||
if(0 != pFrame.bus || pFrame.len>8)
|
||||
if(0 != pFrame.bus /*|| pFrame.len>8*/)
|
||||
return false;
|
||||
if (!mDev_p) return false;
|
||||
|
||||
/* fill frame */
|
||||
QCanBusFrame frame;
|
||||
frame.setExtendedFrameFormat(pFrame.extended);
|
||||
frame.setFrameId(pFrame.ID);
|
||||
if (pFrame.remote) {
|
||||
frame.setFrameType(QCanBusFrame::FrameType::RemoteRequestFrame);
|
||||
} else {
|
||||
frame.setFrameType(QCanBusFrame::FrameType::DataFrame);
|
||||
}
|
||||
frame.setPayload(QByteArray(reinterpret_cast<const char *>(pFrame.data),
|
||||
static_cast<int>(pFrame.len)));
|
||||
return mDev_p->writeFrame(frame);
|
||||
return mDev_p->writeFrame(pFrame);
|
||||
}
|
||||
|
||||
|
||||
@@ -190,17 +179,19 @@ void SerialBusConnection::framesReceived()
|
||||
continue;
|
||||
|
||||
/* check frame */
|
||||
if (recFrame.payload().length() <= 8) {
|
||||
//if (recFrame.payload().length() <= 8) {
|
||||
CANFrame* frame_p = getQueue().get();
|
||||
if(frame_p) {
|
||||
frame_p->len = static_cast<uint32_t>(recFrame.payload().length());
|
||||
frame_p->bus = 0;
|
||||
frame_p->setPayload(recFrame.payload());
|
||||
frame_p->bus = 0;
|
||||
|
||||
if (recFrame.frameType() == QCanBusFrame::ErrorFrame) {
|
||||
// Constants defined in include/uapi/linux/can/error.h
|
||||
//since the whole program uses QCanBusFrame now we can just grab errors directly out of the proper place from now on
|
||||
/*
|
||||
switch (recFrame.error()) {
|
||||
case QCanBusFrame::TransmissionTimeoutError:
|
||||
frame_p->ID = 0x20000001;
|
||||
frame_p->setFrameId(0x20000001);
|
||||
break;
|
||||
case QCanBusFrame::LostArbitrationError:
|
||||
frame_p->ID = 0x20000002;
|
||||
@@ -230,25 +221,27 @@ void SerialBusConnection::framesReceived()
|
||||
break;
|
||||
}
|
||||
frame_p->extended = true;
|
||||
*/
|
||||
} else {
|
||||
frame_p->extended = recFrame.hasExtendedFrameFormat();
|
||||
frame_p->ID = recFrame.frameId();
|
||||
frame_p->remote = (recFrame.frameType() == recFrame.RemoteRequestFrame);
|
||||
memcpy(frame_p->data, recFrame.payload().data(), frame_p->len);
|
||||
frame_p->setExtendedFrameFormat(recFrame.hasExtendedFrameFormat());
|
||||
frame_p->setFrameId(recFrame.frameId());
|
||||
frame_p->setTimeStamp(recFrame.timeStamp());
|
||||
frame_p->setFrameType(recFrame.frameType());
|
||||
frame_p->setError(recFrame.error());
|
||||
frame_p->isReceived = true;
|
||||
}
|
||||
|
||||
frame_p->isReceived = true;
|
||||
if (useSystemTime) {
|
||||
frame_p->timestamp = QDateTime::currentMSecsSinceEpoch() * 1000ul;
|
||||
frame_p->setTimeStamp(QCanBusFrame::TimeStamp(0, QDateTime::currentMSecsSinceEpoch() * 1000ul));
|
||||
}
|
||||
else frame_p->timestamp = (recFrame.timeStamp().seconds() * 1000000ul + recFrame.timeStamp().microSeconds()) - timeBasis;
|
||||
else frame_p->setTimeStamp(QCanBusFrame::TimeStamp(0, (recFrame.timeStamp().seconds() * 1000000ul + recFrame.timeStamp().microSeconds()) - timeBasis));
|
||||
|
||||
checkTargettedFrame(*frame_p);
|
||||
|
||||
/* enqueue frame */
|
||||
getQueue().queue();
|
||||
}
|
||||
//}
|
||||
#if 0
|
||||
else
|
||||
qDebug() << "can't get a frame, ERROR";
|
||||
|
||||
@@ -48,7 +48,7 @@ bool DBC_SIGNAL::processAsText(const CANFrame &frame, QString &outString, bool o
|
||||
QString buildString;
|
||||
int startByte = startBit / 8;
|
||||
int bytes = signalSize / 8;
|
||||
for (int x = 0; x < bytes; x++) buildString.append(frame.data[startByte + x]);
|
||||
for (int x = 0; x < bytes; x++) buildString.append(frame.payload().data()[startByte + x]);
|
||||
outString = buildString;
|
||||
cachedValue = outString;
|
||||
return true;
|
||||
@@ -69,7 +69,7 @@ bool DBC_SIGNAL::processAsText(const CANFrame &frame, QString &outString, bool o
|
||||
if (valType == SIGNED_INT) isSigned = true;
|
||||
if (valType == SIGNED_INT || valType == UNSIGNED_INT)
|
||||
{
|
||||
result = Utility::processIntegerSignal(frame.data, startBit, signalSize, intelByteOrder, isSigned);
|
||||
result = Utility::processIntegerSignal(frame.payload(), startBit, signalSize, intelByteOrder, isSigned);
|
||||
endResult = ((double)result * factor) + bias;
|
||||
result = (int64_t)endResult;
|
||||
// if factor is an integer, we don't need the possibly human-unreadable float representation
|
||||
@@ -82,19 +82,19 @@ bool DBC_SIGNAL::processAsText(const CANFrame &frame, QString &outString, bool o
|
||||
//that the bytes that make up the integer are instead treated as having made up
|
||||
//a 32 bit single precision float. That's evil incarnate but it is very fast and small
|
||||
//in terms of new code.
|
||||
result = Utility::processIntegerSignal(frame.data, startBit, 32, intelByteOrder, false);
|
||||
result = Utility::processIntegerSignal(frame.payload(), startBit, 32, intelByteOrder, false);
|
||||
endResult = (*((float *)(&result)) * factor) + bias;
|
||||
}
|
||||
else //double precision float
|
||||
{
|
||||
if ( frame.len < 8 )
|
||||
if ( frame.payload().length() < 8 )
|
||||
{
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
//like the above, this is rotten and evil and wrong in so many ways. Force
|
||||
//calculation of a 64 bit integer and then cast it into a double.
|
||||
result = Utility::processIntegerSignal(frame.data, 0, 64, intelByteOrder, false);
|
||||
result = Utility::processIntegerSignal(frame.payload(), 0, 64, intelByteOrder, false);
|
||||
endResult = (*((double *)(&result)) * factor) + bias;
|
||||
}
|
||||
|
||||
@@ -158,13 +158,13 @@ bool DBC_SIGNAL::processAsInt(const CANFrame &frame, int32_t &outValue)
|
||||
}
|
||||
|
||||
if (valType == SIGNED_INT) isSigned = true;
|
||||
if ( static_cast<int>(frame.len * 8) < (startBit + signalSize) )
|
||||
if ( static_cast<int>(frame.payload().length() * 8) < (startBit + signalSize) )
|
||||
{
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
result = static_cast<int32_t>(Utility::processIntegerSignal(frame.data, startBit, signalSize, intelByteOrder, isSigned));
|
||||
result = static_cast<int32_t>(Utility::processIntegerSignal(frame.payload(), startBit, signalSize, intelByteOrder, isSigned));
|
||||
|
||||
double endResult = (result * factor) + bias;
|
||||
result = static_cast<int32_t>(endResult);
|
||||
@@ -203,19 +203,19 @@ bool DBC_SIGNAL::processAsDouble(const CANFrame &frame, double &outValue)
|
||||
if (valType == SIGNED_INT) isSigned = true;
|
||||
if (valType == SIGNED_INT || valType == UNSIGNED_INT)
|
||||
{
|
||||
if ( frame.len*8 < (startBit+signalSize) )
|
||||
if ( frame.payload().length() * 8 < (startBit+signalSize) )
|
||||
{
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
result = Utility::processIntegerSignal(frame.data, startBit, signalSize, intelByteOrder, isSigned);
|
||||
result = Utility::processIntegerSignal(frame.payload(), startBit, signalSize, intelByteOrder, isSigned);
|
||||
endResult = ((double)result * factor) + bias;
|
||||
result = (int64_t)endResult;
|
||||
}
|
||||
/*TODO: It should be noted that the below floating point has not even been tested. For shame! Test it!*/
|
||||
else if (valType == SP_FLOAT)
|
||||
{
|
||||
if ( frame.len*8 < (startBit+32) )
|
||||
if ( frame.payload().length() * 8 < (startBit + 32) )
|
||||
{
|
||||
result = 0;
|
||||
return false;
|
||||
@@ -225,19 +225,19 @@ bool DBC_SIGNAL::processAsDouble(const CANFrame &frame, double &outValue)
|
||||
//that the bytes that make up the integer are instead treated as having made up
|
||||
//a 32 bit single precision float. That's evil incarnate but it is very fast and small
|
||||
//in terms of new code.
|
||||
result = Utility::processIntegerSignal(frame.data, startBit, 32, false, false);
|
||||
result = Utility::processIntegerSignal(frame.payload(), startBit, 32, false, false);
|
||||
endResult = (*((float *)(&result)) * factor) + bias;
|
||||
}
|
||||
else //double precision float
|
||||
{
|
||||
if ( frame.len < 8 )
|
||||
if ( frame.payload().length() < 8 )
|
||||
{
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
//like the above, this is rotten and evil and wrong in so many ways. Force
|
||||
//calculation of a 64 bit integer and then cast it into a double.
|
||||
result = Utility::processIntegerSignal(frame.data, 0, 64, false, false);
|
||||
result = Utility::processIntegerSignal(frame.payload(), 0, 64, false, false);
|
||||
endResult = (*((double *)(&result)) * factor) + bias;
|
||||
}
|
||||
cachedValue = endResult;
|
||||
|
||||
@@ -74,6 +74,11 @@ public:
|
||||
|
||||
DBC_ATTRIBUTE_VALUE *findAttrValByName(QString name);
|
||||
DBC_ATTRIBUTE_VALUE *findAttrValByIdx(int idx);
|
||||
|
||||
friend bool operator<(const DBC_NODE& l, const DBC_NODE& r)
|
||||
{
|
||||
return (l.name.toLower() < r.name.toLower());
|
||||
}
|
||||
};
|
||||
|
||||
class DBC_MESSAGE; //forward reference so that DBC_SIGNAL can compile before we get to real definition of DBC_MESSAGE
|
||||
@@ -107,6 +112,11 @@ public: //TODO: this is sloppy. It shouldn't all be public!
|
||||
QString makePrettyOutput(double floatVal, int64_t intVal, bool outputName = true, bool isInteger = false);
|
||||
DBC_ATTRIBUTE_VALUE *findAttrValByName(QString name);
|
||||
DBC_ATTRIBUTE_VALUE *findAttrValByIdx(int idx);
|
||||
|
||||
friend bool operator<(const DBC_SIGNAL& l, const DBC_SIGNAL& r)
|
||||
{
|
||||
return (l.name.toLower() < r.name.toLower());
|
||||
}
|
||||
};
|
||||
|
||||
class DBCSignalHandler; //forward declaration to keep from having to include dbchandler.h in this file and thus create a loop
|
||||
@@ -129,6 +139,11 @@ public:
|
||||
|
||||
DBC_ATTRIBUTE_VALUE *findAttrValByName(QString name);
|
||||
DBC_ATTRIBUTE_VALUE *findAttrValByIdx(int idx);
|
||||
|
||||
friend bool operator<(const DBC_MESSAGE& l, const DBC_MESSAGE& r)
|
||||
{
|
||||
return (l.name.toLower() < r.name.toLower());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#include <QApplication>
|
||||
#include <QPalette>
|
||||
#include <QSettings>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include "utility.h"
|
||||
#include "connections/canconmanager.h"
|
||||
|
||||
@@ -43,9 +46,16 @@ bool DBCSignalHandler::addSignal(DBC_SIGNAL &sig)
|
||||
|
||||
bool DBCSignalHandler::removeSignal(DBC_SIGNAL *sig)
|
||||
{
|
||||
Q_UNUSED(sig);
|
||||
//if (sigs.removeAll(*sig) > 0) return true;
|
||||
return false;
|
||||
qDebug() << "Total # of signals: " << getCount();
|
||||
for (int i = 0; i < getCount(); i++)
|
||||
{
|
||||
if (sigs[i].name == sig->name)
|
||||
{
|
||||
sigs.removeAt(i);
|
||||
qDebug() << "Removed signal at idx " << i;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBCSignalHandler::removeSignal(int idx)
|
||||
@@ -82,6 +92,11 @@ int DBCSignalHandler::getCount()
|
||||
return sigs.count();
|
||||
}
|
||||
|
||||
void DBCSignalHandler::sort()
|
||||
{
|
||||
std::sort(sigs.begin(), sigs.end());
|
||||
}
|
||||
|
||||
DBC_MESSAGE* DBCMessageHandler::findMsgByID(uint32_t id)
|
||||
{
|
||||
if (messages.count() == 0) return nullptr;
|
||||
@@ -148,6 +163,20 @@ DBC_MESSAGE* DBCMessageHandler::findMsgByName(QString name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//allow for finding a message just by part of the name
|
||||
DBC_MESSAGE* DBCMessageHandler::findMsgByPartialName(QString name)
|
||||
{
|
||||
if (messages.count() == 0) return nullptr;
|
||||
for (int i = 0; i < messages.count(); i++)
|
||||
{
|
||||
if (messages[i].name.contains(name, Qt::CaseInsensitive))
|
||||
{
|
||||
return &messages[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DBCMessageHandler::addMessage(DBC_MESSAGE &msg)
|
||||
{
|
||||
messages.append(msg);
|
||||
@@ -156,9 +185,16 @@ bool DBCMessageHandler::addMessage(DBC_MESSAGE &msg)
|
||||
|
||||
bool DBCMessageHandler::removeMessage(DBC_MESSAGE *msg)
|
||||
{
|
||||
Q_UNUSED(msg);
|
||||
//if (messages.removeAll(*msg) > 0) return true;
|
||||
return false;
|
||||
qDebug() << "Total # of messages: " << getCount();
|
||||
for (int i = 0; i < getCount(); i++)
|
||||
{
|
||||
if (messages[i].name == msg->name)
|
||||
{
|
||||
messages.removeAt(i);
|
||||
qDebug() << "Removed message at idx " << i;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBCMessageHandler::removeMessageByIndex(int idx)
|
||||
@@ -210,6 +246,15 @@ int DBCMessageHandler::getCount()
|
||||
return messages.count();
|
||||
}
|
||||
|
||||
void DBCMessageHandler::sort()
|
||||
{
|
||||
std::sort(messages.begin(), messages.end());
|
||||
for (int i = 0; i < messages.count(); i++)
|
||||
{
|
||||
messages[i].sigHandler->sort();
|
||||
}
|
||||
}
|
||||
|
||||
bool DBCMessageHandler::filterLabeling()
|
||||
{
|
||||
return filterLabelingEnabled;
|
||||
@@ -235,6 +280,7 @@ DBCFile::DBCFile()
|
||||
messageHandler = new DBCMessageHandler;
|
||||
messageHandler->setMatchingCriteria(EXACT);
|
||||
messageHandler->setFilterLabeling(false);
|
||||
isDirty = false;
|
||||
}
|
||||
|
||||
DBCFile::DBCFile(const DBCFile& cpy) : QObject()
|
||||
@@ -252,6 +298,7 @@ DBCFile::DBCFile(const DBCFile& cpy) : QObject()
|
||||
dbc_nodes.append(cpy.dbc_nodes);
|
||||
dbc_attributes.clear();
|
||||
dbc_attributes.append(cpy.dbc_attributes);
|
||||
isDirty = cpy.isDirty;
|
||||
}
|
||||
|
||||
DBCFile& DBCFile::operator=(const DBCFile& cpy)
|
||||
@@ -270,6 +317,12 @@ DBCFile& DBCFile::operator=(const DBCFile& cpy)
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DBCFile::sort()
|
||||
{
|
||||
std::sort(dbc_nodes.begin(), dbc_nodes.end()); //sort node names
|
||||
messageHandler->sort(); //sort messages, each of which sorts its signals too
|
||||
}
|
||||
|
||||
DBC_NODE* DBCFile::findNodeByIdx(int idx)
|
||||
{
|
||||
if (idx < 0) return nullptr;
|
||||
@@ -349,6 +402,17 @@ void DBCFile::findAttributesByType(DBC_ATTRIBUTE_TYPE typ, QList<DBC_ATTRIBUTE>
|
||||
}
|
||||
}
|
||||
|
||||
//there's no external way to clear the flag. It is only cleared when the file is saved by this object.
|
||||
void DBCFile::setDirtyFlag()
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
bool DBCFile::getDirtyFlag()
|
||||
{
|
||||
return isDirty;
|
||||
}
|
||||
|
||||
DBC_MESSAGE* DBCFile::parseMessageLine(QString line)
|
||||
{
|
||||
QRegularExpression regex;
|
||||
@@ -704,6 +768,7 @@ void DBCFile::loadFile(QString fileName)
|
||||
DBC_MESSAGE *currentMessage = nullptr;
|
||||
DBC_ATTRIBUTE attr;
|
||||
int numSigFaults = 0, numMsgFaults = 0;
|
||||
int linesSinceYield = 0;
|
||||
|
||||
bool inMultilineBU = false;
|
||||
|
||||
@@ -730,6 +795,12 @@ void DBCFile::loadFile(QString fileName)
|
||||
rawLine = QString(inFile->readLine());
|
||||
line = rawLine.simplified();
|
||||
|
||||
linesSinceYield++;
|
||||
if (linesSinceYield > 100)
|
||||
{
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
||||
if (inMultilineBU)
|
||||
{
|
||||
if (rawLine.startsWith("\t") || rawLine.startsWith(" "))
|
||||
@@ -967,6 +1038,7 @@ void DBCFile::loadFile(QString fileName)
|
||||
this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function.
|
||||
filePath = fileName.left(fileName.length() - this->fileName.length());
|
||||
assocBuses = -1;
|
||||
isDirty = false;
|
||||
}
|
||||
|
||||
QVariant DBCFile::processAttributeVal(QString input, DBC_ATTRIBUTE_VAL_TYPE typ)
|
||||
@@ -1183,7 +1255,10 @@ void DBCFile::saveFile(QString fileName)
|
||||
msgNumber++;
|
||||
}
|
||||
|
||||
msgOutput.append("BO_ " + QString::number(msg->ID) + " " + msg->name + ": " + QString::number(msg->len) +
|
||||
uint32_t ID = msg->ID;
|
||||
if (msg->ID > 0x7FF) msg->ID += 0x80000000ul; //set bit 31 if this ID is extended.
|
||||
|
||||
msgOutput.append("BO_ " + QString::number(ID) + " " + msg->name + ": " + QString::number(msg->len) +
|
||||
" " + msg->sender->name + "\n");
|
||||
if (msg->comment.length() > 0)
|
||||
{
|
||||
@@ -1374,6 +1449,8 @@ void DBCFile::saveFile(QString fileName)
|
||||
outFile->close();
|
||||
delete outFile;
|
||||
|
||||
isDirty = false;
|
||||
|
||||
QStringList fileList = fileName.split('/');
|
||||
this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function.
|
||||
filePath = fileName.left(fileName.length() - this->fileName.length());
|
||||
@@ -1417,6 +1494,7 @@ int DBCHandler::createBlankFile()
|
||||
//add our custom attributes to the new file so that we know they're already there.
|
||||
attr.attrType = MESSAGE;
|
||||
attr.defaultValue = QApplication::palette().color(QPalette::Base).name();
|
||||
qDebug() << attr.defaultValue;
|
||||
attr.enumVals.clear();
|
||||
attr.lower = 0;
|
||||
attr.upper = 0;
|
||||
@@ -1426,6 +1504,7 @@ int DBCHandler::createBlankFile()
|
||||
|
||||
attr.attrType = MESSAGE;
|
||||
attr.defaultValue = QApplication::palette().color(QPalette::WindowText).name();
|
||||
qDebug() << attr.defaultValue;
|
||||
attr.enumVals.clear();
|
||||
attr.lower = 0;
|
||||
attr.upper = 0;
|
||||
@@ -1451,6 +1530,11 @@ int DBCHandler::createBlankFile()
|
||||
attr.valType = QINT;
|
||||
newFile.dbc_attributes.append(attr);
|
||||
|
||||
DBC_NODE falseNode;
|
||||
falseNode.name = "Vector__XXX";
|
||||
falseNode.comment = "Default node if none specified";
|
||||
newFile.dbc_nodes.append(falseNode);
|
||||
|
||||
loadedFiles.append(newFile);
|
||||
return loadedFiles.count();
|
||||
}
|
||||
@@ -1495,6 +1579,158 @@ DBCFile* DBCHandler::loadDBCFile(int idx)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DBCFile* DBCHandler::loadJSONFile(QString filename)
|
||||
{
|
||||
QSettings settings;
|
||||
DBCFile *thisFile;
|
||||
DBC_MESSAGE *pMsg;
|
||||
|
||||
createBlankFile();
|
||||
thisFile = &loadedFiles.last();
|
||||
QFile *inFile = new QFile(filename);
|
||||
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
qDebug() << "Could not open JSON file for reading.";
|
||||
delete inFile;
|
||||
return nullptr;
|
||||
}
|
||||
QByteArray wholeFileData = inFile->readAll();
|
||||
inFile->close();
|
||||
delete inFile;
|
||||
//qDebug() << "Data Length: " << wholeFileData.length();
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(wholeFileData);
|
||||
if (jsonDoc.isNull())
|
||||
{
|
||||
qDebug() << "Couldn't load and parse the JSON file for some reason.";
|
||||
return nullptr;
|
||||
}
|
||||
qDebug() << "Loaded JSON";
|
||||
|
||||
QJsonObject jsonObj = jsonDoc.object();
|
||||
QJsonObject jsonMessages = jsonObj["messages"].toObject();
|
||||
|
||||
QJsonObject::iterator iter;
|
||||
for (iter = jsonMessages.begin(); iter != jsonMessages.end(); iter++)
|
||||
{
|
||||
qDebug() << iter.key();
|
||||
DBC_MESSAGE msg;
|
||||
msg.ID = static_cast<uint32_t>(iter->toObject().find("message_id").value().toInt());
|
||||
msg.name = QString(iter.key().toUtf8());
|
||||
msg.len = static_cast<unsigned int>(iter->toObject().find("length_bytes").value().toInt());
|
||||
msg.sender = thisFile->findNodeByIdx(0);
|
||||
QString nodeName = iter->toObject().find("originNode").value().toString();
|
||||
msg.sender = thisFile->findNodeByName(nodeName);
|
||||
msg.bgColor = QColor(thisFile->findAttributeByName("GenMsgBackgroundColor")->defaultValue.toString());
|
||||
msg.fgColor = QColor(thisFile->findAttributeByName("GenMsgForegroundColor")->defaultValue.toString());
|
||||
if (!msg.sender && nodeName.length() > 1)
|
||||
{
|
||||
DBC_NODE node;
|
||||
node.name = nodeName;
|
||||
thisFile->dbc_nodes.append(node);
|
||||
msg.sender = thisFile->findNodeByName(nodeName);
|
||||
}
|
||||
if (nodeName.length() < 2) msg.sender = thisFile->findNodeByIdx(0);
|
||||
thisFile->messageHandler->addMessage(msg);
|
||||
pMsg = thisFile->messageHandler->findMsgByID(msg.ID);
|
||||
if (!pMsg)
|
||||
{
|
||||
qDebug() << "pMsg was null... I have no idea how that is possible. DEBUG ME";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QJsonObject jsonSigs = iter->toObject().find("signals").value().toObject();
|
||||
QJsonObject::iterator sigIter;
|
||||
for (sigIter = jsonSigs.begin(); sigIter != jsonSigs.end(); sigIter++)
|
||||
{
|
||||
qDebug() << sigIter.key();
|
||||
DBC_SIGNAL sig;
|
||||
QJsonObject sigObj = sigIter->toObject();
|
||||
if (sigObj.isEmpty())
|
||||
{
|
||||
qDebug() << "EMPTY!?";
|
||||
}
|
||||
sig.name = QString(sigIter.key().toUtf8());
|
||||
sig.factor = sigObj.find("scale").value().toDouble();
|
||||
sig.bias = sigObj.find("offset").value().toDouble();
|
||||
sig.max = sigObj.find("max").value().toDouble();
|
||||
sig.min = sigObj.find("min").value().toDouble();
|
||||
sig.startBit = sigObj.find("start_position").value().toInt();
|
||||
sig.unitName = sigObj.find("units").value().toString();
|
||||
sig.signalSize = sigObj.find("width").value().toInt();
|
||||
sig.isMultiplexed = false;
|
||||
sig.isMultiplexor = false;
|
||||
sig.parentMessage = pMsg;
|
||||
if (!sigObj.find("mux_id")->isUndefined())
|
||||
{
|
||||
QJsonValue muxVal = sigObj.find("mux_id").value();
|
||||
sig.multiplexValue = muxVal.toInt();
|
||||
sig.isMultiplexed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sig.multiplexValue = 0;
|
||||
}
|
||||
QJsonValue muxerVal = sigObj.find("is_muxer").value();
|
||||
if (!muxerVal.isNull())
|
||||
{
|
||||
if (muxerVal.toBool())
|
||||
{
|
||||
sig.isMultiplexor = true;
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject valuesObj = sigObj.find("value_description")->toObject();
|
||||
if (!valuesObj.isEmpty())
|
||||
{
|
||||
QJsonObject::iterator valIter;
|
||||
for (valIter = valuesObj.begin(); valIter != valuesObj.end(); valIter++)
|
||||
{
|
||||
DBC_VAL_ENUM_ENTRY valEnum;
|
||||
valEnum.value = valIter.value().toInt();
|
||||
valEnum.descript = valIter.key().toUtf8();
|
||||
sig.valList.append(valEnum);
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray rxArray = sigObj.find("receivers")->toArray();
|
||||
if (rxArray.size() < 1) sig.receiver = thisFile->findNodeByIdx(0);
|
||||
else
|
||||
{
|
||||
qDebug() << rxArray[0].toString();
|
||||
sig.receiver = thisFile->findNodeByName(rxArray[0].toString());
|
||||
if (!sig.receiver && rxArray[0].toString().length() > 1)
|
||||
{
|
||||
DBC_NODE node;
|
||||
node.name = rxArray[0].toString();
|
||||
thisFile->dbc_nodes.append(node);
|
||||
sig.receiver = thisFile->findNodeByName(rxArray[0].toString());
|
||||
}
|
||||
if (!sig.receiver) sig.receiver = thisFile->findNodeByIdx(0);
|
||||
}
|
||||
if (!sigObj.find("endianness").value().toString().compare("BIG"))
|
||||
{
|
||||
sig.intelByteOrder = false;
|
||||
}
|
||||
else sig.intelByteOrder = true;
|
||||
|
||||
if (!sigObj.find("signedness").value().toString().compare("UNSIGNED"))
|
||||
{
|
||||
sig.valType = DBC_SIG_VAL_TYPE::UNSIGNED_INT;
|
||||
}
|
||||
else sig.valType = DBC_SIG_VAL_TYPE::SIGNED_INT;
|
||||
|
||||
pMsg->sigHandler->addSignal(sig);
|
||||
if (sig.isMultiplexor) //if this signal was the multiplexor then store that info
|
||||
{
|
||||
DBC_SIGNAL *pSig = pMsg->sigHandler->findSignalByName(sig.name);
|
||||
if (pSig) pMsg->multiplexorSignal = pSig;
|
||||
}
|
||||
}
|
||||
}
|
||||
thisFile->setDirtyFlag();
|
||||
return thisFile;
|
||||
}
|
||||
|
||||
void DBCHandler::removeDBCFile(int idx)
|
||||
{
|
||||
if (loadedFiles.count() == 0) return;
|
||||
@@ -1516,7 +1752,7 @@ void DBCHandler::swapFiles(int pos1, int pos2)
|
||||
if (pos2 < 0) return;
|
||||
if (pos2 >= loadedFiles.count()) return;
|
||||
|
||||
loadedFiles.swap(pos1, pos2);
|
||||
loadedFiles.swapItemsAt(pos1, pos2);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1529,9 +1765,9 @@ DBC_MESSAGE* DBCHandler::findMessage(const CANFrame &frame)
|
||||
{
|
||||
for(int i = 0; i < loadedFiles.count(); i++)
|
||||
{
|
||||
if (loadedFiles[i].getAssocBus() == -1 || frame.bus == (unsigned int)loadedFiles[i].getAssocBus())
|
||||
if (loadedFiles[i].getAssocBus() == -1 || frame.bus == loadedFiles[i].getAssocBus())
|
||||
{
|
||||
DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(frame.ID);
|
||||
DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(frame.frameId());
|
||||
if (msg != nullptr) return msg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ public:
|
||||
bool removeSignal(QString name);
|
||||
void removeAllSignals();
|
||||
int getCount();
|
||||
void sort();
|
||||
|
||||
private:
|
||||
QList<DBC_SIGNAL> sigs; //signals is a reserved word or I'd have used that
|
||||
};
|
||||
@@ -40,6 +42,7 @@ public:
|
||||
DBC_MESSAGE *findMsgByID(uint32_t id);
|
||||
DBC_MESSAGE *findMsgByIdx(int idx);
|
||||
DBC_MESSAGE *findMsgByName(QString name);
|
||||
DBC_MESSAGE *findMsgByPartialName(QString name);
|
||||
bool addMessage(DBC_MESSAGE &msg);
|
||||
bool removeMessage(DBC_MESSAGE *msg);
|
||||
bool removeMessageByIndex(int idx);
|
||||
@@ -51,6 +54,8 @@ public:
|
||||
void setMatchingCriteria(MatchingCriteria_t mc);
|
||||
void setFilterLabeling( bool labelFiltering );
|
||||
bool filterLabeling();
|
||||
void sort();
|
||||
|
||||
private:
|
||||
QList<DBC_MESSAGE> messages;
|
||||
MatchingCriteria_t matchingCriteria;
|
||||
@@ -78,6 +83,9 @@ public:
|
||||
QString getPath();
|
||||
int getAssocBus();
|
||||
void setAssocBus(int bus);
|
||||
void setDirtyFlag();
|
||||
bool getDirtyFlag();
|
||||
void sort();
|
||||
|
||||
DBCMessageHandler *messageHandler;
|
||||
QList<DBC_NODE> dbc_nodes;
|
||||
@@ -86,6 +94,7 @@ private:
|
||||
QString fileName;
|
||||
QString filePath;
|
||||
int assocBuses; //-1 = all buses, 0 = first bus, 1 = second bus, etc.
|
||||
bool isDirty; //has the file been modified?
|
||||
|
||||
bool parseAttribute(QString inpString, DBC_ATTRIBUTE &attr);
|
||||
QVariant processAttributeVal(QString input, DBC_ATTRIBUTE_VAL_TYPE typ);
|
||||
@@ -113,6 +122,7 @@ public:
|
||||
DBCFile* getFileByIdx(int idx);
|
||||
DBCFile* getFileByName(QString name);
|
||||
int createBlankFile();
|
||||
DBCFile* loadJSONFile(QString);
|
||||
static DBCHandler *getReference();
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "dbcloadsavewindow.h"
|
||||
#include "ui_dbcloadsavewindow.h"
|
||||
#include <QComboBox>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <qevent.h>
|
||||
#include "helpwindow.h"
|
||||
@@ -152,7 +153,38 @@ void DBCLoadSaveWindow::newFile()
|
||||
|
||||
void DBCLoadSaveWindow::loadFile()
|
||||
{
|
||||
DBCFile *file = dbcHandler->loadDBCFile(-1);
|
||||
DBCFile *file = nullptr;
|
||||
QString filename;
|
||||
QFileDialog dialog;
|
||||
QSettings settings;
|
||||
|
||||
QStringList filters;
|
||||
filters.append(QString(tr("DBC File (*.dbc)")));
|
||||
filters.append(QString(tr("Tesla JSON File (*.json)")));
|
||||
|
||||
dialog.setDirectory(settings.value("DBC/LoadSaveDirectory", dialog.directory().path()).toString());
|
||||
dialog.setFileMode(QFileDialog::ExistingFile);
|
||||
dialog.setNameFilters(filters);
|
||||
dialog.setViewMode(QFileDialog::Detail);
|
||||
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
filename = dialog.selectedFiles()[0];
|
||||
//right now there is only one file type that can be loaded here so just do it.
|
||||
settings.setValue("DBC/LoadSaveDirectory", dialog.directory().path());
|
||||
|
||||
if (dialog.selectedNameFilter() == filters[0])
|
||||
{
|
||||
if (!filename.contains('.')) filename += ".dbc";
|
||||
file = dbcHandler->loadDBCFile(filename);
|
||||
}
|
||||
if (dialog.selectedNameFilter() == filters[1])
|
||||
{
|
||||
if (!filename.contains('.')) filename += ".json";
|
||||
file = dbcHandler->loadJSONFile(filename);
|
||||
}
|
||||
}
|
||||
|
||||
if(file) {
|
||||
inhibitCellProcessing=true;
|
||||
int idx = ui->tableFiles->rowCount();
|
||||
@@ -184,6 +216,11 @@ void DBCLoadSaveWindow::loadFile()
|
||||
}
|
||||
}
|
||||
|
||||
void DBCLoadSaveWindow::loadJSON()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DBCLoadSaveWindow::saveFile()
|
||||
{
|
||||
int idx = ui->tableFiles->currentRow();
|
||||
@@ -247,6 +284,8 @@ void DBCLoadSaveWindow::editFile()
|
||||
|
||||
void DBCLoadSaveWindow::matchingCriteriaChanged(int index)
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
|
||||
if (inhibitCellProcessing) return;
|
||||
// We don't know which combobox changed, so we just update all of them
|
||||
for (int row=0; row<ui->tableFiles->rowCount(); row++)
|
||||
@@ -289,7 +328,7 @@ void DBCLoadSaveWindow::cellChanged(int row, int col)
|
||||
{
|
||||
DBCFile *file = dbcHandler->getFileByIdx(row);
|
||||
int bus = ui->tableFiles->item(row, col)->text().toInt();
|
||||
int numBuses = CANConManager::getInstance()->getNumBuses();
|
||||
//int numBuses = CANConManager::getInstance()->getNumBuses();
|
||||
if (bus > -2)
|
||||
{
|
||||
file->setAssocBus(bus);
|
||||
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void loadFile();
|
||||
void loadJSON();
|
||||
void saveFile();
|
||||
void removeFile();
|
||||
void moveUp();
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <QTreeWidget>
|
||||
#include <QRandomGenerator>
|
||||
#include "dbchandler.h"
|
||||
#include "dbcsignaleditor.h"
|
||||
#include "dbcmessageeditor.h"
|
||||
#include "dbcnodeeditor.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace Ui {
|
||||
@@ -20,35 +25,57 @@ public:
|
||||
~DBCMainEditor();
|
||||
void setFileIdx(int idx);
|
||||
|
||||
public slots:
|
||||
void updatedNode(DBC_NODE *node);
|
||||
void updatedMessage(DBC_MESSAGE *msg);
|
||||
void updatedSignal(DBC_SIGNAL *sig);
|
||||
|
||||
private slots:
|
||||
void onCellChangedNode(int,int);
|
||||
void onCellClickedNode(int, int);
|
||||
void onCellClickedMessage(int, int);
|
||||
void onCellChangedMessage(int,int);
|
||||
void onCustomMenuNode(QPoint);
|
||||
void onCustomMenuMessage(QPoint);
|
||||
void deleteCurrentNode();
|
||||
void deleteCurrentMessage();
|
||||
void onTreeDoubleClicked(const QModelIndex &index);
|
||||
void currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *prev);
|
||||
void onCustomMenuTree(QPoint);
|
||||
void deleteCurrentTreeItem();
|
||||
void deleteNode(DBC_NODE *node);
|
||||
void deleteMessage(DBC_MESSAGE *msg);
|
||||
void deleteSignal(DBC_SIGNAL *sig);
|
||||
void handleSearch();
|
||||
void handleSearchForward();
|
||||
void handleSearchBackward();
|
||||
void newNode();
|
||||
void newMessage();
|
||||
void newSignal();
|
||||
|
||||
private:
|
||||
Ui::DBCMainEditor *ui;
|
||||
DBCHandler *dbcHandler;
|
||||
const QVector<CANFrame> *referenceFrames;
|
||||
DBCSignalEditor *sigEditor;
|
||||
int currRow;
|
||||
DBCMessageEditor *msgEditor;
|
||||
DBCNodeEditor *nodeEditor;
|
||||
DBCFile *dbcFile;
|
||||
int fileIdx;
|
||||
bool inhibitCellChanged;
|
||||
QIcon nodeIcon;
|
||||
QIcon messageIcon;
|
||||
QIcon signalIcon;
|
||||
QIcon multiplexorSignalIcon;
|
||||
QIcon multiplexedSignalIcon;
|
||||
QList<QTreeWidgetItem *> searchItems;
|
||||
int searchItemPos;
|
||||
//bidirectional mapping of QTreeWidget items back and forth to DBC objects
|
||||
QMap<DBC_NODE*, QTreeWidgetItem *> nodeToItem;
|
||||
QMap<DBC_MESSAGE*, QTreeWidgetItem *> messageToItem;
|
||||
QMap<DBC_SIGNAL*, QTreeWidgetItem *> signalToItem;
|
||||
QMap<QTreeWidgetItem*, DBC_NODE*> itemToNode;
|
||||
QMap<QTreeWidgetItem*, DBC_MESSAGE*> itemToMessage;
|
||||
QMap<QTreeWidgetItem*, DBC_SIGNAL*> itemToSignal;
|
||||
QRandomGenerator randGen;
|
||||
|
||||
void refreshNodesTable();
|
||||
void refreshMessagesTable(const DBC_NODE *node);
|
||||
void showEvent(QShowEvent* event);
|
||||
void closeEvent(QCloseEvent *event);
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
void readSettings();
|
||||
void writeSettings();
|
||||
void insertBlankRow();
|
||||
|
||||
void refreshTree();
|
||||
};
|
||||
|
||||
#endif // DBCMAINEDITOR_H
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
#include "dbcmessageeditor.h"
|
||||
#include "ui_dbcmessageeditor.h"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QKeyEvent>
|
||||
#include <QColorDialog>
|
||||
#include "helpwindow.h"
|
||||
#include "utility.h"
|
||||
|
||||
DBCMessageEditor::DBCMessageEditor(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::DBCMessageEditor)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
readSettings();
|
||||
|
||||
dbcHandler = DBCHandler::getReference();
|
||||
dbcMessage = nullptr;
|
||||
suppressEditCallbacks = false;
|
||||
|
||||
connect(ui->lineComment, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (dbcMessage == nullptr) return;
|
||||
if (suppressEditCallbacks) return;
|
||||
if (dbcMessage->comment != ui->lineComment->text()) dbcFile->setDirtyFlag();
|
||||
dbcMessage->comment = ui->lineComment->text();
|
||||
emit updatedTreeInfo(dbcMessage);
|
||||
});
|
||||
|
||||
connect(ui->lineFrameID, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (dbcMessage == nullptr) return;
|
||||
if (suppressEditCallbacks) return;
|
||||
if (dbcMessage->ID != Utility::ParseStringToNum(ui->lineFrameID->text())) dbcFile->setDirtyFlag();
|
||||
dbcMessage->ID = Utility::ParseStringToNum(ui->lineFrameID->text());
|
||||
emit updatedTreeInfo(dbcMessage);
|
||||
});
|
||||
|
||||
connect(ui->lineMsgName, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (dbcMessage == nullptr) return;
|
||||
if (suppressEditCallbacks) return;
|
||||
if (dbcMessage->name != ui->lineMsgName->text().simplified().replace(' ', '_')) dbcFile->setDirtyFlag();
|
||||
dbcMessage->name = ui->lineMsgName->text().simplified().replace(' ', '_');
|
||||
emit updatedTreeInfo(dbcMessage);
|
||||
});
|
||||
|
||||
connect(ui->lineFrameLen, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (dbcMessage == nullptr) return;
|
||||
if (suppressEditCallbacks) return;
|
||||
if (dbcMessage->len != Utility::ParseStringToNum(ui->lineFrameLen->text())) dbcFile->setDirtyFlag();
|
||||
dbcMessage->len = Utility::ParseStringToNum(ui->lineFrameLen->text());
|
||||
});
|
||||
|
||||
connect(ui->comboSender, &QComboBox::currentTextChanged,
|
||||
[=](const QString newText)
|
||||
{
|
||||
if (dbcMessage == nullptr) return;
|
||||
if (suppressEditCallbacks) return;
|
||||
DBC_NODE *node = dbcFile->findNodeByName(newText);
|
||||
if (!node) return;
|
||||
if (node != dbcMessage->sender) dbcFile->setDirtyFlag();
|
||||
dbcMessage->sender = node;
|
||||
emit updatedTreeInfo(dbcMessage);
|
||||
});
|
||||
|
||||
connect(ui->comboSender->lineEdit(), &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (dbcMessage == nullptr) return;
|
||||
if (suppressEditCallbacks) return;
|
||||
QString newText = ui->comboSender->currentText();
|
||||
DBC_NODE *node = dbcFile->findNodeByName(newText);
|
||||
if (!node)
|
||||
{
|
||||
DBC_NODE newNode;
|
||||
newNode.name = newText;
|
||||
dbcFile->dbc_nodes.append(newNode);
|
||||
node = dbcFile->findNodeByName(newText);
|
||||
ui->comboSender->addItem(newText);
|
||||
}
|
||||
if (node != dbcMessage->sender) dbcFile->setDirtyFlag();
|
||||
dbcMessage->sender = node;
|
||||
emit updatedTreeInfo(dbcMessage);
|
||||
});
|
||||
|
||||
connect(ui->btnTextColor, &QAbstractButton::clicked,
|
||||
[=]()
|
||||
{
|
||||
if (suppressEditCallbacks) return;
|
||||
QColor newColor = QColorDialog::getColor(dbcMessage->fgColor);
|
||||
if (dbcMessage->fgColor != newColor) dbcFile->setDirtyFlag();
|
||||
dbcMessage->fgColor = newColor;
|
||||
DBC_ATTRIBUTE_VALUE *val = dbcMessage->findAttrValByName("GenMsgForegroundColor");
|
||||
if (val)
|
||||
{
|
||||
val->value = newColor.name();
|
||||
}
|
||||
else
|
||||
{
|
||||
DBC_ATTRIBUTE_VALUE newVal;
|
||||
newVal.attrName = "GenMsgForegroundColor";
|
||||
newVal.value = newColor.name();
|
||||
dbcMessage->attributes.append(newVal);
|
||||
}
|
||||
generateSampleText();
|
||||
});
|
||||
|
||||
connect(ui->btnBackgroundColor, &QAbstractButton::clicked,
|
||||
[=]()
|
||||
{
|
||||
if (suppressEditCallbacks) return;
|
||||
QColor newColor = QColorDialog::getColor(dbcMessage->bgColor);
|
||||
if (dbcMessage->bgColor != newColor) dbcFile->setDirtyFlag();
|
||||
dbcMessage->bgColor = newColor;
|
||||
DBC_ATTRIBUTE_VALUE *val = dbcMessage->findAttrValByName("GenMsgBackgroundColor");
|
||||
if (val)
|
||||
{
|
||||
val->value = newColor.name();
|
||||
}
|
||||
else
|
||||
{
|
||||
DBC_ATTRIBUTE_VALUE newVal;
|
||||
newVal.attrName = "GenMsgBackgroundColor";
|
||||
newVal.value = newColor.name();
|
||||
dbcMessage->attributes.append(newVal);
|
||||
}
|
||||
generateSampleText();
|
||||
});
|
||||
|
||||
installEventFilter(this);
|
||||
}
|
||||
|
||||
DBCMessageEditor::~DBCMessageEditor()
|
||||
{
|
||||
removeEventFilter(this);
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void DBCMessageEditor::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
bool DBCMessageEditor::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::KeyRelease) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
switch (keyEvent->key())
|
||||
{
|
||||
case Qt::Key_F1:
|
||||
HelpWindow::getRef()->showHelp("messageeditor.html");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// standard event processing
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DBCMessageEditor::setFileIdx(int idx)
|
||||
{
|
||||
if (idx < 0 || idx > dbcHandler->getFileCount() - 1) return;
|
||||
dbcFile = dbcHandler->getFileByIdx(idx);
|
||||
|
||||
suppressEditCallbacks = true;
|
||||
ui->comboSender->clear();
|
||||
for (int x = 0; x < dbcFile->dbc_nodes.count(); x++)
|
||||
{
|
||||
ui->comboSender->addItem(dbcFile->dbc_nodes[x].name);
|
||||
}
|
||||
suppressEditCallbacks = false;
|
||||
}
|
||||
|
||||
void DBCMessageEditor::readSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
||||
{
|
||||
resize(settings.value("DBCMessageEditor/WindowSize", QSize(340, 400)).toSize());
|
||||
move(settings.value("DBCMessageEditor/WindowPos", QPoint(100, 100)).toPoint());
|
||||
}
|
||||
}
|
||||
|
||||
void DBCMessageEditor::writeSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
||||
{
|
||||
settings.setValue("DBCMessageEditor/WindowSize", size());
|
||||
settings.setValue("DBCMessageEditor/WindowPos", pos());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DBCMessageEditor::setMessageRef(DBC_MESSAGE *msg)
|
||||
{
|
||||
dbcMessage = msg;
|
||||
}
|
||||
|
||||
void DBCMessageEditor::showEvent(QShowEvent* event)
|
||||
{
|
||||
QDialog::showEvent(event);
|
||||
|
||||
refreshView();
|
||||
}
|
||||
|
||||
void DBCMessageEditor::refreshView()
|
||||
{
|
||||
suppressEditCallbacks = true;
|
||||
|
||||
ui->lineComment->setText(dbcMessage->comment);
|
||||
ui->lineFrameID->setText(Utility::formatCANID(dbcMessage->ID));
|
||||
ui->lineMsgName->setText(dbcMessage->name);
|
||||
ui->lineFrameLen->setText(QString::number(dbcMessage->len));
|
||||
for (int i = 0; i < ui->comboSender->count(); i++)
|
||||
{
|
||||
if (ui->comboSender->itemText(i) == dbcMessage->sender->name)
|
||||
{
|
||||
ui->comboSender->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
suppressEditCallbacks = false;
|
||||
|
||||
generateSampleText();
|
||||
}
|
||||
|
||||
void DBCMessageEditor::generateSampleText()
|
||||
{
|
||||
QBrush fg, bg;
|
||||
|
||||
if (dbcMessage->fgColor.isValid()) fg = QBrush(dbcMessage->fgColor);
|
||||
else fg = QBrush(QColor(dbcFile->findAttributeByName("GenMsgForegroundColor")->defaultValue.toString()));
|
||||
if (dbcMessage->bgColor.isValid()) bg = QBrush(dbcMessage->bgColor);
|
||||
else bg = QBrush(QColor(dbcFile->findAttributeByName("GenMsgBackgroundColor")->defaultValue.toString()));
|
||||
|
||||
ui->listSample->clear();
|
||||
QListWidgetItem *item = new QListWidgetItem("Test String");
|
||||
item->setForeground(fg);
|
||||
item->setBackground(bg);
|
||||
ui->listSample->addItem(item);
|
||||
item = new QListWidgetItem("0x20F TestMsg");
|
||||
item->setForeground(fg);
|
||||
item->setBackground(bg);
|
||||
ui->listSample->addItem(item);
|
||||
item = new QListWidgetItem("20 FF 10 A1 BB CC 4D");
|
||||
item->setForeground(fg);
|
||||
item->setBackground(bg);
|
||||
ui->listSample->addItem(item);
|
||||
item = new QListWidgetItem("1024.3434");
|
||||
item->setForeground(fg);
|
||||
item->setBackground(bg);
|
||||
ui->listSample->addItem(item);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef DBCMESSAGEEDITOR_H
|
||||
#define DBCMESSAGEEDITOR_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "dbc_classes.h"
|
||||
#include "dbchandler.h"
|
||||
|
||||
namespace Ui {
|
||||
class DBCMessageEditor;
|
||||
}
|
||||
|
||||
class DBCMessageEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DBCMessageEditor(QWidget *parent = nullptr);
|
||||
~DBCMessageEditor();
|
||||
void showEvent(QShowEvent*);
|
||||
void setMessageRef(DBC_MESSAGE *msg);
|
||||
void setFileIdx(int idx);
|
||||
void refreshView();
|
||||
|
||||
signals:
|
||||
void updatedTreeInfo(DBC_MESSAGE *msg);
|
||||
|
||||
private:
|
||||
Ui::DBCMessageEditor *ui;
|
||||
|
||||
DBCHandler *dbcHandler;
|
||||
DBC_MESSAGE *dbcMessage;
|
||||
DBCFile *dbcFile;
|
||||
bool suppressEditCallbacks;
|
||||
|
||||
void closeEvent(QCloseEvent *event);
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
void readSettings();
|
||||
void writeSettings();
|
||||
void generateSampleText();
|
||||
};
|
||||
|
||||
#endif // DBCMESSAGEEDITOR_H
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "dbcnodeeditor.h"
|
||||
#include "ui_dbcnodeeditor.h"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QKeyEvent>
|
||||
#include <QColorDialog>
|
||||
#include "helpwindow.h"
|
||||
#include "utility.h"
|
||||
|
||||
DBCNodeEditor::DBCNodeEditor(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::DBCNodeEditor)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
readSettings();
|
||||
|
||||
dbcHandler = DBCHandler::getReference();
|
||||
dbcNode = nullptr;
|
||||
|
||||
connect(ui->lineComment, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (dbcNode == nullptr) return;
|
||||
if (dbcNode->comment != ui->lineComment->text()) dbcFile->setDirtyFlag();
|
||||
dbcNode->comment = ui->lineComment->text();
|
||||
emit updatedTreeInfo(dbcNode);
|
||||
});
|
||||
|
||||
connect(ui->lineMsgName, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (dbcNode == nullptr) return;
|
||||
if (dbcNode->name != ui->lineMsgName->text()) dbcFile->setDirtyFlag();
|
||||
dbcNode->name = ui->lineMsgName->text();
|
||||
emit updatedTreeInfo(dbcNode);
|
||||
});
|
||||
|
||||
installEventFilter(this);
|
||||
}
|
||||
|
||||
DBCNodeEditor::~DBCNodeEditor()
|
||||
{
|
||||
removeEventFilter(this);
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void DBCNodeEditor::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
bool DBCNodeEditor::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::KeyRelease) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
switch (keyEvent->key())
|
||||
{
|
||||
case Qt::Key_F1:
|
||||
HelpWindow::getRef()->showHelp("nodeeditor.html");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// standard event processing
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DBCNodeEditor::setFileIdx(int idx)
|
||||
{
|
||||
if (idx < 0 || idx > dbcHandler->getFileCount() - 1) return;
|
||||
dbcFile = dbcHandler->getFileByIdx(idx);
|
||||
}
|
||||
|
||||
void DBCNodeEditor::readSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
||||
{
|
||||
resize(settings.value("DBCNodeEditor/WindowSize", QSize(312, 128)).toSize());
|
||||
move(settings.value("DBCNodeEditor/WindowPos", QPoint(100, 100)).toPoint());
|
||||
}
|
||||
}
|
||||
|
||||
void DBCNodeEditor::writeSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
||||
{
|
||||
settings.setValue("DBCNodeEditor/WindowSize", size());
|
||||
settings.setValue("DBCNodeEditor/WindowPos", pos());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DBCNodeEditor::setNodeRef(DBC_NODE *node)
|
||||
{
|
||||
dbcNode = node;
|
||||
}
|
||||
|
||||
void DBCNodeEditor::showEvent(QShowEvent* event)
|
||||
{
|
||||
QDialog::showEvent(event);
|
||||
|
||||
refreshView();
|
||||
}
|
||||
|
||||
void DBCNodeEditor::refreshView()
|
||||
{
|
||||
ui->lineComment->setText(dbcNode->comment);
|
||||
ui->lineMsgName->setText(dbcNode->name);
|
||||
|
||||
//generateSampleText();
|
||||
}
|
||||
|
||||
void DBCNodeEditor::generateSampleText()
|
||||
{
|
||||
/*
|
||||
QBrush fg, bg;
|
||||
|
||||
if (dbcMessage->fgColor.isValid()) fg = QBrush(dbcMessage->fgColor);
|
||||
else fg = QBrush(QColor(dbcFile->findAttributeByName("GenMsgForegroundColor")->defaultValue.toString()));
|
||||
if (dbcMessage->bgColor.isValid()) bg = QBrush(dbcMessage->bgColor);
|
||||
else bg = QBrush(QColor(dbcFile->findAttributeByName("GenMsgBackgroundColor")->defaultValue.toString()));
|
||||
|
||||
ui->listSample->clear();
|
||||
QListWidgetItem *item = new QListWidgetItem("Test String");
|
||||
item->setForeground(fg);
|
||||
item->setBackground(bg);
|
||||
ui->listSample->addItem(item);
|
||||
item = new QListWidgetItem("0x20F TestMsg");
|
||||
item->setForeground(fg);
|
||||
item->setBackground(bg);
|
||||
ui->listSample->addItem(item);
|
||||
item = new QListWidgetItem("20 FF 10 A1 BB CC 4D");
|
||||
item->setForeground(fg);
|
||||
item->setBackground(bg);
|
||||
ui->listSample->addItem(item);
|
||||
item = new QListWidgetItem("1024.3434");
|
||||
item->setForeground(fg);
|
||||
item->setBackground(bg);
|
||||
ui->listSample->addItem(item);
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef DBCNODEEDITOR_H
|
||||
#define DBCNODEEDITOR_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "dbc_classes.h"
|
||||
#include "dbchandler.h"
|
||||
|
||||
namespace Ui {
|
||||
class DBCNodeEditor;
|
||||
}
|
||||
|
||||
class DBCNodeEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DBCNodeEditor(QWidget *parent = nullptr);
|
||||
~DBCNodeEditor();
|
||||
void showEvent(QShowEvent*);
|
||||
void setNodeRef(DBC_NODE *node);
|
||||
void setFileIdx(int idx);
|
||||
void refreshView();
|
||||
|
||||
signals:
|
||||
void updatedTreeInfo(DBC_NODE *node);
|
||||
|
||||
private:
|
||||
Ui::DBCNodeEditor *ui;
|
||||
|
||||
DBCHandler *dbcHandler;
|
||||
DBC_NODE *dbcNode;
|
||||
DBCFile *dbcFile;
|
||||
|
||||
void closeEvent(QCloseEvent *event);
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
void readSettings();
|
||||
void writeSettings();
|
||||
void generateSampleText();
|
||||
};
|
||||
|
||||
#endif // DBCNODEEDITOR_H
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QDebug>
|
||||
#include <QMenu>
|
||||
#include <QSettings>
|
||||
#include <QRandomGenerator>
|
||||
#include <qevent.h>
|
||||
#include "helpwindow.h"
|
||||
|
||||
@@ -15,8 +16,6 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
|
||||
readSettings();
|
||||
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch());
|
||||
|
||||
dbcHandler = DBCHandler::getReference();
|
||||
dbcMessage = nullptr;
|
||||
currentSignal = nullptr;
|
||||
@@ -35,10 +34,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
ui->comboType->addItem("DOUBLE PRECISION");
|
||||
ui->comboType->addItem("STRING");
|
||||
|
||||
connect(ui->signalsList, SIGNAL(currentRowChanged(int)), this, SLOT(clickSignalList(int)));
|
||||
connect(ui->bitfield, SIGNAL(gridClicked(int,int)), this, SLOT(bitfieldClicked(int,int)));
|
||||
connect(ui->signalsList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuSignals(QPoint)));
|
||||
ui->signalsList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->valuesTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuValues(QPoint)));
|
||||
ui->valuesTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->valuesTable, SIGNAL(cellChanged(int,int)), this, SLOT(onValuesCellChanged(int,int)));
|
||||
@@ -48,6 +44,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == nullptr) return;
|
||||
if (currentSignal->intelByteOrder != ui->cbIntelFormat->isChecked()) dbcFile->setDirtyFlag();
|
||||
currentSignal->intelByteOrder = ui->cbIntelFormat->isChecked();
|
||||
fillSignalForm(currentSignal);
|
||||
});
|
||||
@@ -56,7 +53,9 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == nullptr) return;
|
||||
currentSignal->receiver = dbcFile->findNodeByName(ui->comboReceiver->currentText());
|
||||
DBC_NODE *node = dbcFile->findNodeByName(ui->comboReceiver->currentText());
|
||||
if (currentSignal->receiver != node) dbcFile->setDirtyFlag();
|
||||
currentSignal->receiver = node;
|
||||
});
|
||||
connect(ui->comboType, &QComboBox::currentTextChanged,
|
||||
[=]()
|
||||
@@ -84,6 +83,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
currentSignal->valType = STRING;
|
||||
break;
|
||||
}
|
||||
dbcFile->setDirtyFlag();
|
||||
fillSignalForm(currentSignal);
|
||||
});
|
||||
connect(ui->txtBias, &QLineEdit::editingFinished,
|
||||
@@ -93,7 +93,11 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
double temp;
|
||||
bool result;
|
||||
temp = ui->txtBias->text().toDouble(&result);
|
||||
if (result) currentSignal->bias = temp;
|
||||
if (result)
|
||||
{
|
||||
if (currentSignal->bias != temp) dbcFile->setDirtyFlag();
|
||||
currentSignal->bias = temp;
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->txtMaxVal, &QLineEdit::editingFinished,
|
||||
@@ -103,7 +107,11 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
double temp;
|
||||
bool result;
|
||||
temp = ui->txtMaxVal->text().toDouble(&result);
|
||||
if (result) currentSignal->max = temp;
|
||||
if (result)
|
||||
{
|
||||
if (currentSignal->max != temp) dbcFile->setDirtyFlag();
|
||||
currentSignal->max = temp;
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->txtMinVal, &QLineEdit::editingFinished,
|
||||
@@ -113,7 +121,11 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
double temp;
|
||||
bool result;
|
||||
temp = ui->txtMinVal->text().toDouble(&result);
|
||||
if (result) currentSignal->min = temp;
|
||||
if (result)
|
||||
{
|
||||
if (currentSignal->min != temp) dbcFile->setDirtyFlag();
|
||||
currentSignal->min = temp;
|
||||
}
|
||||
});
|
||||
connect(ui->txtScale, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
@@ -122,19 +134,26 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
double temp;
|
||||
bool result;
|
||||
temp = ui->txtScale->text().toDouble(&result);
|
||||
if (result) currentSignal->factor = temp;
|
||||
if (result)
|
||||
{
|
||||
if (currentSignal->factor != temp) dbcFile->setDirtyFlag();
|
||||
currentSignal->factor = temp;
|
||||
}
|
||||
});
|
||||
connect(ui->txtComment, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == nullptr) return;
|
||||
if (currentSignal->comment != ui->txtComment->text().simplified().replace(' ','_')) dbcFile->setDirtyFlag();
|
||||
currentSignal->comment = ui->txtComment->text().simplified().replace(' ', '_');
|
||||
emit updatedTreeInfo(currentSignal);
|
||||
});
|
||||
|
||||
connect(ui->txtUnitName, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == nullptr) return;
|
||||
if (currentSignal->unitName != ui->txtUnitName->text().simplified().replace(' ','_')) dbcFile->setDirtyFlag();
|
||||
currentSignal->unitName = ui->txtUnitName->text().simplified().replace(' ', '_');
|
||||
});
|
||||
connect(ui->txtBitLength, &QLineEdit::textChanged,
|
||||
@@ -143,8 +162,9 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
if (currentSignal == nullptr) return;
|
||||
int temp;
|
||||
temp = Utility::ParseStringToNum(ui->txtBitLength->text());
|
||||
if (temp < 0) return;
|
||||
if (temp > 63) return;
|
||||
if (temp < 1) return;
|
||||
if (temp > 64) return;
|
||||
if (currentSignal->signalSize != temp) dbcFile->setDirtyFlag();
|
||||
if (currentSignal->valType != SP_FLOAT && currentSignal->valType != DP_FLOAT)
|
||||
currentSignal->signalSize = temp;
|
||||
fillSignalForm(currentSignal);
|
||||
@@ -154,9 +174,10 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
{
|
||||
if (currentSignal == nullptr) return;
|
||||
QString tempNameStr = ui->txtName->text().simplified().replace(' ', '_');
|
||||
if (currentSignal->name != tempNameStr) dbcFile->setDirtyFlag();
|
||||
if (tempNameStr.length() > 0) currentSignal->name = tempNameStr;
|
||||
//need to update the list too.
|
||||
ui->signalsList->currentItem()->setText(currentSignal->name);
|
||||
//need to update the tree too.
|
||||
emit updatedTreeInfo(currentSignal);
|
||||
});
|
||||
|
||||
connect(ui->txtMultiplexValue, &QLineEdit::editingFinished,
|
||||
@@ -165,6 +186,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
if (currentSignal == nullptr) return;
|
||||
int temp;
|
||||
temp = Utility::ParseStringToNum(ui->txtMultiplexValue->text());
|
||||
if (currentSignal->multiplexValue != temp) dbcFile->setDirtyFlag();
|
||||
//TODO: could look up the multiplexor and ensure that the value is within a range that the multiplexor could return
|
||||
currentSignal->multiplexValue = temp;
|
||||
});
|
||||
@@ -178,6 +200,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
//if the set multiplexor for the message was this signal then clear it
|
||||
if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = nullptr;
|
||||
}
|
||||
dbcFile->setDirtyFlag();
|
||||
});
|
||||
|
||||
connect(ui->rbMultiplexor, &QRadioButton::toggled,
|
||||
@@ -192,6 +215,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
//we just set that this is the multiplexor so update the message to show that as well.
|
||||
dbcMessage->multiplexorSignal = currentSignal;
|
||||
}
|
||||
dbcFile->setDirtyFlag();
|
||||
});
|
||||
|
||||
connect(ui->rbNotMulti, &QRadioButton::toggled,
|
||||
@@ -203,6 +227,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) :
|
||||
currentSignal->isMultiplexor = false;
|
||||
if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = nullptr;
|
||||
}
|
||||
dbcFile->setDirtyFlag();
|
||||
});
|
||||
|
||||
installEventFilter(this);
|
||||
@@ -229,9 +254,6 @@ bool DBCSignalEditor::eventFilter(QObject *obj, QEvent *event)
|
||||
case Qt::Key_F1:
|
||||
HelpWindow::getRef()->showHelp("signaleditor.html");
|
||||
break;
|
||||
case Qt::Key_F2:
|
||||
cloneSignal();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@@ -279,12 +301,24 @@ void DBCSignalEditor::setMessageRef(DBC_MESSAGE *msg)
|
||||
dbcMessage = msg;
|
||||
}
|
||||
|
||||
void DBCSignalEditor::setSignalRef(DBC_SIGNAL *sig)
|
||||
{
|
||||
currentSignal = sig;
|
||||
}
|
||||
|
||||
|
||||
void DBCSignalEditor::showEvent(QShowEvent* event)
|
||||
{
|
||||
QDialog::showEvent(event);
|
||||
|
||||
currentSignal = nullptr;
|
||||
refreshSignalsList();
|
||||
fillSignalForm(currentSignal);
|
||||
fillValueTable(currentSignal);
|
||||
}
|
||||
|
||||
void DBCSignalEditor::refreshView()
|
||||
{
|
||||
fillSignalForm(currentSignal);
|
||||
fillValueTable(currentSignal);
|
||||
}
|
||||
|
||||
void DBCSignalEditor::onValuesCellChanged(int row,int col)
|
||||
@@ -318,21 +352,6 @@ void DBCSignalEditor::onValuesCellChanged(int row,int col)
|
||||
}
|
||||
}
|
||||
|
||||
void DBCSignalEditor::onCustomMenuSignals(QPoint point)
|
||||
{
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
menu->addAction(tr("Add a new signal"), this, SLOT(addNewSignal()));
|
||||
if (ui->signalsList->currentRow() != -1)
|
||||
{
|
||||
menu->addAction(tr("Clone currently selected signal"), this, SLOT(cloneSignal()));
|
||||
menu->addAction(tr("Delete currently selected signal"), this, SLOT(deleteCurrentSignal()));
|
||||
}
|
||||
|
||||
menu->popup(ui->signalsList->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void DBCSignalEditor::onCustomMenuValues(QPoint point)
|
||||
{
|
||||
QMenu *menu = new QMenu(this);
|
||||
@@ -343,86 +362,6 @@ void DBCSignalEditor::onCustomMenuValues(QPoint point)
|
||||
menu->popup(ui->valuesTable->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void DBCSignalEditor::addNewSignal()
|
||||
{
|
||||
int num = qrand() % 70000;
|
||||
QString newName = "SIGNAL" + QString::number(num);
|
||||
DBC_SIGNAL newSig;
|
||||
newSig.name = newName;
|
||||
newSig.bias = 0.0;
|
||||
newSig.factor = 1.0;
|
||||
newSig.intelByteOrder = true;
|
||||
newSig.max = 0.0;
|
||||
newSig.min = 0.0;
|
||||
newSig.receiver = dbcFile->findNodeByIdx(0);
|
||||
newSig.signalSize = 1;
|
||||
newSig.startBit = 0;
|
||||
newSig.valType = UNSIGNED_INT;
|
||||
newSig.isMultiplexed = false;
|
||||
newSig.isMultiplexor = false;
|
||||
newSig.multiplexValue = 0;
|
||||
newSig.parentMessage = dbcMessage;
|
||||
|
||||
dbcMessage->sigHandler->addSignal(newSig);
|
||||
|
||||
/* add item at the end of the list and select it */
|
||||
/* this will call clickSignalList */
|
||||
ui->signalsList->addItem(newName);
|
||||
ui->signalsList->setCurrentRow(ui->signalsList->count()-1);
|
||||
}
|
||||
|
||||
void DBCSignalEditor::cloneSignal()
|
||||
{
|
||||
int num = qrand() % 100;
|
||||
|
||||
int idx = ui->signalsList->currentRow();
|
||||
if (idx < 0) return;
|
||||
|
||||
DBC_SIGNAL *oldSig = dbcMessage->sigHandler->findSignalByIdx(idx);
|
||||
if (!oldSig) return;
|
||||
|
||||
QString newName = oldSig->name + QString::number(num);
|
||||
DBC_SIGNAL newSig;
|
||||
newSig.name = newName;
|
||||
newSig.bias = oldSig->bias;
|
||||
newSig.factor = oldSig->factor;
|
||||
newSig.intelByteOrder = oldSig->intelByteOrder;
|
||||
newSig.max = oldSig->max;
|
||||
newSig.min = oldSig->min;
|
||||
newSig.receiver = oldSig->receiver;
|
||||
newSig.signalSize = oldSig->signalSize;
|
||||
newSig.startBit = oldSig->startBit;
|
||||
newSig.valType = oldSig->valType;
|
||||
newSig.valList.append(oldSig->valList);
|
||||
newSig.isMultiplexed = oldSig->isMultiplexed;
|
||||
newSig.isMultiplexor = oldSig->isMultiplexor; //maybe should force this false since you can't have two or more!
|
||||
newSig.multiplexValue = oldSig->multiplexValue;
|
||||
newSig.parentMessage = dbcMessage;
|
||||
|
||||
dbcMessage->sigHandler->addSignal(newSig);
|
||||
|
||||
/* add item at the end of the list */
|
||||
ui->signalsList->addItem(newName);
|
||||
|
||||
//unlike adding a signal we don't want to select the new signal here.
|
||||
//ui->signalsList->setCurrentRow(ui->signalsList->count()-1);
|
||||
}
|
||||
|
||||
void DBCSignalEditor::deleteCurrentSignal()
|
||||
{
|
||||
int currIdx = ui->signalsList->currentRow();
|
||||
|
||||
//if(currIdx==ui->signalsList->count()-1)
|
||||
//return;
|
||||
|
||||
if (currIdx > -1)
|
||||
{
|
||||
delete(ui->signalsList->item(currIdx));
|
||||
dbcMessage->sigHandler->removeSignal(currIdx);
|
||||
currentSignal = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DBCSignalEditor::deleteCurrentValue()
|
||||
{
|
||||
int currIdx = ui->valuesTable->currentRow();
|
||||
@@ -433,25 +372,6 @@ void DBCSignalEditor::deleteCurrentValue()
|
||||
}
|
||||
}
|
||||
|
||||
void DBCSignalEditor::refreshSignalsList()
|
||||
{
|
||||
ui->signalsList->clear();
|
||||
|
||||
|
||||
for (int x = 0; x < dbcMessage->sigHandler->getCount(); x++)
|
||||
{
|
||||
DBC_SIGNAL *sig = dbcMessage->sigHandler->findSignalByIdx(x);
|
||||
ui->signalsList->addItem(sig->name);
|
||||
}
|
||||
|
||||
if( ui->signalsList->count()>0 ) {
|
||||
/* click first element */
|
||||
ui->signalsList->setCurrentRow(0);
|
||||
}
|
||||
else
|
||||
clickSignalList(-1);
|
||||
}
|
||||
|
||||
/* fillSignalForm also handles group "enabled" state */
|
||||
/* WARNING: fillSignalForm can be called recursively since it is in the listener of cbIntelFormat */
|
||||
void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig)
|
||||
@@ -585,7 +505,7 @@ void DBCSignalEditor::fillValueTable(DBC_SIGNAL *sig)
|
||||
|
||||
for (int i = 0; i < sig->valList.count(); i++)
|
||||
{
|
||||
QTableWidgetItem *val = new QTableWidgetItem(Utility::formatNumber(sig->valList[i].value));
|
||||
QTableWidgetItem *val = new QTableWidgetItem(Utility::formatNumber((uint64_t)sig->valList[i].value));
|
||||
QTableWidgetItem *desc = new QTableWidgetItem(sig->valList[i].descript);
|
||||
rowIdx = ui->valuesTable->rowCount();
|
||||
ui->valuesTable->insertRow(rowIdx);
|
||||
@@ -599,16 +519,6 @@ void DBCSignalEditor::fillValueTable(DBC_SIGNAL *sig)
|
||||
inhibitCellChanged = false;
|
||||
}
|
||||
|
||||
void DBCSignalEditor::clickSignalList(int row)
|
||||
{
|
||||
//qDebug() << ui->signalsList->item(row)->text();
|
||||
|
||||
DBC_SIGNAL *thisSig = (row<0) ? nullptr : dbcMessage->sigHandler->findSignalByName(ui->signalsList->item(row)->text());
|
||||
currentSignal = thisSig;
|
||||
fillSignalForm(thisSig);
|
||||
fillValueTable(thisSig);
|
||||
}
|
||||
|
||||
void DBCSignalEditor::bitfieldClicked(int x, int y)
|
||||
{
|
||||
int bit = (7 - x) + (y * 8);
|
||||
@@ -644,6 +554,7 @@ void DBCSignalEditor::generateUsedBits()
|
||||
{
|
||||
int byt = y / 8;
|
||||
usedBits[byt] |= 1 << (y % 8);
|
||||
ui->bitfield->setUsedSignalNum(y, x);
|
||||
}
|
||||
}
|
||||
else //big endian / motorola format
|
||||
@@ -654,6 +565,7 @@ void DBCSignalEditor::generateUsedBits()
|
||||
{
|
||||
int byt = startBit / 8;
|
||||
usedBits[byt] |= 1 << (startBit % 8);
|
||||
ui->bitfield->setUsedSignalNum(startBit, x);
|
||||
size--;
|
||||
if ((startBit % 8) == 0) startBit += 15;
|
||||
else startBit--;
|
||||
|
||||
@@ -16,19 +16,19 @@ class DBCSignalEditor : public QDialog
|
||||
public:
|
||||
explicit DBCSignalEditor(QWidget *parent = 0);
|
||||
void setMessageRef(DBC_MESSAGE *msg);
|
||||
void showEvent(QShowEvent*);
|
||||
void setFileIdx(int idx);
|
||||
void setSignalRef(DBC_SIGNAL *sig);
|
||||
void showEvent(QShowEvent*);
|
||||
~DBCSignalEditor();
|
||||
void refreshView();
|
||||
|
||||
signals:
|
||||
void updatedTreeInfo(DBC_SIGNAL *sig);
|
||||
|
||||
private slots:
|
||||
void clickSignalList(int);
|
||||
void bitfieldClicked(int x, int y);
|
||||
void onValuesCellChanged(int row,int col);
|
||||
void onCustomMenuSignals(QPoint);
|
||||
void onCustomMenuValues(QPoint);
|
||||
void addNewSignal();
|
||||
void cloneSignal();
|
||||
void deleteCurrentSignal();
|
||||
void deleteCurrentValue();
|
||||
|
||||
private:
|
||||
@@ -39,7 +39,6 @@ private:
|
||||
DBCFile *dbcFile;
|
||||
bool inhibitCellChanged;
|
||||
|
||||
void refreshSignalsList();
|
||||
void fillSignalForm(DBC_SIGNAL *sig);
|
||||
void fillValueTable(DBC_SIGNAL *sig);
|
||||
void generateUsedBits();
|
||||
|
||||
@@ -13,12 +13,13 @@ Some people are big fans of fuzzing, some people have no use for it. There usual
|
||||
|
||||
Fuzzing is Dangerous
|
||||
====================
|
||||
You have now been warned. Sending random garbage over the CAN bus to see what happens could mess something up. It just might put your vehicle into gear and cause you to drive over a box full of kittens. Be careful! Never fuzz a car unless you're right there and you can stop it. Even better, don't do it unless your car is up on a hoist or something and can't drive over anyone. Even still, there is a small chance you could cause an adverse effect to your car.
|
||||
You have now been warned. Sending random garbage over the CAN bus to see what happens could mess something up. It just might put your vehicle into gear and cause you to drive over a box full of kittens. Be careful! Never fuzz a car unless you're right there and you can stop it. Even better, don't do it unless your car is up on a hoist or jacks and can't drive over anyone. Even still, there is a small chance you could cause an adverse effect to your car. If you break something you own the pieces.
|
||||
|
||||
Controlling the Fuzzy Beast
|
||||
===========================
|
||||
|
||||
So, you want to give it a try? Let's do it! First of all, you can set the delay between frames and the burst rate. The burst rate can cause the program to send one than one frame each interval. This is useful as a CAN bus could potentially support 2000 to 8000 frames per second. Then you can set the number of bytes to send. Ordinarily this would be the full 8 but you can experiment with smaller frames. You can set to send on a specific bus. That's all the simple settings. It gets a bit more complicated now.
|
||||
So, you want to give it a try? Let's do it! First of all, you can set the delay between frames and the burst rate. The delay is in milliseconds but can be set as low as 0. If the delay is set to 0 then the system will attempt to send frames as fast as it can. However, even then you may not get quite as many frames per second as you'd like. Even at 0 it will still be scheduled by your operating system and so might not quite get to the speed you want. The burst rate can cause the program to send one than one frame each interval. This is useful as a CAN bus could potentially support 2000 to 8000 frames per second. If you need rapid frame sending your best bet is to set the sending interval to 1-2ms and then adjust the burst
|
||||
rate until you get your desired sending rate. Then you can set the number of bytes to send. Ordinarily this would be the full 8 but you can experiment with smaller frames. You can set to send on a specific bus. That's all the simple settings. It gets a bit more complicated now.
|
||||
|
||||
The "ID Scanning" box has two radio buttons:
|
||||
|
||||
@@ -36,9 +37,10 @@ The last box is "Bit Scanning"
|
||||
2. Sweep causes the system to set the first one, then unset that one and set the second bit, then unset that, etc. Thus the fuzzed bit sweeps and only one fuzzed bit is set at once.
|
||||
3. Random will randomly pick whether each fuzzed bit is set or not.
|
||||
|
||||
In order to fuzz bits you need to set which bits to fuzz and which not to. As listed at the bottom of the window, there is a color code to the 8x8 grid. Clicking cells in the grid will toggle them between their various values. White bits are never set, black bits are always set no matter what, green bits follow the fuzzing pattern you specified in "Bit Scanning"
|
||||
In order to fuzz bits you need to set which bits to fuzz and which not to. As listed at the bottom of the window, there is a color code to the 8x8 grid. Clicking cells in the grid will toggle them between their various values. White bits are never set, black bits are always set no matter what, green bits follow the fuzzing pattern you specified in "Bit Scanning" You can also set the bytes directly with the text boxes above the 8x8 grid. Setting a hexadecimal value in these
|
||||
boxes will set the relavent bits in the 8x8 grid. You must press the ENTER/RETURN key to set the values. Merely changing the value will not update it (as a safety measure).
|
||||
|
||||
Pulling the Trigger
|
||||
===================
|
||||
|
||||
Once you've configured everything click "Start Fuzzing" to give it a shot. You will see the number of frames sent so far listed below the button. You can stop the fuzzing by pushing the button again.
|
||||
Once you've configured everything click "Start Fuzzing" to give it a shot. You will see the number of frames sent so far listed below the button. Approximately four times per second the current value for each byte is copied into the text boxes just above the 8x8 grid. This can be used to see what is going on and to ensure that it is working the way you want it to. You can stop the fuzzing by pushing the start button again.
|
||||
|
||||
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 65 KiB |
@@ -22,22 +22,22 @@ QString FilterUtility::getId( QListWidgetItem * item )
|
||||
return getId(item->text());
|
||||
}
|
||||
|
||||
uint32_t FilterUtility::getGMLanArbitrationId(uint32_t id)
|
||||
uint32_t FilterUtility::getGMLanArbitrationId(int32_t id)
|
||||
{
|
||||
return (id >> 13) & 0x1FFF;
|
||||
}
|
||||
|
||||
uint32_t FilterUtility::getGMLanPriorityBits(uint32_t id)
|
||||
uint32_t FilterUtility::getGMLanPriorityBits(int32_t id)
|
||||
{
|
||||
return (id >> 26) & 0x7;
|
||||
}
|
||||
|
||||
uint32_t FilterUtility::getGMLanSenderId(uint32_t id)
|
||||
uint32_t FilterUtility::getGMLanSenderId(int32_t id)
|
||||
{
|
||||
return id & 0x1FFF;
|
||||
}
|
||||
|
||||
QListWidgetItem * FilterUtility::createCheckableFilterItem(uint32_t id, bool checked, QListWidget* parent)
|
||||
QListWidgetItem * FilterUtility::createCheckableFilterItem(int32_t id, bool checked, QListWidget* parent)
|
||||
{
|
||||
QListWidgetItem * thisItem = createFilterItem(id,parent);
|
||||
thisItem->setFlags(thisItem->flags() | Qt::ItemIsUserCheckable);
|
||||
@@ -48,19 +48,22 @@ QListWidgetItem * FilterUtility::createCheckableFilterItem(uint32_t id, bool che
|
||||
return thisItem;
|
||||
}
|
||||
|
||||
QListWidgetItem * FilterUtility::createFilterItem(uint32_t id, QListWidget* parent)
|
||||
QListWidgetItem * FilterUtility::createFilterItem(int32_t id, QListWidget* parent)
|
||||
{
|
||||
QSettings settings;
|
||||
DBCHandler * dbcHandler = DBCHandler::getReference();
|
||||
QListWidgetItem *thisItem = new QListWidgetItem(parent);
|
||||
QString filterItemName = Utility::formatCANID(id);
|
||||
|
||||
//Note, there are multiple filter labeling preferences. There is one in main settings to globally
|
||||
//enable or disable them all. Then each loaded DBC file also can be selected on/off
|
||||
//Both must be enabled for you to see labeling.
|
||||
if (settings.value("Main/FilterLabeling", false).toBool())
|
||||
{
|
||||
{
|
||||
// Filter labeling (show interpreted frame names next to the CAN addr ID)
|
||||
MatchingCriteria_t matchingCriteria;
|
||||
DBC_MESSAGE *msg = dbcHandler->findMessageForFilter(id,&matchingCriteria);
|
||||
if (msg != NULL)
|
||||
if (msg != nullptr)
|
||||
{
|
||||
filterItemName.append(" ");
|
||||
filterItemName.append(msg->name);
|
||||
@@ -77,4 +80,4 @@ QListWidgetItem * FilterUtility::createFilterItem(uint32_t id, QListWidget* pare
|
||||
|
||||
thisItem->setText(filterItemName);
|
||||
return thisItem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,16 @@ class FilterUtility
|
||||
{
|
||||
|
||||
public:
|
||||
static QListWidgetItem * createFilterItem(uint32_t id, QListWidget* parent=NULL); // if parent is given, add item automatically to listwidget
|
||||
static QListWidgetItem * createCheckableFilterItem(uint32_t id, bool checked, QListWidget* parent=NULL);
|
||||
static QListWidgetItem * createFilterItem(int32_t id, QListWidget* parent=NULL); // if parent is given, add item automatically to listwidget
|
||||
static QListWidgetItem * createCheckableFilterItem(int32_t id, bool checked, QListWidget* parent=NULL);
|
||||
|
||||
static uint32_t getIdAsInt( QListWidgetItem * item );
|
||||
static QString getId( QListWidgetItem * item );
|
||||
static QString getId( QString itemText );
|
||||
|
||||
static uint32_t getGMLanArbitrationId(uint32_t id);
|
||||
static uint32_t getGMLanSenderId(uint32_t id);
|
||||
static uint32_t getGMLanPriorityBits(uint32_t id);
|
||||
static uint32_t getGMLanArbitrationId(int32_t id);
|
||||
static uint32_t getGMLanSenderId(int32_t id);
|
||||
static uint32_t getGMLanPriorityBits(int32_t id);
|
||||
};
|
||||
|
||||
#endif // DBCLOADSAVEWINDOW_H
|
||||
#endif // DBCLOADSAVEWINDOW_H
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
|
||||
#include <QFile>
|
||||
|
||||
//You might wonder: Collin, what in the hell is this for? Firmware uploader? For what? I'm interested! Well, it's a custom
|
||||
//firmware uploader for a motor controller I built. Why would that be in this project. Cuz. It's not really relevant
|
||||
//to anyone else but might serve as a decent reference for a few things: How to make an uploader interface that runs over CAN,
|
||||
//how to lay out a screen like this, how to make a comm protocol for firmware updating over CAN. But, most things use UDS
|
||||
//for firmware updates and wouldn't need this specific code. But, it might be able to be turned into a UDS firmware uploader or downloader.
|
||||
//Note that this screen is specifically hidden by default because of it's oddball status. You have to re-enable it in mainwindow.cpp to see it.
|
||||
|
||||
FirmwareUploaderWindow::FirmwareUploaderWindow(const QVector<CANFrame> *frames, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::FirmwareUploaderWindow)
|
||||
@@ -70,17 +77,20 @@ void FirmwareUploaderWindow::updatedFrames(int numFrames)
|
||||
|
||||
void FirmwareUploaderWindow::gotTargettedFrame(CANFrame frame)
|
||||
{
|
||||
qDebug() << "FUW: Got targetted frame with id " << frame.ID;
|
||||
if (frame.ID == (uint32_t)(baseAddress + 0x10)) {
|
||||
const unsigned char *data = reinterpret_cast<const unsigned char *>(frame.payload().constData());
|
||||
int dataLen = frame.payload().count();
|
||||
|
||||
qDebug() << "FUW: Got targetted frame with id " << frame.frameId();
|
||||
if (frame.frameId() == (uint32_t)(baseAddress + 0x10) && (dataLen == 8) ) {
|
||||
qDebug() << "Start firmware reply";
|
||||
if ((frame.data[0] == 0xAD) && (frame.data[1] == 0xDE))
|
||||
if ((data[0] == 0xAD) && (data[1] == 0xDE))
|
||||
{
|
||||
if ((frame.data[2] == 0xAF) && (frame.data[3] == 0xDE))
|
||||
if ((data[2] == 0xAF) && (data[3] == 0xDE))
|
||||
{
|
||||
qDebug() << "THere's dead beef here";
|
||||
if ((frame.data[4] == (token & 0xFF)) && (frame.data[5] == ((token >> 8) & 0xFF)))
|
||||
qDebug() << "There's dead beef here";
|
||||
if ( (data[4] == (token & 0xFF)) && (data[5] == ((token >> 8) & 0xFF) ) )
|
||||
{
|
||||
if ((frame.data[6] == ((token >> 16) & 0xFF)) && (frame.data[7] == ((token >> 24) & 0xFF)))
|
||||
if ((data[6] == ((token >> 16) & 0xFF)) && (data[7] == ((token >> 24) & 0xFF)))
|
||||
{
|
||||
qDebug() << "starting firmware process";
|
||||
//MainWindow::getReference()->setTargettedID(baseAddress + 0x20);
|
||||
@@ -92,9 +102,9 @@ void FirmwareUploaderWindow::gotTargettedFrame(CANFrame frame)
|
||||
}
|
||||
}
|
||||
|
||||
if (frame.ID == (uint32_t)(baseAddress + 0x20)) {
|
||||
if (frame.frameId() == (uint32_t)(baseAddress + 0x20)) {
|
||||
qDebug() << "Firmware reception success reply";
|
||||
int seq = frame.data[0] + (256 * frame.data[1]);
|
||||
int seq = data[0] + (256 * data[1]);
|
||||
if (seq == currentSendingPosition)
|
||||
{
|
||||
currentSendingPosition++;
|
||||
@@ -126,18 +136,19 @@ void FirmwareUploaderWindow::sendFirmwareChunk()
|
||||
CANFrame *output = new CANFrame;
|
||||
int firmwareLocation = currentSendingPosition * 4;
|
||||
int xorByte = 0;
|
||||
output->extended = false;
|
||||
output->len = 7;
|
||||
output->setExtendedFrameFormat(false);
|
||||
QByteArray bytes(7,0);
|
||||
output->bus = bus;
|
||||
output->ID = baseAddress + 0x16;
|
||||
output->data[0] = currentSendingPosition & 0xFF;
|
||||
output->data[1] = (currentSendingPosition >> 8) & 0xFF;
|
||||
output->data[2] = firmwareData[firmwareLocation++];
|
||||
output->data[3] = firmwareData[firmwareLocation++];
|
||||
output->data[4] = firmwareData[firmwareLocation++];
|
||||
output->data[5] = firmwareData[firmwareLocation++];
|
||||
for (int i = 0; i < 6; i++) xorByte = xorByte ^ output->data[i];
|
||||
output->data[6] = xorByte;
|
||||
output->setFrameId(baseAddress + 0x16);
|
||||
output->payload()[0] = currentSendingPosition & 0xFF;
|
||||
output->payload()[1] = (currentSendingPosition >> 8) & 0xFF;
|
||||
output->payload()[2] = firmwareData[firmwareLocation++];
|
||||
output->payload()[3] = firmwareData[firmwareLocation++];
|
||||
output->payload()[4] = firmwareData[firmwareLocation++];
|
||||
output->payload()[5] = firmwareData[firmwareLocation++];
|
||||
for (int i = 0; i < 6; i++) xorByte = xorByte ^ static_cast<unsigned char>(output->payload()[i]);
|
||||
output->payload()[6] = xorByte;
|
||||
output->setPayload(bytes);
|
||||
sendCANFrame(output);
|
||||
timer->start();
|
||||
}
|
||||
@@ -145,14 +156,15 @@ void FirmwareUploaderWindow::sendFirmwareChunk()
|
||||
void FirmwareUploaderWindow::sendFirmwareEnding()
|
||||
{
|
||||
CANFrame *output = new CANFrame;
|
||||
output->extended = false;
|
||||
output->setExtendedFrameFormat(false);
|
||||
output->bus = bus;
|
||||
output->len = 4;
|
||||
output->ID = baseAddress + 0x30;
|
||||
output->data[3] = 0xC0;
|
||||
output->data[2] = 0xDE;
|
||||
output->data[1] = 0xFA;
|
||||
output->data[0] = 0xDE;
|
||||
QByteArray bytes(4,0);
|
||||
output->setFrameId(baseAddress + 0x30);
|
||||
output->payload()[3] = 0xC0;
|
||||
output->payload()[2] = 0xDE;
|
||||
output->payload()[1] = 0xFA;
|
||||
output->payload()[0] = 0xDE;
|
||||
output->setPayload(bytes);
|
||||
//sendCANFrame(output, bus);
|
||||
}
|
||||
|
||||
@@ -171,19 +183,19 @@ void FirmwareUploaderWindow::handleStartStopTransfer()
|
||||
CANConManager::getInstance()->addTargettedFrame(bus, baseAddress + 0x10, 0x7FF, this);
|
||||
CANConManager::getInstance()->addTargettedFrame(bus, baseAddress + 0x20, 0x7FF, this);
|
||||
CANFrame *output = new CANFrame;
|
||||
output->extended = false;
|
||||
output->len = 8;
|
||||
output->setExtendedFrameFormat(false);
|
||||
QByteArray bytes(8,0);
|
||||
output->bus = bus;
|
||||
output->ID = baseAddress;
|
||||
output->setFrameId(baseAddress);
|
||||
|
||||
output->data[0] = 0xEF;
|
||||
output->data[1] = 0xBE;
|
||||
output->data[2] = 0xAD;
|
||||
output->data[3] = 0xDE;
|
||||
output->data[4] = token & 0xFF;
|
||||
output->data[5] = (token >> 8) & 0xFF;
|
||||
output->data[6] = (token >> 16) & 0xFF;
|
||||
output->data[7] = (token >> 24) & 0xFF;
|
||||
output->payload()[0] = 0xEF;
|
||||
output->payload()[1] = 0xBE;
|
||||
output->payload()[2] = 0xAD;
|
||||
output->payload()[3] = 0xDE;
|
||||
output->payload()[4] = token & 0xFF;
|
||||
output->payload()[5] = (token >> 8) & 0xFF;
|
||||
output->payload()[6] = (token >> 16) & 0xFF;
|
||||
output->payload()[7] = (token >> 24) & 0xFF;
|
||||
sendCANFrame(output);
|
||||
}
|
||||
else //stop anything in process
|
||||
|
||||
@@ -68,7 +68,7 @@ quint64 FramePlaybackObject::updatePosition(bool forward)
|
||||
//only send frame out if its ID is checked in the list. Otherwise discard it.
|
||||
CANFrame *thisFrame = ¤tSeqItem->data[currentPosition];
|
||||
uint32_t originalBus = thisFrame->bus;
|
||||
if (currentSeqItem->idFilters.find(thisFrame->ID).value())
|
||||
if (currentSeqItem->idFilters.find(thisFrame->frameId()).value())
|
||||
{
|
||||
if (whichBusSend > -1)
|
||||
{
|
||||
@@ -90,7 +90,7 @@ quint64 FramePlaybackObject::updatePosition(bool forward)
|
||||
|
||||
thisFrame->bus = originalBus;
|
||||
}
|
||||
return thisFrame->timestamp;
|
||||
return thisFrame->timeStamp().microSeconds();
|
||||
}
|
||||
|
||||
quint64 FramePlaybackObject::peekPosition(bool forward)
|
||||
@@ -113,7 +113,7 @@ quint64 FramePlaybackObject::peekPosition(bool forward)
|
||||
}
|
||||
}
|
||||
CANFrame *thisFrame = ¤tSeqItem->data[peekCurrentPosition];
|
||||
return thisFrame->timestamp;
|
||||
return thisFrame->timeStamp().microSeconds();
|
||||
}
|
||||
|
||||
void FramePlaybackObject::piStart()
|
||||
@@ -197,8 +197,8 @@ void FramePlaybackObject::startPlaybackForward()
|
||||
{
|
||||
playbackTimer->setInterval(1);
|
||||
playbackElapsed.start();
|
||||
if (currentSeqItem->data[currentPosition].timestamp > 2000)
|
||||
playbackLastTimeStamp = currentSeqItem->data[currentPosition].timestamp - 2000;
|
||||
if (currentSeqItem->data[currentPosition].timeStamp().microSeconds() > 2000)
|
||||
playbackLastTimeStamp = currentSeqItem->data[currentPosition].timeStamp().microSeconds() - 2000;
|
||||
else playbackLastTimeStamp = 0;
|
||||
}
|
||||
playbackTimer->start();
|
||||
@@ -219,7 +219,7 @@ void FramePlaybackObject::startPlaybackBackward()
|
||||
{
|
||||
playbackElapsed.start();
|
||||
playbackTimer->setInterval(1);
|
||||
playbackLastTimeStamp = currentSeqItem->data[currentPosition].timestamp + 2000;
|
||||
playbackLastTimeStamp = currentSeqItem->data[currentPosition].timeStamp().microSeconds() + 2000;
|
||||
}
|
||||
playbackTimer->start();
|
||||
}
|
||||
@@ -350,8 +350,8 @@ void FramePlaybackObject::timerTriggered()
|
||||
if (peekPosition(true) == 0xFFFFFFFFFFFFFFFFull)
|
||||
{
|
||||
updatePosition(true); //this'll go to the next log (if there is one)
|
||||
if (currentSeqItem->data[currentPosition].timestamp > 1000)
|
||||
playbackLastTimeStamp = currentSeqItem->data[currentPosition].timestamp - 1000;
|
||||
if (currentSeqItem->data[currentPosition].timeStamp().microSeconds() > 1000)
|
||||
playbackLastTimeStamp = currentSeqItem->data[currentPosition].timeStamp().microSeconds() - 1000;
|
||||
else playbackLastTimeStamp = 0;
|
||||
}
|
||||
}
|
||||
@@ -367,7 +367,7 @@ void FramePlaybackObject::timerTriggered()
|
||||
if (peekPosition(false) == 0xFFFFFFFFFFFFFFFFull)
|
||||
{
|
||||
updatePosition(false); //this'll go to the next log (if there is one)
|
||||
playbackLastTimeStamp = currentSeqItem->data[currentPosition].timestamp + 1000;
|
||||
playbackLastTimeStamp = currentSeqItem->data[currentPosition].timeStamp().microSeconds() + 1000;
|
||||
}
|
||||
}
|
||||
statusCounter++;
|
||||
|
||||
@@ -297,7 +297,7 @@ void FramePlaybackWindow::refreshIDList()
|
||||
QHash<int, bool>::Iterator filterIter;
|
||||
for (filterIter = currentSeqItem->idFilters.begin(); filterIter != currentSeqItem->idFilters.end(); ++filterIter)
|
||||
{
|
||||
QListWidgetItem* listItem = FilterUtility::createCheckableFilterItem(filterIter.key(), filterIter.value(), ui->listID);
|
||||
/*QListWidgetItem* listItem = */ FilterUtility::createCheckableFilterItem(filterIter.key(), filterIter.value(), ui->listID);
|
||||
}
|
||||
//default is to sort in ascending order
|
||||
ui->listID->sortItems();
|
||||
@@ -426,7 +426,7 @@ void FramePlaybackWindow::fillIDHash(SequenceItem &item)
|
||||
|
||||
for (int i = 0; i < item.data.count(); i++)
|
||||
{
|
||||
id = item.data[i].ID;
|
||||
id = item.data[i].frameId();
|
||||
if (!item.idFilters.contains(id))
|
||||
{
|
||||
item.idFilters.insert(id, true);
|
||||
@@ -479,7 +479,7 @@ void FramePlaybackWindow::btnLoadFile()
|
||||
|
||||
if (FrameFileIO::loadFrameFile(filename, &item.data))
|
||||
{
|
||||
qSort(item.data); //sort by timestamp to be sure it's in order
|
||||
std::sort(item.data.begin(), item.data.end()); //sort by timestamp to be sure it's in order
|
||||
QStringList fileList = filename.split('/');
|
||||
item.filename = fileList[fileList.length() - 1];
|
||||
item.currentLoopCount = 0;
|
||||
@@ -513,7 +513,7 @@ void FramePlaybackWindow::btnLoadLive()
|
||||
item.currentLoopCount = 0;
|
||||
item.maxLoops = 1;
|
||||
item.data = QVector<CANFrame>(*modelFrames); //create a copy of the current frames from the main view
|
||||
qSort(item.data); //be sure it's all in time based order
|
||||
std::sort(item.data.begin(), item.data.end()); //be sure it's all in time based order
|
||||
fillIDHash(item);
|
||||
if (ui->tblSequence->currentRow() == -1)
|
||||
{
|
||||
|
||||
@@ -130,13 +130,13 @@ void FrameSenderWindow::buildFrameCache()
|
||||
for (int i = 0; i < modelFrames->count(); i++)
|
||||
{
|
||||
thisFrame = modelFrames->at(i);
|
||||
if (!frameCache.contains(thisFrame.ID))
|
||||
if (!frameCache.contains(thisFrame.frameId()))
|
||||
{
|
||||
frameCache.insert(thisFrame.ID, thisFrame);
|
||||
frameCache.insert(thisFrame.frameId(), thisFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
frameCache[thisFrame.ID] = thisFrame;
|
||||
frameCache[thisFrame.frameId()] = thisFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,13 +160,13 @@ void FrameSenderWindow::updatedFrames(int numFrames)
|
||||
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
|
||||
{
|
||||
thisFrame = modelFrames->at(i);
|
||||
if (!frameCache.contains(thisFrame.ID))
|
||||
if (!frameCache.contains(thisFrame.frameId()))
|
||||
{
|
||||
frameCache.insert(thisFrame.ID, thisFrame);
|
||||
frameCache.insert(thisFrame.frameId(), thisFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
frameCache[thisFrame.ID] = thisFrame;
|
||||
frameCache[thisFrame.frameId()] = thisFrame;
|
||||
}
|
||||
processIncomingFrame(&thisFrame);
|
||||
}
|
||||
@@ -182,10 +182,10 @@ void FrameSenderWindow::processIncomingFrame(CANFrame *frame)
|
||||
{
|
||||
Trigger *thisTrigger = &sendingData[sd].triggers[trig];
|
||||
qDebug() << "Trigger ID: " << thisTrigger->ID;
|
||||
qDebug() << "Frame ID: " << frame->ID;
|
||||
if (thisTrigger->ID > 0 && (uint32_t)thisTrigger->ID == frame->ID)
|
||||
qDebug() << "Frame ID: " << frame->frameId();
|
||||
if (thisTrigger->ID > 0 && (uint32_t)thisTrigger->ID == frame->frameId())
|
||||
{
|
||||
if ((uint32_t)thisTrigger->bus == frame->bus || thisTrigger->bus == -1)
|
||||
if (thisTrigger->bus == frame->bus || thisTrigger->bus == -1)
|
||||
{
|
||||
if (thisTrigger->currCount < thisTrigger->maxCount)
|
||||
{
|
||||
@@ -511,7 +511,9 @@ void FrameSenderWindow::doModifiers(int idx)
|
||||
}
|
||||
}
|
||||
//Finally, drop the result into the proper data byte
|
||||
sendData->data[mod->destByte] = (unsigned char) shadowReg;
|
||||
QByteArray newArr(sendData->payload());
|
||||
newArr[mod->destByte] = (char) shadowReg;
|
||||
sendData->setPayload(newArr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,16 +527,16 @@ int FrameSenderWindow::fetchOperand(int idx, ModifierOperand op)
|
||||
}
|
||||
else if (op.ID == -2) //fetch data from a data byte within the output frame
|
||||
{
|
||||
if (op.notOper) return ~sendingData.at(idx).data[op.databyte];
|
||||
else return sendingData.at(idx).data[op.databyte];
|
||||
if (op.notOper) return ~((unsigned char)sendingData.at(idx).payload()[op.databyte]);
|
||||
else return (unsigned char)sendingData.at(idx).payload()[op.databyte];
|
||||
}
|
||||
else //look up external data byte
|
||||
{
|
||||
tempFrame = lookupFrame(op.ID, op.bus);
|
||||
if (tempFrame != nullptr)
|
||||
{
|
||||
if (op.notOper) return ~tempFrame->data[op.databyte];
|
||||
else return tempFrame->data[op.databyte];
|
||||
if (op.notOper) return ~((unsigned char)tempFrame->payload()[op.databyte]);
|
||||
else return (unsigned char)tempFrame->payload()[op.databyte];
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
@@ -550,7 +552,7 @@ CANFrame* FrameSenderWindow::lookupFrame(int ID, int bus)
|
||||
{
|
||||
if (!frameCache.contains(ID)) return nullptr;
|
||||
|
||||
if (bus == -1 || frameCache[ID].bus == (unsigned int)bus) return &frameCache[ID];
|
||||
if (bus == -1 || frameCache[ID].bus == bus) return &frameCache[ID];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -643,7 +645,7 @@ void FrameSenderWindow::processModifierText(int line)
|
||||
}
|
||||
else thisOp.second.notOper = false;
|
||||
thisOp.second.bus = sendingData[line].bus;
|
||||
thisOp.second.ID = sendingData[line].ID;
|
||||
thisOp.second.ID = sendingData[line].frameId();
|
||||
parseOperandString(secondOp.split(":"), thisOp.second);
|
||||
thisMod.operations.append(thisOp);
|
||||
}
|
||||
@@ -786,17 +788,21 @@ ModifierOperationType FrameSenderWindow::parseOperation(QString op)
|
||||
void FrameSenderWindow::updateGridRow(int idx)
|
||||
{
|
||||
qDebug() << "updateGridRow";
|
||||
|
||||
inhibitChanged = true;
|
||||
FrameSendData *temp = &sendingData[idx];
|
||||
int gridLine = idx;
|
||||
QString dataString;
|
||||
QTableWidgetItem *item = ui->tableSender->item(gridLine, 9);
|
||||
const unsigned char *data = reinterpret_cast<const unsigned char *>(temp->payload().constData());
|
||||
int dataLen = temp->payload().length();
|
||||
|
||||
if (item == nullptr) item = new QTableWidgetItem();
|
||||
item->setText(QString::number(temp->count));
|
||||
if (!temp->remote) {
|
||||
for (unsigned int i = 0; i < temp->len; i++)
|
||||
if (temp->frameType() != QCanBusFrame::RemoteRequestFrame) {
|
||||
for (int i = 0; i < dataLen; i++)
|
||||
{
|
||||
dataString.append(Utility::formatNumber(temp->data[i]));
|
||||
dataString.append(Utility::formatNumber(data[i]));
|
||||
dataString.append(" ");
|
||||
}
|
||||
ui->tableSender->item(gridLine, 6)->setText(dataString);
|
||||
@@ -818,14 +824,15 @@ void FrameSenderWindow::processCellChange(int line, int col)
|
||||
{
|
||||
FrameSendData tempData;
|
||||
tempData.enabled = false;
|
||||
tempData.remote = false;
|
||||
tempData.extended = false;
|
||||
tempData.setFrameType(QCanBusFrame::DataFrame);
|
||||
tempData.setExtendedFrameFormat(false);
|
||||
sendingData.append(tempData);
|
||||
}
|
||||
|
||||
sendingData[line].count = 0;
|
||||
|
||||
int numBuses = CANConManager::getInstance()->getNumBuses();
|
||||
QByteArray arr;
|
||||
|
||||
switch (col)
|
||||
{
|
||||
@@ -848,9 +855,9 @@ void FrameSenderWindow::processCellChange(int line, int col)
|
||||
tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, 2)->text());
|
||||
if (tempVal < 0) tempVal = 0;
|
||||
if (tempVal > 0x7FFFFFFF) tempVal = 0x7FFFFFFF;
|
||||
sendingData[line].ID = tempVal;
|
||||
if (sendingData[line].ID > 0x7FF) {
|
||||
sendingData[line].extended = true;
|
||||
sendingData[line].setFrameId(tempVal);
|
||||
if (sendingData[line].frameId() > 0x7FF) {
|
||||
sendingData[line].setExtendedFrameFormat(true);
|
||||
ui->tableSender->blockSignals(true);
|
||||
ui->tableSender->item(line, 4)->setCheckState(Qt::Checked);
|
||||
ui->tableSender->blockSignals(false);
|
||||
@@ -860,31 +867,35 @@ void FrameSenderWindow::processCellChange(int line, int col)
|
||||
case 3: //length field
|
||||
tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, 3)->text());
|
||||
if (tempVal < 0) tempVal = 0;
|
||||
if (tempVal > 8) tempVal = 8;
|
||||
sendingData[line].len = tempVal;
|
||||
if (tempVal > 8) tempVal = 8;
|
||||
arr.resize(tempVal);
|
||||
sendingData[line].setPayload(arr);
|
||||
break;
|
||||
case 4: // Ext
|
||||
if (ui->tableSender->item(line, 4)->checkState() == Qt::Checked) {
|
||||
sendingData[line].extended = true;
|
||||
sendingData[line].setExtendedFrameFormat(true);
|
||||
} else {
|
||||
sendingData[line].extended = false;
|
||||
sendingData[line].setExtendedFrameFormat(false);
|
||||
}
|
||||
break;
|
||||
case 5: // Rem
|
||||
if (ui->tableSender->item(line, 5)->checkState() == Qt::Checked) {
|
||||
sendingData[line].remote = true;
|
||||
sendingData[line].setFrameType(QCanBusFrame::RemoteRequestFrame);
|
||||
} else {
|
||||
sendingData[line].remote = false;
|
||||
sendingData[line].setFrameType(QCanBusFrame::DataFrame);
|
||||
}
|
||||
break;
|
||||
case 6: //Data bytes
|
||||
for (int i = 0; i < 8; i++) sendingData[line].data[i] = 0;
|
||||
for (int i = 0; i < 8; i++) sendingData[line].payload().data()[i] = 0;
|
||||
|
||||
tokens = ui->tableSender->item(line, 6)->text().split(" ");
|
||||
arr.clear();
|
||||
arr.reserve(tokens.count());
|
||||
for (int j = 0; j < tokens.count(); j++)
|
||||
{
|
||||
sendingData[line].data[j] = (uint8_t)Utility::ParseStringToNum(tokens[j]);
|
||||
arr.append((uint8_t)Utility::ParseStringToNum(tokens[j]));
|
||||
}
|
||||
sendingData[line].setPayload(arr);
|
||||
break;
|
||||
case 7: //triggers
|
||||
processTriggerText(line);
|
||||
|
||||
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 65 KiB |
@@ -9,7 +9,7 @@
|
||||
* Released under the MIT license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* Date: 2019-01-30T03:06Z
|
||||
* Date: 2019-04-19T06:52Z
|
||||
*/
|
||||
( function( global, factory ) {
|
||||
|
||||
@@ -261,8 +261,9 @@ jQuery.extend = jQuery.fn.extend = function() {
|
||||
src = target[ name ];
|
||||
copy = options[ name ];
|
||||
|
||||
// Prevent Object.prototype pollution
|
||||
// Prevent never-ending loop
|
||||
if ( target === copy ) {
|
||||
if ( name === "__proto__" || target === copy ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,11 +25,12 @@
|
||||
</div>
|
||||
<div class="section" id="fuzzing-is-dangerous">
|
||||
<h1>60. Fuzzing is Dangerous</h1>
|
||||
<p>You have now been warned. Sending random garbage over the CAN bus to see what happens could mess something up. It just might put your vehicle into gear and cause you to drive over a box full of kittens. Be careful! Never fuzz a car unless you’re right there and you can stop it. Even better, don’t do it unless your car is up on a hoist or something and can’t drive over anyone. Even still, there is a small chance you could cause an adverse effect to your car.</p>
|
||||
<p>You have now been warned. Sending random garbage over the CAN bus to see what happens could mess something up. It just might put your vehicle into gear and cause you to drive over a box full of kittens. Be careful! Never fuzz a car unless you’re right there and you can stop it. Even better, don’t do it unless your car is up on a hoist or jacks and can’t drive over anyone. Even still, there is a small chance you could cause an adverse effect to your car. If you break something you own the pieces.</p>
|
||||
</div>
|
||||
<div class="section" id="controlling-the-fuzzy-beast">
|
||||
<h1>61. Controlling the Fuzzy Beast</h1>
|
||||
<p>So, you want to give it a try? Let’s do it! First of all, you can set the delay between frames and the burst rate. The burst rate can cause the program to send one than one frame each interval. This is useful as a CAN bus could potentially support 2000 to 8000 frames per second. Then you can set the number of bytes to send. Ordinarily this would be the full 8 but you can experiment with smaller frames. You can set to send on a specific bus. That’s all the simple settings. It gets a bit more complicated now.</p>
|
||||
<p>So, you want to give it a try? Let’s do it! First of all, you can set the delay between frames and the burst rate. The delay is in milliseconds but can be set as low as 0. If the delay is set to 0 then the system will attempt to send frames as fast as it can. However, even then you may not get quite as many frames per second as you’d like. Even at 0 it will still be scheduled by your operating system and so might not quite get to the speed you want. The burst rate can cause the program to send one than one frame each interval. This is useful as a CAN bus could potentially support 2000 to 8000 frames per second. If you need rapid frame sending your best bet is to set the sending interval to 1-2ms and then adjust the burst
|
||||
rate until you get your desired sending rate. Then you can set the number of bytes to send. Ordinarily this would be the full 8 but you can experiment with smaller frames. You can set to send on a specific bus. That’s all the simple settings. It gets a bit more complicated now.</p>
|
||||
<p>The “ID Scanning” box has two radio buttons:</p>
|
||||
<ol class="arabic simple">
|
||||
<li>Sequential will go from “Start ID” to “End ID” then reset back to Start over and over.</li>
|
||||
@@ -46,11 +47,12 @@
|
||||
<li>Sweep causes the system to set the first one, then unset that one and set the second bit, then unset that, etc. Thus the fuzzed bit sweeps and only one fuzzed bit is set at once.</li>
|
||||
<li>Random will randomly pick whether each fuzzed bit is set or not.</li>
|
||||
</ol>
|
||||
<p>In order to fuzz bits you need to set which bits to fuzz and which not to. As listed at the bottom of the window, there is a color code to the 8x8 grid. Clicking cells in the grid will toggle them between their various values. White bits are never set, black bits are always set no matter what, green bits follow the fuzzing pattern you specified in “Bit Scanning”</p>
|
||||
<p>In order to fuzz bits you need to set which bits to fuzz and which not to. As listed at the bottom of the window, there is a color code to the 8x8 grid. Clicking cells in the grid will toggle them between their various values. White bits are never set, black bits are always set no matter what, green bits follow the fuzzing pattern you specified in “Bit Scanning” You can also set the bytes directly with the text boxes above the 8x8 grid. Setting a hexadecimal value in these
|
||||
boxes will set the relavent bits in the 8x8 grid. You must press the ENTER/RETURN key to set the values. Merely changing the value will not update it (as a safety measure).</p>
|
||||
</div>
|
||||
<div class="section" id="pulling-the-trigger">
|
||||
<h1>62. Pulling the Trigger</h1>
|
||||
<p>Once you’ve configured everything click “Start Fuzzing” to give it a shot. You will see the number of frames sent so far listed below the button. You can stop the fuzzing by pushing the button again.</p>
|
||||
<p>Once you’ve configured everything click “Start Fuzzing” to give it a shot. You will see the number of frames sent so far listed below the button. Approximately four times per second the current value for each byte is copied into the text boxes just above the 8x8 grid. This can be used to see what is going on and to ensure that it is working the way you want it to. You can stop the fuzzing by pushing the start button again.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -6,5 +6,14 @@
|
||||
<file>images/PlayHS.png</file>
|
||||
<file>images/ReverseHS.png</file>
|
||||
<file>images/StopHS.png</file>
|
||||
<file>images/signal.png</file>
|
||||
<file>images/node.png</file>
|
||||
<file>images/message.png</file>
|
||||
<file>images/multiplexed-signal.png</file>
|
||||
<file>images/multiplexor-signal.png</file>
|
||||
<file>images/skull.png</file>
|
||||
<file>images/signal_new.png</file>
|
||||
<file>images/node_new.png</file>
|
||||
<file>images/message_new.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,319 @@
|
||||
#!/usr/bin/env bash
|
||||
#############################################################################
|
||||
##
|
||||
## Copyright (C) 2019 Richard Weickelt.
|
||||
## Contact: https://www.qt.io/licensing/
|
||||
##
|
||||
## This file is part of Qbs.
|
||||
##
|
||||
## $QT_BEGIN_LICENSE:LGPL$
|
||||
## Commercial License Usage
|
||||
## Licensees holding valid commercial Qt licenses may use this file in
|
||||
## accordance with the commercial license agreement provided with the
|
||||
## Software or, alternatively, in accordance with the terms contained in
|
||||
## a written agreement between you and The Qt Company. For licensing terms
|
||||
## and conditions see https://www.qt.io/terms-conditions. For further
|
||||
## information use the contact form at https://www.qt.io/contact-us.
|
||||
##
|
||||
## GNU Lesser General Public License Usage
|
||||
## Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
## General Public License version 3 as published by the Free Software
|
||||
## Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
## packaging of this file. Please review the following information to
|
||||
## ensure the GNU Lesser General Public License version 3 requirements
|
||||
## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
##
|
||||
## GNU General Public License Usage
|
||||
## Alternatively, this file may be used under the terms of the GNU
|
||||
## General Public License version 2.0 or (at your option) the GNU General
|
||||
## Public license version 3 or any later version approved by the KDE Free
|
||||
## Qt Foundation. The licenses are as published by the Free Software
|
||||
## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
## included in the packaging of this file. Please review the following
|
||||
## information to ensure the GNU General Public License requirements will
|
||||
## be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
## https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
##
|
||||
## $QT_END_LICENSE$
|
||||
##
|
||||
#############################################################################
|
||||
set -eu
|
||||
|
||||
function help() {
|
||||
cat <<EOF
|
||||
usage: install-qt [options] [components]
|
||||
|
||||
Examples
|
||||
./install-qt.sh --version 5.13.1 qtbase
|
||||
./install-qt.sh --version 5.14.0 --target android --toolchain any qtbase qtscript
|
||||
|
||||
Positional arguments
|
||||
components
|
||||
SDK components to be installed. All possible components
|
||||
can be found in the online repository at https://download.qt.io,
|
||||
for instance: qtbase, qtdeclarative, qtscript, qttools, icu, ...
|
||||
|
||||
In addition to Qt packages, qtcreator is also accepted.
|
||||
|
||||
The actual availability of packages may differ depending on
|
||||
target and toolchain.
|
||||
|
||||
Options
|
||||
-d, --directory <directory>
|
||||
Root directory where to install the components.
|
||||
Maps to C:/Qt on Windows, /opt/Qt on Linux, /usr/local/Qt on Mac
|
||||
by default.
|
||||
|
||||
-f, --force
|
||||
Force download and do not attempt to re-use an existing installation.
|
||||
|
||||
--host <host-os>
|
||||
The host operating system. Can be one of linux_x64, mac_x64,
|
||||
windows_x86. Auto-detected by default.
|
||||
|
||||
--target <target-platform>
|
||||
The desired target platform. Can be one of desktop, android, ios.
|
||||
The default value is desktop.
|
||||
|
||||
--toolchain <toolchain-type>
|
||||
The toolchain that has been used to build the binaries.
|
||||
Possible values depend on --host and --target, respectively:
|
||||
|
||||
linux_x64
|
||||
android
|
||||
any, android_armv7, android_arm64_v8a
|
||||
desktop
|
||||
gcc_64 (default)
|
||||
|
||||
mac_x64
|
||||
android
|
||||
any, android_armv7, android_arm64_v8a
|
||||
desktop
|
||||
clang_64 (default),
|
||||
ios
|
||||
ios
|
||||
|
||||
windows_x86
|
||||
android
|
||||
any, android_armv7, android_arm64_v8a
|
||||
desktop
|
||||
win64_mingw73, win64_msvc2017_64 (default)
|
||||
|
||||
--version <version>
|
||||
The desired Qt version. Currently supported are all versions
|
||||
above 5.9.0.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
TARGET_PLATFORM=desktop
|
||||
COMPONENTS=
|
||||
VERSION=
|
||||
FORCE_DOWNLOAD=false
|
||||
MD5_TOOL=md5sum
|
||||
|
||||
case "$OSTYPE" in
|
||||
*linux*)
|
||||
HOST_OS=linux_x64
|
||||
INSTALL_DIR=/opt/Qt
|
||||
TOOLCHAIN=gcc_64
|
||||
;;
|
||||
*darwin*)
|
||||
HOST_OS=mac_x64
|
||||
INSTALL_DIR=/usr/local/Qt
|
||||
TOOLCHAIN=clang_64
|
||||
MD5_TOOL="md5 -r"
|
||||
;;
|
||||
msys)
|
||||
HOST_OS=windows_x86
|
||||
INSTALL_DIR=/c/Qt
|
||||
TOOLCHAIN=win64_msvc2015_64
|
||||
;;
|
||||
*)
|
||||
HOST_OS=
|
||||
INSTALL_DIR=
|
||||
;;
|
||||
esac
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--directory|-d)
|
||||
INSTALL_DIR="$2"
|
||||
shift
|
||||
;;
|
||||
--force|-f)
|
||||
FORCE_DOWNLOAD=true
|
||||
;;
|
||||
--host)
|
||||
HOST_OS="$2"
|
||||
shift
|
||||
;;
|
||||
--target)
|
||||
TARGET_PLATFORM="$2"
|
||||
shift
|
||||
;;
|
||||
--toolchain)
|
||||
TOOLCHAIN=$(echo $2 | tr '[A-Z]' '[a-z]')
|
||||
shift
|
||||
;;
|
||||
--version)
|
||||
VERSION="$2"
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
COMPONENTS="${COMPONENTS} $1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -z "${HOST_OS}" ]; then
|
||||
echo "No --host specified or auto-detection failed." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${INSTALL_DIR}" ]; then
|
||||
echo "No --directory specified or auto-detection failed." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${VERSION}" ]; then
|
||||
echo "No --version specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${COMPONENTS}" ]; then
|
||||
echo "No components specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$TARGET_PLATFORM" in
|
||||
android)
|
||||
;;
|
||||
ios)
|
||||
;;
|
||||
desktop)
|
||||
;;
|
||||
*)
|
||||
echo "Error: TARGET_PLATFORM=${TARGET_PLATFORM} is not valid." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
HASH=$(echo "${OSTYPE} ${TARGET_PLATFORM} ${TOOLCHAIN} ${VERSION} ${INSTALL_DIR}" | ${MD5_TOOL} | head -c 16)
|
||||
HASH_FILEPATH="${INSTALL_DIR}/${HASH}.manifest"
|
||||
INSTALLATION_IS_VALID=false
|
||||
if ! ${FORCE_DOWNLOAD} && [ -f "${HASH_FILEPATH}" ]; then
|
||||
INSTALLATION_IS_VALID=true
|
||||
while read filepath; do
|
||||
if [ ! -e "${filepath}" ]; then
|
||||
INSTALLATION_IS_VALID=false
|
||||
break
|
||||
fi
|
||||
done <"${HASH_FILEPATH}"
|
||||
fi
|
||||
|
||||
if ${INSTALLATION_IS_VALID}; then
|
||||
echo "Already installed. Skipping download."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
DOWNLOAD_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'install-qt'`
|
||||
|
||||
#
|
||||
# The repository structure is a mess. Try different URL variants
|
||||
#
|
||||
function compute_url(){
|
||||
local COMPONENT=$1
|
||||
local CURL="curl -s -L"
|
||||
local BASE_URL="http://download.qt.io/online/qtsdkrepository/${HOST_OS}/${TARGET_PLATFORM}"
|
||||
|
||||
if [[ "${COMPONENT}" =~ "qtcreator" ]]; then
|
||||
|
||||
REMOTE_BASE="tools_qtcreator/qt.tools.qtcreator"
|
||||
REMOTE_PATH="$(${CURL} ${BASE_URL}/${REMOTE_BASE}/ | grep -o -E "${VERSION}[0-9\-]*${COMPONENT}\.7z" | tail -1)"
|
||||
|
||||
if [ ! -z "${REMOTE_PATH}" ]; then
|
||||
echo "${BASE_URL}/${REMOTE_BASE}/${REMOTE_PATH}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
else
|
||||
REMOTE_BASES=(
|
||||
# New repository format (>=5.9.6)
|
||||
"qt5_${VERSION//./}/qt.qt5.${VERSION//./}.${TOOLCHAIN}"
|
||||
"qt5_${VERSION//./}/qt.qt5.${VERSION//./}.${COMPONENT}.${TOOLCHAIN}"
|
||||
# Multi-abi Android since 5.14
|
||||
"qt5_${VERSION//./}/qt.qt5.${VERSION//./}.${TARGET_PLATFORM}"
|
||||
"qt5_${VERSION//./}/qt.qt5.${VERSION//./}.${COMPONENT}.${TARGET_PLATFORM}"
|
||||
# Older repository format (<5.9.0)
|
||||
"qt5_${VERSION//./}/qt.${VERSION//./}.${TOOLCHAIN}"
|
||||
"qt5_${VERSION//./}/qt.${VERSION//./}.${COMPONENT}.${TOOLCHAIN}"
|
||||
)
|
||||
|
||||
for REMOTE_BASE in ${REMOTE_BASES[*]}; do
|
||||
REMOTE_PATH="$(${CURL} ${BASE_URL}/${REMOTE_BASE}/ | grep -o -E "[[:alnum:]_.\-]*7z" | grep "${COMPONENT}" | tail -1)"
|
||||
if [ ! -z "${REMOTE_PATH}" ]; then
|
||||
echo "${BASE_URL}/${REMOTE_BASE}/${REMOTE_PATH}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo "Could not determine a remote URL for ${COMPONENT} with version ${VERSION}">&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
mkdir -p ${INSTALL_DIR}
|
||||
rm -f "${HASH_FILEPATH}"
|
||||
|
||||
for COMPONENT in ${COMPONENTS}; do
|
||||
|
||||
URL="$(compute_url ${COMPONENT})"
|
||||
echo "Downloading ${COMPONENT}..." >&2
|
||||
curl --progress-bar -L -o ${DOWNLOAD_DIR}/package.7z ${URL} >&2
|
||||
7z x -y -o${INSTALL_DIR} ${DOWNLOAD_DIR}/package.7z >/dev/null 2>&1
|
||||
7z l -ba -slt -y ${DOWNLOAD_DIR}/package.7z | tr '\\' '/' | sed -n -e "s|^Path\ =\ |${INSTALL_DIR}/|p" >> "${HASH_FILEPATH}" 2>/dev/null
|
||||
rm -f ${DOWNLOAD_DIR}/package.7z
|
||||
|
||||
#
|
||||
# conf file is needed for qmake
|
||||
#
|
||||
if [ "${COMPONENT}" == "qtbase" ]; then
|
||||
if [[ "${TOOLCHAIN}" =~ "win64_mingw" ]]; then
|
||||
SUBDIR="${TOOLCHAIN/win64_/}_64"
|
||||
elif [[ "${TOOLCHAIN}" =~ "win32_mingw" ]]; then
|
||||
SUBDIR="${TOOLCHAIN/win32_/}_32"
|
||||
elif [[ "${TOOLCHAIN}" =~ "win64_msvc" ]]; then
|
||||
SUBDIR="${TOOLCHAIN/win64_/}"
|
||||
elif [[ "${TOOLCHAIN}" =~ "win32_msvc" ]]; then
|
||||
SUBDIR="${TOOLCHAIN/win32_/}"
|
||||
elif [[ "${TOOLCHAIN}" =~ "any" ]] && [[ "${TARGET_PLATFORM}" == "android" ]]; then
|
||||
SUBDIR="android"
|
||||
else
|
||||
SUBDIR="${TOOLCHAIN}"
|
||||
fi
|
||||
|
||||
CONF_FILE="${INSTALL_DIR}/${VERSION}/${SUBDIR}/bin/qt.conf"
|
||||
echo "[Paths]" > ${CONF_FILE}
|
||||
echo "Prefix = .." >> ${CONF_FILE}
|
||||
|
||||
# Adjust the license to be able to run qmake
|
||||
# sed with -i requires intermediate file on Mac OS
|
||||
PRI_FILE="${INSTALL_DIR}/${VERSION}/${SUBDIR}/mkspecs/qconfig.pri"
|
||||
sed -i.bak 's/Enterprise/OpenSource/g' "${PRI_FILE}"
|
||||
sed -i.bak 's/licheck.*//g' "${PRI_FILE}"
|
||||
rm "${PRI_FILE}.bak"
|
||||
|
||||
# Print the directory so that the caller can
|
||||
# adjust the PATH variable.
|
||||
echo $(dirname "${CONF_FILE}")
|
||||
elif [[ "${COMPONENT}" =~ "qtcreator" ]]; then
|
||||
echo "${INSTALL_DIR}/Tools/QtCreator/bin"
|
||||
fi
|
||||
|
||||
done
|
||||
@@ -997,11 +997,11 @@ void JSEdit::updateSidebar()
|
||||
int maxLines = blockCount();
|
||||
for (int number = 10; number < maxLines; number *= 10)
|
||||
++digits;
|
||||
sw += fontMetrics().width('w') * digits;
|
||||
sw += fontMetrics().horizontalAdvance('w') * digits;
|
||||
}
|
||||
if (d->codeFolding) {
|
||||
int fh = fontMetrics().lineSpacing();
|
||||
int fw = fontMetrics().width('w');
|
||||
int fw = fontMetrics().horizontalAdvance('w');
|
||||
d->sidebar->foldIndicatorWidth = qMax(fw, fh);
|
||||
sw += d->sidebar->foldIndicatorWidth;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#include "mainsettingsdialog.h"
|
||||
#include "ui_mainsettingsdialog.h"
|
||||
#include "helpwindow.h"
|
||||
|
||||
#include <qevent.h>
|
||||
#include "simplecrypt.h"
|
||||
|
||||
//using this simple encryption library to obfuscate stored password a bit. It's not super secure but better than
|
||||
//storing a password in straight plaintext. You have the source to this application anyway, whatever algorithm used,
|
||||
//whatever key, you'd see it. Just behave yourselves
|
||||
SimpleCrypt crypto(Q_UINT64_C(0xdeadbeefface6285));
|
||||
|
||||
MainSettingsDialog::MainSettingsDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
@@ -11,6 +16,7 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) :
|
||||
QSettings settings;
|
||||
ui->setupUi(this);
|
||||
|
||||
//TODO: This is still hard coded to support only two buses. Sometimes there is none, sometimes 1, sometimes much more than 2. Fix this.
|
||||
ui->comboSendingBus->addItem(tr("None"));
|
||||
ui->comboSendingBus->addItem(tr("0"));
|
||||
ui->comboSendingBus->addItem(tr("1"));
|
||||
@@ -30,8 +36,12 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) :
|
||||
ui->spinPlaybackSpeed->setValue(settings.value("Playback/DefSpeed", 5).toInt());
|
||||
ui->lineClockFormat->setText(settings.value("Main/TimeFormat", "MMM-dd HH:mm:ss.zzz").toString());
|
||||
ui->lineRemoteHost->setText(settings.value("Remote/Host", "api.savvycan.com").toString());
|
||||
ui->lineRemotePort->setText(settings.value("Remote/Port", "21315").toString()); // = 0x5343 = SC. Yep, really creative port number
|
||||
ui->cbAutoStartRemote->setChecked(settings.value("Remote/AutoStart", false).toBool());
|
||||
ui->lineRemotePort->setText(settings.value("Remote/Port", "8883").toString()); //default port for SSL enabled MQTT
|
||||
ui->lineRemoteUser->setText(settings.value("Remote/User", "Anonymous").toString());
|
||||
QByteArray encPass = settings.value("Remote/Pass", "").toByteArray();
|
||||
QString decPass = crypto.decryptToString(encPass);
|
||||
ui->lineRemotePassword->setText(decPass);
|
||||
|
||||
ui->cbLoadConnections->setChecked(settings.value("Main/SaveRestoreConnections", false).toBool());
|
||||
|
||||
ui->spinFontSize->setValue(settings.value("Main/FontSize", ui->cbDisplayHex->font().pointSize()).toUInt());
|
||||
@@ -82,9 +92,10 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) :
|
||||
connect(ui->cbUseFiltered, SIGNAL(toggled(bool)), this, SLOT(updateSettings()));
|
||||
connect(ui->lineClockFormat, SIGNAL(editingFinished()), this, SLOT(updateSettings()));
|
||||
connect(ui->cbUseOpenGL, SIGNAL(toggled(bool)), this, SLOT(updateSettings()));
|
||||
connect(ui->cbAutoStartRemote, SIGNAL(toggled(bool)), this, SLOT(updateSettings()));
|
||||
connect(ui->lineRemoteHost, SIGNAL(editingFinished()), this, SLOT(updateSettings()));
|
||||
connect(ui->lineRemotePort, SIGNAL(editingFinished()), this, SLOT(updateSettings()));
|
||||
connect(ui->lineRemoteUser, SIGNAL(editingFinished()), this, SLOT(updateSettings()));
|
||||
connect(ui->lineRemotePassword, SIGNAL(editingFinished()), this, SLOT(updateSettings()));
|
||||
connect(ui->cbLoadConnections, SIGNAL(toggled(bool)), this, SLOT(updateSettings()));
|
||||
connect(ui->cbFilterLabeling, SIGNAL(toggled(bool)), this, SLOT(updateSettings()));
|
||||
installEventFilter(this);
|
||||
@@ -144,7 +155,9 @@ void MainSettingsDialog::updateSettings()
|
||||
settings.setValue("Main/FontSize", ui->spinFontSize->value());
|
||||
settings.setValue("Remote/Host", ui->lineRemoteHost->text());
|
||||
settings.setValue("Remote/Port", ui->lineRemotePort->text());
|
||||
settings.setValue("Remote/AutoStart", ui->cbAutoStartRemote->isChecked());
|
||||
settings.setValue("Remote/User", ui->lineRemoteUser->text());
|
||||
QByteArray encPass = crypto.encryptToByteArray(ui->lineRemotePassword->text());
|
||||
settings.setValue("Remote/Pass", encPass);
|
||||
settings.setValue("Main/FilterLabeling", ui->cbFilterLabeling->isChecked());
|
||||
|
||||
settings.sync();
|
||||
|
||||
@@ -51,9 +51,15 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
|
||||
readSettings();
|
||||
|
||||
//QHeaderView *verticalHeader = ui->canFramesView->verticalHeader();
|
||||
//verticalHeader->setSectionResizeMode(QHeaderView::Fixed);
|
||||
//verticalHeader->setDefaultSectionSize(10);
|
||||
QHeaderView *verticalHeader = ui->canFramesView->verticalHeader();
|
||||
verticalHeader->setSectionResizeMode(QHeaderView::Fixed);
|
||||
QSettings settings;
|
||||
int fontSize = settings.value("Main/FontSize", 9).toUInt();
|
||||
QFont sysFont = QFont(); //get default font
|
||||
sysFont.setPointSize(fontSize);
|
||||
verticalHeader->setDefaultSectionSize(sysFont.pixelSize());
|
||||
ui->canFramesView->setFont(sysFont);
|
||||
|
||||
QHeaderView *HorzHdr = ui->canFramesView->horizontalHeader();
|
||||
HorzHdr->setStretchLastSection(true); //causes the data column to automatically fill the tableview
|
||||
connect(HorzHdr, SIGNAL(sectionClicked(int)), this, SLOT(headerClicked(int)));
|
||||
@@ -80,6 +86,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
bisectWindow = nullptr;
|
||||
signalViewerWindow = nullptr;
|
||||
temporalGraphWindow = nullptr;
|
||||
dbcComparatorWindow = nullptr;
|
||||
dbcHandler = DBCHandler::getReference();
|
||||
bDirty = false;
|
||||
inhibitFilterUpdate = false;
|
||||
@@ -111,6 +118,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
connect(ui->actionSave_Decoded_Frames, &QAction::triggered, this, &MainWindow::handleSaveDecoded);
|
||||
connect(ui->actionSingle_Multi_State_2, &QAction::triggered, this, &MainWindow::showSingleMultiWindow);
|
||||
connect(ui->actionFile_Comparison, &QAction::triggered, this, &MainWindow::showComparisonWindow);
|
||||
connect(ui->actionDBC_Comparison, &QAction::triggered, this, &MainWindow::showDBCComparisonWindow);
|
||||
connect(ui->actionScripting_INterface, &QAction::triggered, this, &MainWindow::showScriptingWindow);
|
||||
connect(ui->btnNormalize, &QAbstractButton::clicked, this, &MainWindow::normalizeTiming);
|
||||
connect(ui->actionPreferences, &QAction::triggered, this, &MainWindow::showSettingsDialog);
|
||||
@@ -129,6 +137,8 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
connect(ui->actionSignal_Viewer, &QAction::triggered, this, &MainWindow::showSignalViewer);
|
||||
connect(ui->actionSave_Continuous_Logfile, &QAction::triggered, this, &MainWindow::handleContinousLogging);
|
||||
connect(ui->actionTemporal_Graph, &QAction::triggered, this, &MainWindow::showTemporalGraphWindow);
|
||||
connect(ui->btnExpandAll, &QAbstractButton::clicked, this, &MainWindow::expandAllRows);
|
||||
connect(ui->btnCollapseAll, &QAbstractButton::clicked, this, &MainWindow::collapseAllRows);
|
||||
|
||||
connect(CANConManager::getInstance(), &CANConManager::framesReceived, model, &CANFrameModel::addFrames);
|
||||
|
||||
@@ -168,12 +178,9 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
//of scaling or font differences between different computers.
|
||||
CANFrame temp;
|
||||
temp.bus = 0;
|
||||
temp.ID = 0x100;
|
||||
temp.len = 0;
|
||||
temp.extended = false;
|
||||
temp.setFrameId(0x100);
|
||||
temp.isReceived = true;
|
||||
temp.remote = false;
|
||||
temp.timestamp = 100000000;
|
||||
temp.setTimeStamp(QCanBusFrame::TimeStamp(0, 100000000));
|
||||
model->addFrame(temp, true);
|
||||
qApp->processEvents();
|
||||
tickGUIUpdate(); //force a GUI refresh so that the row exists to measure
|
||||
@@ -258,12 +265,31 @@ void MainWindow::exitApp()
|
||||
QApplication::quit(); //forces the whole application to terminate when the main window is closed
|
||||
}
|
||||
|
||||
|
||||
//the close event can be trapped and ignored so put unsaved warnings in here so the user can abort the program closing if they forgot to save things.
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
QMessageBox::StandardButton confirmDialog;
|
||||
|
||||
for (int i = 0; i < dbcHandler->getFileCount(); i++)
|
||||
{
|
||||
DBCFile *file = dbcHandler->getFileByIdx(i);
|
||||
if (file->getDirtyFlag())
|
||||
{
|
||||
confirmDialog = QMessageBox::question(this, "Unsaved DBC", "DBC File:\n" + file->getFilename() + "\nAppears to have unsaved changes\nReally close without saving?", QMessageBox::Yes|QMessageBox::No);
|
||||
if (confirmDialog != QMessageBox::Yes)
|
||||
{
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeEventFilter(this);
|
||||
writeSettings();
|
||||
exitApp();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
|
||||
@@ -385,6 +411,48 @@ void MainWindow::headerClicked(int logicalIndex)
|
||||
model->sortByColumn(logicalIndex);
|
||||
}
|
||||
|
||||
void MainWindow::expandAllRows()
|
||||
{
|
||||
bool goAhead = false;
|
||||
int numRows = ui->canFramesView->model()->rowCount();
|
||||
|
||||
if (numRows > 20000)
|
||||
{
|
||||
QMessageBox::StandardButton confirmDialog;
|
||||
confirmDialog = QMessageBox::question(this, "Really?", "It's not recommended to use this\non more than 20000 frames.\nIt can take a long time.\n\nYou have been warned!\nStill do it?",
|
||||
QMessageBox::Yes|QMessageBox::No);
|
||||
|
||||
if (confirmDialog == QMessageBox::Yes) goAhead = true;
|
||||
}
|
||||
else goAhead = true;
|
||||
|
||||
if (goAhead)
|
||||
{
|
||||
ui->canFramesView->resizeRowsToContents();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::collapseAllRows()
|
||||
{
|
||||
bool goAhead = false;
|
||||
int numRows = ui->canFramesView->model()->rowCount();
|
||||
|
||||
if (numRows > 50000)
|
||||
{
|
||||
QMessageBox::StandardButton confirmDialog;
|
||||
confirmDialog = QMessageBox::question(this, "Really?", "It's not recommended to use this\non more than 50000 frames.\nIt can take a long time.\n\nYou have been warned!\nStill do it?",
|
||||
QMessageBox::Yes|QMessageBox::No);
|
||||
|
||||
if (confirmDialog == QMessageBox::Yes) goAhead = true;
|
||||
}
|
||||
else goAhead = true;
|
||||
|
||||
if (goAhead)
|
||||
{
|
||||
for (int i = 0; i < numRows; i++) ui->canFramesView->setRowHeight(i, normalRowHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::gridClicked(QModelIndex idx)
|
||||
{
|
||||
//qDebug() << "Grid Clicked";
|
||||
@@ -402,7 +470,7 @@ void MainWindow::gridDoubleClicked(QModelIndex idx)
|
||||
//qDebug() << "Grid double clicked";
|
||||
//grab ID and timestamp and send them away
|
||||
CANFrame frame = model->getListReference()->at(idx.row());
|
||||
emit sendCenterTimeID(frame.ID, frame.timestamp / 1000000.0);
|
||||
emit sendCenterTimeID(frame.frameId(), frame.timeStamp().microSeconds() / 1000000.0);
|
||||
}
|
||||
|
||||
void MainWindow::interpretToggled(bool state)
|
||||
@@ -447,7 +515,7 @@ void MainWindow::updateFilterList()
|
||||
QMap<int, bool>::const_iterator filterIter;
|
||||
for (filterIter = filters->begin(); filterIter != filters->end(); ++filterIter)
|
||||
{
|
||||
QListWidgetItem *thisItem = FilterUtility::createCheckableFilterItem(filterIter.key(), filterIter.value(), ui->listFilters);
|
||||
/*QListWidgetItem *thisItem = */FilterUtility::createCheckableFilterItem(filterIter.key(), filterIter.value(), ui->listFilters);
|
||||
}
|
||||
inhibitFilterUpdate = false;
|
||||
}
|
||||
@@ -595,10 +663,13 @@ void MainWindow::handleLoadFile()
|
||||
|
||||
if (!loadResult)
|
||||
{
|
||||
confirmDialog = QMessageBox::question(this, "Error Loading", "Do you want to salvage what could be loaded?",
|
||||
if (tempFrames.count() > 0) //only ask if at least one frame was decoded.
|
||||
{
|
||||
confirmDialog = QMessageBox::question(this, "Error Loading", "Do you want to salvage what could be loaded?",
|
||||
QMessageBox::Yes|QMessageBox::No);
|
||||
if (confirmDialog == QMessageBox::Yes) {
|
||||
loadResult = true;
|
||||
if (confirmDialog == QMessageBox::Yes) {
|
||||
loadResult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,6 +803,10 @@ void MainWindow::saveDecodedTextFile(QString filename)
|
||||
QFile *outFile = new QFile(filename);
|
||||
const QVector<CANFrame> *frames = model->getFilteredListReference();
|
||||
|
||||
const unsigned char *data;
|
||||
int dataLen;
|
||||
const CANFrame *frame;
|
||||
|
||||
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
return;
|
||||
/*
|
||||
@@ -741,20 +816,23 @@ Data Bytes: 88 10 00 13 BB 00 06 00
|
||||
*/
|
||||
for (int c = 0; c < frames->count(); c++)
|
||||
{
|
||||
CANFrame thisFrame = frames->at(c);
|
||||
frame = &frames->at(c);
|
||||
data = reinterpret_cast<const unsigned char *>(frame->payload().constData());
|
||||
dataLen = frame->payload().count();
|
||||
|
||||
QString builderString;
|
||||
builderString += tr("Time: ") + QString::number((thisFrame.timestamp / 1000000.0), 'f', 6);
|
||||
builderString += tr(" ID: ") + Utility::formatCANID(thisFrame.ID, thisFrame.extended);
|
||||
if (thisFrame.extended) builderString += tr(" Ext ");
|
||||
builderString += tr("Time: ") + QString::number((frame->timeStamp().microSeconds() / 1000000.0), 'f', 6);
|
||||
builderString += tr(" ID: ") + Utility::formatCANID(frame->frameId(), frame->hasExtendedFrameFormat());
|
||||
if (frame->hasExtendedFrameFormat()) builderString += tr(" Ext ");
|
||||
else builderString += tr(" Std ");
|
||||
builderString += tr("Bus: ") + QString::number(thisFrame.bus);
|
||||
builderString += " Len: " + QString::number(thisFrame.len) + "\n";
|
||||
builderString += tr("Bus: ") + QString::number(frame->bus);
|
||||
builderString += " Len: " + QString::number(dataLen) + "\n";
|
||||
outFile->write(builderString.toUtf8());
|
||||
|
||||
builderString = tr("Data Bytes: ");
|
||||
for (unsigned int temp = 0; temp < thisFrame.len; temp++)
|
||||
for (int temp = 0; temp < dataLen; temp++)
|
||||
{
|
||||
builderString += Utility::formatNumber(thisFrame.data[temp]) + " ";
|
||||
builderString += Utility::formatNumber(data[temp]) + " ";
|
||||
}
|
||||
builderString += "\n";
|
||||
outFile->write(builderString.toUtf8());
|
||||
@@ -762,14 +840,14 @@ Data Bytes: 88 10 00 13 BB 00 06 00
|
||||
builderString = "";
|
||||
if (dbcHandler != nullptr)
|
||||
{
|
||||
DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame);
|
||||
DBC_MESSAGE *msg = dbcHandler->findMessage(*frame);
|
||||
if (msg != nullptr)
|
||||
{
|
||||
for (int j = 0; j < msg->sigHandler->getCount(); j++)
|
||||
{
|
||||
|
||||
QString temp;
|
||||
if (msg->sigHandler->findSignalByIdx(j)->processAsText(thisFrame, temp))
|
||||
if (msg->sigHandler->findSignalByIdx(j)->processAsText(*frame, temp))
|
||||
{
|
||||
builderString.append("\t" + temp);
|
||||
builderString.append("\n");
|
||||
@@ -845,14 +923,14 @@ void MainWindow::showGraphingWindow()
|
||||
{
|
||||
if (!graphingWindow) {
|
||||
graphingWindow = new GraphingWindow(model->getListReference());
|
||||
connect(graphingWindow, SIGNAL(sendCenterTimeID(int32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(this, SIGNAL(sendCenterTimeID(int32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(graphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(this, SIGNAL(sendCenterTimeID(uint32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
}
|
||||
|
||||
if (flowViewWindow) //connect the two external windows together
|
||||
{
|
||||
connect(graphingWindow, SIGNAL(sendCenterTimeID(int32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(flowViewWindow, SIGNAL(sendCenterTimeID(int32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(graphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
}
|
||||
graphingWindow->show();
|
||||
}
|
||||
@@ -953,6 +1031,15 @@ void MainWindow::showComparisonWindow()
|
||||
comparatorWindow->show();
|
||||
}
|
||||
|
||||
void MainWindow::showDBCComparisonWindow()
|
||||
{
|
||||
if (!dbcComparatorWindow)
|
||||
{
|
||||
dbcComparatorWindow = new DBCComparatorWindow();
|
||||
}
|
||||
dbcComparatorWindow->show();
|
||||
}
|
||||
|
||||
void MainWindow::showSingleMultiWindow()
|
||||
{
|
||||
if (!discreteStateWindow)
|
||||
@@ -1022,14 +1109,14 @@ void MainWindow::showFlowViewWindow()
|
||||
flowViewWindow = new FlowViewWindow(model->getListReference());
|
||||
else
|
||||
flowViewWindow = new FlowViewWindow(model->getFilteredListReference());
|
||||
connect(flowViewWindow, SIGNAL(sendCenterTimeID(int32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(this, SIGNAL(sendCenterTimeID(int32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(this, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
}
|
||||
|
||||
if (graphingWindow)
|
||||
{
|
||||
connect(graphingWindow, SIGNAL(sendCenterTimeID(int32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(flowViewWindow, SIGNAL(sendCenterTimeID(int32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(graphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
connect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
||||
}
|
||||
|
||||
flowViewWindow->show();
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "motorcontrollerconfigwindow.h"
|
||||
#include "signalviewerwindow.h"
|
||||
#include "re/temporalgraphwindow.h"
|
||||
#include "re/dbccomparatorwindow.h"
|
||||
|
||||
class CANConnection;
|
||||
class ConnectionWindow;
|
||||
@@ -63,6 +64,8 @@ private slots:
|
||||
void showGraphingWindow();
|
||||
void showFrameDataAnalysis();
|
||||
void clearFrames();
|
||||
void expandAllRows();
|
||||
void collapseAllRows();
|
||||
void showPlaybackWindow();
|
||||
void showFlowViewWindow();
|
||||
void showFrameSenderWindow();
|
||||
@@ -83,6 +86,7 @@ private slots:
|
||||
void showBisectWindow();
|
||||
void showSignalViewer();
|
||||
void showTemporalGraphWindow();
|
||||
void showDBCComparisonWindow();
|
||||
void exitApp();
|
||||
void handleSaveDecoded();
|
||||
void connectionStatusUpdated(int conns);
|
||||
@@ -115,7 +119,7 @@ signals:
|
||||
void framesUpdated(int numFrames); //something has updated the frame list (send at gui update frequency)
|
||||
void frameUpdateRapid(int numFrames);
|
||||
void settingsUpdated();
|
||||
void sendCenterTimeID(int32_t ID, double timestamp);
|
||||
void sendCenterTimeID(uint32_t ID, double timestamp);
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
@@ -163,6 +167,7 @@ private:
|
||||
BisectWindow* bisectWindow;
|
||||
SignalViewerWindow *signalViewerWindow;
|
||||
TemporalGraphWindow *temporalGraphWindow;
|
||||
DBCComparatorWindow *dbcComparatorWindow;
|
||||
|
||||
//various private storage
|
||||
QLabel lbStatusConnected;
|
||||
|
||||
@@ -4,6 +4,17 @@
|
||||
#include "mainwindow.h"
|
||||
#include <QDebug>
|
||||
|
||||
/*
|
||||
* Nothing to see here, these are not the droids you're looking for. Go away
|
||||
*
|
||||
*
|
||||
*
|
||||
* You didn't go away, did you... This is a screen that allows for setting EEPROM configuration for a
|
||||
* custom motor controller project I was working on. It's hidden by default. You could re-enable it and play around
|
||||
* with it if you're bored. It might be a good basis for how to set a list of parameters on a device. But, it could be broken
|
||||
* these days too. It is not maintained any longer as the project it was meant for is abandoned. YMMV.
|
||||
*/
|
||||
|
||||
MotorControllerConfigWindow::MotorControllerConfigWindow(const QVector<CANFrame> *frames, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::MotorControllerConfigWindow)
|
||||
@@ -40,9 +51,8 @@ MotorControllerConfigWindow::~MotorControllerConfigWindow()
|
||||
void MotorControllerConfigWindow::updatedFrames(int numFrames)
|
||||
{
|
||||
CANFrame thisFrame;
|
||||
int id;
|
||||
int param;
|
||||
QTableWidgetItem *item;
|
||||
uint32_t id;
|
||||
QTableWidgetItem *item = nullptr;
|
||||
if (numFrames == -1) //all frames deleted
|
||||
{
|
||||
|
||||
@@ -57,18 +67,18 @@ void MotorControllerConfigWindow::updatedFrames(int numFrames)
|
||||
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
|
||||
{
|
||||
thisFrame = modelFrames->at(i);
|
||||
id = thisFrame.ID;
|
||||
id = thisFrame.frameId();
|
||||
|
||||
if (id == 0xC2) //response to a query we made
|
||||
{
|
||||
if (thisFrame.data[2] == 0)
|
||||
if ((char)thisFrame.payload()[2] == 0)
|
||||
{
|
||||
int paramID = thisFrame.data[0] + (thisFrame.data[1] * 256);
|
||||
uint32_t paramID = static_cast<uint32_t>(thisFrame.payload()[0] + (thisFrame.payload()[1] * 256));
|
||||
for (int i = 0; i < params.length(); i++)
|
||||
{
|
||||
if (params[i].paramID == paramID)
|
||||
{
|
||||
params[i].value = thisFrame.data[4] + (thisFrame.data[5] * 256);
|
||||
params[i].value = static_cast<uint16_t>(thisFrame.payload()[4] + (thisFrame.payload()[5] * 256));
|
||||
if (params[i].paramType == ASCII) item = new QTableWidgetItem(); //QString::fromUtf8((char *)params[i].value, 2));
|
||||
if (params[i].paramType == HEX) item = new QTableWidgetItem(Utility::formatHexNum(params[i].value));
|
||||
if (params[i].paramType == DEC)
|
||||
@@ -177,18 +187,19 @@ void MotorControllerConfigWindow::timerTick()
|
||||
{
|
||||
qDebug() << "Request: " << QString::number(transmitStep);
|
||||
|
||||
outFrame.ID = 0xC1;
|
||||
outFrame.len = 8;
|
||||
outFrame.setFrameId(0xC1);
|
||||
QByteArray bytes(8, 0);
|
||||
outFrame.bus = 0;
|
||||
outFrame.extended = false;
|
||||
outFrame.data[0] = params[transmitStep].paramID & 0xFF;
|
||||
outFrame.data[1] = (params[transmitStep].paramID >> 8) & 0xFF;
|
||||
outFrame.data[2] = 0; //0 = read, 1 = write
|
||||
outFrame.data[3] = 0; //reserved
|
||||
outFrame.data[4] = 0; //value goes in bytes 4,5 when writing
|
||||
outFrame.data[5] = 0;
|
||||
outFrame.data[6] = 0; //reserved
|
||||
outFrame.data[7] = 0; //reserved
|
||||
outFrame.setExtendedFrameFormat(false);
|
||||
bytes[0] = params[transmitStep].paramID & 0xFF;
|
||||
bytes[1] = (params[transmitStep].paramID >> 8) & 0xFF;
|
||||
bytes[2] = 0; //0 = read, 1 = write
|
||||
bytes[3] = 0; //reserved
|
||||
bytes[4] = 0; //value goes in bytes 4,5 when writing
|
||||
bytes[5] = 0;
|
||||
bytes[6] = 0; //reserved
|
||||
bytes[7] = 0; //reserved
|
||||
outFrame.setPayload(bytes);
|
||||
|
||||
CANConManager::getInstance()->sendFrame(outFrame);
|
||||
|
||||
@@ -213,18 +224,18 @@ void MotorControllerConfigWindow::timerTick()
|
||||
|
||||
if (thisValue != params[transmitStep].value)
|
||||
{
|
||||
outFrame.ID = 0xC1;
|
||||
outFrame.len = 8;
|
||||
outFrame.setFrameId(0xC1);
|
||||
QByteArray bytes(8, 0);
|
||||
outFrame.bus = 0;
|
||||
outFrame.extended = false;
|
||||
outFrame.data[0] = params[transmitStep].paramID & 0xFF;
|
||||
outFrame.data[1] = (params[transmitStep].paramID >> 8) & 0xFF;
|
||||
outFrame.data[2] = 1; //0 = read, 1 = write
|
||||
outFrame.data[3] = 0; //reserved
|
||||
outFrame.data[4] = params[transmitStep].value & 0xFF;
|
||||
outFrame.data[5] = (params[transmitStep].value >> 8) & 0xFF;
|
||||
outFrame.data[6] = 0; //reserved
|
||||
outFrame.data[7] = 0; //reserved
|
||||
outFrame.setExtendedFrameFormat(false);
|
||||
bytes[0] = params[transmitStep].paramID & 0xFF;
|
||||
bytes[1] = (params[transmitStep].paramID >> 8) & 0xFF;
|
||||
bytes[2] = 1; //0 = read, 1 = write
|
||||
bytes[3] = 0; //reserved
|
||||
bytes[4] = params[transmitStep].value & 0xFF;
|
||||
bytes[5] = (params[transmitStep].value >> 8) & 0xFF;
|
||||
bytes[6] = 0; //reserved
|
||||
bytes[7] = 0; //reserved
|
||||
|
||||
CANConManager::getInstance()->sendFrame(outFrame);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
This project is dual licensed under the Eclipse Public License 1.0 and the
|
||||
Eclipse Distribution License 1.0 as described in the epl-v10 and edl-v10 files.
|
||||
@@ -0,0 +1,139 @@
|
||||
QMQTT
|
||||
=====
|
||||
|
||||
mqtt client for Qt
|
||||
|
||||
**Please compile the library with Qt >= 5.3 version. On Windows you need to specify `CONFIG += NO_UNIT_TESTS`, since gtest is not supported.**
|
||||
|
||||
SSL is enabled by default, if the version of OpenSSL < 1.0.2, SSL may not be supported.
|
||||
|
||||
Disable the SSL in CMakeLists.txt (cmake):
|
||||
|
||||
option( ${PROJECT_NAME}_SSL "Enable SSL support for MQTT" OFF )
|
||||
|
||||
Disable the SSL with src/mqtt/qmqtt.pro (qmake):
|
||||
|
||||
CONFIG+=QMQTT_NO_SSL
|
||||
|
||||
To add websocket support, compile the library with Qt >= 5.7, and specify 'CONFIG += QMQTT_WEBSOCKETS'.
|
||||
This also works when compiling qmqtt for WebAssembly.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
In your QMake project, add:
|
||||
|
||||
QT += qmqtt
|
||||
|
||||
Connect using TCP:
|
||||
|
||||
#include "qmqtt.h"
|
||||
|
||||
QMQTT::Client *client = new QMQTT::Client(QHostAddress::LocalHost, 1883);
|
||||
client->setClientId("clientId");
|
||||
client->setUsername("user");
|
||||
client->setPassword("password");
|
||||
client->connectToHost();
|
||||
|
||||
Connect using SSL:
|
||||
|
||||
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||
// Add custom SSL options here (for example extra certificates)
|
||||
QMQTT::Client *client = new QMQTT::Client("example.com", 8883, sslConfig);
|
||||
client->setClientId("clientId");
|
||||
client->setUsername("user");
|
||||
client->setPassword("password");
|
||||
// Optionally, set ssl errors you want to ignore. Be careful, because this may weaken security.
|
||||
// See QSslSocket::ignoreSslErrors(const QList<QSslError> &) for more information.
|
||||
client->ignoreSslErrors(<list of expected ssl errors>)
|
||||
client->connectToHost();
|
||||
// Here's another option to suppress SSL errors (again, be careful)
|
||||
QObject::connect(client, &QMQTT::Client::sslErrors, [&](const QList<QSslError> &errors) {
|
||||
// Investigate the errors here, if you find no serious problems, call ignoreSslErrors()
|
||||
// to continue connecting.
|
||||
client->ignoreSslErrors();
|
||||
});
|
||||
|
||||
Connect using WebSockets:
|
||||
|
||||
QMQTT::Client *client = new QMQTT::Client("ws://www.example.com/mqtt", "<origin>", QWebSocketProtocol::VersionLatest);
|
||||
client->setClientId("clientId");
|
||||
client->setUsername("user");
|
||||
client->setPassword("password");
|
||||
client->connectToHost();
|
||||
|
||||
Slots
|
||||
=====
|
||||
|
||||
void setHost(const QHostAddress& host);
|
||||
void setPort(const quint16 port);
|
||||
void setClientId(const QString& clientId);
|
||||
void setUsername(const QString& username);
|
||||
void setPassword(const QString& password);
|
||||
void setKeepAlive(const int keepAlive);
|
||||
void setCleanSession(const bool cleansess);
|
||||
void setAutoReconnect(const bool value);
|
||||
void setAutoReconnectInterval(const int autoReconnectInterval);
|
||||
void setWillTopic(const QString& willTopic);
|
||||
void setWillQos(const quint8 willQos);
|
||||
void setWillRetain(const bool willRetain);
|
||||
void setWillMessage(const QString& willMessage);
|
||||
|
||||
void connectToHost();
|
||||
void disconnectFromHost();
|
||||
|
||||
quint16 subscribe(const QString& topic, const quint8 qos);
|
||||
void unsubscribe(const QString& topic);
|
||||
|
||||
quint16 publish(const Message& message);
|
||||
|
||||
Signals
|
||||
=======
|
||||
|
||||
void connected();
|
||||
void disconnected();
|
||||
void error(const QMQTT::ClientError error);
|
||||
|
||||
void subscribed(const QString& topic, const quint8 qos);
|
||||
void unsubscribed(const QString& topic);
|
||||
void published(const quint16 msgid, const quint8 qos);
|
||||
void pingresp();
|
||||
void received(const QMQTT::Message& message);
|
||||
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
New BSD License
|
||||
|
||||
|
||||
Contributors
|
||||
=============
|
||||
|
||||
[@Kampfgnom](https://github.com/Kampfgnom)
|
||||
|
||||
[@rafaeldelucena](https://github.com/rafaeldelucena)
|
||||
|
||||
[@Vortex375](https://github.com/Vortex375)
|
||||
|
||||
[@mwallnoefer](https://github.com/mwallnoefer)
|
||||
|
||||
[@KonstantinRitt](https://github.com/KonstantinRitt)
|
||||
|
||||
[@cncap](https://github.com/cncap)
|
||||
|
||||
[@Psy-Kai](https://github.com/Psy-Kai)
|
||||
|
||||
[@ejvr](https://github.com/ejvr)
|
||||
|
||||
[@keytee](https://github.com/keytee)
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
[@emqplus](https://github.com/emqplus) Feng Lee <feng@emqtt.io>
|
||||
|
||||
[@wguynes](https://github.com/wguynes) William Guynes <wguynes@gmail.com>
|
||||
|
||||
[@wuming123057](https://github.com/wuming123057) Xuan <huacai123057@163.com>
|
||||
@@ -0,0 +1,30 @@
|
||||
Eclipse Distribution License - v 1.0
|
||||
|
||||
Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
Neither the name of the Eclipse Foundation, Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,220 @@
|
||||
Eclipse Public License - v 1.0
|
||||
|
||||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
|
||||
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
|
||||
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
|
||||
|
||||
1. DEFINITIONS
|
||||
|
||||
"Contribution" means:
|
||||
a) in the case of the initial Contributor, the initial code and
|
||||
documentation distributed under this Agreement, and
|
||||
|
||||
b) in the case of each subsequent Contributor:
|
||||
|
||||
i) changes to the Program, and
|
||||
ii) additions to the Program;
|
||||
|
||||
where such changes and/or additions to the Program originate from and are
|
||||
distributed by that particular Contributor. A Contribution 'originates' from a
|
||||
Contributor if it was added to the Program by such Contributor itself or anyone
|
||||
acting on such Contributor's behalf. Contributions do not include additions to
|
||||
the Program which: (i) are separate modules of software distributed in
|
||||
conjunction with the Program under their own license agreement, and (ii) are
|
||||
not derivative works of the Program.
|
||||
|
||||
"Contributor" means any person or entity that distributes the Program.
|
||||
|
||||
"Licensed Patents " mean patent claims licensable by a Contributor which are
|
||||
necessarily infringed by the use or sale of its Contribution alone or when
|
||||
combined with the Program.
|
||||
|
||||
"Program" means the Contributions distributed in accordance with this Agreement.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement,
|
||||
including all Contributors.
|
||||
|
||||
|
||||
2. GRANT OF RIGHTS
|
||||
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby grants
|
||||
Recipient a non-exclusive, worldwide, royalty-free copyright license to
|
||||
reproduce, prepare derivative works of, publicly display, publicly
|
||||
perform, distribute and sublicense the Contribution of such Contributor,
|
||||
if any, and such derivative works, in source code and object code form.
|
||||
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby grants
|
||||
Recipient a non-exclusive, worldwide, royalty-free patent license under
|
||||
Licensed Patents to make, use, sell, offer to sell, import and otherwise
|
||||
transfer the Contribution of such Contributor, if any, in source code and
|
||||
object code form. This patent license shall apply to the combination of the
|
||||
Contribution and the Program if, at the time the Contribution is added by the
|
||||
Contributor, such addition of the Contribution causes such combination to be
|
||||
covered by the Licensed Patents. The patent license shall not apply to any
|
||||
other combinations which include the Contribution. No hardware per se is
|
||||
licensed hereunder.
|
||||
|
||||
c) Recipient understands that although each Contributor grants the licenses
|
||||
to its Contributions set forth herein, no assurances are provided by any
|
||||
Contributor that the Program does not infringe the patent or other
|
||||
intellectual property rights of any other entity. Each Contributor disclaims
|
||||
any liability to Recipient for claims brought by any other entity based on
|
||||
infringement of intellectual property rights or otherwise. As a condition to
|
||||
exercising the rights and licenses granted hereunder, each Recipient hereby
|
||||
assumes sole responsibility to secure any other intellectual property rights
|
||||
needed, if any. For example, if a third party patent license is required to
|
||||
allow Recipient to distribute the Program, it is Recipient's responsibility
|
||||
to acquire that license before distributing the Program.
|
||||
|
||||
d) Each Contributor represents that to its knowledge it has sufficient
|
||||
copyright rights in its Contribution, if any, to grant the copyright license
|
||||
set forth in this Agreement.
|
||||
|
||||
|
||||
3. REQUIREMENTS
|
||||
|
||||
A Contributor may choose to distribute the Program in object code form under
|
||||
its own license agreement, provided that:
|
||||
|
||||
a) it complies with the terms and conditions of this Agreement; and
|
||||
|
||||
b) its license agreement:
|
||||
|
||||
i) effectively disclaims on behalf of all Contributors all warranties and
|
||||
conditions, express and implied, including warranties or conditions of
|
||||
title and non-infringement, and implied warranties or conditions of
|
||||
merchantability and fitness for a particular purpose;
|
||||
|
||||
ii) effectively excludes on behalf of all Contributors all liability for
|
||||
damages, including direct, indirect, special, incidental and consequential
|
||||
damages, such as lost profits;
|
||||
|
||||
iii) states that any provisions which differ from this Agreement are offered
|
||||
by that Contributor alone and not by any other party; and
|
||||
|
||||
iv) states that source code for the Program is available from such
|
||||
Contributor, and informs licensees how to obtain it in a reasonable manner
|
||||
on or through a medium customarily used for software exchange.
|
||||
|
||||
When the Program is made available in source code form:
|
||||
|
||||
a) it must be made available under this Agreement; and
|
||||
|
||||
b) a copy of this Agreement must be included with each copy of the Program.
|
||||
|
||||
Contributors may not remove or alter any copyright notices contained within
|
||||
the Program.
|
||||
|
||||
Each Contributor must identify itself as the originator of its Contribution,
|
||||
if any, in a manner that reasonably allows subsequent Recipients to identify
|
||||
the originator of the Contribution.
|
||||
|
||||
|
||||
4. COMMERCIAL DISTRIBUTION
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities with
|
||||
respect to end users, business partners and the like. While this license is
|
||||
intended to facilitate the commercial use of the Program, the Contributor who
|
||||
includes the Program in a commercial product offering should do so in a
|
||||
manner which does not create potential liability for other Contributors.
|
||||
Therefore, if a Contributor includes the Program in a commercial product
|
||||
offering, such Contributor ("Commercial Contributor") hereby agrees to defend
|
||||
and indemnify every other Contributor ("Indemnified Contributor") against any
|
||||
losses, damages and costs (collectively "Losses") arising from claims,
|
||||
lawsuits and other legal actions brought by a third party against the
|
||||
Indemnified Contributor to the extent caused by the acts or omissions of such
|
||||
Commercial Contributor in connection with its distribution of the Program in
|
||||
a commercial product offering. The obligations in this section do not apply
|
||||
to any claims or Losses relating to any actual or alleged intellectual
|
||||
property infringement. In order to qualify, an Indemnified Contributor must:
|
||||
a) promptly notify the Commercial Contributor in writing of such claim, and
|
||||
b) allow the Commercial Contributor to control, and cooperate with the
|
||||
Commercial Contributor in, the defense and any related settlement
|
||||
negotiations. The Indemnified Contributor may participate in any such claim
|
||||
at its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial product
|
||||
offering, Product X. That Contributor is then a Commercial Contributor. If
|
||||
that Commercial Contributor then makes performance claims, or offers
|
||||
warranties related to Product X, those performance claims and warranties are
|
||||
such Commercial Contributor's responsibility alone. Under this section, the
|
||||
Commercial Contributor would have to defend claims against the other
|
||||
Contributors related to those performance claims and warranties, and if a
|
||||
court requires any other Contributor to pay any damages as a result, the
|
||||
Commercial Contributor must pay those damages.
|
||||
|
||||
|
||||
5. NO WARRANTY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON
|
||||
AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
|
||||
EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR
|
||||
CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
|
||||
PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the
|
||||
appropriateness of using and distributing the Program and assumes all risks
|
||||
associated with its exercise of rights under this Agreement , including but
|
||||
not limited to the risks and costs of program errors, compliance with
|
||||
applicable laws, damage to or loss of data, programs or equipment, and
|
||||
unavailability or interruption of operations.
|
||||
|
||||
|
||||
6. DISCLAIMER OF LIABILITY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
|
||||
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
|
||||
LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
|
||||
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
OF SUCH DAMAGES.
|
||||
|
||||
|
||||
7. GENERAL
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under
|
||||
applicable law, it shall not affect the validity or enforceability of the
|
||||
remainder of the terms of this Agreement, and without further action by the
|
||||
parties hereto, such provision shall be reformed to the minimum extent
|
||||
necessary to make such provision valid and enforceable.
|
||||
|
||||
If Recipient institutes patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Program itself
|
||||
(excluding combinations of the Program with other software or hardware)
|
||||
infringes such Recipient's patent(s), then such Recipient's rights granted
|
||||
under Section 2(b) shall terminate as of the date such litigation is filed.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it fails to
|
||||
comply with any of the material terms or conditions of this Agreement and
|
||||
does not cure such failure in a reasonable period of time after becoming
|
||||
aware of such noncompliance. If all Recipient's rights under this Agreement
|
||||
terminate, Recipient agrees to cease use and distribution of the Program as
|
||||
soon as reasonably practicable. However, Recipient's obligations under this
|
||||
Agreement and any licenses granted by Recipient relating to the Program shall
|
||||
continue and survive.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement, but in
|
||||
order to avoid inconsistency the Agreement is copyrighted and may only be
|
||||
modified in the following manner. The Agreement Steward reserves the right to
|
||||
publish new versions (including revisions) of this Agreement from time to
|
||||
time. No one other than the Agreement Steward has the right to modify this
|
||||
Agreement. The Eclipse Foundation is the initial Agreement Steward. The
|
||||
Eclipse Foundation may assign the responsibility to serve as the Agreement
|
||||
Steward to a suitable separate entity. Each new version of the Agreement will
|
||||
be given a distinguishing version number. The Program (including
|
||||
Contributions) may always be distributed subject to the version of the
|
||||
Agreement under which it was received. In addition, after a new version of
|
||||
the Agreement is published, Contributor may elect to distribute the Program
|
||||
(including its Contributions) under the new version. Except as expressly
|
||||
stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
|
||||
licenses to the intellectual property of any Contributor under this
|
||||
Agreement, whether expressly, by implication, estoppel or otherwise. All
|
||||
rights in the Program not expressly granted under this Agreement are
|
||||
reserved.
|
||||
|
||||
This Agreement is governed by the laws of the State of New York and the
|
||||
intellectual property laws of the United States of America. No party to this
|
||||
Agreement will bring a legal action under this Agreement more than one year
|
||||
after the cause of action arose. Each party waives its rights to a jury trial
|
||||
in any resulting litigation.
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* qmqtt.h - qmqtt library heaer
|
||||
*
|
||||
* Copyright (c) 2013 Ery Lee <ery.lee at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of mqttc nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#ifndef QMQTT_H
|
||||
#define QMQTT_H
|
||||
|
||||
#include "qmqtt_message.h"
|
||||
#include "qmqtt_client.h"
|
||||
|
||||
#endif // QMQTT_H
|
||||
@@ -0,0 +1,48 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
PUBLIC_HEADERS += \
|
||||
$$PWD/qmqtt_client.h \
|
||||
$$PWD/qmqtt_frame.h \
|
||||
$$PWD/qmqtt_message.h \
|
||||
$$PWD/qmqtt_routesubscription.h \
|
||||
$$PWD/qmqtt_routedmessage.h \
|
||||
$$PWD/qmqtt_router.h \
|
||||
$$PWD/qmqtt_networkinterface.h \
|
||||
$$PWD/qmqtt_socketinterface.h \
|
||||
$$PWD/qmqtt_timerinterface.h
|
||||
|
||||
PRIVATE_HEADERS += \
|
||||
$$PWD/qmqtt_client_p.h \
|
||||
$$PWD/qmqtt_message_p.h \
|
||||
$$PWD/qmqtt_network_p.h \
|
||||
$$PWD/qmqtt_socket_p.h \
|
||||
$$PWD/qmqtt_timer_p.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/qmqtt_client_p.cpp \
|
||||
$$PWD/qmqtt_client.cpp \
|
||||
$$PWD/qmqtt_frame.cpp \
|
||||
$$PWD/qmqtt_message.cpp \
|
||||
$$PWD/qmqtt_network.cpp \
|
||||
$$PWD/qmqtt_routesubscription.cpp \
|
||||
$$PWD/qmqtt_router.cpp \
|
||||
$$PWD/qmqtt_socket.cpp \
|
||||
$$PWD/qmqtt_timer.cpp
|
||||
|
||||
QMQTT_WEBSOCKETS {
|
||||
PRIVATE_HEADERS += \
|
||||
$$PWD/qmqtt_websocket_p.h \
|
||||
$$PWD/qmqtt_websocketiodevice_p.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/qmqtt_websocket.cpp \
|
||||
$$PWD/qmqtt_websocketiodevice.cpp
|
||||
}
|
||||
|
||||
!contains(CONFIG, QMQTT_NO_SSL) {
|
||||
PRIVATE_HEADERS += \
|
||||
$$PWD/qmqtt_ssl_socket_p.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/qmqtt_ssl_socket.cpp
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
TARGET = QtQmqtt
|
||||
QT = core network
|
||||
qtHaveModule(websockets): QMQTT_WEBSOCKETS: QT += websockets
|
||||
|
||||
DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
|
||||
|
||||
# CONFIG += QMQTT_NO_SSL
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/qmqtt_global.h \
|
||||
$$PWD/qmqtt.h
|
||||
|
||||
include(qmqtt.pri)
|
||||
|
||||
HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS
|
||||
|
||||
load(qt_module)
|
||||
@@ -0,0 +1,134 @@
|
||||
import qbs
|
||||
import qbs.TextFile
|
||||
import qbs.FileInfo
|
||||
|
||||
Product {
|
||||
name: "qmqtt"
|
||||
type: [libraryType]
|
||||
property bool webSocketSupport: false
|
||||
property string libraryType: "dynamiclibrary"
|
||||
targetName: "qmqtt"
|
||||
version: "1.0.0"
|
||||
|
||||
cpp.defines: [
|
||||
"QT_BUILD_QMQTT_LIB",
|
||||
"QT_NO_CAST_TO_ASCII",
|
||||
"QT_NO_CAST_FROM_ASCII",
|
||||
]
|
||||
|
||||
cpp.includePaths: sourceDirectory
|
||||
|
||||
files: [
|
||||
"*.cpp"
|
||||
]
|
||||
|
||||
Group {
|
||||
name: "Public Headers"
|
||||
fileTags: [
|
||||
"hpp",
|
||||
"public_headers"
|
||||
]
|
||||
files: [
|
||||
"*.h"
|
||||
]
|
||||
excludeFiles: privateHeaders.files
|
||||
}
|
||||
|
||||
Group {
|
||||
id: privateHeaders
|
||||
name: "Private Headers"
|
||||
fileTags: [
|
||||
"hpp",
|
||||
"private_headers",
|
||||
]
|
||||
files: [
|
||||
"*_p.h"
|
||||
]
|
||||
}
|
||||
|
||||
Group {
|
||||
fileTagsFilter: libraryType
|
||||
qbs.install: true
|
||||
qbs.installDir: "lib"
|
||||
}
|
||||
|
||||
Group {
|
||||
fileTagsFilter: "public_headers"
|
||||
qbs.install: true
|
||||
qbs.installDir: FileInfo.joinPaths("include", product.name)
|
||||
}
|
||||
|
||||
Group {
|
||||
fileTagsFilter: "private_headers"
|
||||
qbs.install: true
|
||||
qbs.installDir: FileInfo.joinPaths("include", product.name, version, product.name)
|
||||
}
|
||||
|
||||
Group {
|
||||
fileTagsFilter: "Exporter.qbs.module"
|
||||
qbs.installDir: FileInfo.joinPaths("qbs", "modules", product.name, "private")
|
||||
qbs.install: true
|
||||
}
|
||||
|
||||
Group {
|
||||
fileTagsFilter: "Exporter.pkgconfig.pc"
|
||||
qbs.installDir: FileInfo.joinPaths("lib" ,"pkgconfig")
|
||||
qbs.install: true
|
||||
}
|
||||
|
||||
Depends {
|
||||
name: "cpp"
|
||||
}
|
||||
|
||||
Depends {
|
||||
name: "Qt"
|
||||
property var baseModules: ["core", "network"]
|
||||
|
||||
Properties {
|
||||
condition: webSocketSupport
|
||||
submodules: baseModules.concat(["websockets"])
|
||||
}
|
||||
Properties {
|
||||
condition: !webSocketSupport
|
||||
submodules: baseModules
|
||||
}
|
||||
}
|
||||
|
||||
Depends {
|
||||
name: "Exporter.qbs"
|
||||
}
|
||||
Depends {
|
||||
name: "Exporter.pkgconfig"
|
||||
}
|
||||
|
||||
Export {
|
||||
Depends {
|
||||
name: "cpp"
|
||||
}
|
||||
|
||||
Depends {
|
||||
name: "Qt"
|
||||
property var baseModules: ["core", "network"]
|
||||
Properties {
|
||||
condition: product.webSocketSupport
|
||||
submodules: baseModules.concat(["websockets"])
|
||||
}
|
||||
Properties {
|
||||
condition: !product.webSocketSupport
|
||||
submodules: baseModules
|
||||
}
|
||||
}
|
||||
|
||||
cpp.includePaths: [
|
||||
product.sourceDirectory,
|
||||
FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, "include"),
|
||||
]
|
||||
prefixMapping: [
|
||||
{
|
||||
prefix: product.sourceDirectory,
|
||||
replacement: FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, "include",
|
||||
product.name)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* qmqtt_client.cpp - qmqtt client
|
||||
*
|
||||
* Copyright (c) 2013 Ery Lee <ery.lee at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of mqttc nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qmqtt_client.h"
|
||||
#include "qmqtt_client_p.h"
|
||||
|
||||
QMQTT::Client::Client(const QHostAddress& host,
|
||||
const quint16 port,
|
||||
QObject* parent)
|
||||
: QObject(parent)
|
||||
, d_ptr(new ClientPrivate(this))
|
||||
{
|
||||
Q_D(Client);
|
||||
d->init(host, port);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
QMQTT::Client::Client(const QString &hostName,
|
||||
const quint16 port,
|
||||
const QSslConfiguration &config,
|
||||
const bool ignoreSelfSigned, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d_ptr(new ClientPrivate(this))
|
||||
{
|
||||
Q_D(Client);
|
||||
d->init(hostName, port, config, ignoreSelfSigned);
|
||||
}
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
QMQTT::Client::Client(const QString &hostName,
|
||||
const quint16 port,
|
||||
const bool ssl,
|
||||
const bool ignoreSelfSigned,
|
||||
QObject* parent)
|
||||
: QObject(parent)
|
||||
, d_ptr(new ClientPrivate(this))
|
||||
{
|
||||
Q_D(Client);
|
||||
d->init(hostName, port, ssl, ignoreSelfSigned);
|
||||
}
|
||||
|
||||
#ifdef QT_WEBSOCKETS_LIB
|
||||
QMQTT::Client::Client(const QString& url,
|
||||
const QString& origin,
|
||||
QWebSocketProtocol::Version version,
|
||||
bool ignoreSelfSigned,
|
||||
QObject* parent)
|
||||
: QObject(parent)
|
||||
, d_ptr(new ClientPrivate(this))
|
||||
{
|
||||
Q_D(Client);
|
||||
#ifndef QT_NO_SSL
|
||||
d->init(url, origin, version, NULL, ignoreSelfSigned);
|
||||
#else
|
||||
Q_UNUSED(ignoreSelfSigned)
|
||||
d->init(url, origin, version);
|
||||
#endif // QT_NO_SSL
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
QMQTT::Client::Client(const QString& url,
|
||||
const QString& origin,
|
||||
QWebSocketProtocol::Version version,
|
||||
const QSslConfiguration& sslConfig,
|
||||
const bool ignoreSelfSigned,
|
||||
QObject* parent)
|
||||
: QObject(parent)
|
||||
, d_ptr(new ClientPrivate(this))
|
||||
{
|
||||
Q_D(Client);
|
||||
d->init(url, origin, version, &sslConfig, ignoreSelfSigned);
|
||||
}
|
||||
#endif // QT_NO_SSL
|
||||
#endif // QT_WEBSOCKETS_LIB
|
||||
|
||||
QMQTT::Client::Client(NetworkInterface* network,
|
||||
const QHostAddress& host,
|
||||
const quint16 port,
|
||||
QObject* parent)
|
||||
: QObject(parent)
|
||||
, d_ptr(new ClientPrivate(this))
|
||||
{
|
||||
Q_D(Client);
|
||||
d->init(host, port, network);
|
||||
}
|
||||
|
||||
QMQTT::Client::~Client()
|
||||
{
|
||||
}
|
||||
|
||||
QHostAddress QMQTT::Client::host() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->host();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setHost(const QHostAddress& host)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setHost(host);
|
||||
}
|
||||
|
||||
QString QMQTT::Client::hostName() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->hostName();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setHostName(const QString &hostName)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setHostName(hostName);
|
||||
}
|
||||
|
||||
quint16 QMQTT::Client::port() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->port();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setPort(const quint16 port)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setPort(port);
|
||||
}
|
||||
|
||||
QString QMQTT::Client::clientId() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->clientId();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setClientId(const QString& clientId)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setClientId(clientId);
|
||||
}
|
||||
|
||||
QString QMQTT::Client::username() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->username();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setUsername(const QString& username)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setUsername(username);
|
||||
}
|
||||
|
||||
QMQTT::MQTTVersion QMQTT::Client::version() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->version();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setVersion(const QMQTT::MQTTVersion version)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setVersion(version);
|
||||
}
|
||||
|
||||
QByteArray QMQTT::Client::password() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->password();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setPassword(const QByteArray &password)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setPassword(password);
|
||||
}
|
||||
|
||||
quint16 QMQTT::Client::keepAlive() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->keepAlive();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setKeepAlive(const quint16 keepAlive)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setKeepAlive(keepAlive);
|
||||
}
|
||||
|
||||
bool QMQTT::Client::cleanSession() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->cleanSession();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setCleanSession(const bool cleanSession)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setCleanSession(cleanSession);
|
||||
}
|
||||
|
||||
bool QMQTT::Client::autoReconnect() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->autoReconnect();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setAutoReconnect(const bool value)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setAutoReconnect(value);
|
||||
}
|
||||
|
||||
int QMQTT::Client::autoReconnectInterval() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->autoReconnectInterval();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setAutoReconnectInterval(const int autoReconnectInterval)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setAutoReconnectInterval(autoReconnectInterval);
|
||||
}
|
||||
|
||||
QString QMQTT::Client::willTopic() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->willTopic();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setWillTopic(const QString& willTopic)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setWillTopic(willTopic);
|
||||
}
|
||||
|
||||
quint8 QMQTT::Client::willQos() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->willQos();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setWillQos(const quint8 willQos)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setWillQos(willQos);
|
||||
}
|
||||
|
||||
bool QMQTT::Client::willRetain() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->willRetain();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setWillRetain(const bool willRetain)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setWillRetain(willRetain);
|
||||
}
|
||||
|
||||
QByteArray QMQTT::Client::willMessage() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->willMessage();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setWillMessage(const QByteArray &willMessage)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setWillMessage(willMessage);
|
||||
}
|
||||
|
||||
QMQTT::ConnectionState QMQTT::Client::connectionState() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->connectionState();
|
||||
}
|
||||
|
||||
bool QMQTT::Client::isConnectedToHost() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->isConnectedToHost();
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
QSslConfiguration QMQTT::Client::sslConfiguration() const
|
||||
{
|
||||
Q_D(const Client);
|
||||
return d->sslConfiguration();
|
||||
}
|
||||
|
||||
void QMQTT::Client::setSslConfiguration(const QSslConfiguration& config)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->setSslConfiguration(config);
|
||||
}
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
void QMQTT::Client::connectToHost()
|
||||
{
|
||||
Q_D(Client);
|
||||
d->connectToHost();
|
||||
}
|
||||
|
||||
void QMQTT::Client::onNetworkConnected()
|
||||
{
|
||||
Q_D(Client);
|
||||
d->onNetworkConnected();
|
||||
}
|
||||
|
||||
quint16 QMQTT::Client::publish(const Message& message)
|
||||
{
|
||||
Q_D(Client);
|
||||
return d->publish(message);
|
||||
}
|
||||
|
||||
void QMQTT::Client::subscribe(const QString& topic, const quint8 qos)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->subscribe(topic, qos);
|
||||
}
|
||||
|
||||
void QMQTT::Client::unsubscribe(const QString& topic)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->unsubscribe(topic);
|
||||
}
|
||||
|
||||
void QMQTT::Client::onTimerPingReq()
|
||||
{
|
||||
Q_D(Client);
|
||||
d->onTimerPingReq();
|
||||
}
|
||||
|
||||
void QMQTT::Client::onPingTimeout()
|
||||
{
|
||||
Q_D(Client);
|
||||
d->onPingTimeout();
|
||||
}
|
||||
|
||||
void QMQTT::Client::disconnectFromHost()
|
||||
{
|
||||
Q_D(Client);
|
||||
d->disconnectFromHost();
|
||||
}
|
||||
|
||||
void QMQTT::Client::onNetworkReceived(const QMQTT::Frame& frame)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->onNetworkReceived(frame);
|
||||
}
|
||||
|
||||
void QMQTT::Client::onNetworkDisconnected()
|
||||
{
|
||||
Q_D(Client);
|
||||
d->onNetworkDisconnected();
|
||||
}
|
||||
|
||||
void QMQTT::Client::onNetworkError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->onNetworkError(error);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
void QMQTT::Client::onSslErrors(const QList<QSslError>& errors)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->onSslErrors(errors);
|
||||
}
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
void QMQTT::Client::ignoreSslErrors()
|
||||
{
|
||||
Q_D(Client);
|
||||
d->ignoreSslErrors();
|
||||
}
|
||||
|
||||
void QMQTT::Client::ignoreSslErrors(const QList<QSslError>& errors)
|
||||
{
|
||||
Q_D(Client);
|
||||
d->ignoreSslErrors(errors);
|
||||
}
|
||||
#endif // QT_NO_SSL
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* qmqtt_client.h - qmqtt client header
|
||||
*
|
||||
* Copyright (c) 2013 Ery Lee <ery.lee at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of mqttc nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#ifndef QMQTT_CLIENT_H
|
||||
#define QMQTT_CLIENT_H
|
||||
|
||||
#include "qmqtt_global.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QHostAddress>
|
||||
#include <QByteArray>
|
||||
#include <QAbstractSocket>
|
||||
#include <QScopedPointer>
|
||||
#include <QList>
|
||||
|
||||
#ifdef QT_WEBSOCKETS_LIB
|
||||
#include <QWebSocket>
|
||||
#endif // QT_WEBSOCKETS_LIB
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
#include <QSslConfiguration>
|
||||
QT_FORWARD_DECLARE_CLASS(QSslError)
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
namespace QMQTT {
|
||||
|
||||
static const quint8 LIBRARY_VERSION_MAJOR = 0;
|
||||
static const quint8 LIBRARY_VERSION_MINOR = 3;
|
||||
static const quint8 LIBRARY_VERSION_REVISION = 1;
|
||||
//static const char* LIBRARY_VERSION = "0.3.1";
|
||||
|
||||
enum MQTTVersion
|
||||
{
|
||||
V3_1_0 = 3,
|
||||
V3_1_1 = 4
|
||||
};
|
||||
|
||||
enum ConnectionState
|
||||
{
|
||||
STATE_INIT = 0,
|
||||
STATE_CONNECTING,
|
||||
STATE_CONNECTED,
|
||||
STATE_DISCONNECTED
|
||||
};
|
||||
|
||||
enum ClientError
|
||||
{
|
||||
UnknownError = 0,
|
||||
SocketConnectionRefusedError,
|
||||
SocketRemoteHostClosedError,
|
||||
SocketHostNotFoundError,
|
||||
SocketAccessError,
|
||||
SocketResourceError,
|
||||
SocketTimeoutError,
|
||||
SocketDatagramTooLargeError,
|
||||
SocketNetworkError,
|
||||
SocketAddressInUseError,
|
||||
SocketAddressNotAvailableError,
|
||||
SocketUnsupportedSocketOperationError,
|
||||
SocketUnfinishedSocketOperationError,
|
||||
SocketProxyAuthenticationRequiredError,
|
||||
SocketSslHandshakeFailedError,
|
||||
SocketProxyConnectionRefusedError,
|
||||
SocketProxyConnectionClosedError,
|
||||
SocketProxyConnectionTimeoutError,
|
||||
SocketProxyNotFoundError,
|
||||
SocketProxyProtocolError,
|
||||
SocketOperationError,
|
||||
SocketSslInternalError,
|
||||
SocketSslInvalidUserDataError,
|
||||
SocketTemporaryError,
|
||||
MqttUnacceptableProtocolVersionError=1<<16,
|
||||
MqttIdentifierRejectedError,
|
||||
MqttServerUnavailableError,
|
||||
MqttBadUserNameOrPasswordError,
|
||||
MqttNotAuthorizedError,
|
||||
MqttNoPingResponse
|
||||
};
|
||||
|
||||
class ClientPrivate;
|
||||
class Message;
|
||||
class Frame;
|
||||
class NetworkInterface;
|
||||
|
||||
class Q_MQTT_EXPORT Client : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(quint16 _port READ port WRITE setPort)
|
||||
Q_PROPERTY(QHostAddress _host READ host WRITE setHost)
|
||||
Q_PROPERTY(QString _hostName READ hostName WRITE setHostName)
|
||||
Q_PROPERTY(QString _clientId READ clientId WRITE setClientId)
|
||||
Q_PROPERTY(QString _username READ username WRITE setUsername)
|
||||
Q_PROPERTY(QByteArray _password READ password WRITE setPassword)
|
||||
Q_PROPERTY(quint16 _keepAlive READ keepAlive WRITE setKeepAlive)
|
||||
Q_PROPERTY(MQTTVersion _version READ version WRITE setVersion)
|
||||
Q_PROPERTY(bool _autoReconnect READ autoReconnect WRITE setAutoReconnect)
|
||||
Q_PROPERTY(int _autoReconnectInterval READ autoReconnectInterval WRITE setAutoReconnectInterval)
|
||||
Q_PROPERTY(bool _cleanSession READ cleanSession WRITE setCleanSession)
|
||||
Q_PROPERTY(QString _willTopic READ willTopic WRITE setWillTopic)
|
||||
Q_PROPERTY(quint8 _willQos READ willQos WRITE setWillQos)
|
||||
Q_PROPERTY(bool _willRetain READ willRetain WRITE setWillRetain)
|
||||
Q_PROPERTY(QByteArray _willMessage READ willMessage WRITE setWillMessage)
|
||||
Q_PROPERTY(QString _connectionState READ connectionState)
|
||||
#ifndef QT_NO_SSL
|
||||
Q_PROPERTY(QSslConfiguration _sslConfiguration READ sslConfiguration WRITE setSslConfiguration)
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
public:
|
||||
Client(const QHostAddress& host = QHostAddress::LocalHost,
|
||||
const quint16 port = 1883,
|
||||
QObject* parent = NULL);
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
Client(const QString& hostName,
|
||||
const quint16 port,
|
||||
const QSslConfiguration& config,
|
||||
const bool ignoreSelfSigned=false,
|
||||
QObject* parent = NULL);
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
// This function is provided for backward compatibility with older versions of QMQTT.
|
||||
// If the ssl parameter is true, this function will load a private key ('cert.key') and a local
|
||||
// certificate ('cert.crt') from the current working directory. It will also set PeerVerifyMode
|
||||
// to None. This may not be the safest way to set up an SSL connection.
|
||||
Client(const QString& hostName,
|
||||
const quint16 port,
|
||||
const bool ssl,
|
||||
const bool ignoreSelfSigned,
|
||||
QObject* parent = NULL);
|
||||
|
||||
#ifdef QT_WEBSOCKETS_LIB
|
||||
// Create a connection over websockets
|
||||
Client(const QString& url,
|
||||
const QString& origin,
|
||||
QWebSocketProtocol::Version version,
|
||||
bool ignoreSelfSigned = false,
|
||||
QObject* parent = NULL);
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
Client(const QString& url,
|
||||
const QString& origin,
|
||||
QWebSocketProtocol::Version version,
|
||||
const QSslConfiguration& config,
|
||||
const bool ignoreSelfSigned = false,
|
||||
QObject* parent = NULL);
|
||||
#endif // QT_NO_SSL
|
||||
#endif // QT_WEBSOCKETS_LIB
|
||||
|
||||
// for testing purposes only
|
||||
Client(NetworkInterface* network,
|
||||
const QHostAddress& host = QHostAddress::LocalHost,
|
||||
const quint16 port = 1883,
|
||||
QObject* parent = NULL);
|
||||
|
||||
virtual ~Client();
|
||||
|
||||
QHostAddress host() const;
|
||||
QString hostName() const;
|
||||
quint16 port() const;
|
||||
QString clientId() const;
|
||||
QString username() const;
|
||||
QByteArray password() const;
|
||||
QMQTT::MQTTVersion version() const;
|
||||
quint16 keepAlive() const;
|
||||
bool cleanSession() const;
|
||||
bool autoReconnect() const;
|
||||
int autoReconnectInterval() const;
|
||||
ConnectionState connectionState() const;
|
||||
QString willTopic() const;
|
||||
quint8 willQos() const;
|
||||
bool willRetain() const;
|
||||
QByteArray willMessage() const;
|
||||
|
||||
bool isConnectedToHost() const;
|
||||
#ifndef QT_NO_SSL
|
||||
QSslConfiguration sslConfiguration() const;
|
||||
void setSslConfiguration(const QSslConfiguration& config);
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
public slots:
|
||||
void setHost(const QHostAddress& host);
|
||||
void setHostName(const QString& hostName);
|
||||
void setPort(const quint16 port);
|
||||
void setClientId(const QString& clientId);
|
||||
void setUsername(const QString& username);
|
||||
void setPassword(const QByteArray& password);
|
||||
void setVersion(const MQTTVersion version);
|
||||
void setKeepAlive(const quint16 keepAlive);
|
||||
void setCleanSession(const bool cleanSession);
|
||||
void setAutoReconnect(const bool value);
|
||||
void setAutoReconnectInterval(const int autoReconnectInterval);
|
||||
void setWillTopic(const QString& willTopic);
|
||||
void setWillQos(const quint8 willQos);
|
||||
void setWillRetain(const bool willRetain);
|
||||
void setWillMessage(const QByteArray& willMessage);
|
||||
|
||||
void connectToHost();
|
||||
void disconnectFromHost();
|
||||
|
||||
void subscribe(const QString& topic, const quint8 qos = 0);
|
||||
void unsubscribe(const QString& topic);
|
||||
|
||||
quint16 publish(const QMQTT::Message& message);
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
void ignoreSslErrors();
|
||||
void ignoreSslErrors(const QList<QSslError>& errors);
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
void disconnected();
|
||||
void error(const QMQTT::ClientError error);
|
||||
|
||||
void subscribed(const QString& topic, const quint8 qos = 0);
|
||||
void unsubscribed(const QString& topic);
|
||||
void published(const QMQTT::Message& message, quint16 msgid = 0);
|
||||
void received(const QMQTT::Message& message);
|
||||
void pingresp();
|
||||
#ifndef QT_NO_SSL
|
||||
void sslErrors(const QList<QSslError>& errors);
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
protected slots:
|
||||
void onNetworkConnected();
|
||||
void onNetworkDisconnected();
|
||||
void onNetworkReceived(const QMQTT::Frame& frame);
|
||||
void onTimerPingReq();
|
||||
void onPingTimeout();
|
||||
void onNetworkError(QAbstractSocket::SocketError error);
|
||||
#ifndef QT_NO_SSL
|
||||
void onSslErrors(const QList<QSslError>& errors);
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
protected:
|
||||
QScopedPointer<ClientPrivate> d_ptr;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(Client)
|
||||
Q_DECLARE_PRIVATE(Client)
|
||||
};
|
||||
|
||||
} // namespace QMQTT
|
||||
|
||||
Q_DECLARE_METATYPE(QMQTT::ClientError)
|
||||
|
||||
#endif // QMQTT_CLIENT_H
|
||||
@@ -0,0 +1,811 @@
|
||||
/*
|
||||
* qmqtt_client_p.cpp - qmqtt client private
|
||||
*
|
||||
* Copyright (c) 2013 Ery Lee <ery.lee at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of mqttc nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qmqtt_client_p.h"
|
||||
#include "qmqtt_network_p.h"
|
||||
#include "qmqtt_frame.h"
|
||||
#include "qmqtt_message.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <QUuid>
|
||||
#ifndef QT_NO_SSL
|
||||
#include <QFile>
|
||||
#include <QSslConfiguration>
|
||||
#include <QSslKey>
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
Q_LOGGING_CATEGORY(client, "qmqtt.client")
|
||||
|
||||
static const quint8 QOS0 = 0x00;
|
||||
static const quint8 QOS1 = 0x01;
|
||||
static const quint8 QOS2 = 0x02;
|
||||
|
||||
QMQTT::ClientPrivate::ClientPrivate(Client* qq_ptr)
|
||||
: _host(QHostAddress::LocalHost)
|
||||
, _port(1883)
|
||||
, _gmid(1)
|
||||
, _version(MQTTVersion::V3_1_0)
|
||||
, _clientId(QUuid::createUuid().toString())
|
||||
, _cleanSession(false)
|
||||
, _connectionState(STATE_INIT)
|
||||
, _willQos(0)
|
||||
, _willRetain(false)
|
||||
, q_ptr(qq_ptr)
|
||||
{
|
||||
setKeepAlive(300);
|
||||
}
|
||||
|
||||
QMQTT::ClientPrivate::~ClientPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::init(const QHostAddress& host, const quint16 port, NetworkInterface* network)
|
||||
{
|
||||
Q_Q(Client);
|
||||
_host = host;
|
||||
_port = port;
|
||||
if(network == NULL)
|
||||
{
|
||||
init(new Network(q));
|
||||
}
|
||||
else
|
||||
{
|
||||
init(network);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
void QMQTT::ClientPrivate::init(const QString& hostName, const quint16 port,
|
||||
const QSslConfiguration &config, const bool ignoreSelfSigned)
|
||||
{
|
||||
Q_Q(Client);
|
||||
_hostName = hostName;
|
||||
_port = port;
|
||||
_ignoreSelfSigned = ignoreSelfSigned;
|
||||
init(new Network(config, q));
|
||||
QObject::connect(_network.data(), &QMQTT::Network::sslErrors, q, &QMQTT::Client::onSslErrors);
|
||||
}
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
void QMQTT::ClientPrivate::init(const QString& hostName, const quint16 port, const bool ssl,
|
||||
const bool ignoreSelfSigned)
|
||||
{
|
||||
Q_Q(Client);
|
||||
_hostName = hostName;
|
||||
_port = port;
|
||||
if (ssl)
|
||||
{
|
||||
#ifndef QT_NO_SSL
|
||||
QSslConfiguration sslConf = QSslConfiguration::defaultConfiguration();
|
||||
QList<QSslCertificate> certs = QSslCertificate::fromPath(QStringLiteral("./cert.crt"));
|
||||
if (!certs.isEmpty())
|
||||
sslConf.setLocalCertificate(certs.first());
|
||||
QFile file(QStringLiteral("./cert.key"));
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
sslConf.setPrivateKey(QSslKey(file.readAll(), QSsl::Rsa));
|
||||
}
|
||||
sslConf.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||
init(hostName, port, sslConf, ignoreSelfSigned);
|
||||
#else
|
||||
Q_UNUSED(ignoreSelfSigned)
|
||||
qCritical() << "SSL not supported in this QT build";
|
||||
#endif // QT_NO_SSL
|
||||
}
|
||||
else
|
||||
{
|
||||
init(new Network(q));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef QT_WEBSOCKETS_LIB
|
||||
#ifndef QT_NO_SSL
|
||||
void QMQTT::ClientPrivate::init(const QString& url,
|
||||
const QString& origin,
|
||||
QWebSocketProtocol::Version version,
|
||||
const QSslConfiguration* sslConfig,
|
||||
bool ignoreSelfSigned)
|
||||
{
|
||||
Q_Q(Client);
|
||||
_hostName = url;
|
||||
_ignoreSelfSigned = ignoreSelfSigned;
|
||||
init(new Network(origin, version, sslConfig, q));
|
||||
}
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
void QMQTT::ClientPrivate::init(const QString& url,
|
||||
const QString& origin,
|
||||
QWebSocketProtocol::Version version)
|
||||
{
|
||||
Q_Q(Client);
|
||||
_hostName = url;
|
||||
init(new Network(origin, version, q));
|
||||
}
|
||||
#endif // QT_WEBSOCKETS_LIB
|
||||
|
||||
void QMQTT::ClientPrivate::init(NetworkInterface* network)
|
||||
{
|
||||
Q_Q(Client);
|
||||
|
||||
_network.reset(network);
|
||||
_timer.setSingleShot(true);
|
||||
_pingResponseTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(&_timer, &QTimer::timeout, q, &Client::onTimerPingReq);
|
||||
QObject::connect(&_pingResponseTimer, &QTimer::timeout, q, &Client::onPingTimeout);
|
||||
QObject::connect(_network.data(), &Network::connected,
|
||||
q, &Client::onNetworkConnected);
|
||||
QObject::connect(_network.data(), &Network::disconnected,
|
||||
q, &Client::onNetworkDisconnected);
|
||||
QObject::connect(_network.data(), &Network::received,
|
||||
q, &Client::onNetworkReceived);
|
||||
QObject::connect(_network.data(), &Network::error,
|
||||
q, &Client::onNetworkError);
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::connectToHost()
|
||||
{
|
||||
_connectionState = ConnectionState::STATE_CONNECTING;
|
||||
if (_hostName.isEmpty())
|
||||
{
|
||||
_network->connectToHost(_host, _port);
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->connectToHost(_hostName, _port);
|
||||
}
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::onNetworkConnected()
|
||||
{
|
||||
sendConnect();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::sendConnect()
|
||||
{
|
||||
quint8 header = CONNECT;
|
||||
quint8 flags = 0;
|
||||
|
||||
//header
|
||||
Frame frame(header);
|
||||
|
||||
//flags
|
||||
flags = FLAG_CLEANSESS(flags, _cleanSession ? 1 : 0 );
|
||||
flags = FLAG_WILL(flags, willTopic().isEmpty() ? 0 : 1);
|
||||
if (!willTopic().isEmpty())
|
||||
{
|
||||
flags = FLAG_WILLQOS(flags, willQos());
|
||||
flags = FLAG_WILLRETAIN(flags, willRetain() ? 1 : 0);
|
||||
}
|
||||
if (!username().isEmpty())
|
||||
{
|
||||
flags = FLAG_USERNAME(flags, 1);
|
||||
flags = FLAG_PASSWD(flags, !password().isEmpty() ? 1 : 0);
|
||||
}
|
||||
|
||||
//payload
|
||||
if(_version == V3_1_1)
|
||||
{
|
||||
frame.writeString(QStringLiteral(PROTOCOL_MAGIC_3_1_1));
|
||||
}
|
||||
else
|
||||
{
|
||||
frame.writeString(QStringLiteral(PROTOCOL_MAGIC_3_1_0));
|
||||
}
|
||||
frame.writeChar(_version);
|
||||
frame.writeChar(flags);
|
||||
frame.writeInt(keepAlive());
|
||||
frame.writeString(_clientId);
|
||||
if(!willTopic().isEmpty())
|
||||
{
|
||||
frame.writeString(willTopic());
|
||||
// According to the specs (http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718031)
|
||||
// the will message gets always sent together with the topic, also when it is empty which is perfectly valid.
|
||||
frame.writeByteArray(_willMessage);
|
||||
}
|
||||
if (!_username.isEmpty())
|
||||
{
|
||||
frame.writeString(_username);
|
||||
if (!_password.isEmpty())
|
||||
{
|
||||
frame.writeByteArray(_password);
|
||||
}
|
||||
}
|
||||
sendFrame(frame);
|
||||
}
|
||||
|
||||
quint16 QMQTT::ClientPrivate::sendPublish(const Message &message)
|
||||
{
|
||||
quint16 msgid = message.id();
|
||||
|
||||
quint8 header = PUBLISH;
|
||||
header = SETRETAIN(header, message.retain() ? 1 : 0);
|
||||
header = SETQOS(header, message.qos());
|
||||
header = SETDUP(header, message.dup() ? 1 : 0);
|
||||
Frame frame(header);
|
||||
frame.writeString(message.topic());
|
||||
if(message.qos() > QOS0) {
|
||||
if (msgid == 0)
|
||||
msgid = nextmid();
|
||||
frame.writeInt(msgid);
|
||||
}
|
||||
if(!message.payload().isEmpty()) {
|
||||
frame.writeRawData(message.payload());
|
||||
}
|
||||
sendFrame(frame);
|
||||
return msgid;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::sendPuback(const quint8 type, const quint16 mid)
|
||||
{
|
||||
Frame frame(type);
|
||||
frame.writeInt(mid);
|
||||
sendFrame(frame);
|
||||
}
|
||||
|
||||
quint16 QMQTT::ClientPrivate::sendSubscribe(const QString & topic, const quint8 qos)
|
||||
{
|
||||
quint16 mid = nextmid();
|
||||
Frame frame(SETQOS(SUBSCRIBE, QOS1));
|
||||
frame.writeInt(mid);
|
||||
frame.writeString(topic);
|
||||
frame.writeChar(qos);
|
||||
sendFrame(frame);
|
||||
return mid;
|
||||
}
|
||||
|
||||
quint16 QMQTT::ClientPrivate::sendUnsubscribe(const QString &topic)
|
||||
{
|
||||
quint16 mid = nextmid();
|
||||
Frame frame(SETQOS(UNSUBSCRIBE, QOS1));
|
||||
frame.writeInt(mid);
|
||||
frame.writeString(topic);
|
||||
sendFrame(frame);
|
||||
return mid;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::onTimerPingReq()
|
||||
{
|
||||
if (!isConnectedToHost())
|
||||
return;
|
||||
Frame frame(PINGREQ);
|
||||
sendFrame(frame);
|
||||
_pingResponseTimer.start();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::onPingTimeout()
|
||||
{
|
||||
Q_Q(Client);
|
||||
emit q->error(MqttNoPingResponse);
|
||||
disconnectFromHost();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::disconnectFromHost()
|
||||
{
|
||||
_connectionState = ConnectionState::STATE_DISCONNECTED;
|
||||
sendDisconnect();
|
||||
_network->disconnectFromHost();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::sendDisconnect()
|
||||
{
|
||||
Frame frame(DISCONNECT);
|
||||
sendFrame(frame);
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::sendFrame(const Frame &frame)
|
||||
{
|
||||
_network->sendFrame(frame);
|
||||
_timer.start();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::stopKeepAlive()
|
||||
{
|
||||
_timer.stop();
|
||||
_pingResponseTimer.stop();
|
||||
}
|
||||
|
||||
quint16 QMQTT::ClientPrivate::nextmid()
|
||||
{
|
||||
return _gmid++;
|
||||
}
|
||||
|
||||
quint16 QMQTT::ClientPrivate::publish(const Message& message)
|
||||
{
|
||||
Q_Q(Client);
|
||||
quint16 msgid = sendPublish(message);
|
||||
|
||||
// Emit published only at QOS0
|
||||
if (message.qos() == QOS0)
|
||||
emit q->published(message, msgid);
|
||||
else
|
||||
_midToMessage[msgid] = message;
|
||||
|
||||
return msgid;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::puback(const quint8 type, const quint16 msgid)
|
||||
{
|
||||
sendPuback(type, msgid);
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::subscribe(const QString& topic, const quint8 qos)
|
||||
{
|
||||
quint16 msgid = sendSubscribe(topic, qos);
|
||||
_midToTopic[msgid] = topic;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::unsubscribe(const QString& topic)
|
||||
{
|
||||
quint16 msgid = sendUnsubscribe(topic);
|
||||
_midToTopic[msgid] = topic;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::onNetworkDisconnected()
|
||||
{
|
||||
Q_Q(Client);
|
||||
|
||||
stopKeepAlive();
|
||||
_midToTopic.clear();
|
||||
_midToMessage.clear();
|
||||
emit q->disconnected();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::onNetworkReceived(const QMQTT::Frame& frm)
|
||||
{
|
||||
QMQTT::Frame frame(frm);
|
||||
quint8 qos = 0;
|
||||
bool retain, dup;
|
||||
QString topic;
|
||||
quint16 mid = 0;
|
||||
quint8 header = frame.header();
|
||||
quint8 type = GETTYPE(header);
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case CONNACK:
|
||||
frame.readChar();
|
||||
handleConnack(frame.readChar());
|
||||
break;
|
||||
case PUBLISH:
|
||||
qos = GETQOS(header);
|
||||
retain = GETRETAIN(header);
|
||||
dup = GETDUP(header);
|
||||
topic = frame.readString();
|
||||
if( qos > QOS0) {
|
||||
mid = frame.readInt();
|
||||
}
|
||||
handlePublish(Message(mid, topic, frame.data(), qos, retain, dup));
|
||||
break;
|
||||
case PUBACK:
|
||||
case PUBREC:
|
||||
case PUBREL:
|
||||
case PUBCOMP:
|
||||
mid = frame.readInt();
|
||||
handlePuback(type, mid);
|
||||
break;
|
||||
case SUBACK:
|
||||
mid = frame.readInt();
|
||||
topic = _midToTopic.take(mid);
|
||||
qos = frame.readChar();
|
||||
handleSuback(topic, qos);
|
||||
break;
|
||||
case UNSUBACK:
|
||||
mid = frame.readInt();
|
||||
topic = _midToTopic.take(mid);
|
||||
handleUnsuback(topic);
|
||||
break;
|
||||
case PINGRESP:
|
||||
handlePingresp();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::handleConnack(const quint8 ack)
|
||||
{
|
||||
Q_Q(Client);
|
||||
|
||||
switch (ack)
|
||||
{
|
||||
case 0:
|
||||
_connectionState = ConnectionState::STATE_CONNECTED;
|
||||
emit q->connected();
|
||||
break;
|
||||
case 1:
|
||||
emit q->error(MqttUnacceptableProtocolVersionError);
|
||||
break;
|
||||
case 2:
|
||||
emit q->error(MqttIdentifierRejectedError);
|
||||
break;
|
||||
case 3:
|
||||
emit q->error(MqttServerUnavailableError);
|
||||
break;
|
||||
case 4:
|
||||
emit q->error(MqttBadUserNameOrPasswordError);
|
||||
break;
|
||||
case 5:
|
||||
emit q->error(MqttNotAuthorizedError);
|
||||
break;
|
||||
default:
|
||||
emit q->error(UnknownError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::handlePublish(const Message& message)
|
||||
{
|
||||
Q_Q(Client);
|
||||
|
||||
if(message.qos() == QOS1)
|
||||
{
|
||||
sendPuback(PUBACK, message.id());
|
||||
}
|
||||
else if(message.qos() == QOS2)
|
||||
{
|
||||
sendPuback(PUBREC, message.id());
|
||||
}
|
||||
emit q->received(message);
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::handlePuback(const quint8 type, const quint16 msgid)
|
||||
{
|
||||
Q_Q(Client);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case PUBREC:
|
||||
sendPuback(SETQOS(PUBREL, QOS1), msgid);
|
||||
break;
|
||||
case PUBREL:
|
||||
sendPuback(PUBCOMP, msgid);
|
||||
break;
|
||||
case PUBACK:
|
||||
case PUBCOMP:
|
||||
// Emit published on PUBACK at QOS1 and on PUBCOMP at QOS2
|
||||
emit q->published(_midToMessage.take(msgid), msgid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::handlePingresp()
|
||||
{
|
||||
// Stop the ping response timer to prevent disconnection. It will be restarted when the next
|
||||
// ping request has been sent.
|
||||
_pingResponseTimer.stop();
|
||||
Q_Q(Client);
|
||||
emit q->pingresp();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::handleSuback(const QString &topic, const quint8 qos)
|
||||
{
|
||||
Q_Q(Client);
|
||||
emit q->subscribed(topic, qos);
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::handleUnsuback(const QString &topic) {
|
||||
Q_Q(Client);
|
||||
emit q->unsubscribed(topic);
|
||||
}
|
||||
|
||||
bool QMQTT::ClientPrivate::autoReconnect() const
|
||||
{
|
||||
return _network->autoReconnect();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setAutoReconnect(const bool autoReconnect)
|
||||
{
|
||||
_network->setAutoReconnect(autoReconnect);
|
||||
}
|
||||
|
||||
int QMQTT::ClientPrivate::autoReconnectInterval() const
|
||||
{
|
||||
return _network->autoReconnectInterval();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setAutoReconnectInterval(const int autoReconnectInterval)
|
||||
{
|
||||
_network->setAutoReconnectInterval(autoReconnectInterval);
|
||||
}
|
||||
|
||||
bool QMQTT::ClientPrivate::isConnectedToHost() const
|
||||
{
|
||||
return _network->isConnectedToHost();
|
||||
}
|
||||
|
||||
QMQTT::ConnectionState QMQTT::ClientPrivate::connectionState() const
|
||||
{
|
||||
return _connectionState;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setCleanSession(const bool cleanSession)
|
||||
{
|
||||
_cleanSession = cleanSession;
|
||||
}
|
||||
|
||||
bool QMQTT::ClientPrivate::cleanSession() const
|
||||
{
|
||||
return _cleanSession;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setKeepAlive(const quint16 keepAlive)
|
||||
{
|
||||
// _timer will be started when a message is sent.
|
||||
_timer.setInterval(keepAlive*1000);
|
||||
// The MQTT specification does not mention a timeout value in this case, so we use 10% of the
|
||||
// keep alive interval.
|
||||
_pingResponseTimer.setInterval(qBound(keepAlive * 100, 10000, keepAlive * 1000));
|
||||
}
|
||||
|
||||
quint16 QMQTT::ClientPrivate::keepAlive() const
|
||||
{
|
||||
return _timer.interval() / 1000;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setPassword(const QByteArray& password)
|
||||
{
|
||||
_password = password;
|
||||
}
|
||||
|
||||
QByteArray QMQTT::ClientPrivate::password() const
|
||||
{
|
||||
return _password;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setUsername(const QString& username)
|
||||
{
|
||||
_username = username;
|
||||
}
|
||||
|
||||
QString QMQTT::ClientPrivate::username() const
|
||||
{
|
||||
return _username;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setVersion(const MQTTVersion version)
|
||||
{
|
||||
_version = version;
|
||||
}
|
||||
|
||||
QMQTT::MQTTVersion QMQTT::ClientPrivate::version() const
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setClientId(const QString& clientId)
|
||||
{
|
||||
if(clientId.isEmpty())
|
||||
{
|
||||
_clientId = QUuid::createUuid().toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
_clientId = clientId;
|
||||
}
|
||||
}
|
||||
|
||||
QString QMQTT::ClientPrivate::clientId() const
|
||||
{
|
||||
return _clientId;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setPort(const quint16 port)
|
||||
{
|
||||
_port = port;
|
||||
}
|
||||
|
||||
quint16 QMQTT::ClientPrivate::port() const
|
||||
{
|
||||
return _port;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setHost(const QHostAddress& host)
|
||||
{
|
||||
_host = host;
|
||||
}
|
||||
|
||||
QHostAddress QMQTT::ClientPrivate::host() const
|
||||
{
|
||||
return _host;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setHostName(const QString& hostName)
|
||||
{
|
||||
_hostName = hostName;
|
||||
}
|
||||
|
||||
QString QMQTT::ClientPrivate::hostName() const
|
||||
{
|
||||
return _hostName;
|
||||
}
|
||||
|
||||
QString QMQTT::ClientPrivate::willTopic() const
|
||||
{
|
||||
return _willTopic;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setWillTopic(const QString& willTopic)
|
||||
{
|
||||
_willTopic = willTopic;
|
||||
}
|
||||
|
||||
quint8 QMQTT::ClientPrivate::willQos() const
|
||||
{
|
||||
return _willQos;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setWillQos(const quint8 willQos)
|
||||
{
|
||||
_willQos = willQos;
|
||||
}
|
||||
|
||||
bool QMQTT::ClientPrivate::willRetain() const
|
||||
{
|
||||
return _willRetain;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setWillRetain(const bool willRetain)
|
||||
{
|
||||
_willRetain = willRetain;
|
||||
}
|
||||
|
||||
QByteArray QMQTT::ClientPrivate::willMessage() const
|
||||
{
|
||||
return _willMessage;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setWillMessage(const QByteArray& willMessage)
|
||||
{
|
||||
_willMessage = willMessage;
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::onNetworkError(QAbstractSocket::SocketError socketError)
|
||||
{
|
||||
Q_Q(Client);
|
||||
|
||||
switch (socketError)
|
||||
{
|
||||
case QAbstractSocket::ConnectionRefusedError:
|
||||
emit q->error(SocketConnectionRefusedError);
|
||||
break;
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
emit q->error(SocketRemoteHostClosedError);
|
||||
break;
|
||||
case QAbstractSocket::HostNotFoundError:
|
||||
emit q->error(SocketHostNotFoundError);
|
||||
break;
|
||||
case QAbstractSocket::SocketAccessError:
|
||||
emit q->error(SocketAccessError);
|
||||
break;
|
||||
case QAbstractSocket::SocketResourceError:
|
||||
emit q->error(SocketResourceError);
|
||||
break;
|
||||
case QAbstractSocket::SocketTimeoutError:
|
||||
emit q->error(SocketTimeoutError);
|
||||
break;
|
||||
case QAbstractSocket::DatagramTooLargeError:
|
||||
emit q->error(SocketDatagramTooLargeError);
|
||||
break;
|
||||
case QAbstractSocket::NetworkError:
|
||||
emit q->error(SocketNetworkError);
|
||||
break;
|
||||
case QAbstractSocket::AddressInUseError:
|
||||
emit q->error(SocketAddressInUseError);
|
||||
break;
|
||||
case QAbstractSocket::SocketAddressNotAvailableError:
|
||||
emit q->error(SocketAddressNotAvailableError);
|
||||
break;
|
||||
case QAbstractSocket::UnsupportedSocketOperationError:
|
||||
emit q->error(SocketUnsupportedSocketOperationError);
|
||||
break;
|
||||
case QAbstractSocket::UnfinishedSocketOperationError:
|
||||
emit q->error(SocketUnfinishedSocketOperationError);
|
||||
break;
|
||||
case QAbstractSocket::ProxyAuthenticationRequiredError:
|
||||
emit q->error(SocketProxyAuthenticationRequiredError);
|
||||
break;
|
||||
case QAbstractSocket::SslHandshakeFailedError:
|
||||
emit q->error(SocketSslHandshakeFailedError);
|
||||
break;
|
||||
case QAbstractSocket::ProxyConnectionRefusedError:
|
||||
emit q->error(SocketProxyConnectionRefusedError);
|
||||
break;
|
||||
case QAbstractSocket::ProxyConnectionClosedError:
|
||||
emit q->error(SocketProxyConnectionClosedError);
|
||||
break;
|
||||
case QAbstractSocket::ProxyConnectionTimeoutError:
|
||||
emit q->error(SocketProxyConnectionTimeoutError);
|
||||
break;
|
||||
case QAbstractSocket::ProxyNotFoundError:
|
||||
emit q->error(SocketProxyNotFoundError);
|
||||
break;
|
||||
case QAbstractSocket::ProxyProtocolError:
|
||||
emit q->error(SocketProxyProtocolError);
|
||||
break;
|
||||
case QAbstractSocket::OperationError:
|
||||
emit q->error(SocketOperationError);
|
||||
break;
|
||||
case QAbstractSocket::SslInternalError:
|
||||
emit q->error(SocketSslInternalError);
|
||||
break;
|
||||
case QAbstractSocket::SslInvalidUserDataError:
|
||||
emit q->error(SocketSslInvalidUserDataError);
|
||||
break;
|
||||
case QAbstractSocket::TemporaryError:
|
||||
emit q->error(SocketTemporaryError);
|
||||
break;
|
||||
default:
|
||||
emit q->error(UnknownError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
void QMQTT::ClientPrivate::ignoreSslErrors()
|
||||
{
|
||||
_network->ignoreSslErrors();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::ignoreSslErrors(const QList<QSslError>& errors)
|
||||
{
|
||||
_network->ignoreSslErrors(errors);
|
||||
}
|
||||
|
||||
QSslConfiguration QMQTT::ClientPrivate::sslConfiguration() const
|
||||
{
|
||||
return _network->sslConfiguration();
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::setSslConfiguration(const QSslConfiguration& config)
|
||||
{
|
||||
_network->setSslConfiguration(config);
|
||||
}
|
||||
|
||||
void QMQTT::ClientPrivate::onSslErrors(const QList<QSslError>& errors)
|
||||
{
|
||||
Q_Q(Client);
|
||||
|
||||
emit q->sslErrors(errors);
|
||||
|
||||
if (!_ignoreSelfSigned)
|
||||
return;
|
||||
foreach (QSslError error, errors)
|
||||
{
|
||||
if (error.error() != QSslError::SelfSignedCertificate &&
|
||||
error.error() != QSslError::SelfSignedCertificateInChain)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
ignoreSslErrors();
|
||||
}
|
||||
#endif // QT_NO_SSL
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* qmqtt_client_p.h - qmqtt client private header
|
||||
*
|
||||
* Copyright (c) 2013 Ery Lee <ery.lee at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of mqttc nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#ifndef QMQTT_CLIENT_P_H
|
||||
#define QMQTT_CLIENT_P_H
|
||||
|
||||
#include "qmqtt_client.h"
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QTimer>
|
||||
#include <QAbstractSocket>
|
||||
|
||||
#ifdef QT_WEBSOCKETS_LIB
|
||||
#include <QWebSocket>
|
||||
#endif // QT_WEBSOCKETS_LIB
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
#include <QSslConfiguration>
|
||||
QT_FORWARD_DECLARE_CLASS(QSslError)
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
namespace QMQTT {
|
||||
|
||||
class NetworkInterface;
|
||||
|
||||
class ClientPrivate
|
||||
{
|
||||
public:
|
||||
ClientPrivate(Client* qq_ptr);
|
||||
~ClientPrivate();
|
||||
|
||||
void init(const QHostAddress& host, const quint16 port, NetworkInterface* network = NULL);
|
||||
#ifndef QT_NO_SSL
|
||||
void init(const QString& hostName, const quint16 port, const QSslConfiguration& config,
|
||||
const bool ignoreSelfSigned=false);
|
||||
#endif // QT_NO_SSL
|
||||
void init(const QString& hostName, const quint16 port, const bool ssl, const bool ignoreSelfSigned);
|
||||
#ifdef QT_WEBSOCKETS_LIB
|
||||
#ifndef QT_NO_SSL
|
||||
void init(const QString& url,
|
||||
const QString& origin,
|
||||
QWebSocketProtocol::Version version,
|
||||
const QSslConfiguration* sslConfig,
|
||||
bool ignoreSelfSigned);
|
||||
#endif // QT_NO_SSL
|
||||
void init(const QString& url,
|
||||
const QString& origin,
|
||||
QWebSocketProtocol::Version version);
|
||||
#endif // QT_WEBSOCKETS_LIB
|
||||
void init(NetworkInterface* network);
|
||||
|
||||
QHostAddress _host;
|
||||
QString _hostName;
|
||||
quint16 _port;
|
||||
#ifdef QT_WEBSOCKETS_LIB
|
||||
QWebSocketProtocol::Version _webSocketVersion;
|
||||
#endif // QT_WEBSOCKETS_LIB
|
||||
#ifndef QT_NO_SSL
|
||||
bool _ignoreSelfSigned;
|
||||
#endif // QT_NO_SSL
|
||||
quint16 _gmid;
|
||||
MQTTVersion _version;
|
||||
QString _clientId;
|
||||
QString _username;
|
||||
QByteArray _password;
|
||||
bool _cleanSession;
|
||||
ConnectionState _connectionState;
|
||||
QScopedPointer<NetworkInterface> _network;
|
||||
QTimer _timer;
|
||||
QTimer _pingResponseTimer;
|
||||
QString _willTopic;
|
||||
quint8 _willQos;
|
||||
bool _willRetain;
|
||||
QByteArray _willMessage;
|
||||
QHash<quint16, QString> _midToTopic;
|
||||
QHash<quint16, Message> _midToMessage;
|
||||
|
||||
Client* const q_ptr;
|
||||
|
||||
quint16 nextmid();
|
||||
void connectToHost();
|
||||
void sendConnect();
|
||||
void onTimerPingReq();
|
||||
void onPingTimeout();
|
||||
quint16 sendUnsubscribe(const QString &topic);
|
||||
quint16 sendSubscribe(const QString &topic, const quint8 qos);
|
||||
quint16 sendPublish(const Message &message);
|
||||
void sendPuback(const quint8 type, const quint16 mid);
|
||||
void sendDisconnect();
|
||||
void sendFrame(const Frame &frame);
|
||||
void disconnectFromHost();
|
||||
void stopKeepAlive();
|
||||
void onNetworkConnected();
|
||||
void onNetworkDisconnected();
|
||||
quint16 publish(const Message& message);
|
||||
void puback(const quint8 type, const quint16 msgid);
|
||||
void subscribe(const QString& topic, const quint8 qos);
|
||||
void unsubscribe(const QString& topic);
|
||||
void onNetworkReceived(const QMQTT::Frame& frame);
|
||||
void handleConnack(const quint8 ack);
|
||||
void handlePublish(const Message& message);
|
||||
void handlePuback(const quint8 type, const quint16 msgid);
|
||||
void handleSuback(const QString& topic, const quint8 qos);
|
||||
void handleUnsuback(const QString& topic);
|
||||
void handlePingresp();
|
||||
bool autoReconnect() const;
|
||||
void setAutoReconnect(const bool autoReconnect);
|
||||
int autoReconnectInterval() const;
|
||||
void setAutoReconnectInterval(const int autoReconnectInterval);
|
||||
bool isConnectedToHost() const;
|
||||
QMQTT::ConnectionState connectionState() const;
|
||||
void setCleanSession(const bool cleanSession);
|
||||
bool cleanSession() const;
|
||||
void setKeepAlive(const quint16 keepAlive);
|
||||
quint16 keepAlive() const;
|
||||
void setPassword(const QByteArray& password);
|
||||
QByteArray password() const;
|
||||
void setUsername(const QString& username);
|
||||
QString username() const;
|
||||
void setVersion(const MQTTVersion);
|
||||
MQTTVersion version() const;
|
||||
void setClientId(const QString& clientId);
|
||||
QString clientId() const;
|
||||
void setPort(const quint16 port);
|
||||
quint16 port() const;
|
||||
void setHost(const QHostAddress& host);
|
||||
QHostAddress host() const;
|
||||
void setHostName(const QString& hostName);
|
||||
QString hostName() const;
|
||||
void setWillTopic(const QString& willTopic);
|
||||
void setWillQos(const quint8 willQos);
|
||||
void setWillRetain(const bool willRetain);
|
||||
void setWillMessage(const QByteArray& willMessage);
|
||||
QString willTopic() const;
|
||||
quint8 willQos() const;
|
||||
bool willRetain() const;
|
||||
QByteArray willMessage() const;
|
||||
void onNetworkError(QAbstractSocket::SocketError error);
|
||||
#ifndef QT_NO_SSL
|
||||
void ignoreSslErrors();
|
||||
void ignoreSslErrors(const QList<QSslError>& errors);
|
||||
QSslConfiguration sslConfiguration() const;
|
||||
void setSslConfiguration(const QSslConfiguration& config);
|
||||
void onSslErrors(const QList<QSslError>& errors);
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
Q_DECLARE_PUBLIC(Client)
|
||||
};
|
||||
|
||||
} // namespace QMQTT
|
||||
|
||||
#endif // QMQTT_CLIENT_P_H
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* qmqtt_frame.cpp - qmqtt frame
|
||||
*
|
||||
* Copyright (c) 2013 Ery Lee <ery.lee at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of mqttc nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qmqtt_frame.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <QDataStream>
|
||||
|
||||
namespace QMQTT {
|
||||
|
||||
Q_LOGGING_CATEGORY(frame, "qmqtt.frame")
|
||||
|
||||
Frame::Frame()
|
||||
: _header(0)
|
||||
, _data(QByteArray())
|
||||
{
|
||||
}
|
||||
|
||||
Frame::Frame(const quint8 header)
|
||||
: _header(header)
|
||||
, _data(QByteArray())
|
||||
{
|
||||
}
|
||||
|
||||
Frame::Frame(const quint8 header, const QByteArray &data)
|
||||
: _header(header)
|
||||
, _data(data)
|
||||
{
|
||||
}
|
||||
|
||||
Frame::Frame(const Frame& other)
|
||||
{
|
||||
_header = other._header;
|
||||
_data = other._data;
|
||||
}
|
||||
|
||||
Frame& Frame::operator=(const Frame& other)
|
||||
{
|
||||
_header = other._header;
|
||||
_data = other._data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Frame::operator==(const Frame& other) const
|
||||
{
|
||||
return _header == other._header
|
||||
&& _data == other._data;
|
||||
}
|
||||
|
||||
|
||||
Frame::~Frame()
|
||||
{
|
||||
}
|
||||
|
||||
quint8 Frame::header() const
|
||||
{
|
||||
return _header;
|
||||
}
|
||||
|
||||
QByteArray Frame::data() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
quint8 Frame::readChar()
|
||||
{
|
||||
char c = _data.at(0);
|
||||
_data.remove(0, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
quint16 Frame::readInt()
|
||||
{
|
||||
quint8 msb = static_cast<quint8>(_data.at(0));
|
||||
quint8 lsb = static_cast<quint8>(_data.at(1));
|
||||
_data.remove(0, 2);
|
||||
return (msb << 8) | lsb;
|
||||
}
|
||||
|
||||
QByteArray Frame::readByteArray()
|
||||
{
|
||||
quint16 len = readInt();
|
||||
QByteArray data = _data.left(len);
|
||||
_data.remove(0, len);
|
||||
return data;
|
||||
}
|
||||
|
||||
QString Frame::readString()
|
||||
{
|
||||
quint16 len = readInt();
|
||||
QString s = QString::fromUtf8(_data.left(len));
|
||||
_data.remove(0, len);
|
||||
return s;
|
||||
}
|
||||
|
||||
void Frame::writeInt(const quint16 i)
|
||||
{
|
||||
_data.append(MSB(i));
|
||||
_data.append(LSB(i));
|
||||
}
|
||||
|
||||
void Frame::writeByteArray(const QByteArray &data)
|
||||
{
|
||||
if (data.size() > (int)USHRT_MAX)
|
||||
{
|
||||
qCritical("qmqtt: Binary data size bigger than %u bytes, truncate it!", USHRT_MAX);
|
||||
writeInt(USHRT_MAX);
|
||||
_data.append(data.left(USHRT_MAX));
|
||||
return;
|
||||
}
|
||||
|
||||
writeInt(data.size());
|
||||
_data.append(data);
|
||||
}
|
||||
|
||||
void Frame::writeString(const QString &string)
|
||||
{
|
||||
QByteArray data = string.toUtf8();
|
||||
if (data.size() > (int)USHRT_MAX)
|
||||
{
|
||||
qCritical("qmqtt: String size bigger than %u bytes, truncate it!", USHRT_MAX);
|
||||
data.resize(USHRT_MAX);
|
||||
}
|
||||
writeInt(data.size());
|
||||
_data.append(data);
|
||||
}
|
||||
|
||||
void Frame::writeChar(const quint8 c)
|
||||
{
|
||||
_data.append(c);
|
||||
}
|
||||
|
||||
void Frame::writeRawData(const QByteArray &data)
|
||||
{
|
||||
_data.append(data);
|
||||
}
|
||||
|
||||
void Frame::write(QDataStream &stream) const
|
||||
{
|
||||
QByteArray lenbuf;
|
||||
|
||||
if (!encodeLength(lenbuf, _data.size()))
|
||||
{
|
||||
qCritical("qmqtt: Control packet bigger than 256 MB, dropped!");
|
||||
return;
|
||||
}
|
||||
|
||||
stream << (quint8)_header;
|
||||
if(_data.size() == 0) {
|
||||
stream << (quint8)0;
|
||||
return;
|
||||
}
|
||||
if (stream.writeRawData(lenbuf.data(), lenbuf.size()) != lenbuf.size())
|
||||
{
|
||||
qCritical("qmqtt: Control packet write error!");
|
||||
return;
|
||||
}
|
||||
if (stream.writeRawData(_data.data(), _data.size()) != _data.size())
|
||||
{
|
||||
qCritical("qmqtt: Control packet write error!");
|
||||
}
|
||||
}
|
||||
|
||||
bool Frame::encodeLength(QByteArray &lenbuf, int length) const
|
||||
{
|
||||
lenbuf.clear();
|
||||
quint8 d;
|
||||
do {
|
||||
d = length % 128;
|
||||
length /= 128;
|
||||
if (length > 0) {
|
||||
d |= 0x80;
|
||||
}
|
||||
lenbuf.append(d);
|
||||
} while (length > 0);
|
||||
|
||||
return lenbuf.size() <= 4;
|
||||
}
|
||||
|
||||
} // namespace QMQTT
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* qmqtt_frame.h - qmqtt frame heaer
|
||||
*
|
||||
* Copyright (c) 2013 Ery Lee <ery.lee at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of mqttc nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#ifndef QMQTT_FRAME_H
|
||||
#define QMQTT_FRAME_H
|
||||
|
||||
#include "qmqtt_global.h"
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QDataStream)
|
||||
|
||||
#define PROTOCOL_MAGIC_3_1_0 "MQIsdp"
|
||||
#define PROTOCOL_MAGIC_3_1_1 "MQTT"
|
||||
|
||||
#define RANDOM_CLIENT_PREFIX "QMQTT-"
|
||||
|
||||
#define CONNECT 0x10
|
||||
#define CONNACK 0x20
|
||||
#define PUBLISH 0x30
|
||||
#define PUBACK 0x40
|
||||
#define PUBREC 0x50
|
||||
#define PUBREL 0x60
|
||||
#define PUBCOMP 0x70
|
||||
#define SUBSCRIBE 0x80
|
||||
#define SUBACK 0x90
|
||||
#define UNSUBSCRIBE 0xA0
|
||||
#define UNSUBACK 0xB0
|
||||
#define PINGREQ 0xC0
|
||||
#define PINGRESP 0xD0
|
||||
#define DISCONNECT 0xE0
|
||||
|
||||
#define LSB(A) quint8(A & 0x00FF)
|
||||
#define MSB(A) quint8((A & 0xFF00) >> 8)
|
||||
|
||||
/*
|
||||
|--------------------------------------
|
||||
| 7 6 5 4 | 3 | 2 1 | 0 |
|
||||
| Type | DUP flag | QoS | RETAIN |
|
||||
|--------------------------------------
|
||||
*/
|
||||
#define GETTYPE(HDR) (HDR & 0xF0)
|
||||
#define SETQOS(HDR, Q) (HDR | ((Q) << 1))
|
||||
#define GETQOS(HDR) ((HDR & 0x06) >> 1)
|
||||
#define SETDUP(HDR, D) (HDR | ((D) << 3))
|
||||
#define GETDUP(HDR) ((HDR & 0x08) >> 3)
|
||||
#define SETRETAIN(HDR, R) (HDR | (R))
|
||||
#define GETRETAIN(HDR) (HDR & 0x01)
|
||||
|
||||
/*
|
||||
|----------------------------------------------------------------------------------
|
||||
| 7 | 6 | 5 | 4 3 | 2 | 1 | 0 |
|
||||
| username | password | willretain | willqos | willflag | cleansession | reserved |
|
||||
|----------------------------------------------------------------------------------
|
||||
*/
|
||||
#define FLAG_CLEANSESS(F, C) (F | ((C) << 1))
|
||||
#define FLAG_WILL(F, W) (F | ((W) << 2))
|
||||
#define FLAG_WILLQOS(F, Q) (F | ((Q) << 3))
|
||||
#define FLAG_WILLRETAIN(F, R) (F | ((R) << 5))
|
||||
#define FLAG_PASSWD(F, P) (F | ((P) << 6))
|
||||
#define FLAG_USERNAME(F, U) (F | ((U) << 7))
|
||||
|
||||
namespace QMQTT {
|
||||
|
||||
class Q_MQTT_EXPORT Frame
|
||||
{
|
||||
public:
|
||||
explicit Frame();
|
||||
explicit Frame(const quint8 header);
|
||||
explicit Frame(const quint8 header, const QByteArray &data);
|
||||
virtual ~Frame();
|
||||
|
||||
Frame(const Frame& other);
|
||||
Frame& operator=(const Frame& other);
|
||||
|
||||
bool operator==(const Frame& other) const;
|
||||
inline bool operator!=(const Frame& other) const
|
||||
{ return !operator==(other); }
|
||||
|
||||
quint8 header() const;
|
||||
QByteArray data() const;
|
||||
|
||||
quint16 readInt();
|
||||
quint8 readChar();
|
||||
QByteArray readByteArray();
|
||||
QString readString();
|
||||
|
||||
void writeInt(const quint16 i);
|
||||
void writeChar(const quint8 c);
|
||||
void writeByteArray(const QByteArray &data);
|
||||
void writeString(const QString &string);
|
||||
void writeRawData(const QByteArray &data);
|
||||
|
||||
//TODO: FIXME LATER
|
||||
void write(QDataStream &stream) const;
|
||||
bool encodeLength(QByteArray &lenbuf, int length) const;
|
||||
|
||||
private:
|
||||
quint8 _header;
|
||||
QByteArray _data;
|
||||
};
|
||||
|
||||
} // namespace QMQTT
|
||||
|
||||
Q_DECLARE_METATYPE(QMQTT::Frame)
|
||||
|
||||
#endif // QMQTT_FRAME_H
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* qmqtt_global.h - qmqtt libray global
|
||||
*
|
||||
* Copyright (c) 2013 Ery Lee <ery.lee at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of mqttc nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#ifndef QMQTT_GLOBAL_H
|
||||
#define QMQTT_GLOBAL_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
//#ifndef QT_STATIC
|
||||
//# if defined(QT_BUILD_QMQTT_LIB)
|
||||
//# define Q_MQTT_EXPORT Q_DECL_EXPORT
|
||||
//# else
|
||||
//# define Q_MQTT_EXPORT Q_DECL_IMPORT
|
||||
//# endif
|
||||
//#else
|
||||
#define Q_MQTT_EXPORT
|
||||
//#endif
|
||||
|
||||
#endif // QMQTT_GLOBAL_H
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* qmqtt_message.cpp - qmqtt message
|
||||
*
|
||||
* Copyright (c) 2013 Ery Lee <ery.lee at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of mqttc nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qmqtt_message.h"
|
||||
#include "qmqtt_message_p.h"
|
||||
|
||||
namespace QMQTT {
|
||||
|
||||
Message::Message()
|
||||
: d(new MessagePrivate)
|
||||
{
|
||||
}
|
||||
|
||||
Message::Message(const quint16 id, const QString &topic, const QByteArray &payload,
|
||||
const quint8 qos, const bool retain, const bool dup)
|
||||
: d(new MessagePrivate(id, topic, payload, qos, retain, dup))
|
||||
{
|
||||
}
|
||||
|
||||
Message::Message(const Message &other)
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
Message::~Message()
|
||||
{
|
||||
}
|
||||
|
||||
Message &Message::operator=(const Message &other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Message::operator==(const Message &other) const
|
||||
{
|
||||
if (d == other.d)
|
||||
return true;
|
||||
return d->id == other.d->id
|
||||
&& d->qos == other.d->qos
|
||||
&& d->retain == other.d->retain
|
||||
&& d->dup == other.d->dup
|
||||
&& d->topic == other.d->topic
|
||||
&& d->payload == other.d->payload;
|
||||
}
|
||||
|
||||
quint16 Message::id() const
|
||||
{
|
||||
return d->id;
|
||||
}
|
||||
|
||||
void Message::setId(const quint16 id)
|
||||
{
|
||||
d->id = id;
|
||||
}
|
||||
|
||||
quint8 Message::qos() const
|
||||
{
|
||||
return d->qos;
|
||||
}
|
||||
|
||||
void Message::setQos(const quint8 qos)
|
||||
{
|
||||
d->qos = qos;
|
||||
}
|
||||
|
||||
bool Message::retain() const
|
||||
{
|
||||
return d->retain;
|
||||
}
|
||||
|
||||
void Message::setRetain(const bool retain)
|
||||
{
|
||||
d->retain = retain;
|
||||
}
|
||||
|
||||
bool Message::dup() const
|
||||
{
|
||||
return d->dup;
|
||||
}
|
||||
|
||||
void Message::setDup(const bool dup)
|
||||
{
|
||||
d->dup = dup;
|
||||
}
|
||||
|
||||
QString Message::topic() const
|
||||
{
|
||||
return d->topic;
|
||||
}
|
||||
|
||||
void Message::setTopic(const QString &topic)
|
||||
{
|
||||
d->topic = topic;
|
||||
}
|
||||
|
||||
QByteArray Message::payload() const
|
||||
{
|
||||
return d->payload;
|
||||
}
|
||||
|
||||
void Message::setPayload(const QByteArray &payload)
|
||||
{
|
||||
d->payload = payload;
|
||||
}
|
||||
|
||||
} // namespace QMQTT
|
||||