feat(cpn): improve flashing messages (#6588)

This commit is contained in:
Neil Horne
2025-09-19 11:38:08 +10:00
committed by GitHub
parent 8c23ea75c0
commit 5ece93fd0e
8 changed files with 125 additions and 90 deletions
+8 -14
View File
@@ -301,12 +301,12 @@ void FlashFirmwareDialog::startWrite(const QString &filename)
CompanionIcon("write_flash.png"));
auto progress = progressDialog.progress();
progress->addMessage(tr("Flashing with file: %1").arg(filename));
bool checkHw = g.checkHardwareCompatibility();
bool backup = g.backupOnFlash();
FirmwareInterface newfw(filename);
if (g.checkHardwareCompatibility() || backup) {
progress->updateInfoAndMessages(tr("Reading old firmware..."));
readFirmware(
[this, &newfw, progress, checkHw, backup](const QByteArray &_data) {
FirmwareInterface currfw(_data);
@@ -318,42 +318,36 @@ void FlashFirmwareDialog::startWrite(const QString &filename)
}
if (backup) {
progress->addMessage(tr("Backing up old firmware"));
if (!writeFirmwareToFile(this, _data, progress)) {
return;
}
} else {
progress->addMessage(tr("Backup of old firmware not requested"));
}
if (checkHw) {
progress->addMessage(tr("Performing compatibity check"));
if (!newfw.isHardwareCompatible(currfw)) {
QString errMsg(tr("New firmware is not compatible"));
progress->addMessage(errMsg, QtFatalMsg);
progress->setInfo(errMsg);
QString errMsg(tr("New firmware is not compatible with old firmware"));
progress->updateInfoAndMessages(errMsg, QtFatalMsg);
return;
} else {
progress->addMessage(tr("New firmware is compatible"));
}
} else {
progress->addMessage(tr("New firmware compatibity check not requested"));
}
progress->addMessage(tr("Start flashing firmware"));
progress->addMessage(tr("Flashing new firmware"));
writeFirmware(newfw.getFlash(), progress);
},
[this, progress](const QString &err) {
[progress](const QString &err) {
progress->addMessage(tr("Could not read current firmware: %1").arg(err));
},
progress);
} else {
progress->addMessage(tr("Start flashing firmware (no backup or compatiblity check)"));
progress->setInfo(tr("Flashing new firmware..."));
writeFirmware(newfw.getFlash(), progress);
}
progressDialog.exec();
if (isTempFileName(filename)) {
qDebug() << "Removing temporary file" << filename;
qunlink(filename);
}
}
+1 -1
View File
@@ -118,7 +118,7 @@ class SyncProcess : public QObject
void fileCountChanged(int count);
void statusUpdate(const SyncProcess::SyncStatus & status);
void statusMessage(const QString & text, const int & type = QtInfoMsg);
void progressMessage(const QString & text, const int & type = QtInfoMsg, bool richText = false);
void progressMessage(const QString & text, const int & type = QtInfoMsg, bool richText = false, bool updateLast = false);
protected:
enum FileFilterResult { FILE_ALLOW, FILE_OVERSIZE, FILE_EXCLUDE, FILE_LINK_IGNORE };
+67 -59
View File
@@ -38,7 +38,7 @@
constexpr int UF2_TRANSFER_SIZE {4 * 1024};
constexpr char UF2_FIRMWARE_FILENAME[] {"CURRENT.UF2"};
constexpr char UF2_INFO_FILENAME[] {"INFO_UF2.TXT"};
constexpr char UF2_INDEX_FILENAME[] {"INDEX.HTM"};
//constexpr char UF2_INDEX_FILENAME[] {"INDEX.HTM"}; // commented to stop compiler warning
constexpr char UF2_NEW_FIRMWARE_FILENAME[] {"firmware.uf2"};
using namespace std::chrono_literals;
@@ -55,6 +55,8 @@ FirmwareReaderWorker::~FirmwareReaderWorker()
void FirmwareReaderWorker::run()
{
emit statusChanged(tr("Reading..."));
emit progressChanged(0, 100);
isUf2DeviceFound() ? runUf2() : runDfu();
}
@@ -70,7 +72,7 @@ void FirmwareReaderWorker::runDfu()
throw std::runtime_error(tr("More than one DFU device found").toStdString());
}
emit statusChanged("Resetting state...");
emit newMessage(tr("Reset state"));
auto &device = devices[0];
device.reset_state();
@@ -81,6 +83,8 @@ void FirmwareReaderWorker::runDfu()
size_t xfer_size = ctx->get_transfer_size();
size_t bytes_uploaded = 0;
emit newMessage("placeholder"); // anchor for updateMessage
QByteArray data;
while (bytes_uploaded < total_length) {
auto rest_length = uint32_t(total_length - bytes_uploaded);
@@ -88,18 +92,17 @@ void FirmwareReaderWorker::runDfu()
bytes_uploaded += single_xfer_size;
emit progressChanged(bytes_uploaded, total_length);
emit statusChanged(tr("Reading %1 of %2").arg(bytes_uploaded).arg(total_length));
emit updateMessage(tr("Reading %1 of %2").arg(bytes_uploaded).arg(total_length));
data.append(ctx->upload(single_xfer_size));
}
qDebug() << "Reading done";
emit statusChanged("Reading done");
emit statusChanged(tr("Reading finished"));
emit complete(data);
// TODO: handle shouldStop
} catch (const std::exception &e) {
emit error(QString("DFU failed: %1").arg(e.what()));
emit error(tr("DFU failed: %1").arg(e.what()));
}
}
@@ -111,12 +114,6 @@ void FirmwareReaderWorker::runUf2()
QFile fw(path);
char buf[UF2_TRANSFER_SIZE];
const int blockstotal = (fw.size() + UF2_TRANSFER_SIZE - 1) / UF2_TRANSFER_SIZE;
emit progressChanged(0, blockstotal);
emit statusChanged(tr("Reading..."));
qDebug() << "Reading started - file:" << fw.fileName()
<< "size:" << fw.size()
<< "blocks:" << blockstotal;
QByteArray data;
if (fw.open(QIODevice::ReadOnly)) {
@@ -132,14 +129,15 @@ void FirmwareReaderWorker::runUf2()
.arg(fw.errorString()).toStdString());
}
emit newMessage("placeholder"); // anchor for updateMessage
do
{
// qDebug() << "read:" << read;
bytesread += read;
blockcnt++;
data.append(buf, read);
emit progressChanged(blockcnt, blockstotal);
emit statusChanged(tr("Read block %1 of %2").arg(blockcnt).arg(blockstotal));
emit updateMessage(tr("Read block %1 of %2").arg(blockcnt).arg(blockstotal));
read = fw.read(buf, UF2_TRANSFER_SIZE);
} while (read > 0);
@@ -163,12 +161,12 @@ void FirmwareReaderWorker::runUf2()
}
fw.close();
qDebug() << "Reading done";
emit statusChanged("Reading done");
emit statusChanged(tr("Reading finished"));
emit complete(data);
} catch (const std::exception &e) {
emit error(QString("UF2 failed: %1").arg(e.what()));
emit error(tr("UF2 failed: %1").arg(e.what()));
}
}
@@ -185,6 +183,8 @@ FirmwareWriterWorker::~FirmwareWriterWorker()
void FirmwareWriterWorker::run()
{
emit statusChanged(tr("Initialise"));
emit progressChanged(0, 100);
isUf2DeviceFound() ? runUf2() : runDfu();
}
@@ -205,22 +205,19 @@ void FirmwareWriterWorker::runUf2()
QFile newfw(Helpers::concatPath(path, UF2_NEW_FIRMWARE_FILENAME));
int blockstotal = (firmwareData.size() + UF2_TRANSFER_SIZE - 1) / UF2_TRANSFER_SIZE;
emit progressChanged(0, blockstotal);
emit statusChanged(tr("Writing..."));
if (firmwareData.size() == 0)
throw std::runtime_error(tr("No data to write to new firmware file").toStdString());
qDebug() << "Writing started - file:"
<< newfw.fileName()
<< "size:" << firmwareData.size()
<< "blocks:" << blockstotal;
emit statusChanged(tr("Writing..."));
if (newfw.open(QIODevice::ReadWrite)) {
int blockcnt = 0;
qint64 byteswritten = 0;
QByteArray ba = firmwareData.mid(blockcnt * UF2_TRANSFER_SIZE, UF2_TRANSFER_SIZE);
emit newMessage("placeholder"); // anchor for updateMessage
do
{
qint64 written = newfw.write(ba, ba.size());
@@ -239,7 +236,7 @@ void FirmwareWriterWorker::runUf2()
byteswritten += written;
blockcnt++;
emit progressChanged(blockcnt, blockstotal);
emit statusChanged(tr("Writing block %1 of %2").arg(blockcnt).arg(blockstotal));
emit updateMessage(tr("Writing block %1 of %2").arg(blockcnt).arg(blockstotal));
ba.clear(); // just to be on the safe side
ba = firmwareData.mid(blockcnt * UF2_TRANSFER_SIZE, UF2_TRANSFER_SIZE);
@@ -259,12 +256,11 @@ void FirmwareWriterWorker::runUf2()
}
newfw.close();
qDebug() << "Writing done";
emit statusChanged("Writing done");
emit statusChanged(tr("Writing finished"));
emit complete();
} catch (const std::exception &e) {
emit error(QString("UF2 failed: %1").arg(e.what()));
emit error(tr("UF2 failed: %1").arg(e.what()));
}
}
@@ -280,7 +276,7 @@ void FirmwareWriterWorker::runDfu()
throw std::runtime_error(tr("More than one DFU device found").toStdString());
}
emit statusChanged("Resetting state...");
emit newMessage(tr("Reset state"));
auto &device = devices[0];
device.reset_state();
@@ -296,13 +292,13 @@ void FirmwareWriterWorker::runDfu()
device.leave();
emit statusChanged("Flashing done");
emit statusChanged(tr("Writing finished"));
emit complete();
// TODO: handle shouldStop
} catch (const std::exception &e) {
emit error(QString("DFU failed: %1").arg(e.what()));
emit error(tr("DFU failed: %1").arg(e.what()));
}
}
@@ -328,6 +324,8 @@ void FirmwareWriterWorker::writeUf2(DfuDevice &device, const SliceU8 &data)
void FirmwareWriterWorker::writeRegion(const DfuDevice &device, uint32_t addr,
const SliceU8 &data)
{
emit statusChanged(tr("Erasing..."));
auto start_address = addr;
auto end_address = start_address + data.size() - 1;
@@ -335,15 +333,21 @@ void FirmwareWriterWorker::writeRegion(const DfuDevice &device, uint32_t addr,
auto erase_pages = ctx->get_erase_pages();
auto pages = erase_pages.size();
emit newMessage("placeholder"); // anchor for updateMessage
for (size_t i = 0; i < pages; i++) {
updateEraseStatus(i + 1, pages);
ctx->page_erase(erase_pages[i]);
}
emit statusChanged(tr("Writing..."));
size_t bytes_downloaded = 0;
auto data_ptr = data.data();
auto xfer_size = ctx->get_transfer_size();
emit newMessage("placeholder"); // anchor for updateMessage
while (bytes_downloaded < data.size()) {
auto single_xfer_size =
std::min(uint32_t(xfer_size), uint32_t(data.size() - bytes_downloaded));
@@ -358,13 +362,13 @@ void FirmwareWriterWorker::writeRegion(const DfuDevice &device, uint32_t addr,
void FirmwareWriterWorker::updateEraseStatus(size_t page, size_t pages)
{
emit progressChanged(page, pages);
emit statusChanged(tr("Erasing page %1 of %2").arg(page).arg(pages));
emit updateMessage(tr("Erasing page %1 of %2").arg(page).arg(pages));
}
void FirmwareWriterWorker::updateDownloadStatus(size_t bytes, size_t total)
{
emit progressChanged(bytes, total);
emit statusChanged(tr("Writing %1 of %2").arg(bytes).arg(total));
emit updateMessage(tr("Writing %1 of %2").arg(bytes).arg(total));
}
template <typename Duration>
@@ -373,19 +377,19 @@ void FirmwareWriterWorker::rebootAndRediscover(DfuDevice &device, uint32_t addr,
uint32_t reboot_addr,
Duration timeout)
{
emit statusChanged(tr("Rebooting into DFU..."));
emit newMessage(tr("Rebooting into DFU..."));
emit progressChanged(0, 2);
device.reboot(addr, data, reboot_addr);
emit progressChanged(1, 2);
emit statusChanged(tr("Waiting for device to reconnect..."));
emit newMessage(tr("Waiting for device to reconnect..."));
auto start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start < timeout) {
if (device.rediscover()) {
emit statusChanged(tr("Device reconnected"));
emit newMessage(tr("Device reconnected"));
emit progressChanged(2, 2);
return;
}
@@ -418,8 +422,17 @@ void connectProgress(Worker *worker, ProgressWidget *progress)
progress->connect(worker, &Worker::statusChanged, progress,
[progress](const QString& status) {
progress->addMessage(status);
progress->setInfo(status);
progress->updateInfoAndMessages(status);
});
progress->connect(worker, &Worker::newMessage, progress,
[progress](const QString& msg) {
progress->addMessage(msg);
});
progress->connect(worker, &Worker::updateMessage, progress,
[progress](const QString& msg) {
progress->updateLastMessage(msg);
});
progress->connect(worker, &Worker::error, progress,
@@ -466,11 +479,11 @@ bool readFirmware(const QString &filename, ProgressWidget *progress)
[filename](const QByteArray &data) {
QFile file(filename);
if (!file.open(QFile::WriteOnly)) {
qDebug() << "Unable to write to " << filename;
//qDebug() << "Unable to write to " << filename;
} else {
file.write(data);
file.close();
qDebug() << "Firmware written to " << filename;
//qDebug() << "Firmware written to " << filename;
}
},
progress);
@@ -528,13 +541,13 @@ QString getDevicesInfo()
auto device_filter = DfuDeviceFilter::empty_filter();
auto devices = device_filter->find_devices();
if (devices.empty()) {
return QString("No DFU devices found");
return QString(TR("No DFU devices found"));
}
return printDevicesInfo(devices);
} catch (const std::exception& e) {
return QString("Error: %1").arg(e.what());
return TR("Error: %1").arg(e.what());
}
return QString();
@@ -608,13 +621,13 @@ QString findMassStoragePath(const QString &filename, bool onlyPath, ProgressWidg
if (!si.isReady() || si.isReadOnly() || !QString(si.fileSystemType()).contains(fstypeRe))
continue;
qDebug() << "device:" << si.device()
/* qDebug() << "device:" << si.device()
<< "type:" << si.fileSystemType()
<< "block size:" << si.blockSize()
<< "capacity:" << si.bytesTotal() / 1000 / 1000 << "MB"
<< "available:" << si.bytesFree() / 1000/ 1000 << "MB"
<< "root path:" << si.rootPath();
*/
QString temppath = si.rootPath();
QString probefile = Helpers::concatPath(temppath, filename);
qDebug() << "Searching for" << probefile;
@@ -712,13 +725,12 @@ bool isRadioConnected()
bool writeFirmwareToFile(QWidget *parent, const QByteArray &data, ProgressWidget *progress, bool promptForFile)
{
QString msg(TR("Writing radio firmware to file starting..."));
progress->setInfo(msg);
progress->addMessage(msg);
progress->updateInfoAndMessages(TR("Writing..."));
progress->addMessage(TR("Initialise"));
FirmwareInterface fw(data);
if (!fw.isValid()) {
QString errMsg(TR("Firmware is not invalid"));
QString errMsg(TR("Firmware is not valid"));
progress->addMessage(errMsg, QtFatalMsg);
progress->setInfo(errMsg);
return false;
@@ -727,7 +739,6 @@ bool writeFirmwareToFile(QWidget *parent, const QByteArray &data, ProgressWidget
QString backupDir;
if (g.currentProfile().getVariantFromType() == fw.getFlavour()) {
progress->addMessage(TR("Current profile firmware type matches radio"));
backupDir = g.currentProfile().pBackupDir();
}
else {
@@ -764,8 +775,6 @@ bool writeFirmwareToFile(QWidget *parent, const QByteArray &data, ProgressWidget
if (backupDir.isEmpty())
backupDir = g.backupDir();
progress->addMessage(TR("Backup directory: %1").arg(QDir::toNativeSeparators(backupDir)));
// include time in file name as there could be multiple backups in a day
QString filePath = QString("%1/fw-%2-%3-%4.%5")
.arg(backupDir.replace("\\", "/")) // for Windows
@@ -775,7 +784,7 @@ bool writeFirmwareToFile(QWidget *parent, const QByteArray &data, ProgressWidget
.arg(fw.typeFileExtn());
if (promptForFile) {
filePath = QFileDialog::getSaveFileName(parent, TR("Save Firmware to File"),
filePath = QFileDialog::getSaveFileName(parent, TR("Firmware File"),
filePath, getFirmwareFilesFilter());
if (filePath.isEmpty()) {
@@ -786,36 +795,35 @@ bool writeFirmwareToFile(QWidget *parent, const QByteArray &data, ProgressWidget
QtFatalMsg);
return false;
} else if (!QFileInfo::exists(backupDir)) {
progress->addMessage(TR("Backup directory does not exist: %1")
progress->addMessage(TR("Configured backup directory does not exist: %1")
.arg(QDir::toNativeSeparators(backupDir)), QtFatalMsg);
return false;
}
progress->addMessage(TR("Writing firmware to: %1").arg(QDir::toNativeSeparators(filePath)));
QFile f(filePath);
if (!f.open(QIODevice::ReadWrite)) {
progress->addMessage(TR("Error opening file for writing: %1 (reason: %2)")
progress->addMessage(TR("Error opening: %1 (reason: %2)")
.arg(QDir::toNativeSeparators(f.fileName()))
.arg(f.errorString()), QtFatalMsg);
progress->setInfo(TR("Writing failed"));
return false;
}
if (f.write(data) <= 0) {
progress->addMessage(TR("Error writing to file: %1 (reason: %2)")
progress->addMessage(TR("Error writing: %1 (reason: %2)")
.arg(QDir::toNativeSeparators(f.fileName()))
.arg(f.errorString()), QtFatalMsg);
progress->setInfo(TR("Writing failed"));
f.close();
return false;
} else {
msg = TR("Writing radio firmware to file finished");
progress->addMessage(msg);
progress->setInfo(msg);
progress->updateInfoAndMessages(TR("Writing finished"));
}
f.close();
return true;
}
QStringList dfuFileExtensions()
{
return QStringList() << "bin" << "uf2";
+4
View File
@@ -41,6 +41,8 @@ class FirmwareReaderWorker : public QThread
void run() override;
signals:
void newMessage(const QString &msg);
void updateMessage(const QString &msg);
void progressChanged(int value, int total);
void statusChanged(const QString &status);
void error(const QString &error);
@@ -66,6 +68,8 @@ class FirmwareWriterWorker : public QThread
void run() override;
signals:
void newMessage(const QString &msg);
void updateMessage(const QString &msg);
void progressChanged(int value, int total);
void statusChanged(const QString &status);
void error(const QString &error);
+2 -2
View File
@@ -601,7 +601,7 @@ void MainWindow::readFlash()
QMessageBox::critical(this, tr("Detect Radio"),
tr("Radio could not be detected by DFU or UF2 modes") % ".\n" %
tr("Check cable is securely connected and radio lights are illuminated") % ".\n" %
tr("Note: USB mode is not suitable for flashing firmware."));
tr("Note: USB mode is not suitable for reading firmware."));
return;
}
@@ -614,7 +614,7 @@ void MainWindow::readFlash()
[this, progress](const QByteArray &_data) {
writeFirmwareToFile(this, _data, progress, true);
},
[this](const QString &err) {
[](const QString &err) {
qDebug() << tr("Could not read radio firmware: %1").arg(err);
},
progress);
+34 -7
View File
@@ -97,7 +97,7 @@ void ProgressWidget::setValue(int value)
ui->progressBar->setValue(value);
}
void ProgressWidget::addText(const QString &text, const bool richText)
void ProgressWidget::addText(const QString &text, const bool richText, const bool updateLast)
{
if (!m_hasDetails) {
m_hasDetails = true;
@@ -105,6 +105,21 @@ void ProgressWidget::addText(const QString &text, const bool richText)
toggleDetails();
}
// TODO handle rich text
if (!richText && updateLast) {
// delete last message
ui->textEdit->setFocus();
QTextCursor cursor = ui->textEdit->textCursor();
if (cursor.position() > 0) {
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
cursor.deletePreviousChar(); // remove \n otherwise next instruction does nothing
cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
ui->textEdit->setTextCursor(cursor);
}
}
QTextCursor cursor(ui->textEdit->textCursor());
// is the scrollbar at the end?
@@ -121,12 +136,12 @@ void ProgressWidget::addText(const QString &text, const bool richText)
}
}
void ProgressWidget::addHtml(const QString & text)
void ProgressWidget::addHtml(const QString & text, const bool updateLast)
{
addText(text, true);
addText(text, true, updateLast);
}
void ProgressWidget::addMessage(const QString & text, const int & type, bool richText)
void ProgressWidget::addMessage(const QString & text, const int & type, const bool richText, const bool updateLast)
{
QString color;
switch (type) {
@@ -146,14 +161,15 @@ void ProgressWidget::addMessage(const QString & text, const int & type, bool ric
default:
break;
}
if (color.isEmpty()) {
if (richText)
addHtml(text % "<br>");
addHtml(text % "<br>", updateLast);
else
addText(text % "\n");
addText(text % "\n", false, updateLast);
}
else {
addHtml(QString("<font color=%1>").arg(color) % text % "</font><br>");
addHtml(QString("<font color=%1>").arg(color) % text % "</font><br>", updateLast);
}
}
@@ -218,3 +234,14 @@ void ProgressWidget::refresh()
ui->progressBar->update();
ui->textEdit->update();
}
void ProgressWidget::updateInfoAndMessages(const QString & text, const int & type, const bool richText)
{
setInfo(text);
addMessage(text, type, richText);
}
void ProgressWidget::updateLastMessage(const QString & text, const int & type, const bool richText)
{
addMessage(text, type, richText, true);
}
+6 -3
View File
@@ -22,6 +22,7 @@
#pragma once
#include <QWidget>
#include <QTextCursor>
namespace Ui {
class ProgressWidget;
@@ -41,9 +42,9 @@ class ProgressWidget : public QWidget
public slots:
void lock(bool lock);
void addText(const QString &text, const bool richText = false);
void addHtml(const QString &text);
void addMessage(const QString & text, const int & type = QtInfoMsg, bool richText = false);
void addText(const QString &text, const bool richText = false, const bool updateLast = false);
void addHtml(const QString &text, const bool updateLast = false);
void addMessage(const QString & text, const int & type = QtInfoMsg, const bool richText = false, const bool updateLast = false);
void setInfo(const QString &text);
void setMaximum(int value);
void setValue(int value);
@@ -54,6 +55,8 @@ class ProgressWidget : public QWidget
void clearDetails() const;
void forceKeepOpen(bool value);
void refresh();
void updateInfoAndMessages(const QString & text, const int & type = QtInfoMsg, const bool richText = false);
void updateLastMessage(const QString & text, const int & type = QtInfoMsg, const bool richText = false);
signals:
void detailsToggled();
+3 -4
View File
@@ -258,16 +258,15 @@ bool FirmwareInterface::isHardwareCompatible(const FirmwareInterface &previousFi
{
QString newFlavour = getFlavour();
if (newFlavour.isEmpty()) {
if (newFlavour.isEmpty())
return true;
}
QString previousFlavour = previousFirmware.getFlavour();
if (previousFlavour.isEmpty()) {
if (previousFlavour.isEmpty())
return true;
}
qDebug() << "Firmware previous:" << previousFlavour << "new:" << newFlavour;
return (newFlavour == previousFlavour);
}