Changed the UDS Scan window quite a bit. Now there are more options

for scan types and the ability to create a list of scans to run.
This commit is contained in:
Collin Kidder
2022-10-04 19:54:55 -04:00
parent 9ebbaca668
commit 61b49c9126
3 changed files with 1010 additions and 475 deletions
+541 -150
View File
@@ -6,6 +6,23 @@
#include "utility.h"
#include "helpwindow.h"
static QVector<QString> SCANTYPE_NAMES = {
QString("Tester Present"),
QString("Session Control"),
QString("Communication Control"),
QString("ECU Reset"),
QString("Clear DTCs"),
QString("Read DTCs"),
QString("Security Access"),
QString("Read By ID"),
QString("Read By Address"),
QString("Read Scaling Data By ID"),
QString("IO Control"),
QString("Routine Control"),
QString("Custom UDS"),
};
UDSScanWindow::UDSScanWindow(const QVector<CANFrame> *frames, QWidget *parent) :
QDialog(parent),
ui(new Ui::UDSScanWindow)
@@ -21,15 +38,23 @@ UDSScanWindow::UDSScanWindow(const QVector<CANFrame> *frames, QWidget *parent) :
waitTimer->setInterval(100);
udsHandler = new UDS_HANDLER;
inhibitUpdates = false;
for (int i = 0; i < 13; i++) ui->cbScanType->addItem(SCANTYPE_NAMES[i]);
ui->cbSessType->addItem("No Change");
ui->cbSessType->addItem("Default");
ui->cbSessType->addItem("Programming");
ui->cbSessType->addItem("Extended Diag");
ui->cbSessType->addItem("Safety Sys Diag");
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
connect(udsHandler, &UDS_HANDLER::newUDSMessage, this, &UDSScanWindow::gotUDSReply);
connect(ui->btnScan, &QPushButton::clicked, this, &UDSScanWindow::scanUDS);
connect(ui->btnScanAll, &QPushButton::clicked, this, &UDSScanWindow::scanAll);
connect(ui->btnScanSelected, &QPushButton::clicked, this, &UDSScanWindow::scanSelected);
connect(waitTimer, &QTimer::timeout, this, &UDSScanWindow::timeOut);
connect(ui->btnSaveResults, &QPushButton::clicked, this, &UDSScanWindow::saveResults);
connect(ui->ckWildcard, &QCheckBox::toggled, this, &UDSScanWindow::wildcardToggled);
connect(ui->ckReadByAddr, &QCheckBox::toggled, this, &UDSScanWindow::readByToggled);
connect(ui->ckReadByID, &QCheckBox::toggled, this, &UDSScanWindow::readByToggled);
connect(ui->cbScanType, &QComboBox::currentTextChanged, this, &UDSScanWindow::changedScanType);
connect(ui->cbAllowAdaptiveOffset, &QCheckBox::toggled, this, &UDSScanWindow::adaptiveToggled);
connect(ui->spinNumBytes, SIGNAL(valueChanged(int)), this, SLOT(numBytesChanged()));
connect(ui->spinLowerService, SIGNAL(valueChanged(int)), this, SLOT(checkServiceRange()));
@@ -38,10 +63,23 @@ UDSScanWindow::UDSScanWindow(const QVector<CANFrame> *frames, QWidget *parent) :
connect(ui->spinUpperSubfunc, SIGNAL(valueChanged(int)), this, SLOT(checkSubFuncRange()));
connect(ui->spinStartID, SIGNAL(valueChanged(int)), this, SLOT(checkIDRange()));
connect(ui->spinEndID, SIGNAL(valueChanged(int)), this, SLOT(checkIDRange()));
connect(ui->btnAdd, &QPushButton::clicked, this, &UDSScanWindow::addNewScan);
connect(ui->btnDelete, &QPushButton::clicked, this, &UDSScanWindow::deleteSelectedScan);
connect(ui->btnSave, &QPushButton::clicked, this, &UDSScanWindow::saveScans);
connect(ui->btnLoad, &QPushButton::clicked, this, &UDSScanWindow::loadScans);
connect(ui->listScansToRun, &QListWidget::currentRowChanged, this, &UDSScanWindow::displayScanEntry);
connect(ui->ckShowNoReply, &QCheckBox::toggled, this, &UDSScanWindow::setNoReplyVal);
connect(ui->spinDelay, SIGNAL(valueChanged(int)), this, SLOT(setMaxDelayVal()));
connect(ui->spinIncrement, SIGNAL(valueChanged(int)), this, SLOT(setIncrementVal()));
connect(ui->spinReplyOffset, SIGNAL(valueChanged(int)), this, SLOT(setReplyOffset()));
connect(ui->cbSessType, &QComboBox::currentTextChanged, this, &UDSScanWindow::setSessType);
//not handling show no reply, max reply delay, reply offset, increment
int numBuses = CANConManager::getInstance()->getNumBuses();
for (int n = 0; n < numBuses; n++) ui->cbBuses->addItem(QString::number(n));
installEventFilter(this);
addNewScan();
}
UDSScanWindow::~UDSScanWindow()
@@ -71,74 +109,368 @@ bool UDSScanWindow::eventFilter(QObject *obj, QEvent *event)
return false;
}
void UDSScanWindow::displayScanEntry(int idx)
{
if (inhibitUpdates) return;
if (idx == -1) return;
currEditEntry = &scanEntries.data()[idx];
ui->spinStartID->setValue(currEditEntry->startID);
ui->spinEndID->setValue(currEditEntry->endID);
ui->spinReplyOffset->setValue(currEditEntry->idOffset);
ui->cbAllowAdaptiveOffset->setChecked(currEditEntry->bAdaptiveOffset);
ui->ckShowNoReply->setChecked(currEditEntry->bShowNoReplies);
ui->cbBuses->setCurrentIndex(currEditEntry->busToScan);
ui->spinDelay->setValue(currEditEntry->maxWaitTime);
ui->cbScanType->setCurrentIndex((int)currEditEntry->scanType);
ui->cbSessType->setCurrentIndex(currEditEntry->sessType);
ui->spinNumBytes->setValue(currEditEntry->subfunctLen);
ui->spinLowerService->setValue(currEditEntry->serviceLower);
ui->spinUpperService->setValue(currEditEntry->serviceUpper);
ui->spinLowerSubfunc->setValue(currEditEntry->subfunctLower);
ui->spinUpperSubfunc->setValue(currEditEntry->subfunctUpper);
ui->spinIncrement->setValue(currEditEntry->subfunctIncrement);
}
void UDSScanWindow::deleteSelectedScan()
{
int idx = ui->listScansToRun->currentRow();
if (idx == -1) return;
scanEntries.removeAt(idx);
QListWidgetItem *item = ui->listScansToRun->takeItem(idx);
delete item;
int rows = ui->listScansToRun->count();
ui->listScansToRun->setCurrentRow(rows - 1);
for (int i = 0; i < rows; i++)
{
QListWidgetItem *item = ui->listScansToRun->item(i);
item->setText(generateListDesc(i));
}
}
void UDSScanWindow::addNewScan()
{
ScanEntry newEntry;
newEntry.busToScan = 0;
newEntry.scanType = SCAN_TYPE::ST_TESTER_PRESENT;
newEntry.sessType = 0;
newEntry.bAdaptiveOffset = false;
newEntry.bShowNoReplies = false;
newEntry.idOffset = 8;
newEntry.startID = 0x7E0;
newEntry.endID = 0x7E7;
newEntry.maxWaitTime = 100;
newEntry.subfunctLen = 1;
newEntry.subfunctIncrement = 1;
newEntry.subfunctLower = 0;
newEntry.subfunctUpper = 0;
newEntry.serviceLower = 1;
newEntry.serviceUpper = 1;
scanEntries.append(newEntry);
displayScanEntry(scanEntries.length() - 1);
ui->listScansToRun->addItem(generateListDesc(scanEntries.length() - 1));
ui->listScansToRun->setCurrentRow(ui->listScansToRun->count() - 1);
}
void UDSScanWindow::loadScans()
{
QString filename;
QFileDialog dialog(qApp->activeWindow());
QSettings settings;
bool result = false;
QStringList filters;
filters.append(QString(tr("UDS Test Specification (*.uds *.UDS)")));
dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString());
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptOpen);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
QFile inFile(filename);
inFile.open(QIODevice::ReadOnly);
QDataStream load(&inFile);
int fileVersion;
load >> fileVersion;
if (fileVersion != 1)
{
QMessageBox::warning(this, "Cannot Load File", "File is not a supported version.\nCannot load it!");
return;
}
inhibitUpdates = true;;
scanEntries.clear();
ui->listScansToRun->clear();
int numEntries;
load >> numEntries;
for (int i = 0; i < numEntries; i++)
{
ScanEntry entry;
load >> entry.startID >> entry.endID;
load >> entry.idOffset >> entry.bAdaptiveOffset >> entry.bShowNoReplies;
load >> entry.busToScan >> entry.maxWaitTime >> entry.scanType;
load >> entry.sessType >> entry.subfunctLen >> entry.subfunctLower;
load >> entry.subfunctUpper >> entry.subfunctIncrement;
load >> entry.serviceLower >> entry.serviceUpper;
scanEntries.append(entry);
ui->listScansToRun->addItem(generateListDesc(scanEntries.length() - 1));
}
inFile.close();
inhibitUpdates = false;
ui->listScansToRun->setCurrentRow(ui->listScansToRun->count() - 1);
displayScanEntry(scanEntries.length() - 1);
}
}
void UDSScanWindow::saveScans()
{
QString filename;
QFileDialog dialog(qApp->activeWindow());
QSettings settings;
bool result = false;
QStringList filters;
filters.append(QString(tr("UDS Test Specification (*.uds *.UDS)")));
dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString());
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
QFile outFile(filename);
outFile.open(QIODevice::WriteOnly);
QDataStream save(&outFile);
save << 1; //file version
save << scanEntries.count();
for (int i = 0; i < scanEntries.count(); i++)
{
save << scanEntries[i].startID;
save << scanEntries[i].endID;
save << scanEntries[i].idOffset;
save << scanEntries[i].bAdaptiveOffset;
save << scanEntries[i].bShowNoReplies;
save << scanEntries[i].busToScan;
save << scanEntries[i].maxWaitTime;
save << scanEntries[i].scanType;
save << scanEntries[i].sessType;
save << scanEntries[i].subfunctLen;
save << scanEntries[i].subfunctLower;
save << scanEntries[i].subfunctUpper;
save << scanEntries[i].subfunctIncrement;
save << scanEntries[i].serviceLower;
save << scanEntries[i].serviceUpper;
}
outFile.close();
}
}
QString UDSScanWindow::generateListDesc(int idx)
{
QString builder;
ScanEntry entry = scanEntries[idx];
builder = "(0x" + QString::number(entry.startID, 16) + " - 0x" + QString::number(entry.endID, 16) + ") ";
switch (entry.scanType)
{
case ST_TESTER_PRESENT:
builder += "TP";
break;
case ST_SESS_CTRL:
builder += "SESS";
break;
case ST_COMM_CTRL:
builder += "COMM";
break;
case ST_ECU_RESET:
builder += "ECU";
break;
case ST_CLEAR_DTC:
builder += "CDTC";
break;
case ST_READ_DTC:
builder += "RDTC";
break;
case ST_SEC_ACCESS:
builder += "SECU";
break;
case ST_READ_ID:
builder += "RID";
break;
case ST_READ_ADDR:
builder += "RADD";
break;
case ST_READ_SCALING:
builder += "RSCAL";
break;
case ST_IO_CTRL:
builder += "IO";
break;
case ST_ROUTINE_CTRL:
builder += "ROUT";
break;
case ST_CUSTOM:
builder += "CUST";
break;
}
return builder;
}
void UDSScanWindow::adaptiveToggled()
{
if (inhibitUpdates) return;
if (ui->cbAllowAdaptiveOffset->isChecked()) ui->spinReplyOffset->setEnabled(false);
else ui->spinReplyOffset->setEnabled(true);
if (currEditEntry) currEditEntry->bAdaptiveOffset = ui->cbAllowAdaptiveOffset->isChecked();
}
void UDSScanWindow::wildcardToggled()
void UDSScanWindow::setSessType()
{
bool state = false;
if (ui->ckWildcard->isChecked()) state = false;
else state = true;
ui->ckReset->setEnabled(state);
ui->ckSecurity->setEnabled(state);
ui->ckSession->setEnabled(state);
ui->ckTester->setEnabled(state);
ui->ckReadByAddr->setEnabled(state);
ui->ckReadByID->setEnabled(state);
ui->ckReset->setChecked(false);
ui->ckSecurity->setChecked(false);
ui->ckSession->setChecked(false);
ui->ckTester->setChecked(false);
ui->ckReadByAddr->setChecked(false);
ui->ckReadByID->setChecked(false);
ui->spinLowerService->setEnabled(!state);
ui->spinLowerSubfunc->setEnabled(!state);
ui->spinNumBytes->setEnabled(!state);
ui->spinUpperService->setEnabled(!state);
ui->spinUpperSubfunc->setEnabled(!state);
if (inhibitUpdates) return;
if (currEditEntry) currEditEntry->sessType = ui->cbSessType->currentIndex();
}
void UDSScanWindow::readByToggled()
void UDSScanWindow::setNoReplyVal()
{
bool state = false;
if (ui->ckReadByAddr->isChecked() || ui->ckReadByID->isChecked()) state = true;
else state = false;
if (inhibitUpdates) return;
if (currEditEntry) currEditEntry->bShowNoReplies = ui->ckShowNoReply->isChecked();
}
ui->spinLowerService->setEnabled(false);
ui->spinLowerSubfunc->setEnabled(state);
ui->spinNumBytes->setEnabled(state);
ui->spinUpperService->setEnabled(false);
ui->spinUpperSubfunc->setEnabled(state);
void UDSScanWindow::setMaxDelayVal()
{
if (inhibitUpdates) return;
if (currEditEntry) currEditEntry->maxWaitTime = ui->spinDelay->value();
}
void UDSScanWindow::setIncrementVal()
{
if (inhibitUpdates) return;
if (currEditEntry) currEditEntry->subfunctIncrement = ui->spinIncrement->value();
}
void UDSScanWindow::setReplyOffset()
{
if (inhibitUpdates) return;
if (currEditEntry) currEditEntry->idOffset = ui->spinReplyOffset->value();
}
void UDSScanWindow::changedScanType()
{
if (inhibitUpdates) return;
int idx = ui->cbScanType->currentIndex();
if (idx == -1)
{
ui->cbScanType->setCurrentIndex(0);
idx = 0;
}
if (currEditEntry) currEditEntry->scanType = (SCAN_TYPE)idx;
QListWidgetItem* item = ui->listScansToRun->currentItem();
item->setText(generateListDesc(ui->listScansToRun->currentRow()));
switch (idx)
{
case ST_TESTER_PRESENT:
case ST_SESS_CTRL:
ui->cbSessType->setEnabled(false);
ui->spinLowerService->setEnabled(false);
ui->spinLowerSubfunc->setEnabled(false);
ui->spinUpperService->setEnabled(false);
ui->spinUpperSubfunc->setEnabled(false);
ui->spinNumBytes->setEnabled(false);
ui->spinIncrement->setEnabled(false);
ui->cbSessType->setCurrentIndex(0);
break;
case ST_COMM_CTRL:
case ST_ECU_RESET:
case ST_CLEAR_DTC:
case ST_SEC_ACCESS:
case ST_READ_DTC:
ui->cbSessType->setEnabled(true);
ui->spinLowerService->setEnabled(false);
ui->spinUpperService->setEnabled(false);
ui->spinLowerSubfunc->setEnabled(false);
ui->spinUpperSubfunc->setEnabled(false);
ui->spinNumBytes->setEnabled(false);
ui->spinIncrement->setEnabled(false);
break;
case ST_IO_CTRL:
case ST_ROUTINE_CTRL:
case ST_READ_SCALING:
ui->cbSessType->setEnabled(true);
ui->spinLowerService->setEnabled(false);
ui->spinUpperService->setEnabled(false);
ui->spinLowerSubfunc->setEnabled(true);
ui->spinUpperSubfunc->setEnabled(true);
ui->spinNumBytes->setEnabled(false);
ui->spinNumBytes->setValue(2);
ui->spinIncrement->setEnabled(true);
break;
case ST_READ_ID:
case ST_READ_ADDR:
ui->cbSessType->setEnabled(true);
ui->spinLowerService->setEnabled(false);
ui->spinUpperService->setEnabled(false);
ui->spinLowerSubfunc->setEnabled(true);
ui->spinUpperSubfunc->setEnabled(true);
ui->spinNumBytes->setEnabled(true);
ui->spinIncrement->setEnabled(true);
break;
case ST_CUSTOM:
ui->cbSessType->setEnabled(true);
ui->spinLowerService->setEnabled(true);
ui->spinUpperService->setEnabled(true);
ui->spinLowerSubfunc->setEnabled(true);
ui->spinUpperSubfunc->setEnabled(true);
ui->spinNumBytes->setEnabled(true);
ui->spinIncrement->setEnabled(true);
break;
}
}
void UDSScanWindow::numBytesChanged()
{
if (inhibitUpdates) return;
uint64_t upperBound = (1ull << (8ull * ui->spinNumBytes->value())) - 1;
if (upperBound > 0x7FFFFFFF) upperBound = 0x7FFFFFFF;
ui->spinUpperSubfunc->setMaximum(upperBound);
if (currEditEntry) currEditEntry->subfunctLen = ui->spinNumBytes->value();
}
void UDSScanWindow::checkIDRange()
{
if (inhibitUpdates) return;
ui->spinStartID->setMaximum(ui->spinEndID->value());
ui->spinEndID->setMinimum(ui->spinStartID->value());
if (currEditEntry) currEditEntry->startID = ui->spinStartID->value();
if (currEditEntry) currEditEntry->endID = ui->spinEndID->value();
QListWidgetItem* item = ui->listScansToRun->currentItem();
item->setText(generateListDesc(ui->listScansToRun->currentRow()));
}
void UDSScanWindow::checkServiceRange()
{
if (inhibitUpdates) return;
ui->spinLowerService->setMaximum(ui->spinUpperService->value());
ui->spinUpperService->setMinimum(ui->spinLowerService->value());
if (currEditEntry) currEditEntry->serviceLower = ui->spinLowerService->value();
if (currEditEntry) currEditEntry->serviceUpper = ui->spinUpperService->value();
}
void UDSScanWindow::checkSubFuncRange()
{
if (inhibitUpdates) return;
ui->spinLowerSubfunc->setMaximum(ui->spinUpperSubfunc->value());
ui->spinUpperSubfunc->setMinimum(ui->spinLowerSubfunc->value());
if (currEditEntry) currEditEntry->subfunctLower = ui->spinLowerSubfunc->value();
if (currEditEntry) currEditEntry->subfunctUpper = ui->spinUpperSubfunc->value();
}
void UDSScanWindow::saveResults()
@@ -196,20 +528,27 @@ void UDSScanWindow::sendOnBuses(UDS_MESSAGE test, int buses)
sendingFrames.append(test);
}
void UDSScanWindow::scanUDS()
void UDSScanWindow::scanAll()
{
if (currentlyRunning)
sendingFrames.clear();
for (int i = 0; i < scanEntries.count(); i++)
{
waitTimer->stop();
sendingFrames.clear();
udsHandler->setReception(false);
udsHandler->setProcessAllIDs(false);
udsHandler->setFlowCtrl(false);
currentlyRunning = false;
ui->btnScan->setText("Start Scan");
return;
setupScan(i);
}
startScan();
}
void UDSScanWindow::scanSelected()
{
sendingFrames.clear();
int idx = ui->listScansToRun->currentRow();
if (idx < 0) return;
setupScan(idx);
startScan();
}
void UDSScanWindow::startScan()
{
udsHandler->setReception(true);
udsHandler->setProcessAllIDs(true);
udsHandler->setFlowCtrl(true);
@@ -217,121 +556,173 @@ void UDSScanWindow::scanUDS()
waitTimer->setInterval(ui->spinDelay->value());
ui->treeResults->clear();
sendingFrames.clear();
nodeService = nullptr;
nodeID = nullptr;
nodeSubFunc = nullptr;
UDS_MESSAGE test;
int typ, id;
int startID, endID;
startID = ui->spinStartID->value();
endID = ui->spinEndID->value();
if (endID < startID) {
int temp = startID;
startID = endID;
endID = temp;
}
int buses = ui->cbBuses->currentIndex();
for (id = startID; id <= endID; id++)
{
test.setFrameId( id );
test.payload().clear();
if (ui->ckTester->isChecked())
{
test.service = UDS_SERVICES::TESTER_PRESENT;
test.subFunc = 0;
sendOnBuses(test, buses);
}
if (ui->ckSession->isChecked())
{
for (typ = 1; typ < 4; typ++) //try each type of session access
{
test.service = UDS_SERVICES::DIAG_CONTROL;
test.subFunc = typ;
sendOnBuses(test, buses);
}
}
if (ui->ckReset->isChecked()) //try to command a reset of the ECU. You're likely to know if it works. ;)
{
for (typ = 1; typ < 4; typ++) //try each type of session access
{
test.service = UDS_SERVICES::ECU_RESET;
test.subFunc = typ;
sendOnBuses(test, buses);
}
}
if (ui->ckSecurity->isChecked()) //try to enter security mode - very likely to get a response if an ECU exists.
{
for (typ = 1; typ < 0x42; typ = typ + 2) //try each type of session access. In practice only the first 1-3 are likely to work
{
test.service = UDS_SERVICES::SECURITY_ACCESS;
test.subFunc = typ;
sendOnBuses(test, buses);
}
}
if (ui->ckReadByAddr->isChecked())
{
test.subFuncLen = ui->spinNumBytes->value();
test.service = UDS_SERVICES::READ_BY_ADDR;
for (int subf = ui->spinLowerSubfunc->value(); subf <= ui->spinUpperSubfunc->value(); subf++)
{
test.subFunc = subf;
sendOnBuses(test, buses);
}
}
if (ui->ckReadByID->isChecked())
{
test.subFuncLen = ui->spinNumBytes->value();
test.service = UDS_SERVICES::READ_BY_ID;
for (int subf = ui->spinLowerSubfunc->value(); subf <= ui->spinUpperSubfunc->value(); subf++)
{
test.subFunc = subf;
sendOnBuses(test, buses);
}
}
if (ui->ckWildcard->isChecked())
{
//preallocate the whole buffer so we don't have to keep updating the size and moving as we go.
//TODO: this shows a downside to the current method - it might potentially need to create a huge
//number of frames here. Of course, the rest of the system will do the same so I guess it's a bad idea
//any way you go to generate a 1 billion frame test.
int size = (endID - startID) * (ui->spinUpperService->value() - ui->spinLowerService->value());
size *= (ui->spinUpperSubfunc->value() - ui->spinLowerSubfunc->value());
sendingFrames.reserve(size);
test.subFuncLen = ui->spinNumBytes->value();
for (typ = ui->spinLowerService->value(); typ <= ui->spinUpperService->value(); typ++)
{
test.service = typ;
for (int subTyp = ui->spinLowerSubfunc->value(); subTyp <= ui->spinUpperSubfunc->value(); subTyp++)
{
test.subFunc = subTyp;
sendOnBuses(test, buses);
}
}
}
}
waitTimer->start();
currIdx = -1;
currentlyRunning = true;
ui->btnScan->setText("Abort Scan");
//ui->btnScan->setText("Abort Scan");
ui->progressBar->setValue(0);
ui->progressBar->setMaximum(sendingFrames.length());
qDebug() << "Number of operations: " << sendingFrames.length();
sendNextMsg();
}
void UDSScanWindow::stopScan()
{
waitTimer->stop();
sendingFrames.clear();
udsHandler->setReception(false);
udsHandler->setProcessAllIDs(false);
udsHandler->setFlowCtrl(false);
currentlyRunning = false;
//ui->btnScan->setText("Start Scan");
}
void UDSScanWindow::setupScan(int idx)
{
UDS_MESSAGE test;
qDebug() << "Generating scan id: " << idx;
for (uint32_t id = scanEntries[idx].startID; id <= scanEntries[idx].endID; id++)
{
test.setFrameId( id );
if (scanEntries[idx].sessType > 0)
{
test.payload().clear();
test.service = UDS_SERVICES::DIAG_CONTROL;
test.subFuncLen = 1;
test.subFunc = scanEntries[ idx].sessType;
sendOnBuses(test, scanEntries[idx].busToScan);
}
test.payload().clear();
qDebug() << "Generating scan on ID " << QString::number(id, 16) << " of type " << scanEntries[idx].scanType;
switch (scanEntries[idx].scanType)
{
case ST_TESTER_PRESENT:
test.service = UDS_SERVICES::TESTER_PRESENT;
test.subFuncLen = 1;
test.subFunc = 0;
sendOnBuses(test, scanEntries[idx].busToScan);
break;
case ST_SESS_CTRL:
for (int typ = 1; typ < 4; typ++) //try each type of session access
{
test.service = UDS_SERVICES::DIAG_CONTROL;
test.subFuncLen = 1;
test.subFunc = typ;
sendOnBuses(test, scanEntries[idx].busToScan);
}
break;
case ST_COMM_CTRL:
test.service = UDS_SERVICES::COMM_CTRL;
test.subFuncLen = 2; //need two bytes for this one
test.subFunc = 0x100; //00 01 on the bus = enable Rx/Tx
sendOnBuses(test, scanEntries[idx].busToScan);
break;
case ST_ECU_RESET:
for (int typ = 1; typ < 4; typ++) //try each type of session access
{
test.service = UDS_SERVICES::ECU_RESET;
test.subFuncLen = 1;
test.subFunc = typ;
sendOnBuses(test, scanEntries[idx].busToScan);
}
break;
case ST_CLEAR_DTC:
test.service = UDS_SERVICES::CLEAR_DIAG;
test.subFuncLen = 3; //DTC groups are sent as 3 bytes
test.subFunc = 0xFFFFFF; //clear everything!
sendOnBuses(test, scanEntries[idx].busToScan);
break;
case ST_READ_DTC:
test.service = UDS_SERVICES::READ_DTC;
test.subFuncLen = 2;
test.subFunc = 0x8702; //get DTCs by mask (87 is the mask)
sendOnBuses(test, scanEntries[idx].busToScan);
break;
case ST_SEC_ACCESS:
for (int typ = 1; typ < 0x42; typ = typ + 2) //try each type of session access. In practice only the first 1-3 are likely to work
{
test.service = UDS_SERVICES::SECURITY_ACCESS;
test.subFuncLen = 1;
test.subFunc = typ;
sendOnBuses(test, scanEntries[idx].busToScan);
}
break;
case ST_READ_ID:
test.subFuncLen = scanEntries[idx].subfunctLen;
test.service = UDS_SERVICES::READ_BY_ID;
for (int subf = scanEntries[idx].subfunctLower; subf <= scanEntries[idx].subfunctUpper; subf += scanEntries[idx].subfunctIncrement)
{
test.subFunc = subf;
sendOnBuses(test, scanEntries[idx].busToScan);
}
break;
case ST_READ_ADDR:
test.subFuncLen = scanEntries[idx].subfunctLen;
test.service = UDS_SERVICES::READ_BY_ADDR;
for (int subf = scanEntries[idx].subfunctLower; subf <= scanEntries[idx].subfunctUpper; subf += scanEntries[idx].subfunctIncrement)
{
test.subFunc = subf;
sendOnBuses(test, scanEntries[idx].busToScan);
}
break;
case ST_READ_SCALING:
//sub function should always be a length of 2 though. Should conform to this in the GUI
test.subFuncLen = 2;
test.service = UDS_SERVICES::READ_SCALING_ID;
for (int subf = scanEntries[idx].subfunctLower; subf <= scanEntries[idx].subfunctUpper; subf += scanEntries[idx].subfunctIncrement)
{
test.subFunc = subf;
sendOnBuses(test, scanEntries[idx].busToScan);
}
break;
case ST_IO_CTRL:
test.subFuncLen = 3;
test.service = UDS_SERVICES::IO_CTRL;
for (int subf = scanEntries[idx].subfunctLower; subf <= scanEntries[idx].subfunctUpper; subf += scanEntries[idx].subfunctIncrement)
{
test.subFunc = subf; //the upper byte will be 0 which is what we want. 0 = Return control to ECU
sendOnBuses(test, scanEntries[idx].busToScan);
}
break;
case ST_ROUTINE_CTRL:
test.subFuncLen = 3;
test.service = UDS_SERVICES::ROUTINE_CTRL;
for (int subf = scanEntries[idx].subfunctLower; subf <= scanEntries[idx].subfunctUpper; subf += scanEntries[idx].subfunctIncrement)
{
//request results of routine for all the addresses we're testing. This is the safest thing to do.
//starting or stopping arbitrary routines is super dangerous. Don't do that unless you really know
//what the hell you're doing or something really crazy might happen.
test.subFunc = (subf << 8) + 3;
sendOnBuses(test, scanEntries[idx].busToScan);
}
break;
case ST_CUSTOM:
test.subFuncLen = scanEntries[idx].subfunctLen;
for (uint32_t typ = scanEntries[idx].serviceLower; typ <= scanEntries[idx].serviceUpper; typ++)
{
test.service = typ;
for (int subTyp = scanEntries[idx].subfunctLower; subTyp <= scanEntries[idx].subfunctUpper; subTyp += scanEntries[idx].subfunctIncrement)
{
test.subFunc = subTyp;
sendOnBuses(test, scanEntries[idx].busToScan);
}
}
break;
}
}
}
//Updates here are sent about every 1/4 second. That's fine for most windows but not this one.
void UDSScanWindow::updatedFrames(int numFrames)
{
@@ -491,7 +882,7 @@ void UDSScanWindow::sendNextMsg()
udsHandler->setReception(false);
udsHandler->setProcessAllIDs(false);
udsHandler->setFlowCtrl(false);
ui->btnScan->setText("Start Scan");
//ui->btnScan->setText("Start Scan");
currentlyRunning = false;
}
ui->progressBar->setValue(currIdx);
+58 -3
View File
@@ -9,6 +9,44 @@
#include <QFile>
#include <QTreeWidget>
enum SCAN_TYPE
{
ST_TESTER_PRESENT,
ST_SESS_CTRL,
ST_COMM_CTRL,
ST_ECU_RESET,
ST_CLEAR_DTC,
ST_READ_DTC,
ST_SEC_ACCESS,
ST_READ_ID,
ST_READ_ADDR,
ST_READ_SCALING,
ST_IO_CTRL,
ST_ROUTINE_CTRL,
ST_CUSTOM,
};
//stores the parameters for one scan
class ScanEntry
{
public:
uint32_t startID, endID;
int32_t idOffset;
bool bAdaptiveOffset;
bool bShowNoReplies;
uint32_t busToScan;
uint32_t maxWaitTime; //in milliseconds
SCAN_TYPE scanType;
uint32_t sessType;
uint32_t subfunctLen;
qint64 subfunctLower;
qint64 subfunctUpper;
uint32_t subfunctIncrement;
uint32_t serviceLower;
uint32_t serviceUpper;
};
namespace Ui {
class UDSScanWindow;
}
@@ -24,16 +62,25 @@ public:
private slots:
void updatedFrames(int numFrames);
void gotUDSReply(UDS_MESSAGE msg);
void scanUDS();
void scanAll();
void scanSelected();
void saveResults();
void timeOut();
void adaptiveToggled();
void wildcardToggled();
void readByToggled();
void changedScanType();
void numBytesChanged();
void checkIDRange();
void checkServiceRange();
void checkSubFuncRange();
void deleteSelectedScan();
void addNewScan();
void loadScans();
void saveScans();
void setNoReplyVal();
void setMaxDelayVal();
void setIncrementVal();
void setReplyOffset();
void setSessType();
private:
Ui::UDSScanWindow *ui;
@@ -44,9 +91,17 @@ private:
QTreeWidgetItem *nodeID;
QTreeWidgetItem *nodeService;
QTreeWidgetItem *nodeSubFunc;
QVector<ScanEntry> scanEntries;
ScanEntry *currEditEntry;
int currIdx = 0;
bool currentlyRunning;
bool inhibitUpdates;
void displayScanEntry(int idx);
QString generateListDesc(int idx);
void setupScan(int idx);
void startScan();
void stopScan();
void sendNextMsg();
void sendOnBuses(UDS_MESSAGE frame, int buses);
void setupNodes(uint32_t replyID);
+411 -322
View File
@@ -6,61 +6,346 @@
<rect>
<x>0</x>
<y>0</y>
<width>742</width>
<height>628</height>
<width>1053</width>
<height>624</height>
</rect>
</property>
<property name="windowTitle">
<string>UDS Scanner</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_9" stretch="1,5">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Results</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="treeResults">
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSaveResults">
<property name="text">
<string>Save Scan Results</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Scans To Run</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listScansToRun"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<widget class="QLabel" name="label_4">
<widget class="QPushButton" name="btnAdd">
<property name="text">
<string>Starting ID</string>
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinStartID">
<widget class="QPushButton" name="btnDelete">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QPushButton" name="btnLoad">
<property name="text">
<string>Load</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSave">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Starting ID</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinStartID">
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>536870911</number>
</property>
<property name="value">
<number>2016</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Ending ID</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinEndID">
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>536870911</number>
</property>
<property name="value">
<number>2023</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QCheckBox" name="ckShowNoReply">
<property name="text">
<string>Show &quot;No Reply&quot;</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbAllowAdaptiveOffset">
<property name="text">
<string>Allow adaptive reply offset</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Bus to scan</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbBuses"/>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Maximum reply delay (ms)</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinDelay">
<property name="minimum">
<number>50</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Reply Offset:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinReplyOffset">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>-128</number>
</property>
<property name="maximum">
<number>128</number>
</property>
<property name="value">
<number>8</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Scan Type:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbScanType">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_14">
<property name="text">
<string>Run test in session type:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbSessType">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>Subfunct Len:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinNumBytes">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>4</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_9">
<property name="text">
<string>Lower Bound:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinLowerSubfunc">
<property name="enabled">
<bool>false</bool>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>536870911</number>
<number>255</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Upper Bound:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinUpperSubfunc">
<property name="enabled">
<bool>false</bool>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>2016</number>
<number>255</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_15">
<property name="text">
<string>Increment</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinIncrement">
<property name="enabled">
<bool>false</bool>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>1024</number>
</property>
<property name="singleStep">
<number>8</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
@@ -70,24 +355,50 @@
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_3">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Ending ID</string>
<string>Lower Service:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinEndID">
<widget class="QSpinBox" name="spinLowerService">
<property name="enabled">
<bool>false</bool>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>536870911</number>
<number>255</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>Upper Service:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinUpperService">
<property name="enabled">
<bool>false</bool>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>2023</number>
<number>255</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
@@ -97,323 +408,101 @@
</layout>
</item>
<item>
<widget class="QCheckBox" name="ckShowNoReply">
<property name="text">
<string>Show Tests with no Replies</string>
<widget class="Line" name="line">
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="checked">
<bool>true</bool>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Reply Offset:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinReplyOffset">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>-128</number>
</property>
<property name="maximum">
<number>128</number>
</property>
<property name="value">
<number>8</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbAllowAdaptiveOffset">
<property name="text">
<string>Allow adaptive reply offset</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Bus to scan</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbBuses"/>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Maximum reply delay (ms)</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinDelay">
<property name="minimum">
<number>50</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Scan Types:</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ckTester">
<property name="text">
<string>Tester Present</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ckSession">
<property name="text">
<string>Session Ctrl</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ckReset">
<property name="text">
<string>ECU Reset</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ckSecurity">
<property name="text">
<string>Security Access</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QCheckBox" name="ckReadByID">
<property name="text">
<string>Read By ID</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ckReadByAddr">
<property name="text">
<string>Read By Addr</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ckWildcard">
<property name="text">
<string>Wildcard</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Lower Service:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinLowerService">
<property name="enabled">
<bool>false</bool>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>Upper Service:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinUpperService">
<property name="enabled">
<bool>false</bool>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>255</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>Subfunction Bytes:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinNumBytes">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<property name="lineWidth">
<number>4</number>
</property>
<property name="value">
<number>1</number>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_9">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Lower Bound:</string>
<string>Results</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinLowerSubfunc">
<widget class="QTreeWidget" name="treeResults">
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="enabled">
<bool>false</bool>
</property>
<property name="prefix">
<string>0x</string>
<property name="value">
<number>0</number>
</property>
<property name="maximum">
<number>255</number>
<property name="textVisible">
<bool>true</bool>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Upper Bound:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinUpperSubfunc">
<property name="enabled">
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>255</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QPushButton" name="btnScanAll">
<property name="text">
<string>Run All Scans</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnScanSelected">
<property name="text">
<string>Run Selected Scan</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="btnSaveResults">
<property name="text">
<string>Save Scan Results</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnScan">
<property name="text">
<string>Start Scan</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>spinStartID</tabstop>
<tabstop>spinEndID</tabstop>
<tabstop>ckShowNoReply</tabstop>
<tabstop>spinReplyOffset</tabstop>
<tabstop>cbAllowAdaptiveOffset</tabstop>
<tabstop>cbBuses</tabstop>
<tabstop>spinDelay</tabstop>
<tabstop>ckTester</tabstop>
<tabstop>ckSession</tabstop>
<tabstop>ckReset</tabstop>
<tabstop>ckSecurity</tabstop>
<tabstop>ckReadByID</tabstop>
<tabstop>ckReadByAddr</tabstop>
<tabstop>ckWildcard</tabstop>
<tabstop>spinLowerService</tabstop>
<tabstop>spinUpperService</tabstop>
<tabstop>spinNumBytes</tabstop>
<tabstop>spinLowerSubfunc</tabstop>
<tabstop>spinUpperSubfunc</tabstop>
<tabstop>btnScan</tabstop>
<tabstop>treeResults</tabstop>
<tabstop>btnSaveResults</tabstop>
</tabstops>
<resources/>
<connections/>