9#include <QtCore/QTextStream>
10#include <QtCore/QJsonDocument>
11#include <QtCore/QJsonObject>
12#include <QtCore/QFileInfo>
27 if (imageFilename.endsWith(
".bin")) {
29 return _binLoad(imageFilename);
30 }
else if (imageFilename.endsWith(
".px4")) {
32 return _px4Load(imageFilename);
33 }
else if (imageFilename.endsWith(
".apj")) {
35 return _px4Load(imageFilename);
36 }
else if (imageFilename.endsWith(
".ihx")) {
38 return _ihxLoad(imageFilename);
45bool FirmwareImage::_readByteFromStream(QTextStream& stream, uint8_t&
byte)
47 QString hex = stream.read(2);
49 if (hex.length() != 2) {
54 byte = (uint8_t)hex.toInt(&success, 16);
59bool FirmwareImage::_readWordFromStream(QTextStream& stream, uint16_t& word)
61 QString hex = stream.read(4);
63 if (hex.length() != 4) {
68 word = (uint16_t)hex.toInt(&success, 16);
73bool FirmwareImage::_readBytesFromStream(QTextStream& stream, uint8_t byteCount, QByteArray& bytes)
80 if (!_readByteFromStream(stream,
byte)) {
91bool FirmwareImage::_ihxLoad(
const QString& ihxFilename)
96 QFile ihxFile(ihxFilename);
97 if (!ihxFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
98 emit
statusMessage(QString(
"Unable to open firmware file %1, error: %2").arg(ihxFilename, ihxFile.errorString()));
102 QTextStream stream(&ihxFile);
105 if (stream.read(1) !=
":") {
106 emit
statusMessage(
"Incorrectly formatted .ihx file, line does not begin with :");
110 uint8_t blockByteCount;
116 if (!_readByteFromStream(stream, blockByteCount) ||
117 !_readWordFromStream(stream, address) ||
118 !_readByteFromStream(stream, recordType) ||
119 !_readBytesFromStream(stream, blockByteCount, bytes) ||
120 !_readByteFromStream(stream, crc)) {
121 emit
statusMessage(tr(
"Incorrectly formatted line in .ihx file, line too short"));
125 if (!(recordType == 0 || recordType == 1)) {
126 emit
statusMessage(tr(
"Unsupported record type in file: %1").arg(recordType));
130 if (recordType == 0) {
131 bool appendToLastBlock =
false;
135 if (_ihxBlocks.length()) {
136 int lastBlockIndex = _ihxBlocks.length() - 1;
138 if (_ihxBlocks[lastBlockIndex].address + _ihxBlocks[lastBlockIndex].bytes.length() == address) {
139 appendToLastBlock =
true;
143 if (appendToLastBlock) {
144 _ihxBlocks[_ihxBlocks.length() - 1].bytes += bytes;
148 IntelHexBlock_t block;
150 block.address = address;
154 qCDebug(FirmwareUpgradeVerboseLog) << QString(
"_ihxLoad - new block - address:%1 size:%2 block:%3").arg(address).arg(blockByteCount).arg(
ihxBlockCount());
157 _imageSize += blockByteCount;
158 }
else if (recordType == 1) {
160 qCDebug(FirmwareUpgradeLog) << QString(
"_ihxLoad - EOF");
175 if (boardId == firmwareId ) {
180 if (firmwareId == 9) result =
true;
188bool FirmwareImage::_px4Load(
const QString& imageFilename)
194 QFile px4File(imageFilename);
195 if (!px4File.open(QIODevice::ReadOnly | QIODevice::Text)) {
196 emit
statusMessage(tr(
"Unable to open firmware file %1, error: %2").arg(imageFilename, px4File.errorString()));
200 QByteArray bytes = px4File.readAll();
202 QJsonDocument doc = QJsonDocument::fromJson(bytes);
205 emit
statusMessage(tr(
"Supplied file is not a valid JSON document"));
209 QJsonObject px4Json = doc.object();
213 QStringList requiredKeys;
214 requiredKeys << _jsonBoardIdKey << _jsonImageKey << _jsonImageSizeKey;
222 QList<QJsonValue::Type> types;
223 keys << _jsonBoardIdKey << _jsonParamXmlSizeKey << _jsonParamXmlKey << _jsonAirframeXmlSizeKey << _jsonAirframeXmlKey << _jsonImageSizeKey << _jsonImageKey << _jsonMavAutopilotKey;
224 types << QJsonValue::Double << QJsonValue::Double << QJsonValue::String << QJsonValue::Double << QJsonValue::String << QJsonValue::Double << QJsonValue::String << QJsonValue::Double;
230 uint32_t firmwareBoardId = (uint32_t)px4Json.value(_jsonBoardIdKey).toInt();
232 emit
statusMessage(tr(
"Downloaded firmware board id does not match hardware board id: %1 != %2").arg(firmwareBoardId).arg(_boardId));
237 MAV_AUTOPILOT firmwareType = (MAV_AUTOPILOT)px4Json[_jsonMavAutopilotKey].toInt(MAV_AUTOPILOT_PX4);
238 emit
statusMessage(QString(
"MAV_AUTOPILOT = %1").arg(firmwareType));
241 QByteArray decompressedBytes;
242 bool success = _decompressJsonValue(px4Json,
244 _jsonParamXmlSizeKey,
251 if (parameterFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
252 qint64 bytesWritten = parameterFile.write(decompressedBytes);
253 if (bytesWritten != decompressedBytes.length()) {
254 emit
statusMessage(tr(
"Write failed for parameter meta data file, error: %1").arg(parameterFile.errorString()));
255 parameterFile.close();
256 QFile::remove(parameterFilename);
258 parameterFile.close();
261 emit
statusMessage(tr(
"Unable to open parameter meta data file %1 for writing, error: %2").arg(parameterFilename, parameterFile.errorString()));
269 success = _decompressJsonValue(px4Json,
271 _jsonAirframeXmlSizeKey,
279 if (airframeFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
280 qint64 bytesWritten = airframeFile.write(decompressedBytes);
281 if (bytesWritten != decompressedBytes.length()) {
283 emit
statusMessage(tr(
"Write failed for airframe meta data file, error: %1").arg(airframeFile.errorString()));
284 airframeFile.close();
285 QFile::remove(airframeFilename);
287 airframeFile.close();
290 emit
statusMessage(tr(
"Unable to open airframe meta data file %1 for writing, error: %2").arg(airframeFilename, airframeFile.errorString()));
295 _imageSize = px4Json.value(QString(
"image_size")).toInt();
296 success = _decompressJsonValue(px4Json,
306 while ((decompressedBytes.length() % 4) != 0) {
307 decompressedBytes.append(
static_cast<char>(
static_cast<unsigned char>(0xFF)));
311 QDir imageDir = QFileInfo(imageFilename).dir();
312 QString decompressFilename = imageDir.filePath(
"PX4FlashUpgrade.bin");
315 if (!
decompressFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
316 emit
statusMessage(tr(
"Unable to open decompressed file %1 for writing, error: %2").arg(decompressFilename,
decompressFile.errorString()));
321 if (bytesWritten != decompressedBytes.length()) {
327 _binFilename = decompressFilename;
333bool FirmwareImage::_decompressJsonValue(
const QJsonObject& jsonObject,
334 const QByteArray& jsonDocBytes,
335 const QString& sizeKey,
336 const QString& bytesKey,
337 QByteArray& decompressedBytes)
340 if (!jsonObject.contains(sizeKey)) {
341 emit
statusMessage(QString(
"Firmware file missing %1 key").arg(sizeKey));
344 int decompressedSize = jsonObject.value(QString(sizeKey)).toInt();
345 if (decompressedSize == 0) {
346 emit
statusMessage(tr(
"Firmware file has invalid decompressed size for %1").arg(sizeKey));
356 QStringList parts = QString(jsonDocBytes).split(QString(
"\"%1\": \"").arg(bytesKey));
357 if (parts.length() == 1) {
358 emit
statusMessage(tr(
"Could not find compressed bytes for %1 in Firmware file").arg(bytesKey));
361 parts = parts.last().split(
"\"");
362 if (parts.length() == 1) {
363 emit
statusMessage(tr(
"Incorrectly formed compressed bytes section for %1 in Firmware file").arg(bytesKey));
369 raw.append((
unsigned char)((decompressedSize >> 24) & 0xFF));
370 raw.append((
unsigned char)((decompressedSize >> 16) & 0xFF));
371 raw.append((
unsigned char)((decompressedSize >> 8) & 0xFF));
372 raw.append((
unsigned char)((decompressedSize >> 0) & 0xFF));
374 QByteArray raw64 = parts.first().toUtf8();
375 raw.append(QByteArray::fromBase64(raw64));
376 decompressedBytes = qUncompress(raw);
378 if (decompressedBytes.length() == 0) {
379 emit
statusMessage(tr(
"Firmware file has 0 length %1").arg(bytesKey));
382 if (decompressedBytes.length() != decompressedSize) {
383 emit
statusMessage(tr(
"Size for decompressed %1 does not match stored size: Expected(%1) Actual(%2)").arg(decompressedSize).arg(decompressedBytes.length()));
387 emit
statusMessage(tr(
"Successfully decompressed %1").arg(bytesKey));
394 return _ihxBlocks.length();
403 address = _ihxBlocks[index].address;
404 bytes = _ihxBlocks[index].bytes;
411bool FirmwareImage::_binLoad(
const QString& imageFilename)
413 QFile binFile(imageFilename);
414 if (!binFile.open(QIODevice::ReadOnly)) {
415 emit
statusMessage(tr(
"Unabled to open firmware file %1, %2").arg(imageFilename, binFile.errorString()));
419 _imageSize = (uint32_t)binFile.size();
423 _binFilename = imageFilename;
static const int boardIDPX4FMUV3
static void _cachePX4MetaDataFile(const QString &metaDataFile)
bool isCompatible(uint32_t boardId, uint32_t firmwareId)
bool load(const QString &imageFilename, uint32_t boardId)
void statusMessage(const QString &warningtring)
bool ihxGetBlock(uint16_t index, uint16_t &address, QByteArray &bytes) const
FirmwareImage(QObject *parent=0)
uint16_t ihxBlockCount(void) const
static QString cachedParameterMetaDataFile()
static QString cachedAirframeMetaDataFile()
bool validateKeyTypes(const QJsonObject &jsonObject, const QStringList &keys, const QList< QJsonValue::Type > &types, QString &errorString)
bool validateRequiredKeys(const QJsonObject &jsonObject, const QStringList &keys, QString &errorString)
Validates that all listed keys are present in the object.
bool decompressFile(const QString &inputPath, const QString &outputPath, Format format, ProgressCallback progress, qint64 maxDecompressedBytes)