Add ability to set directory where Tesla firmware is stored, use it to populate fields and allow selection of ECU to flash.
This commit is contained in:
@@ -7,6 +7,10 @@
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QDateTime>
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonArray>
|
||||
|
||||
UDSFirmwareUploaderWindow::UDSFirmwareUploaderWindow(const QVector<CANFrame> *frames, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
@@ -42,9 +46,15 @@ UDSFirmwareUploaderWindow::UDSFirmwareUploaderWindow(const QVector<CANFrame> *fr
|
||||
connect(ui->btnLoadFile, &QPushButton::clicked, this, &UDSFirmwareUploaderWindow::handleLoadFile);
|
||||
connect(ui->btnStartStop, &QPushButton::clicked, this, &UDSFirmwareUploaderWindow::handleStartStop);
|
||||
connect(ui->btnAbort, &QPushButton::clicked, this, &UDSFirmwareUploaderWindow::handleAbort);
|
||||
connect(ui->btnBrowseTeslaDir, &QPushButton::clicked, this, &UDSFirmwareUploaderWindow::handleBrowseTeslaDir);
|
||||
connect(ui->btnReloadNodes, &QPushButton::clicked, this, &UDSFirmwareUploaderWindow::handleReloadNodes);
|
||||
connect(udsHandler, &UDS_HANDLER::newUDSMessage, this, &UDSFirmwareUploaderWindow::gotUDSReply);
|
||||
connect(timeoutTimer, &QTimer::timeout, this, &UDSFirmwareUploaderWindow::timeoutTriggered);
|
||||
connect(testerTimer, &QTimer::timeout, this, &UDSFirmwareUploaderWindow::testerPresentTick);
|
||||
connect(ui->cmbTeslaNode, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &UDSFirmwareUploaderWindow::handleNodeChanged);
|
||||
|
||||
loadTeslaNodes();
|
||||
}
|
||||
|
||||
UDSFirmwareUploaderWindow::~UDSFirmwareUploaderWindow()
|
||||
@@ -450,6 +460,187 @@ void UDSFirmwareUploaderWindow::gotUDSReply(UDS_MESSAGE msg)
|
||||
}
|
||||
}
|
||||
|
||||
void UDSFirmwareUploaderWindow::loadTeslaNodes()
|
||||
{
|
||||
messageIdMap.clear();
|
||||
teslaNodes.clear();
|
||||
|
||||
QStringList searchPaths;
|
||||
|
||||
if (!teslaDir.isEmpty())
|
||||
{
|
||||
searchPaths.append(teslaDir + "/opt/odin/data/Model3/dej");
|
||||
searchPaths.append(teslaDir + "/opt/odin/data/Model3");
|
||||
}
|
||||
|
||||
searchPaths.append(QCoreApplication::applicationDirPath());
|
||||
searchPaths.append(QDir::currentPath());
|
||||
searchPaths.append(QCoreApplication::applicationDirPath() + "/../");
|
||||
searchPaths.append(QDir::currentPath() + "/../");
|
||||
|
||||
QString compactPath;
|
||||
QString nodesPath;
|
||||
for (const QString &path : searchPaths)
|
||||
{
|
||||
if (compactPath.isEmpty())
|
||||
{
|
||||
QString test = path + "/Model3_ETH.compact.json";
|
||||
if (QFile::exists(test)) compactPath = test;
|
||||
}
|
||||
if (nodesPath.isEmpty())
|
||||
{
|
||||
QString test = path + "/nodes.json";
|
||||
if (QFile::exists(test)) nodesPath = test;
|
||||
}
|
||||
}
|
||||
|
||||
// Load Model3_ETH.compact.json first to build message name -> ID map
|
||||
if (!compactPath.isEmpty())
|
||||
{
|
||||
QFile compactFile(compactPath);
|
||||
if (compactFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QByteArray data = compactFile.readAll();
|
||||
compactFile.close();
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||
if (!doc.isNull())
|
||||
{
|
||||
QJsonObject root = doc.object();
|
||||
QJsonObject messages = root["messages"].toObject();
|
||||
for (auto it = messages.begin(); it != messages.end(); it++)
|
||||
{
|
||||
QJsonObject msgObj = it->toObject();
|
||||
int msgId = msgObj["message_id"].toInt(-1);
|
||||
if (msgId >= 0)
|
||||
messageIdMap[it.key()] = static_cast<uint32_t>(msgId);
|
||||
}
|
||||
logMessage("Loaded " + QString::number(messageIdMap.size()) +
|
||||
" message IDs from Model3_ETH.compact.json");
|
||||
}
|
||||
else
|
||||
logMessage("ERROR: Failed to parse Model3_ETH.compact.json");
|
||||
}
|
||||
else
|
||||
logMessage("ERROR: Could not open Model3_ETH.compact.json");
|
||||
}
|
||||
else
|
||||
logMessage("WARNING: Model3_ETH.compact.json not found");
|
||||
|
||||
// Load nodes.json
|
||||
if (!nodesPath.isEmpty())
|
||||
{
|
||||
QFile nodesFile(nodesPath);
|
||||
if (nodesFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QByteArray data = nodesFile.readAll();
|
||||
nodesFile.close();
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||
if (!doc.isNull())
|
||||
{
|
||||
QJsonObject root = doc.object();
|
||||
for (auto it = root.begin(); it != root.end(); it++)
|
||||
{
|
||||
TeslaNodeInfo node;
|
||||
node.name = it.key();
|
||||
QJsonObject obj = it->toObject();
|
||||
|
||||
QString reqName = obj["request_message_name"].toString();
|
||||
QString respName = obj["response_message_name"].toString();
|
||||
|
||||
node.requestId = messageIdMap.value(reqName, 0x7E0);
|
||||
node.responseId = messageIdMap.value(respName, 0x7E8);
|
||||
node.securityLevel = 1;
|
||||
node.useSecurity = true;
|
||||
|
||||
QJsonObject sec = obj["security"].toObject();
|
||||
if (!sec.isEmpty())
|
||||
{
|
||||
node.securityAlgorithm = sec["algorithm"].toString();
|
||||
node.securityBufferSize = sec["buffer_size"].toInt();
|
||||
}
|
||||
|
||||
node.dataUpload = obj["data_upload"].toString();
|
||||
|
||||
teslaNodes.append(node);
|
||||
}
|
||||
logMessage("Loaded " + QString::number(teslaNodes.size()) +
|
||||
" Tesla nodes from nodes.json");
|
||||
populateNodeCombo();
|
||||
}
|
||||
else
|
||||
logMessage("ERROR: Failed to parse nodes.json");
|
||||
}
|
||||
else
|
||||
logMessage("ERROR: Could not open nodes.json");
|
||||
}
|
||||
else
|
||||
logMessage("WARNING: nodes.json not found");
|
||||
|
||||
if (!teslaDir.isEmpty())
|
||||
ui->txtTeslaDir->setText(teslaDir);
|
||||
else if (!nodesPath.isEmpty())
|
||||
{
|
||||
QFileInfo info(nodesPath);
|
||||
teslaDir = info.absolutePath();
|
||||
ui->txtTeslaDir->setText(teslaDir);
|
||||
}
|
||||
}
|
||||
|
||||
void UDSFirmwareUploaderWindow::populateNodeCombo()
|
||||
{
|
||||
ui->cmbTeslaNode->clear();
|
||||
ui->cmbTeslaNode->addItem("-- Manual --");
|
||||
for (const TeslaNodeInfo &node : teslaNodes)
|
||||
ui->cmbTeslaNode->addItem(node.name);
|
||||
}
|
||||
|
||||
void UDSFirmwareUploaderWindow::handleBrowseTeslaDir()
|
||||
{
|
||||
QString dir = QFileDialog::getExistingDirectory(this, "Select Tesla Data Directory", teslaDir);
|
||||
if (!dir.isEmpty())
|
||||
{
|
||||
teslaDir = dir;
|
||||
ui->txtTeslaDir->setText(teslaDir);
|
||||
handleReloadNodes();
|
||||
}
|
||||
}
|
||||
|
||||
void UDSFirmwareUploaderWindow::handleReloadNodes()
|
||||
{
|
||||
teslaDir = ui->txtTeslaDir->text().trimmed();
|
||||
loadTeslaNodes();
|
||||
}
|
||||
|
||||
void UDSFirmwareUploaderWindow::handleNodeChanged(int index)
|
||||
{
|
||||
if (index <= 0)
|
||||
return;
|
||||
autoFillFromNode(index - 1);
|
||||
}
|
||||
|
||||
void UDSFirmwareUploaderWindow::autoFillFromNode(int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= teslaNodes.size())
|
||||
return;
|
||||
|
||||
const TeslaNodeInfo &node = teslaNodes[idx];
|
||||
ui->txtTargetID->setText("0x" + QString::number(node.requestId, 16).toUpper());
|
||||
ui->txtResponseID->setText("0x" + QString::number(node.responseId, 16).toUpper());
|
||||
if (!node.securityAlgorithm.isEmpty() && node.securityAlgorithm != "tesla_hash")
|
||||
{
|
||||
ui->ckUseSecurity->setChecked(false);
|
||||
logMessage("Node " + node.name + " uses " + node.securityAlgorithm +
|
||||
" security; security disabled (unsupported)");
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->ckUseSecurity->setChecked(node.useSecurity);
|
||||
}
|
||||
logMessage("Selected node: " + node.name +
|
||||
" (Req: 0x" + QString::number(node.requestId, 16) +
|
||||
", Resp: 0x" + QString::number(node.responseId, 16) + ")");
|
||||
}
|
||||
|
||||
void UDSFirmwareUploaderWindow::timeoutTriggered()
|
||||
{
|
||||
logMessage("TIMEOUT - No response received");
|
||||
|
||||
@@ -4,9 +4,25 @@
|
||||
#include <QDialog>
|
||||
#include <QTimer>
|
||||
#include <QElapsedTimer>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QMap>
|
||||
#include <QVector>
|
||||
#include "can_structs.h"
|
||||
#include "bus_protocols/uds_handler.h"
|
||||
|
||||
struct TeslaNodeInfo
|
||||
{
|
||||
QString name;
|
||||
uint32_t requestId;
|
||||
uint32_t responseId;
|
||||
int securityLevel;
|
||||
bool useSecurity;
|
||||
QString securityAlgorithm;
|
||||
int securityBufferSize;
|
||||
QString dataUpload;
|
||||
};
|
||||
|
||||
namespace Ui {
|
||||
class UDSFirmwareUploaderWindow;
|
||||
}
|
||||
@@ -44,11 +60,17 @@ private slots:
|
||||
void handleLoadFile();
|
||||
void handleStartStop();
|
||||
void handleAbort();
|
||||
void handleBrowseTeslaDir();
|
||||
void handleReloadNodes();
|
||||
void handleNodeChanged(int index);
|
||||
void gotUDSReply(UDS_MESSAGE msg);
|
||||
void timeoutTriggered();
|
||||
void testerPresentTick();
|
||||
|
||||
private:
|
||||
void loadTeslaNodes();
|
||||
void populateNodeCombo();
|
||||
void autoFillFromNode(int index);
|
||||
void logMessage(const QString &msg);
|
||||
void setState(UPLOAD_STATE newState);
|
||||
void startUpload();
|
||||
@@ -84,6 +106,10 @@ private:
|
||||
int maxBlockLength;
|
||||
int currentDataPos;
|
||||
bool useSecurity;
|
||||
|
||||
QString teslaDir;
|
||||
QVector<TeslaNodeInfo> teslaNodes;
|
||||
QMap<QString, uint32_t> messageIdMap;
|
||||
};
|
||||
|
||||
#endif // UDSFIRMWAREUPLOADERWINDOW_H
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>520</width>
|
||||
<width>624</width>
|
||||
<height>520</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -27,41 +27,69 @@
|
||||
<widget class="QSpinBox" name="spinBus"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelTargetID">
|
||||
<widget class="QLabel" name="labelTeslaDir">
|
||||
<property name="text">
|
||||
<string>Target Request ID:</string>
|
||||
<string>Tesla Data Dir:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="txtTargetID">
|
||||
<layout class="QHBoxLayout" name="horizontalLayoutTeslaDir">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txtTeslaDir">
|
||||
<property name="placeholderText">
|
||||
<string>Path to Tesla firmware root directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="labelNode">
|
||||
<property name="text">
|
||||
<string>0x7E0</string>
|
||||
<string>Target ECU:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="cmbTeslaNode"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="labelRespID">
|
||||
<property name="text">
|
||||
<string>Target Response ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="txtResponseID">
|
||||
<property name="text">
|
||||
<string>0x7E8</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="labelTargetID">
|
||||
<property name="text">
|
||||
<string>Target Request ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLineEdit" name="txtTargetID">
|
||||
<property name="text">
|
||||
<string>0x7E0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="labelSession">
|
||||
<property name="text">
|
||||
<string>Session Type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="8" column="1">
|
||||
<widget class="QComboBox" name="cbSessionType">
|
||||
<item>
|
||||
<property name="text">
|
||||
@@ -75,14 +103,28 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="labelSecurity">
|
||||
<property name="text">
|
||||
<string>Security Level:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="labelAddr">
|
||||
<property name="text">
|
||||
<string>Flash Address:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QLineEdit" name="txtFlashAddr">
|
||||
<property name="text">
|
||||
<string>0x08000000</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinSecurityLevel">
|
||||
@@ -109,17 +151,17 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="labelAddr">
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="btnReloadNodes">
|
||||
<property name="text">
|
||||
<string>Flash Address:</string>
|
||||
<string>Reload</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="txtFlashAddr">
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="btnBrowseTeslaDir">
|
||||
<property name="text">
|
||||
<string>0x08000000</string>
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -164,12 +206,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnAbort">
|
||||
<property name="text">
|
||||
<string>Abort</string>
|
||||
</property>
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Abort</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
Reference in New Issue
Block a user