1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-14 03:49:52 +03:00

[Companion] Work in progress - QTreeView in MdiChild (#4197)

[Companion] QTreeView in MdiChild, Horus and X7 support added, fixes for #4224
This commit is contained in:
Bertrand Songis 2017-01-15 17:45:57 +01:00 committed by GitHub
parent d6b84371a9
commit a65aa3d6c7
60 changed files with 3539 additions and 3153 deletions

View file

@ -185,7 +185,6 @@ set(companion_MOC_HDRS
flashfirmwaredialog.h
flasheepromdialog.h
downloaddialog.h
modelslist.h
mdichild.h
mainwindow.h
radionotfound.h

View file

@ -20,21 +20,16 @@
#include <QApplication>
#include <QTranslator>
#include <QLocale>
#include <QString>
#include <QDir>
#include <QFileInfo>
#include <QSplashScreen>
#include <QThread>
#include <iostream>
#if defined(JOYSTICKS) || defined(SIMU_AUDIO)
#include <SDL.h>
#undef main
#endif
#include "mainwindow.h"
#include "version.h"
#include "eeprominterface.h"
#include "appdata.h"
#include "storage.h"
#if defined _MSC_VER || !defined __GNUC__
#include <windows.h>
@ -106,7 +101,8 @@ int main(int argc, char *argv[])
fprintf(stderr, "ERROR: couldn't initialize SDL: %s\n", SDL_GetError());
}
#endif
registerStorageFactories();
registerEEpromInterfaces();
registerOpenTxFirmwares();
registerSimulators();

View file

@ -21,6 +21,7 @@
#include "contributorsdialog.h"
#include "ui_htmldialog.h"
#include "helpers.h"
#include <QFile>
ContributorsDialog::ContributorsDialog(QWidget * parent):
QDialog(parent),

View file

@ -22,6 +22,7 @@
#define _EEPROMIMPORTEXPORT_H_
#include "customdebug.h"
#include <QBitArray>
#define DIM(arr) (sizeof((arr))/sizeof((arr)[0]))

View file

@ -18,11 +18,6 @@
* GNU General Public License for more details.
*/
#include <stdio.h>
#include <list>
#include <float.h>
#include <QtWidgets>
#include <stdlib.h>
#include "eeprominterface.h"
#include "firmwares/er9x/er9xinterface.h"
#include "firmwares/ersky9x/ersky9xinterface.h"
@ -32,9 +27,41 @@
#include "helpers.h"
#include "wizarddata.h"
#include "firmwareinterface.h"
#include <stdio.h>
#include <list>
#include <float.h>
#include <QtWidgets>
#include <stdlib.h>
#include <bitset>
std::list<QString> EEPROMWarnings;
int getEEpromSize(BoardEnum board)
{
switch (board) {
case BOARD_STOCK:
return EESIZE_STOCK;
case BOARD_M128:
return EESIZE_M128;
case BOARD_MEGA2560:
case BOARD_GRUVIN9X:
return EESIZE_GRUVIN9X;
case BOARD_SKY9X:
return EESIZE_SKY9X;
case BOARD_9XRPRO:
case BOARD_AR9X:
return EESIZE_9XRPRO;
case BOARD_TARANIS_X7:
case BOARD_TARANIS_X9D:
case BOARD_TARANIS_X9DP:
case BOARD_TARANIS_X9E:
case BOARD_FLAMENCO:
return EESIZE_TARANIS;
default:
return 0; // unlimited
}
}
const uint8_t chout_ar[] = { // First number is 0..23 -> template setup, Second is relevant channel out
1,2,3,4 , 1,2,4,3 , 1,3,2,4 , 1,3,4,2 , 1,4,2,3 , 1,4,3,2,
2,1,3,4 , 2,1,4,3 , 2,3,1,4 , 2,3,4,1 , 2,4,1,3 , 2,4,3,1,
@ -631,7 +658,7 @@ QString RawSwitch::toString() const
BoardEnum board = GetEepromInterface()->getBoard();
switch(type) {
case SWITCH_TYPE_SWITCH:
if (IS_HORUS(board) || IS_TARANIS(board)) {
if (IS_HORUS_OR_TARANIS(board)) {
div_t qr = div(index-1, 3);
Firmware::Switch sw = GetCurrentFirmware()->getSwitch(qr.quot);
const char * positions[] = { ARROW_UP, "-", ARROW_DOWN };
@ -1402,6 +1429,11 @@ void ModelData::clearMixes()
mixData[i].clear();
}
RadioData::RadioData()
{
models.resize(GetCurrentFirmware()->getCapability(Models));
}
void ModelData::clear()
{
memset(this, 0, sizeof(ModelData));
@ -1412,7 +1444,7 @@ void ModelData::clear()
moduleData[1].ppm.delay = 300;
moduleData[2].ppm.delay = 300;
int board = GetEepromInterface()->getBoard();
if (IS_TARANIS(board) || IS_HORUS(board)) {
if (IS_HORUS_OR_TARANIS(board)) {
moduleData[0].protocol = PULSES_PXX_XJT_X16;
moduleData[1].protocol = PULSES_OFF;
}
@ -1627,16 +1659,10 @@ int ModelData::getChannelsMax(bool forceExtendedLimits) const
}
QList<EEPROMInterface *> eepromInterfaces;
void registerEEpromInterfaces()
{
eepromInterfaces.push_back(new OpenTxEepromInterface(BOARD_STOCK));
eepromInterfaces.push_back(new OpenTxEepromInterface(BOARD_M128));
eepromInterfaces.push_back(new OpenTxEepromInterface(BOARD_GRUVIN9X));
eepromInterfaces.push_back(new OpenTxEepromInterface(BOARD_SKY9X));
eepromInterfaces.push_back(new OpenTxEepromInterface(BOARD_9XRPRO));
eepromInterfaces.push_back(new OpenTxEepromInterface(BOARD_TARANIS_X9D));
eepromInterfaces.push_back(new OpenTxEepromInterface(BOARD_TARANIS_X9DP));
eepromInterfaces.push_back(new OpenTxEepromInterface(BOARD_TARANIS_X9E));
registerOpenTxEEpromInterfaces();
// eepromInterfaces.push_back(new Ersky9xInterface());
// eepromInterfaces.push_back(new Er9xInterface());
}
@ -1764,7 +1790,7 @@ const int Firmware::getFlashSize()
}
}
Firmware * GetFirmware(QString id)
Firmware * GetFirmware(const QString & id)
{
foreach(Firmware * firmware, firmwares) {
Firmware * result = firmware->getFirmwareVariant(id);

File diff suppressed because it is too large Load diff

View file

@ -92,11 +92,6 @@ Er9xGeneral::operator GeneralSettings ()
result.vBatWarn = vBatWarn;
result.txVoltageCalibration = txVoltageCalibration;
result.trainer = trainer;
result.blightinv=blightinv;
result.stickScroll=stickScroll;
result.crosstrim=crosstrim;
result.hideNameOnSplash=hideNameOnSplash;
result.enablePpmsim=enablePpmsim;
result.view = std::min((uint8_t)4, view);
result.disableThrottleWarning = disableThrottleWarning;
@ -123,9 +118,6 @@ Er9xGeneral::operator GeneralSettings ()
result.preBeep = preBeep;
result.flashBeep = flashBeep;
result.splashMode = disableSplashScreen;
result.disablePotScroll=(disablePotScroll==1);
result.disableBG=(disableBG==1);
result.frskyinternalalarm = frskyinternalalarm;
result.backlightMode = 0;
if (lightSw == 22) {

View file

@ -18,11 +18,12 @@
* GNU General Public License for more details.
*/
#include <iostream>
#include "er9xinterface.h"
#include "er9xeeprom.h"
#include "rlefile.h"
#include "appdata.h"
// #include "appdata.h"
// #include <iostream>
#include <bitset>
#define FILE_TYP_GENERAL 1
#define FILE_TYP_MODEL 2
@ -31,8 +32,8 @@
#define FILE_MODEL(n) (1+n)
Er9xInterface::Er9xInterface():
EEPROMInterface(BOARD_STOCK),
efile(new RleFile())
EEPROMInterface(BOARD_STOCK),
efile(new RleFile())
{
}
@ -46,20 +47,6 @@ const char * Er9xInterface::getName()
return "Er9x";
}
const int Er9xInterface::getEEpromSize()
{
QString avrMCU = g.mcu();
if (avrMCU==QString("m128")) {
return 2*EESIZE_STOCK;
}
return EESIZE_STOCK;
}
const int Er9xInterface::getMaxModels()
{
return 16;
}
inline void applyStickModeToModel(Er9xModelData & model, unsigned int mode)
{
for (int i=0; i<2; i++) {
@ -92,6 +79,7 @@ inline void applyStickModeToModel(Er9xModelData & model, unsigned int mode)
model.swashCollectiveSource = applyStickMode(model.swashCollectiveSource, mode);
}
#if 0
unsigned long Er9xInterface::loadxml(RadioData &radioData, QDomDocument &doc)
{
std::cout << "trying er9x xml import... ";
@ -108,7 +96,7 @@ unsigned long Er9xInterface::loadxml(RadioData &radioData, QDomDocument &doc)
radioData.generalSettings=er9xGeneral;
std::cout << "version " << (unsigned int)er9xGeneral.myVers << " ";
}
for (int i=0; i<getMaxModels(); i++) {
for (int i=0; i<getCapability(Models); i++) {
Er9xModelData er9xModel;
memset(&er9xModel,0,sizeof(er9xModel));
if(loadModelDataXML(&doc, &er9xModel, i)) {
@ -120,6 +108,7 @@ unsigned long Er9xInterface::loadxml(RadioData &radioData, QDomDocument &doc)
errors.set(ALL_OK);
return errors.to_ulong();
}
#endif
unsigned long Er9xInterface::load(RadioData &radioData, const uint8_t *eeprom, int size)
{
@ -127,7 +116,7 @@ unsigned long Er9xInterface::load(RadioData &radioData, const uint8_t *eeprom, i
std::bitset<NUM_ERRORS> errors;
if (size != getEEpromSize()) {
if (size != getEEpromSize(BOARD_STOCK)) {
std::cout << "wrong size\n";
errors.set(WRONG_SIZE);
return errors.to_ulong();
@ -176,7 +165,7 @@ unsigned long Er9xInterface::load(RadioData &radioData, const uint8_t *eeprom, i
}
radioData.generalSettings = er9xGeneral;
for (int i=0; i<getMaxModels(); i++) {
for (int i=0; i<getCapability(Models); i++) {
Er9xModelData er9xModel;
efile->openRd(FILE_MODEL(i));
if (!efile->readRlc1((uint8_t*)&er9xModel, sizeof(Er9xModelData))) {
@ -225,11 +214,11 @@ int Er9xInterface::isAvailable(PulsesProtocol prot, int port)
void Er9xInterface::appendTextElement(QDomDocument * qdoc, QDomElement * pe, QString name, QString value)
{
QDomElement e = qdoc->createElement(name);
QDomText t = qdoc->createTextNode(name);
t.setNodeValue(value);
e.appendChild(t);
pe->appendChild(e);
QDomElement e = qdoc->createElement(name);
QDomText t = qdoc->createTextNode(name);
t.setNodeValue(value);
e.appendChild(t);
pe->appendChild(e);
}
void Er9xInterface::appendNumberElement(QDomDocument * qdoc, QDomElement * pe,QString name, int value, bool forceZeroWrite)
@ -273,13 +262,13 @@ QDomElement Er9xInterface::getModelDataXML(QDomDocument * qdoc, Er9xModelData *
bool Er9xInterface::loadRadioSettingsDataXML(QDomDocument * qdoc, Er9xGeneral * tgen)
{
//look for "GENERAL_DATA" tag
// look for "GENERAL_DATA" tag
QDomElement gde = qdoc->elementsByTagName("GENERAL_DATA").at(0).toElement();
if(gde.isNull()) // couldn't find
return false;
//load cdata into tgen
// load cdata into tgen
QDomNode n = gde.elementsByTagName("Data").at(0).firstChild();// get all children in Data
while (!n.isNull()) {
if (n.isCDATASection()) {
@ -291,17 +280,17 @@ bool Er9xInterface::loadRadioSettingsDataXML(QDomDocument * qdoc, Er9xGeneral *
}
n = n.nextSibling();
}
//check version?
// check version?
return true;
}
bool Er9xInterface::loadModelDataXML(QDomDocument * qdoc, Er9xModelData * tmod, int modelNum)
{
//look for MODEL_DATA with modelNum attribute.
//if modelNum = -1 then just pick the first one
// look for MODEL_DATA with modelNum attribute.
// if modelNum = -1 then just pick the first one
QDomNodeList ndl = qdoc->elementsByTagName("MODEL_DATA");
//cycle through nodes to find correct model number
// cycle through nodes to find correct model number
QDomNode k = ndl.at(0);
if(modelNum>=0) {
while(!k.isNull()) {
@ -312,11 +301,11 @@ bool Er9xInterface::loadModelDataXML(QDomDocument * qdoc, Er9xModelData * tmod,
}
}
if(k.isNull()) // couldn't find
if (k.isNull()) // couldn't find
return false;
//load cdata into tgen
// load cdata into tgen
QDomNode n = k.toElement().elementsByTagName("Data").at(0).firstChild();// get all children in Data
while (!n.isNull()) {
if (n.isCDATASection()) {
@ -328,6 +317,16 @@ bool Er9xInterface::loadModelDataXML(QDomDocument * qdoc, Er9xModelData * tmod,
}
n = n.nextSibling();
}
//check version?
// check version?
return true;
}
int Er9xInterface::getCapability(Capability capability)
{
switch (capability) {
case Models:
return 16;
default:
return 0;
}
}

View file

@ -37,10 +37,6 @@ class Er9xInterface : public EEPROMInterface
virtual const char * getName();
virtual const int getEEpromSize();
virtual const int getMaxModels();
virtual unsigned long load(RadioData &, const uint8_t * eeprom, int size);
virtual unsigned long loadBackup(RadioData &, const uint8_t * eeprom, int esize, int index);
@ -57,6 +53,8 @@ class Er9xInterface : public EEPROMInterface
virtual int getSize(const GeneralSettings &settings);
virtual int isAvailable(PulsesProtocol proto, int port=0);
virtual int getCapability(Capability capability);
protected:

View file

@ -143,8 +143,6 @@ Ersky9xGeneral::operator GeneralSettings ()
result.preBeep = preBeep;
result.flashBeep = flashBeep;
result.splashMode = disableSplashScreen;
result.disablePotScroll=(disablePotScroll==1);
result.disableBG=(disableBG==1);
result.templateSetup = templateSetup;
result.PPM_Multiplier = PPM_Multiplier;
getEEPROMString(result.ownerName, ownerName, sizeof(ownerName));

View file

@ -18,10 +18,11 @@
* GNU General Public License for more details.
*/
#include <iostream>
#include "ersky9xinterface.h"
#include "ersky9xeeprom.h"
#include "rlefile.h"
#include <iostream>
#include <bitset>
#define FILE_TYP_GENERAL 1
#define FILE_TYP_MODEL 2
@ -48,16 +49,6 @@ const char * Ersky9xInterface::getName()
return "Ersky9x";
}
const int Ersky9xInterface::getEEpromSize()
{
return EESIZE_SKY9X;
}
const int Ersky9xInterface::getMaxModels()
{
return 20;
}
inline void applyStickModeToModel(Ersky9xModelData_v10 & model, unsigned int mode)
{
for (int i=0; i<2; i++) {
@ -122,6 +113,7 @@ inline void applyStickModeToModel(Ersky9xModelData_v11 & model, unsigned int mod
model.swashCollectiveSource = applyStickMode(model.swashCollectiveSource, mode);
}
#if 0
unsigned long Ersky9xInterface::loadxml(RadioData &radioData, QDomDocument &doc)
{
std::cout << "trying ersky9x xml import... ";
@ -138,7 +130,7 @@ unsigned long Ersky9xInterface::loadxml(RadioData &radioData, QDomDocument &doc)
radioData.generalSettings=ersky9xGeneral;
std::cout << "version " << (unsigned int)ersky9xGeneral.myVers << " ";
}
for(int i=0; i<getMaxModels(); i++) {
for(int i=0; i<getCapability(Models); i++) {
if (ersky9xGeneral.myVers == 10) {
if (!loadModelDataXML<Ersky9xModelData_v10>(&doc, &radioData.models[i], i, radioData.generalSettings.stickMode+1)) {
std::cout << "ko\n";
@ -158,6 +150,7 @@ unsigned long Ersky9xInterface::loadxml(RadioData &radioData, QDomDocument &doc)
errors.set(ALL_OK);
return errors.to_ulong();
}
#endif
unsigned long Ersky9xInterface::load(RadioData &radioData, const uint8_t *eeprom, int size)
{
@ -206,14 +199,14 @@ unsigned long Ersky9xInterface::load(RadioData &radioData, const uint8_t *eeprom
}
radioData.generalSettings = ersky9xGeneral;
for (int i=0; i<getMaxModels(); i++) {
for (int i=0; i<getCapability(Models); i++) {
uint8_t buffer[4096];
uint size;
memset(buffer,0,sizeof(buffer));
efile->openRd(FILE_MODEL(i));
// if (!efile->readRlc2((uint8_t*)&ersky9xModel, sizeof(Ersky9xModelData))) {
size=efile->readRlc2(buffer, 4096);
size = efile->readRlc2(buffer, 4096);
if (!size) {
radioData.models[i].clear();
}
@ -317,13 +310,13 @@ QDomElement Ersky9xInterface::getModelDataXML(QDomDocument * qdoc, Ersky9xModelD
bool Ersky9xInterface::loadRadioSettingsDataXML(QDomDocument * qdoc, Ersky9xGeneral * tgen)
{
//look for "GENERAL_DATA" tag
// look for "GENERAL_DATA" tag
QDomElement gde = qdoc->elementsByTagName("GENERAL_DATA").at(0).toElement();
if(gde.isNull()) // couldn't find
return false;
//load cdata into tgen
// load cdata into tgen
QDomNode n = gde.elementsByTagName("Data").at(0).firstChild();// get all children in Data
while (!n.isNull()) {
if (n.isCDATASection()) {
@ -335,7 +328,7 @@ bool Ersky9xInterface::loadRadioSettingsDataXML(QDomDocument * qdoc, Ersky9xGene
}
n = n.nextSibling();
}
//check version?
// check version?
return true;
}
@ -345,11 +338,11 @@ bool Ersky9xInterface::loadModelDataXML(QDomDocument * qdoc, ModelData *model, i
T ersky9xModel;
memset(&ersky9xModel, 0, sizeof(ersky9xModel));
//look for MODEL_DATA with modelNum attribute.
// look for MODEL_DATA with modelNum attribute.
//if modelNum = -1 then just pick the first one
QDomNodeList ndl = qdoc->elementsByTagName("MODEL_DATA");
//cycle through nodes to find correct model number
// cycle through nodes to find correct model number
QDomNode k = ndl.at(0);
if (modelNum>=0) {
while(!k.isNull()) {
@ -363,7 +356,7 @@ bool Ersky9xInterface::loadModelDataXML(QDomDocument * qdoc, ModelData *model, i
if (k.isNull()) // couldn't find
return false;
//load cdata into tgen
// load cdata into tgen
QDomNode n = k.toElement().elementsByTagName("Data").at(0).firstChild();// get all children in Data
while (!n.isNull()) {
if (n.isCDATASection()) {
@ -380,3 +373,13 @@ bool Ersky9xInterface::loadModelDataXML(QDomDocument * qdoc, ModelData *model, i
*model = ersky9xModel;
return true;
}
int Ersky9xInterface::getCapability(Capability capability)
{
switch (capability) {
case Models:
return 20;
default:
return 0;
}
}

View file

@ -37,16 +37,11 @@ class Ersky9xInterface : public EEPROMInterface
virtual const char * getName();
virtual const int getEEpromSize();
virtual const int getMaxModels();
virtual unsigned long load(RadioData &, const uint8_t * eeprom, int size);
virtual unsigned long loadBackup(RadioData &, const uint8_t * eeprom, int esize, int index);
virtual unsigned long loadxml(RadioData &radioData, QDomDocument &doc);
virtual int save(uint8_t * eeprom, RadioData & radioData, uint8_t version=0, uint32_t variant=0)
{
return 0;
@ -57,6 +52,8 @@ class Ersky9xInterface : public EEPROMInterface
virtual int getSize(const GeneralSettings & settings);
virtual int isAvailable(PulsesProtocol proto, int port=0);
virtual int getCapability(Capability capability);
protected:

View file

@ -35,13 +35,14 @@
#define MAX_SLIDERS(board) (IS_HORUS(board) ? 4 : (board == BOARD_TARANIS_X7 ? 0 : (IS_TARANIS(board) ? (IS_TARANIS_X9E(board) ? 4 : 2) : 0)))
#define MAX_MOUSE_ANALOGS(board) (IS_HORUS(board) ? 2 : 0)
#define MAX_SWITCHES(board, version) (IS_HORUS(board) ? 8 : (board == BOARD_TARANIS_X7 ? 6 : (IS_TARANIS(board) ? (IS_TARANIS_X9E(board) ? 18 : 8) : 7)))
#define MAX_SWITCHES_POSITION(board, version) (IS_HORUS(board) ? 24 : (board == BOARD_TARANIS_X7 ? 6*3 : (IS_TARANIS_X9E(board) ? 18*3 : (IS_TARANIS(board) ? 8*3 : 9))))
#define MAX_SWITCHES_POSITION(board, version) (IS_TARANIS_X7(board) ? 6*3 : (IS_TARANIS_X9E(board) ? 18*3 : (IS_HORUS_OR_TARANIS(board) ? 8*3 : 9)))
#define MAX_ROTARY_ENCODERS(board) (IS_2560(board) ? 2 : (IS_SKY9X(board) ? 1 : 0))
#define MAX_FLIGHT_MODES(board, version) (IS_ARM(board) ? 9 : (IS_DBLRAM(board, version) ? 6 : 5))
#define MAX_TIMERS(board, version) ((IS_ARM(board) && version >= 217) ? 3 : 2)
#define MAX_MIXERS(board, version) (IS_ARM(board) ? 64 : 32)
#define MAX_CHANNELS(board, version) (IS_ARM(board) ? 32 : 16)
#define MAX_EXPOS(board, version) (IS_ARM(board) ? ((IS_TARANIS(board) && version >= 216) ? 64 : 32) : (IS_DBLRAM(board, version) ? 16 : 14))
#define MAX_TRIMS(board) (IS_HORUS(board) ? 6 : 4)
#define MAX_EXPOS(board, version) (IS_ARM(board) ? ((IS_HORUS_OR_TARANIS(board) && version >= 216) ? 64 : 32) : (IS_DBLRAM(board, version) ? 16 : 14))
#define MAX_LOGICAL_SWITCHES(board, version) (IS_ARM(board) ? (version >= 218 ? 64 : 32) : ((IS_DBLEEPROM(board, version) && version<217) ? 15 : 12))
#define MAX_CUSTOM_FUNCTIONS(board, version) (IS_ARM(board) ? (version >= 216 ? 64 : 32) : (IS_DBLEEPROM(board, version) ? 24 : 16))
#define MAX_CURVES(board, version) (IS_ARM(board) ? ((HAS_LARGE_LCD(board) && version >= 216) ? 32 : 16) : 8)
@ -58,7 +59,7 @@
inline int switchIndex(int i, BoardEnum board, unsigned int version)
{
bool afterrelease21March2013 = IS_AFTER_RELEASE_21_MARCH_2013(board, version);
if (!IS_TARANIS(board) && afterrelease21March2013)
if (!IS_HORUS_OR_TARANIS(board) && afterrelease21March2013)
return (i<=3 ? i+3 : (i<=6 ? i-3 : i));
else
return i;
@ -102,7 +103,7 @@ class SwitchesConversionTable: public ConversionTable {
val++;
}
if (IS_TARANIS(board) && version >= 216) {
if (IS_HORUS_OR_TARANIS(board) && version >= 216) {
for (int i=1; i<=MAX_POTS(board, version)*6; i++) {
addConversion(RawSwitch(SWITCH_TYPE_MULTIPOS_POT, -i), -val+offset);
addConversion(RawSwitch(SWITCH_TYPE_MULTIPOS_POT, i), val++);
@ -110,7 +111,7 @@ class SwitchesConversionTable: public ConversionTable {
}
if (version >= 216) {
for (int i=1; i<=8; i++) {
for (int i=1; i<=2*MAX_TRIMS(board); i++) {
addConversion(RawSwitch(SWITCH_TYPE_TRIM, -i), -val+offset);
addConversion(RawSwitch(SWITCH_TYPE_TRIM, i), val++);
}
@ -245,7 +246,7 @@ class SourcesConversionTable: public ConversionTable {
}
}
for (int i=0; i<CPN_MAX_STICKS+MAX_POTS(board, version)+MAX_SLIDERS(board); i++) {
for (int i=0; i<CPN_MAX_STICKS+MAX_POTS(board, version)+MAX_SLIDERS(board)+MAX_MOUSE_ANALOGS(board); i++) {
addConversion(RawSource(SOURCE_TYPE_STICK, i), val++);
}
@ -267,7 +268,7 @@ class SourcesConversionTable: public ConversionTable {
}
if (afterrelease21March2013) {
for (int i=0; i<CPN_MAX_STICKS; i++)
for (int i=0; i<MAX_TRIMS(board); i++)
addConversion(RawSource(SOURCE_TYPE_TRIM, i), val++);
}

View file

@ -18,13 +18,14 @@
* GNU General Public License for more details.
*/
#include <iostream>
#include <QMessageBox>
#include "opentxinterface.h"
#include "opentxeeprom.h"
#include "rlefile.h"
#include "appdata.h"
#include "storage_sdcard.h"
#include <bitset>
#include <QMessageBox>
#include <QTime>
#include <QUrl>
#define OPENTX_FIRMWARE_DOWNLOADS "http://downloads-22.open-tx.org/firmware"
#define OPENTX_NIGHT_FIRMWARE_DOWNLOADS "http://downloads-22.open-tx.org/nightly/firmware"
@ -89,44 +90,27 @@ const char * OpenTxEepromInterface::getName()
}
}
const int OpenTxEepromInterface::getEEpromSize()
uint32_t OpenTxEepromInterface::getFourCC()
{
switch (board) {
case BOARD_STOCK:
return EESIZE_STOCK;
case BOARD_M128:
return EESIZE_M128;
case BOARD_MEGA2560:
case BOARD_GRUVIN9X:
return EESIZE_GRUVIN9X;
case BOARD_SKY9X:
return EESIZE_SKY9X;
case BOARD_9XRPRO:
case BOARD_AR9X:
return EESIZE_9XRPRO;
case BOARD_HORUS:
return 0x3478746F;
case BOARD_TARANIS_X7:
case BOARD_TARANIS_X9D:
case BOARD_TARANIS_X9DP:
case BOARD_TARANIS_X9E:
case BOARD_FLAMENCO:
return EESIZE_TARANIS;
return 0x3378746F;
case BOARD_SKY9X:
case BOARD_AR9X:
return 0x3278746F;
case BOARD_MEGA2560:
case BOARD_GRUVIN9X:
return 0x3178746F;
default:
return 0; // unlimited
return 0;
}
}
const int OpenTxEepromInterface::getMaxModels()
{
if (IS_ARM(board))
return 60;
else if (board == BOARD_M128)
return 30;
else if (IS_2560(board))
return 30;
else
return 16;
}
bool OpenTxEepromInterface::loadRadioSettingsFromRLE(GeneralSettings & settings, RleFile * rleFile, uint8_t version)
{
QByteArray data(sizeof(settings), 0); // GeneralSettings should be always bigger than the EEPROM struct
@ -162,12 +146,15 @@ bool OpenTxEepromInterface::loadModelFromRLE(ModelData & model, RleFile * rleFil
template <class T, class M>
bool OpenTxEepromInterface::saveToByteArray(const T & src, QByteArray & data, uint8_t version)
{
if (version == 0) {
version = getLastDataVersion(getBoard());
}
QByteArray raw;
M manager((T&)src, board, version, 0);
manager.Dump();
// manager.Dump();
manager.Export(raw);
data.resize(8);
*((uint32_t*)&data.data()[0]) = 0x3178396F;
*((uint32_t*)&data.data()[0]) = getFourCC();
data[4] = version;
data[5] = 'M';
*((uint16_t*)&data.data()[6]) = raw.size();
@ -180,13 +167,19 @@ bool OpenTxEepromInterface::loadFromByteArray(T & dest, const QByteArray & data,
{
M manager(dest, board, version, variant);
manager.Import(data);
manager.Dump(); // Dumps the structure so that it's easy to check with firmware datastructs.h
// manager.Dump(); // Dumps the structure so that it's easy to check with firmware datastructs.h
return true;
}
template <class T, class M>
bool OpenTxEepromInterface::loadFromByteArray(T & dest, const QByteArray & data)
{
uint32_t fourcc = *((uint32_t*)&data.data()[0]);
if (getFourCC() != fourcc) {
qDebug() << QString().sprintf("%s: Wrong fourcc %x vs %x", getName(), fourcc, getFourCC());
return false;
}
qDebug() << QString().sprintf("%s: OK", getName());
uint8_t version = data[4];
QByteArray raw = data.right(data.size() - 8);
return loadFromByteArray<T, M>(dest, raw, version);
@ -215,84 +208,26 @@ bool OpenTxEepromInterface::saveModel(unsigned int index, ModelData &model, uint
return (sz == eeprom.size());
}
unsigned long OpenTxEepromInterface::loadxml(RadioData &radioData, QDomDocument &doc)
QList<OpenTxEepromInterface *> opentxEEpromInterfaces;
void registerOpenTxEEpromInterface(BoardEnum board)
{
std::bitset<NUM_ERRORS> errors;
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
OpenTxEepromInterface * interface = new OpenTxEepromInterface(board);
opentxEEpromInterfaces.push_back(interface);
eepromInterfaces.push_back(interface);
}
int OpenTxEepromInterface::loadFile(RadioData & radioData, const QString & filename)
void registerOpenTxEEpromInterfaces()
{
StorageSdcard storage;
storage.read(filename);
// Radio settings
qDebug() << "Radio settings:" << storage.radio.size();
loadFromByteArray<GeneralSettings, OpenTxGeneralData>(radioData.generalSettings, storage.radio);
// Models
int modelIndex = 0;
QString modelList = QString(storage.modelList);
QList<QByteArray> lines = storage.modelList.split('\n');
QString category = QObject::tr("Unknown");
foreach (const QByteArray & line, lines) {
if (!line.isEmpty()) {
if (line.startsWith('[') && line.endsWith(']')) {
category = line.mid(1, line.size() - 2);
}
else {
qDebug() << "Loading" << line;
foreach (const ModelFile &model, storage.models) {
if (line == model.filename) {
loadFromByteArray<ModelData, OpenTxModelData>(radioData.models[modelIndex], model.data);
strncpy(radioData.models[modelIndex].filename, line.data(), sizeof(radioData.models[modelIndex].filename));
strncpy(radioData.models[modelIndex].category, category.toStdString().c_str(), sizeof(radioData.models[modelIndex].category));
radioData.models[modelIndex].used = true;
modelIndex++;
}
}
}
}
}
return 0;
}
int OpenTxEepromInterface::saveFile(const RadioData & radioData, const QString & filename)
{
StorageSdcard storage;
uint8_t version = getLastDataVersion(board);
// models.txt
storage.modelList = QByteArray();
QString currentCategory = "";
// radio.bin
saveToByteArray<GeneralSettings, OpenTxGeneralData>(radioData.generalSettings, storage.radio, version);
// all models
for (int i=0; i<CPN_MAX_MODELS; i++) {
const ModelData & model = radioData.models[i];
if (!model.isEmpty()) {
QString modelFilename = model.filename;
QByteArray modelData;
saveToByteArray<ModelData, OpenTxModelData>(model, modelData, version);
ModelFile modelFile = { modelFilename, modelData };
storage.models.append(modelFile);
QString modelCategory = model.category;
if (currentCategory != modelCategory) {
storage.modelList.append(QString().sprintf("[%s]\n", model.category));
currentCategory = modelCategory;
}
storage.modelList.append(modelFilename + "\n");
}
}
storage.write(filename);
return 0;
registerOpenTxEEpromInterface(BOARD_STOCK);
registerOpenTxEEpromInterface(BOARD_M128);
registerOpenTxEEpromInterface(BOARD_GRUVIN9X);
registerOpenTxEEpromInterface(BOARD_SKY9X);
registerOpenTxEEpromInterface(BOARD_9XRPRO);
registerOpenTxEEpromInterface(BOARD_TARANIS_X9D);
registerOpenTxEEpromInterface(BOARD_TARANIS_X9DP);
registerOpenTxEEpromInterface(BOARD_TARANIS_X9E);
registerOpenTxEEpromInterface(BOARD_TARANIS_X7);
registerOpenTxEEpromInterface(BOARD_HORUS);
}
unsigned long OpenTxEepromInterface::load(RadioData &radioData, const uint8_t * eeprom, int size)
@ -301,7 +236,7 @@ unsigned long OpenTxEepromInterface::load(RadioData &radioData, const uint8_t *
std::bitset<NUM_ERRORS> errors;
if (size != getEEpromSize()) {
if (size != getEEpromSize(board)) {
if (size == 4096) {
int notnull = false;
for (int i = 2048; i < 4096; i++) {
@ -321,7 +256,7 @@ unsigned long OpenTxEepromInterface::load(RadioData &radioData, const uint8_t *
}
}
else {
std::cout << " wrong size (" << size << "/" << getEEpromSize() << ")\n";
std::cout << " wrong size (" << size << "/" << getEEpromSize(board) << ")\n";
errors.set(WRONG_SIZE);
return errors.to_ulong();
}
@ -362,7 +297,7 @@ unsigned long OpenTxEepromInterface::load(RadioData &radioData, const uint8_t *
}
std::cout << " variant " << radioData.generalSettings.variant;
for (int i = 0; i < getMaxModels(); i++) {
for (int i = 0; i < GetCurrentFirmware()->getCapability(Models); i++) {
if (!loadModelFromRLE(radioData.models[i], efile, i, version, radioData.generalSettings.variant)) {
std::cout << " ko\n";
errors.set(UNKNOWN_ERROR);
@ -389,7 +324,7 @@ uint8_t OpenTxEepromInterface::getLastDataVersion(BoardEnum board)
}
}
int OpenTxEepromInterface::save(uint8_t * eeprom, RadioData & radioData, uint8_t version, uint32_t variant)
int OpenTxEepromInterface::save(uint8_t * eeprom, const RadioData & radioData, uint8_t version, uint32_t variant)
{
EEPROMWarnings.clear();
@ -397,7 +332,7 @@ int OpenTxEepromInterface::save(uint8_t * eeprom, RadioData & radioData, uint8_t
version = getLastDataVersion(board);
}
int size = getEEpromSize();
int size = getEEpromSize(board);
efile->EeFsCreate(eeprom, size, board, version);
@ -408,14 +343,14 @@ int OpenTxEepromInterface::save(uint8_t * eeprom, RadioData & radioData, uint8_t
variant |= TARANIS_X9E_VARIANT;
}
int result = saveRadioSettings<OpenTxGeneralData>(radioData.generalSettings, board, version, variant);
int result = saveRadioSettings<OpenTxGeneralData>((GeneralSettings &)radioData.generalSettings, board, version, variant);
if (!result) {
return 0;
}
for (int i = 0; i < getMaxModels(); i++) {
for (int i = 0; i < GetCurrentFirmware()->getCapability(Models); i++) {
if (!radioData.models[i].isEmpty()) {
result = saveModel<OpenTxModelData>(i, radioData.models[i], version, variant);
result = saveModel<OpenTxModelData>(i, (ModelData &)radioData.models[i], version, variant);
if (!result) {
return 0;
}
@ -501,6 +436,15 @@ Firmware * OpenTxFirmware::getFirmwareVariant(const QString &id)
int OpenTxFirmware::getCapability(Capability capability)
{
switch (capability) {
case Models:
if (IS_ARM(board))
return 60;
else if (board == BOARD_M128)
return 30;
else if (IS_2560(board))
return 30;
else
return 16;
case Imperial:
if (IS_ARM(board))
return 0;
@ -533,12 +477,12 @@ int OpenTxFirmware::getCapability(Capability capability)
case FlightModesHaveFades:
return 1;
case Heli:
if (IS_TARANIS(board) || IS_HORUS(board))
if (IS_HORUS_OR_TARANIS(board))
return id.contains("noheli") ? 0 : 1;
else
return id.contains("heli") ? 1 : 0;
case Gvars:
if (IS_TARANIS(board) || IS_HORUS(board))
if (IS_HORUS_OR_TARANIS(board))
return id.contains("nogvars") ? 0 : 9;
else if (id.contains("gvars"))
return IS_ARM(board) ? 9 : 5;
@ -547,7 +491,7 @@ int OpenTxFirmware::getCapability(Capability capability)
case ModelName:
return (IS_HORUS(board) ? 15 : (HAS_LARGE_LCD(board) ? 12 : 10));
case FlightModesName:
return ((IS_TARANIS(board) || IS_HORUS(board)) ? 10 : 6);
return (IS_HORUS_OR_TARANIS(board) ? 10 : 6);
case GvarsName:
return (IS_9X(board) ? 0 : 6);
case GvarsInCS:
@ -598,14 +542,14 @@ int OpenTxFirmware::getCapability(Capability capability)
return 18;
else if (board == BOARD_TARANIS_X7)
return 6;
else if (IS_TARANIS(board) || board == BOARD_HORUS)
else if (IS_HORUS_OR_TARANIS(board))
return 8;
else
return 7;
case SwitchesPositions:
if (IS_TARANIS_X9E(board))
return 18 * 3;
else if (IS_TARANIS(board))
else if (IS_HORUS_OR_TARANIS(board))
return 8 * 3;
else
return 9;
@ -652,7 +596,7 @@ int OpenTxFirmware::getCapability(Capability capability)
case Haptic:
return (IS_2560(board) || IS_SKY9X(board) || IS_TARANIS_PLUS(board) || id.contains("haptic"));
case ModelTrainerEnable:
if (IS_TARANIS(board))
if (IS_HORUS_OR_TARANIS(board))
return 1;
else
return 0;
@ -669,15 +613,15 @@ int OpenTxFirmware::getCapability(Capability capability)
case NumCurves:
return (HAS_LARGE_LCD(board) ? 32 : (IS_ARM(board) ? 16 : 8));
case HasMixerNames:
return (IS_ARM(board) ? (IS_TARANIS(board) ? 8 : 6) : false);
return (IS_ARM(board) ? (IS_HORUS_OR_TARANIS(board) ? 8 : 6) : false);
case HasExpoNames:
return (IS_ARM(board) ? (IS_TARANIS(board) ? 8 : 6) : false);
return (IS_ARM(board) ? (IS_HORUS_OR_TARANIS(board) ? 8 : 6) : false);
case HasNoExpo:
return (IS_TARANIS(board) ? false : true);
return (IS_HORUS_OR_TARANIS(board) ? false : true);
case ChannelsName:
return (IS_ARM(board) ? (HAS_LARGE_LCD(board) ? 6 : 4) : 0);
case HasCvNames:
return (IS_TARANIS(board) ? 1 : 0);
return (IS_HORUS_OR_TARANIS(board) ? 1 : 0);
case Telemetry:
if (IS_2560(board) || IS_ARM(board) || id.contains("frsky") || id.contains("telemetrez"))
return TM_HASTELEMETRY | TM_HASOFFSET | TM_HASWSHH;
@ -686,7 +630,10 @@ int OpenTxFirmware::getCapability(Capability capability)
case TelemetryBars:
return 1;
case TelemetryCustomScreens:
return IS_ARM(board) ? 4 : 2;
if (IS_HORUS(board))
return 0;
else
return IS_ARM(board) ? 4 : 2;
case TelemetryCustomScreensFieldsPerLine:
return HAS_LARGE_LCD(board) ? 3 : 2;
case NoTelemetryProtocol:
@ -732,6 +679,8 @@ int OpenTxFirmware::getCapability(Capability capability)
case LcdWidth:
if (IS_HORUS(board))
return 480;
else if (IS_TARANIS_X7(board))
return 128;
else if (IS_TARANIS(board))
return 212;
else
@ -744,6 +693,8 @@ int OpenTxFirmware::getCapability(Capability capability)
case LcdDepth:
if (IS_HORUS(board))
return 16;
else if (IS_TARANIS_X7(board))
return 1;
else if (IS_TARANIS(board))
return 4;
else
@ -869,7 +820,7 @@ Firmware::Switch OpenTxFirmware::getSwitch(unsigned int index)
{SWITCH_TOGGLE, "SH"}};
return switches[index];
}
else if (IS_TARANIS(board) || board == BOARD_HORUS) {
else if (IS_HORUS_OR_TARANIS(board)) {
const Switch switches[] = {{SWITCH_3POS, "SA"},
{SWITCH_3POS, "SB"},
{SWITCH_3POS, "SC"},
@ -904,7 +855,7 @@ Firmware::Switch OpenTxFirmware::getSwitch(unsigned int index)
QTime OpenTxFirmware::getMaxTimerStart()
{
if (IS_TARANIS(board) || IS_HORUS(board))
if (IS_HORUS_OR_TARANIS(board))
return QTime(23, 59, 59);
else if (IS_ARM(board))
return QTime(8, 59, 59);
@ -925,7 +876,7 @@ bool OpenTxFirmware::isTelemetrySourceAvailable(int source)
int OpenTxFirmware::isAvailable(PulsesProtocol proto, int port)
{
if (IS_TARANIS(board) || IS_HORUS(board)) {
if (IS_HORUS_OR_TARANIS(board)) {
switch (port) {
case 0:
switch (proto) {
@ -1604,3 +1555,46 @@ void unregisterOpenTxFirmwares()
delete f;
}
}
template <class T, class M>
bool loadFromByteArray(T & dest, const QByteArray & data)
{
foreach(OpenTxEepromInterface * eepromInterface, opentxEEpromInterfaces) {
if (eepromInterface->loadFromByteArray<T, M>(dest, data)) {
return true;
}
}
return false;
}
template <class T, class M>
bool saveToByteArray(const T & dest, QByteArray & data)
{
BoardEnum board = GetCurrentFirmware()->getBoard();
foreach(OpenTxEepromInterface * eepromInterface, opentxEEpromInterfaces) {
if (eepromInterface->getBoard() == board) {
return eepromInterface->saveToByteArray<T, M>(dest, data);
}
}
return false;
}
bool loadModelFromByteArray(ModelData & model, const QByteArray & data)
{
return loadFromByteArray<ModelData, OpenTxModelData>(model, data);
}
bool loadRadioSettingsFromByteArray(GeneralSettings & settings, const QByteArray & data)
{
return loadFromByteArray<GeneralSettings, OpenTxGeneralData>(settings, data);
}
bool writeModelToByteArray(const ModelData & model, QByteArray & data)
{
return saveToByteArray<ModelData, OpenTxModelData>(model, data);
}
bool writeRadioSettingsToByteArray(const GeneralSettings & settings, QByteArray & data)
{
return saveToByteArray<GeneralSettings, OpenTxGeneralData>(settings, data);
}

View file

@ -33,28 +33,18 @@ class OpenTxEepromInterface : public EEPROMInterface
virtual ~OpenTxEepromInterface();
virtual const int getEEpromSize();
virtual const int getMaxModels();
virtual unsigned long load(RadioData &, const uint8_t * eeprom, int size);
bool loadModelFromBackup(ModelData & model, const uint8_t * data, unsigned int size, uint8_t version, uint32_t variant);
virtual unsigned long loadBackup(RadioData &, const uint8_t * eeprom, int esize, int index);
virtual unsigned long loadxml(RadioData & radioData, QDomDocument & doc);
virtual int save(uint8_t * eeprom, RadioData & radioData, uint8_t version=0, uint32_t variant=0);
virtual int save(uint8_t * eeprom, const RadioData & radioData, uint8_t version=0, uint32_t variant=0);
virtual int getSize(const ModelData &);
virtual int getSize(const GeneralSettings &);
virtual int loadFile(RadioData & radioData, const QString & filename);
virtual int saveFile(const RadioData & radioData, const QString & filename);
protected:
const char * getName();
@ -66,11 +56,12 @@ class OpenTxEepromInterface : public EEPROMInterface
template <class T, class M>
bool loadFromByteArray(T & dest, const QByteArray & data, uint8_t version, uint32_t variant=0);
public:
template <class T, class M>
bool loadFromByteArray(T & dest, const QByteArray & data);
template <class T, class M>
bool saveToByteArray(const T & src, QByteArray & data, uint8_t version);
bool saveToByteArray(const T & src, QByteArray & data, uint8_t version=0);
bool loadRadioSettingsFromRLE(GeneralSettings & settings, RleFile * rleFile, uint8_t version);
@ -84,6 +75,8 @@ class OpenTxEepromInterface : public EEPROMInterface
uint8_t getLastDataVersion(BoardEnum board);
uint32_t getFourCC();
RleFile * efile;
};
@ -152,5 +145,14 @@ class OpenTxFirmware: public Firmware
void registerOpenTxFirmwares();
void unregisterOpenTxFirmwares();
void registerOpenTxEEpromInterfaces();
extern QList<OpenTxEepromInterface *> opentxEEpromInterfaces;
bool loadModelFromByteArray(ModelData & model, const QByteArray & data);
bool loadRadioSettingsFromByteArray(GeneralSettings & settings, const QByteArray & data);
bool writeModelToByteArray(const ModelData & model, QByteArray & data);
bool writeRadioSettingsToByteArray(const GeneralSettings & settings, QByteArray & data);
#endif // _OPENTXINTERFACE_H_

View file

@ -22,12 +22,10 @@
#include "ui_flasheepromdialog.h"
#include "eeprominterface.h"
#include "helpers.h"
#include "firmwareinterface.h"
#include "hexinterface.h"
#include "storage.h"
#include "appdata.h"
#include "progressdialog.h"
#include "radiointerface.h"
#include "storage_eeprom.h"
#include "splashlibrarydialog.h"
FlashEEpromDialog::FlashEEpromDialog(QWidget *parent, const QString &filename):
@ -107,88 +105,17 @@ void FlashEEpromDialog::on_eepromLoad_clicked()
}
}
int FlashEEpromDialog::getEEpromVersion(const QString &filename)
int FlashEEpromDialog::getEEpromVersion(const QString & filename)
{
int result = -1;
int eeprom_size = 0;
if (filename.isEmpty()) {
return -1;
}
QFile file(filename);
if (!file.exists()) {
QMessageBox::warning(this, tr("Error"), tr("Unable to find file %1!").arg(filename));
return -1;
}
QByteArray eeprom(EESIZE_MAX, 0);
int fileType = getFileType(filename);
#if 0
if (fileType==FILE_TYPE_XML) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::critical(this, tr("Error"),tr("Error opening file %1:\n%2.").arg(filename).arg(file.errorString()));
return -1;
}
QTextStream inputStream(&file);
XmlInterface(inputStream).load(testData);
}
else
#endif
if (fileType==FILE_TYPE_HEX || fileType==FILE_TYPE_EEPE) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::warning(this, tr("Error"),tr("Error opening file %1:\n%2.").arg(filename).arg(file.errorString()));
return -1;
}
QDomDocument doc(ER9X_EEPROM_FILE_TYPE);
bool xmlOK = doc.setContent(&file);
if (xmlOK) {
RadioData * radioData = new RadioData();
std::bitset<NUM_ERRORS> errors((unsigned long long)LoadEepromXml(*radioData, doc));
if (!errors.test(ALL_OK)) {
QMessageBox::warning(this, tr("Error"), tr("Invalid Models and Settings File %1").arg(filename));
}
else {
result = radioData->generalSettings.version;
}
delete radioData;
return result;
}
file.reset();
QTextStream inputStream(&file);
if (fileType==FILE_TYPE_EEPE) { // read EEPE file header
QString hline = inputStream.readLine();
if (hline!=EEPE_EEPROM_FILE_HEADER) {
QMessageBox::warning(this, tr("Error"), tr("Invalid Models and Settings File %1").arg(filename));
return -1;
}
}
eeprom_size = HexInterface(inputStream).load((uint8_t *)eeprom.data(), EESIZE_MAX);
}
else if (fileType==FILE_TYPE_BIN) { //read binary
eeprom_size = file.size();
if (!file.open(QFile::ReadOnly)) { //reading binary file - TODO HEX support
QMessageBox::warning(this, tr("Error"), tr("Error opening file %1:\n%2.").arg(filename).arg(file.errorString()));
return -1;
}
int len = file.read(eeprom.data(), eeprom_size);
if (len != eeprom_size) {
QMessageBox::warning(this, tr("Error"), tr("Error reading file %1:\n%2.").arg(filename).arg(file.errorString()));
return -1;
}
}
RadioData * radioData = new RadioData();
std::bitset<NUM_ERRORS> errors((unsigned long long)LoadEeprom(*radioData, (const uint8_t *)eeprom.data(), eeprom_size));
if (eeprom_size == 0 || !errors.test(ALL_OK)) {
QMessageBox::warning(this, tr("Error"), tr("Invalid Models and Settings file %1").arg(filename));
}
else {
QSharedPointer<RadioData> radioData = QSharedPointer<RadioData>(new RadioData());
Storage storage(filename);
if (storage.load(*radioData)) {
result = radioData->generalSettings.version;
}
delete radioData;
else {
QMessageBox::warning(this, tr("Error"), storage.error());
}
return result;
}
@ -196,16 +123,16 @@ bool FlashEEpromDialog::patchCalibration()
{
QString calib = g.profile[g.id()].stickPotCalib();
QString trainercalib = g.profile[g.id()].trainerCalib();
int potsnum=GetCurrentFirmware()->getCapability(Pots);
int8_t txVoltageCalibration=(int8_t) g.profile[g.id()].txVoltageCalibration();
int8_t txCurrentCalibration=(int8_t) g.profile[g.id()].txCurrentCalibration();
int8_t PPM_Multiplier=(int8_t) g.profile[g.id()].ppmMultiplier();
int potsnum = GetCurrentFirmware()->getCapability(Pots);
int8_t txVoltageCalibration = (int8_t) g.profile[g.id()].txVoltageCalibration();
int8_t txCurrentCalibration = (int8_t) g.profile[g.id()].txCurrentCalibration();
int8_t PPM_Multiplier = (int8_t) g.profile[g.id()].ppmMultiplier();
if ((calib.length()==(CPN_MAX_STICKS+potsnum)*12) && (trainercalib.length()==16)) {
QString Byte;
int16_t byte16;
bool ok;
for (int i=0; i<(CPN_MAX_STICKS+potsnum); i++) {
for (int i=0; i<CPN_MAX_STICKS+potsnum; i++) {
Byte=calib.mid(i*12,4);
byte16=(int16_t)Byte.toInt(&ok,16);
if (ok)
@ -296,7 +223,7 @@ void FlashEEpromDialog::on_burnButton_clicked()
if (patch) {
QString filename = generateProcessUniqueTempFileName("temp.bin");
QFile file(filename);
uint8_t *eeprom = (uint8_t*)malloc(GetEepromInterface()->getEEpromSize());
uint8_t *eeprom = (uint8_t*)malloc(getEEpromSize(GetCurrentFirmware()->getBoard()));
int eeprom_size = GetEepromInterface()->save(eeprom, *radioData, 0, GetCurrentFirmware()->getVariantNumber());
if (!eeprom_size) {
QMessageBox::warning(this, tr("Error"), tr("Cannot write file %1:\n%2.").arg(filename).arg(file.errorString()));

View file

@ -21,16 +21,13 @@
#include "flashfirmwaredialog.h"
#include "ui_flashfirmwaredialog.h"
#include "appdata.h"
#include "storage_eeprom.h"
#include "eeprominterface.h"
#include "firmwareinterface.h"
#include "process_flash.h"
#include "helpers.h"
#include "hexinterface.h"
#include "progressdialog.h"
#include "radiointerface.h"
#include "progresswidget.h"
#include "splashlibrarydialog.h"
#include "storage.h"
#if defined _MSC_VER || !defined __GNUC__
#include <windows.h>
@ -242,7 +239,7 @@ void FlashFirmwareDialog::on_burnButton_clicked()
}
// write the customized firmware
QString tempFile;
if (getFileType(fwName) == FILE_TYPE_HEX)
if (getStorageType(fwName) == STORAGE_TYPE_HEX)
tempFile = generateProcessUniqueTempFileName("flash.hex");
else
tempFile = generateProcessUniqueTempFileName("flash.bin");

View file

@ -23,22 +23,12 @@
void HardwarePanel::setupSwitchConfig(int index, QLabel *label, AutoLineEdit *name, AutoComboBox *type, bool threePos = true)
{
bool enabled = false;
if (IS_TARANIS(firmware->getBoard())) {
if (IS_TARANIS_X9E(firmware->getBoard())) {
enabled = true;
type->addItem(tr("None"), Firmware::SWITCH_NONE);
}
else if (index < 8) {
enabled = true;
}
}
if (enabled) {
if (IS_STM32(firmware->getBoard()) && index < firmware->getCapability(Switches)) {
type->addItem(tr("None"), Firmware::SWITCH_NONE);
type->addItem(tr("2 Positions Toggle"), Firmware::SWITCH_TOGGLE);
type->addItem(tr("2 Positions"), Firmware::SWITCH_2POS);
if (threePos) type->addItem(tr("3 Positions"), Firmware::SWITCH_3POS);
if (threePos)
type->addItem(tr("3 Positions"), Firmware::SWITCH_3POS);
name->setField(generalSettings.switchName[index], 3, this);
type->setField(generalSettings.switchConfig[index], this);
}
@ -51,20 +41,8 @@ void HardwarePanel::setupSwitchConfig(int index, QLabel *label, AutoLineEdit *na
void HardwarePanel::setupPotConfig(int index, QLabel *label, AutoLineEdit *name, AutoComboBox *type)
{
bool enabled = false;
if (IS_TARANIS_X9E(firmware->getBoard()) && index < 4) {
label->setText(RawSource(SOURCE_TYPE_STICK, index+CPN_MAX_STICKS).toString());
enabled = true;
}
else if (IS_TARANIS_PLUS(firmware->getBoard()) && index < 3) {
enabled = true;
}
else if (IS_TARANIS(firmware->getBoard()) && index < 2) {
enabled = true;
}
if (enabled) {
if (IS_STM32(firmware->getBoard()) && index < firmware->getCapability(Pots)) {
label->setText(RawSource(SOURCE_TYPE_STICK, CPN_MAX_STICKS+index).toString());
type->addItem(tr("None"), GeneralSettings::POT_NONE);
type->addItem(tr("Pot with detent"), GeneralSettings::POT_WITH_DETENT);
type->addItem(tr("Multipos switch"), GeneralSettings::POT_MULTIPOS_SWITCH);
@ -81,21 +59,8 @@ void HardwarePanel::setupPotConfig(int index, QLabel *label, AutoLineEdit *name,
void HardwarePanel::setupSliderConfig(int index, QLabel *label, AutoLineEdit *name, AutoComboBox *type)
{
bool enabled = false;
if (IS_TARANIS(firmware->getBoard()) && index < 2) {
type->setEnabled(false);
enabled = true;
}
else if (IS_TARANIS_X9E(firmware->getBoard()) && index < 4) {
enabled = true;
}
if (IS_TARANIS_X9E(firmware->getBoard())) {
label->setText(RawSource(SOURCE_TYPE_STICK, index+CPN_MAX_STICKS+4).toString());
}
if (enabled) {
if (IS_STM32(firmware->getBoard()) && index < firmware->getCapability(Sliders)) {
label->setText(RawSource(SOURCE_TYPE_STICK, CPN_MAX_STICKS+firmware->getCapability(Pots)+index).toString());
type->addItem(tr("None"), GeneralSettings::SLIDER_NONE);
type->addItem(tr("Slider with detent"), GeneralSettings::SLIDER_WITH_DETENT);
name->setField(generalSettings.sliderName[index], 3, this);
@ -114,7 +79,7 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings
{
ui->setupUi(this);
if (IS_TARANIS(firmware->getBoard())) {
if (IS_STM32(firmware->getBoard())) {
ui->rudName->setField(generalSettings.stickName[0], 3, this);
ui->eleName->setField(generalSettings.stickName[1], 3, this);
ui->thrName->setField(generalSettings.stickName[2], 3, this);

View file

@ -31,10 +31,9 @@
#include "appdata.h"
#include "helpers.h"
#include "modeledit/modeledit.h"
#include "simulatordialog.h"
#include "simulatorinterface.h"
#include "firmwareinterface.h"
#include "storage/storage_sdcard.h"
#include "storage/sdcard.h"
Stopwatch gStopwatch("global");
@ -377,13 +376,14 @@ void populateGvarUseCB(QComboBox *b, unsigned int phase)
void populateSwitchCB(QComboBox *b, const RawSwitch & value, const GeneralSettings & generalSettings, SwitchContext context)
{
BoardEnum board = GetCurrentFirmware()->getBoard();
RawSwitch item;
b->clear();
if (context != MixesContext && context != GlobalFunctionsContext) {
// !FMx
if (IS_ARM(GetCurrentFirmware()->getBoard())) {
if (IS_ARM(board)) {
for (int i=-GetCurrentFirmware()->getCapability(FlightModes); i<0; i++) {
item = RawSwitch(SWITCH_TYPE_FLIGHT_MODE, i);
b->addItem(item.toString(), item.toValue());
@ -424,7 +424,7 @@ void populateSwitchCB(QComboBox *b, const RawSwitch & value, const GeneralSettin
for (int i=-GetCurrentFirmware()->getCapability(SwitchesPositions); i<0; i++) {
item = RawSwitch(SWITCH_TYPE_SWITCH, i);
if (IS_TARANIS(GetCurrentFirmware()->getBoard()) && !generalSettings.switchPositionAllowedTaranis(i)){
if (IS_HORUS_OR_TARANIS(board) && !generalSettings.switchPositionAllowedTaranis(i)) {
continue;
}
b->addItem(item.toString(), item.toValue());
@ -446,7 +446,7 @@ void populateSwitchCB(QComboBox *b, const RawSwitch & value, const GeneralSettin
for (int i=1; i<=GetCurrentFirmware()->getCapability(SwitchesPositions); i++) {
item = RawSwitch(SWITCH_TYPE_SWITCH, i);
if (IS_TARANIS(GetCurrentFirmware()->getBoard()) && !generalSettings.switchPositionAllowedTaranis(i)){
if (IS_HORUS_OR_TARANIS(board) && !generalSettings.switchPositionAllowedTaranis(i)) {
continue;
}
b->addItem(item.toString(), item.toValue());
@ -496,7 +496,7 @@ void populateSwitchCB(QComboBox *b, const RawSwitch & value, const GeneralSettin
// FMx
if (context != MixesContext && context != GlobalFunctionsContext) {
if (IS_ARM(GetCurrentFirmware()->getBoard())) {
if (IS_ARM(board)) {
for (int i=1; i<=GetCurrentFirmware()->getCapability(FlightModes); i++) {
item = RawSwitch(SWITCH_TYPE_FLIGHT_MODE, i);
b->addItem(item.toString(), item.toValue());
@ -612,7 +612,7 @@ void populateSourceCB(QComboBox *b, const RawSource & source, const GeneralSetti
for (int i=0; i<GetCurrentFirmware()->getCapability(Switches); i++) {
item = RawSource(SOURCE_TYPE_SWITCH, i);
b->addItem(item.toString(model), item.toValue());
if (IS_TARANIS(GetCurrentFirmware()->getBoard()) && !generalSettings.switchSourceAllowedTaranis(i)) {
if (IS_HORUS_OR_TARANIS(board) && !generalSettings.switchSourceAllowedTaranis(i)) {
QModelIndex index = b->model()->index(b->count()-1, 0);
QVariant v(0);
b->model()->setData(index, v, Qt::UserRole - 1);
@ -837,12 +837,13 @@ void startSimulation(QWidget * parent, RadioData & radioData, int modelIdx)
if (board == BOARD_HORUS && HORUS_READY_FOR_RELEASE()) {
dialog = new SimulatorDialogHorus(parent, simulator, flags);
GetEepromInterface()->saveFile(*simuData, g.profile[g.id()].sdPath());
SdcardFormat sdcard(g.profile[g.id()].sdPath());
sdcard.write(*simuData);
dialog->start(NULL);
}
else if (board == BOARD_FLAMENCO) {
dialog = new SimulatorDialogFlamenco(parent, simulator, flags);
QByteArray eeprom(GetEepromInterface()->getEEpromSize(), 0);
QByteArray eeprom(getEEpromSize(board), 0);
firmware->saveEEPROM((uint8_t *)eeprom.data(), *simuData);
dialog->start(eeprom);
}
@ -856,13 +857,13 @@ void startSimulation(QWidget * parent, RadioData & radioData, int modelIdx)
}
}
dialog = new SimulatorDialogTaranis(parent, simulator, flags);
QByteArray eeprom(GetEepromInterface()->getEEpromSize(), 0);
QByteArray eeprom(getEEpromSize(board), 0);
firmware->saveEEPROM((uint8_t *)eeprom.data(), *simuData);
dialog->start(eeprom);
}
else {
dialog = new SimulatorDialog9X(parent, simulator, flags);
QByteArray eeprom(GetEepromInterface()->getEEpromSize(), 0);
QByteArray eeprom(getEEpromSize(board), 0);
firmware->saveEEPROM((uint8_t *)eeprom.data(), *simuData, 0, firmware->getCapability(SimulatorVariant));
dialog->start(eeprom);
}

View file

@ -21,13 +21,15 @@
#ifndef _HELPERS_H_
#define _HELPERS_H_
#include "eeprominterface.h"
#include "modeledit/modeledit.h"
#include <QCheckBox>
#include <QSpinBox>
#include <QTableWidget>
#include <QGridLayout>
#include <QDebug>
#include "eeprominterface.h"
#include "modeledit/modeledit.h"
#include <QTime>
#include <QElapsedTimer>
extern const QColor colors[CPN_MAX_CURVES];

View file

@ -18,10 +18,6 @@
* GNU General Public License for more details.
*/
#include <QtGui>
#include <QNetworkProxyFactory>
#include <QFileInfo>
#include <QDesktopServices>
#include "mainwindow.h"
#include "mdichild.h"
#include "burnconfigdialog.h"
@ -48,7 +44,11 @@
#include "process_sync.h"
#include "radiointerface.h"
#include "progressdialog.h"
#include "storage_sdcard.h"
#include "storage.h"
#include <QtGui>
#include <QNetworkProxyFactory>
#include <QFileInfo>
#include <QDesktopServices>
#define OPENTX_COMPANION_DOWNLOADS "http://downloads-22.open-tx.org/companion"
#define DONATE_STR "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QUZ48K4SEXDP2"
@ -128,22 +128,22 @@ MainWindow::MainWindow():
}
if (strl.count()>1) str = strl[1];
if (!str.isEmpty()) {
int fileType = getFileType(str);
int fileType = getStorageType(str);
if (fileType==FILE_TYPE_HEX) {
if (fileType==STORAGE_TYPE_HEX) {
writeFlash(str);
}
if (fileType==FILE_TYPE_EEPE || fileType==FILE_TYPE_EEPM || fileType==FILE_TYPE_BIN) {
MdiChild *child = createMdiChild();
if (fileType==STORAGE_TYPE_EEPE || fileType==STORAGE_TYPE_EEPM || fileType==STORAGE_TYPE_BIN) {
MdiChild * child = createMdiChild();
if (child->loadFile(str)) {
if (!(printing && (model >=0 && model<GetEepromInterface()->getMaxModels()) && !printfilename.isEmpty() )) {
if (!(printing && model >= 0 && model<GetCurrentFirmware()->getCapability(Models) && !printfilename.isEmpty())) {
statusBar()->showMessage(tr("File loaded"), 2000);
child->show();
}
else {
child->show();
child->print(model,printfilename);
child->print(model, printfilename);
child->close();
}
}
@ -729,14 +729,15 @@ void MainWindow::paste()
void MainWindow::writeEeprom()
{
if (activeMdiChild())
activeMdiChild()->writeEeprom();
if (activeMdiChild())
activeMdiChild()->writeEeprom();
}
void MainWindow::simulate()
{
if (activeMdiChild())
activeMdiChild()->simulate();
if (activeMdiChild()) {
activeMdiChild()->radioSimulate();
}
}
@ -754,49 +755,13 @@ void MainWindow::loadBackup()
void MainWindow::readEeprom()
{
if(GetCurrentFirmware()->getBoard()== BOARD_HORUS && HORUS_READY_FOR_RELEASE()) {
// just an example
QString path = findMassstoragePath("RADIO");
if (path.isEmpty()) {
qDebug() << "Horus card not found";
return;
}
QString realPath = path.remove(path.size()- 5, 5);
qDebug() << "Reading files from" << realPath;
StorageSdcard storage;
storage.read(realPath);
// display models.txt
QString modelList = QString(storage.modelList);
qDebug() << "Models: size" << modelList.size() << "contents" << modelList;
// info about radio.bin
qDebug() << "Radio settings:" << storage.radio.size();
// info about all models
QList<QString> models = storage.getModelsFileNames();
qDebug() << "We have" << models.size() << "models:";
foreach(QString filename, models) {
QList<ModelFile>::const_iterator i = storage.getModelIterator(filename);
if (i != storage.models.end()) {
qDebug() << "\tModel:" << i->filename << "size" << i->data.size();
}
}
for (QList<ModelFile>::iterator i = storage.models.begin(); i != storage.models.end(); ++i) {
}
// for test immediately save to current dir
storage.write("./");
if (GetCurrentFirmware()->getBoard()== BOARD_HORUS && HORUS_READY_FOR_RELEASE()) {
// TODO
}
else {
QString tempFile;
EEPROMInterface *eepromInterface = GetEepromInterface();
EEPROMInterface * eepromInterface = GetEepromInterface();
if (IS_ARM(eepromInterface->getBoard()))
tempFile = generateProcessUniqueTempFileName("temp.bin");
@ -861,16 +826,6 @@ void MainWindow::writeBackup()
cd->exec();
}
int MainWindow::getFileType(const QString & fullFileName)
{
if(QFileInfo(fullFileName).suffix().toUpper()=="HEX") return FILE_TYPE_HEX;
if(QFileInfo(fullFileName).suffix().toUpper()=="BIN") return FILE_TYPE_BIN;
if(QFileInfo(fullFileName).suffix().toUpper()=="EEPM") return FILE_TYPE_EEPM;
if(QFileInfo(fullFileName).suffix().toUpper()=="EEPE") return FILE_TYPE_EEPE;
if(QFileInfo(fullFileName).suffix().toUpper()=="XML") return FILE_TYPE_XML;
return 0;
}
void MainWindow::writeFlash(QString fileToFlash)
{
FlashFirmwareDialog *cd = new FlashFirmwareDialog(this);

View file

@ -163,8 +163,7 @@ class MainWindow : public QMainWindow
void updateIconSizeActions();
void updateLanguageActions();
void updateIconThemeActions();
int getFileType(const QString & fullFileName);
QString Theme;
QString ISize;
QString strippedName(const QString & fullFileName);

View file

@ -32,7 +32,7 @@
#include "appdata.h"
#include "wizarddialog.h"
#include "flashfirmwaredialog.h"
#include "storage_eeprom.h"
#include "storage.h"
#if defined _MSC_VER || !defined __GNUC__
#include <windows.h>
@ -48,13 +48,38 @@ MdiChild::MdiChild():
isUntitled(true),
fileChanged(false)
{
BoardEnum board = GetCurrentFirmware()->getBoard();
ui->setupUi(this);
setWindowIcon(CompanionIcon("open.png"));
modelsListModel = new TreeModel(&radioData, this);
ui->modelsList->setModel(modelsListModel);
ui->simulateButton->setIcon(CompanionIcon("simulate.png"));
setAttribute(Qt::WA_DeleteOnClose);
eepromInterfaceChanged();
connect(ui->modelsList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(openModelEditWindow()));
connect(ui->modelsList, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showModelsListContextMenu(const QPoint &)));
// connect(ui->modelsList, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(onCurrentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
ui->modelsList->setContextMenuPolicy(Qt::CustomContextMenu);
ui->modelsList->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->modelsList->setSelectionMode(QAbstractItemView::ExtendedSelection);
// ui->modelsList->setDragEnabled(true);
// ui->modelsList->setAcceptDrops(true);
// ui->modelsList->setDragDropOverwriteMode(true);
// ui->modelsList->setDropIndicatorShown(true);
if (IS_HORUS(board)) {
ui->modelsList->header()->hide();
}
else {
ui->modelsList->setIndentation(0);
}
if (!(isMaximized() || isMinimized())) {
adjustSize();
}
@ -65,9 +90,12 @@ MdiChild::~MdiChild()
delete ui;
}
void MdiChild::eepromInterfaceChanged()
void MdiChild::refresh(bool expand)
{
ui->modelsList->refreshList();
modelsListModel->refresh();
if (1 || expand) {
ui->modelsList->expandAll();
}
if (GetCurrentFirmware()->getBoard() == BOARD_HORUS && !HORUS_READY_FOR_RELEASE()) {
ui->simulateButton->setEnabled(false);
}
@ -77,64 +105,230 @@ void MdiChild::eepromInterfaceChanged()
updateTitle();
}
void MdiChild::confirmDelete()
{
if (QMessageBox::warning(this, "Companion", tr("Delete selected models?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
deleteSelectedModels();
}
}
void MdiChild::deleteSelectedModels()
{
foreach (QModelIndex index, ui->modelsList->selectionModel()->selectedIndexes()) {
if (index.column() == 0) {
unsigned int modelIndex = modelsListModel->getModelIndex(index);
if (radioData.generalSettings.currModelIndex != modelIndex) {
qDebug() << "delete" << modelIndex;
radioData.models[modelIndex].clear();
setModified();
}
else {
QMessageBox::warning(this, "Companion", tr("Cannot delete default model."), QMessageBox::Ok);
}
}
}
}
void MdiChild::showModelsListContextMenu(const QPoint & pos)
{
int modelIndex = getCurrentRow();
QPoint globalPos = ui->modelsList->mapToGlobal(pos);
QMenu contextMenu;
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
bool hasData = mimeData->hasFormat("application/x-companion");
if (modelIndex >= 0) {
contextMenu.addAction(CompanionIcon("edit.png"), tr("&Edit"), this, SLOT(modelEdit()));
contextMenu.addAction(CompanionIcon("open.png"), tr("&Restore from backup"), this, SLOT(loadBackup()));
contextMenu.addAction(CompanionIcon("wizard.png"), tr("&Model Wizard"), this, SLOT(wizardEdit()));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("clear.png"), tr("&Delete"), this, SLOT(confirmDelete()), tr("Delete"));
contextMenu.addAction(CompanionIcon("copy.png"), tr("&Copy"), this, SLOT(copy()), tr("Ctrl+C"));
contextMenu.addAction(CompanionIcon("cut.png"), tr("&Cut"), this, SLOT(cut()), tr("Ctrl+X"));
contextMenu.addAction(CompanionIcon("paste.png"), tr("&Paste"), this, SLOT(paste()), tr("Ctrl+V"))->setEnabled(hasData);
contextMenu.addAction(CompanionIcon("duplicate.png"), tr("D&uplicate"), this, SLOT(duplicate()), tr("Ctrl+U"));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("currentmodel.png"), tr("&Use as default"), this, SLOT(setDefault()));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("print.png"), tr("P&rint model"), this, SLOT(print()), QKeySequence(tr("Ctrl+P")));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("simulate.png"), tr("&Simulate model"), this, SLOT(modelSimulate()), tr("Alt+S"));
}
// TODO context menu for radio settings
// contextMenu.addAction(CompanionIcon("edit.png"), tr("&Edit"), this, SLOT(EditModel()));
contextMenu.exec(globalPos);
}
void MdiChild::eepromInterfaceChanged()
{
refresh();
}
void MdiChild::cut()
{
ui->modelsList->cut();
copy();
deleteSelectedModels();
}
void MdiChild::copy()
{
ui->modelsList->copy();
QByteArray gmData;
doCopy(&gmData);
QMimeData * mimeData = new QMimeData;
mimeData->setData("application/x-companion", gmData);
QClipboard * clipboard = QApplication::clipboard();
clipboard->setMimeData(mimeData, QClipboard::Clipboard);
}
void MdiChild::doCopy(QByteArray * gmData)
{
foreach(QModelIndex index, ui->modelsList->selectionModel()->selectedIndexes()) {
if (index.column() == 0) {
unsigned int modelIndex = modelsListModel->getModelIndex(index);
if (modelIndex >= 0) {
gmData->append('M');
gmData->append((char *) &radioData.models[modelIndex], sizeof(ModelData));
}
}
}
// TODO to copy radio settings
// gmData->append('G');
// gmData->append((char *) &radioData.generalSettings, sizeof(GeneralSettings));
}
void MdiChild::doPaste(QByteArray * gmData, int index)
{
char * gData = gmData->data();
bool modified = false;
int size = 0;
while (size < gmData->size()) {
char c = *gData++;
size++;
if (c == 'G') {
// General settings
int ret = QMessageBox::question(this, "Companion", tr("Do you want to overwrite radio general settings?"),
QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
radioData.generalSettings = *((GeneralSettings *)gData);
modified = 1;
}
gData += sizeof(GeneralSettings);
size += sizeof(GeneralSettings);
}
else if (c == 'M') {
if (index < GetCurrentFirmware()->getCapability(Models)) {
// Model data
int ret = QMessageBox::Yes;
if (!radioData.models[index].isEmpty()) {
ret = QMessageBox::question(this, "Companion", tr("You are pasting on an not empty model, are you sure?"),
QMessageBox::Yes | QMessageBox::No);
}
if (ret == QMessageBox::Yes) {
radioData.models[index] = *((ModelData *)gData);
strcpy(radioData.models[index].filename, radioData.getNextModelFilename().toStdString().c_str());
modified = 1;
}
gData += sizeof(ModelData);
size += sizeof(ModelData);
index++;
}
}
else {
qWarning() << "paste error";
break;
}
}
if (modified) {
setModified();
}
}
void MdiChild::paste()
{
ui->modelsList->paste();
if (hasPasteData()) {
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
QByteArray gmData = mimeData->data("application/x-companion");
doPaste(&gmData, getCurrentRow());
}
}
bool MdiChild::hasPasteData()
bool MdiChild::hasPasteData() const
{
return ui->modelsList->hasPasteData();
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
return mimeData->hasFormat("application/x-companion");
}
bool MdiChild::hasSelection()
bool MdiChild::hasSelection() const
{
return ui->modelsList->hasSelection();
return ui->modelsList->selectionModel()->hasSelection();
}
void MdiChild::updateTitle()
{
QString title = userFriendlyCurrentFile() + "[*]" + " (" + GetCurrentFirmware()->getName() + QString(")");
if (!IS_SKY9X(GetCurrentFirmware()->getBoard()))
title += QString(" - %1 ").arg(EEPromAvail) + tr("free bytes");
int availableEEpromSize = modelsListModel->getAvailableEEpromSize();
if (availableEEpromSize >= 0) {
title += QString(" - %1 ").arg(availableEEpromSize) + tr("free bytes");
}
setWindowTitle(title);
}
void MdiChild::setModified()
{
ui->modelsList->refreshList();
refresh();
fileChanged = true;
updateTitle();
documentWasModified();
}
void MdiChild::keyPressEvent(QKeyEvent * event)
{
if (event->matches(QKeySequence::Delete)) {
deleteSelectedModels();
}
else if (event->matches(QKeySequence::Cut)) {
cut();
}
else if (event->matches(QKeySequence::Copy)) {
copy();
}
else if (event->matches(QKeySequence::Paste)) {
paste();
}
else if (event->matches(QKeySequence::Underline)) {
// TODO duplicate();
}
else {
QWidget::keyPressEvent(event); // run the standard event in case we didn't catch an action
}
}
void MdiChild::on_simulateButton_clicked()
{
startSimulation(this, radioData, -1);
radioSimulate();
}
void MdiChild::checkAndInitModel(int row)
{
ModelData &model = radioData.models[row - 1];
ModelData & model = radioData.models[row];
if (model.isEmpty()) {
model.setDefaultValues(row - 1, radioData.generalSettings);
model.setDefaultValues(row, radioData.generalSettings);
setModified();
}
}
void MdiChild::generalEdit()
{
GeneralEdit *t = new GeneralEdit(this, radioData, GetCurrentFirmware()/*firmware*/);
GeneralEdit * t = new GeneralEdit(this, radioData, GetCurrentFirmware()/*firmware*/);
connect(t, SIGNAL(modified()), this, SLOT(setModified()));
t->show();
}
@ -142,49 +336,47 @@ void MdiChild::generalEdit()
void MdiChild::modelEdit()
{
int row = getCurrentRow();
QApplication::setOverrideCursor(Qt::WaitCursor);
checkAndInitModel(row);
ModelData & model = radioData.models[row];
gStopwatch.restart();
gStopwatch.report("ModelEdit creation");
ModelEdit * t = new ModelEdit(this, radioData, (row), GetCurrentFirmware()/*firmware*/);
gStopwatch.report("ModelEdit created");
t->setWindowTitle(tr("Editing model %1: ").arg(row+1) + model.name);
connect(t, SIGNAL(modified()), this, SLOT(setModified()));
gStopwatch.report("STARTING MODEL EDIT");
t->show();
QApplication::restoreOverrideCursor();
gStopwatch.report("ModelEdit shown");
}
if (row == 0) {
generalEdit();
}
else {
QApplication::setOverrideCursor(Qt::WaitCursor);
checkAndInitModel( row );
ModelData & model = radioData.models[row - 1];
gStopwatch.restart();
gStopwatch.report("ModelEdit creation");
ModelEdit *t = new ModelEdit(this, radioData, (row - 1), GetCurrentFirmware()/*firmware*/);
gStopwatch.report("ModelEdit created");
t->setWindowTitle(tr("Editing model %1: ").arg(row) + model.name);
connect(t, SIGNAL(modified()), this, SLOT(setModified()));
gStopwatch.report("STARTING MODEL EDIT");
t->show();
QApplication::restoreOverrideCursor();
gStopwatch.report("ModelEdit shown");
void MdiChild::setDefault()
{
int row = getCurrentRow();
if (!radioData.models[row].isEmpty() && radioData.generalSettings.currModelIndex != (unsigned)row) {
radioData.setCurrentModel(row);
setModified();
}
}
void MdiChild::wizardEdit()
{
int row = getCurrentRow();
if (row > 0) {
checkAndInitModel(row);
WizardDialog * wizard = new WizardDialog(radioData.generalSettings, row, this);
wizard->exec();
if (wizard->mix.complete /*TODO rather test the exec() result?*/) {
radioData.models[row - 1] = wizard->mix;
setModified();
}
checkAndInitModel(row);
WizardDialog * wizard = new WizardDialog(radioData.generalSettings, row+1, this);
wizard->exec();
if (wizard->mix.complete /*TODO rather test the exec() result?*/) {
radioData.models[row] = wizard->mix;
setModified();
}
}
void MdiChild::openEditWindow()
void MdiChild::openModelEditWindow()
{
int row = getCurrentRow();
if (row == 0){
generalEdit();
}
else{
ModelData & model = radioData.models[row - 1];
if (row >= 0) {
ModelData & model = radioData.models[row];
if (model.isEmpty() && g.useWizard()) {
wizardEdit();
}
@ -197,151 +389,29 @@ void MdiChild::openEditWindow()
void MdiChild::newFile()
{
static int sequenceNumber = 1;
isUntitled = true;
curFile = QString("document%1.eepe").arg(sequenceNumber++);
updateTitle();
}
bool MdiChild::loadFile(const QString & fileName, bool resetCurrentFile)
bool MdiChild::loadFile(const QString & filename, bool resetCurrentFile)
{
QFile file(fileName);
if (!file.exists()) {
QMessageBox::critical(this, tr("Error"), tr("Unable to find file %1!").arg(fileName));
Storage storage(filename);
if (!storage.load(radioData)) {
QMessageBox::critical(this, tr("Error"), storage.error());
return false;
}
int fileType = getFileType(fileName);
#if 0
if (fileType==FILE_TYPE_XML) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { //reading HEX TEXT file
QMessageBox::critical(this, tr("Error"),
tr("Error opening file %1:\n%2.")
.arg(fileName)
.arg(file.errorString()));
return false;
}
QTextStream inputStream(&file);
XmlInterface(inputStream).load(radioData);
}
else
#endif
if (fileType==FILE_TYPE_HEX || fileType==FILE_TYPE_EEPE) { //read HEX file
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { //reading HEX TEXT file
QMessageBox::critical(this, tr("Error"),
tr("Error opening file %1:\n%2.")
.arg(fileName)
.arg(file.errorString()));
return false;
}
QDomDocument doc(ER9X_EEPROM_FILE_TYPE);
bool xmlOK = doc.setContent(&file);
if (xmlOK) {
std::bitset<NUM_ERRORS> errors((unsigned long long)LoadEepromXml(radioData, doc));
if (errors.test(ALL_OK)) {
ui->modelsList->refreshList();
if(resetCurrentFile) setCurrentFile(fileName);
return true;
}
}
file.reset();
QTextStream inputStream(&file);
if (fileType==FILE_TYPE_EEPE) { // read EEPE file header
QString hline = inputStream.readLine();
if (hline!=EEPE_EEPROM_FILE_HEADER) {
file.close();
return false;
}
}
QByteArray eeprom(EESIZE_MAX, 0);
int eeprom_size = HexInterface(inputStream).load((uint8_t *)eeprom.data(), EESIZE_MAX);
if (!eeprom_size) {
QMessageBox::critical(this, tr("Error"),
tr("Invalid EEPROM File %1")
.arg(fileName));
file.close();
return false;
}
file.close();
std::bitset<NUM_ERRORS> errors((unsigned long long)LoadEeprom(radioData, (uint8_t *)eeprom.data(), eeprom_size));
if (!errors.test(ALL_OK)) {
ShowEepromErrors(this, tr("Error"), tr("Invalid EEPROM File %1").arg(fileName), errors.to_ulong());
return false;
}
if (errors.test(HAS_WARNINGS)) {
ShowEepromWarnings(this, tr("Warning"), errors.to_ulong());
}
ui->modelsList->refreshList();
if (resetCurrentFile) setCurrentFile(fileName);
return true;
}
else if (fileType==FILE_TYPE_BIN) { //read binary
int eeprom_size = file.size();
if (!file.open(QFile::ReadOnly)) { //reading binary file - TODO HEX support
QMessageBox::critical(this, tr("Error"),
tr("Error opening file %1:\n%2.")
.arg(fileName)
.arg(file.errorString()));
return false;
}
uint8_t * eeprom = (uint8_t *)malloc(eeprom_size);
memset(eeprom, 0, eeprom_size);
long result = file.read((char*)eeprom, eeprom_size);
file.close();
if (result != eeprom_size) {
QMessageBox::critical(this, tr("Error"),
tr("Error reading file %1:\n%2.")
.arg(fileName)
.arg(file.errorString()));
free(eeprom);
return false;
}
std::bitset<NUM_ERRORS> errorsEeprom((unsigned long long)LoadEeprom(radioData, eeprom, eeprom_size));
if (!errorsEeprom.test(ALL_OK)) {
std::bitset<NUM_ERRORS> errorsBackup((unsigned long long)LoadBackup(radioData, eeprom, eeprom_size, 0));
if (!errorsBackup.test(ALL_OK)) {
ShowEepromErrors(this, tr("Error"), tr("Invalid binary EEPROM File %1").arg(fileName), (errorsEeprom | errorsBackup).to_ulong());
free(eeprom);
return false;
}
if (errorsBackup.test(HAS_WARNINGS)) {
ShowEepromWarnings(this, tr("Warning"), errorsBackup.to_ulong());
}
}
else if (errorsEeprom.test(HAS_WARNINGS)) {
ShowEepromWarnings(this, tr("Warning"), errorsEeprom.to_ulong());
}
ui->modelsList->refreshList();
if (resetCurrentFile)
setCurrentFile(fileName);
free(eeprom);
return true;
}
else if (fileType == FILE_TYPE_OTX) { //read zip archive
if (!GetEepromInterface()->loadFile(radioData, fileName)) {
ui->modelsList->refreshList();
if (resetCurrentFile)
setCurrentFile(fileName);
return true;
}
QString warning = storage.warning();
if (!warning.isEmpty()) {
// TODO ShowEepromWarnings(this, tr("Warning"), warning);
}
return false;
refresh(true);
if (resetCurrentFile)
setCurrentFile(filename);
return true;
}
bool MdiChild::save()
@ -383,78 +453,28 @@ bool MdiChild::saveAs(bool isNew)
return saveFile(fileName,true);
}
bool MdiChild::saveFile(const QString &fileName, bool setCurrent)
bool MdiChild::saveFile(const QString & filename, bool setCurrent)
{
QString myFile;
myFile = fileName;
if (IS_SKY9X(GetEepromInterface()->getBoard())) {
myFile.replace(".eepe", ".bin");
BoardEnum board = GetEepromInterface()->getBoard();
QString path = filename;
if (IS_SKY9X(board)) {
path.replace(".eepe", ".bin");
}
QFile file(myFile);
int fileType = getFileType(myFile);
uint8_t * eeprom = (uint8_t*)malloc(GetEepromInterface()->getEEpromSize());
int eeprom_size = 0;
if (fileType != FILE_TYPE_XML) {
eeprom_size = GetEepromInterface()->save(eeprom, radioData, 0, GetCurrentFirmware()->getVariantNumber());
if (!eeprom_size) {
QMessageBox::warning(this, tr("Error"),tr("Cannot write file %1:\n%2.").arg(myFile).arg(file.errorString()));
return false;
}
radioData.fixModelFilenames();
Storage storage(path);
bool result = storage.write(radioData);
if (result && setCurrent) {
setCurrentFile(path);
}
if (!file.open(fileType == FILE_TYPE_BIN ? QIODevice::WriteOnly : (QIODevice::WriteOnly | QIODevice::Text))) {
QMessageBox::warning(this, tr("Error"),tr("Cannot write file %1:\n%2.").arg(myFile).arg(file.errorString()));
return false;
}
QTextStream outputStream(&file);
#if 0
if (fileType==FILE_TYPE_XML) {
if (!XmlInterface(outputStream).save(radioData)) {
QMessageBox::warning(this, tr("Error"),tr("Cannot write file %1:\n%2.").arg(myFile).arg(file.errorString()));
file.close();
return false;
}
}
else
#endif
if (fileType==FILE_TYPE_HEX || fileType==FILE_TYPE_EEPE) { // write hex
if (fileType==FILE_TYPE_EEPE)
outputStream << EEPE_EEPROM_FILE_HEADER << "\n";
if (!HexInterface(outputStream).save(eeprom, eeprom_size)) {
QMessageBox::warning(this, tr("Error"),tr("Cannot write file %1:\n%2.").arg(myFile).arg(file.errorString()));
file.close();
return false;
}
}
else if (fileType==FILE_TYPE_BIN) // write binary
{
long result = file.write((char*)eeprom, eeprom_size);
if(result!=eeprom_size) {
QMessageBox::warning(this, tr("Error"),tr("Error writing file %1:\n%2.").arg(myFile).arg(file.errorString()));
return false;
}
}
else {
QMessageBox::warning(this, tr("Error"),tr("Error writing file %1:\n%2.").arg(myFile).arg("Unknown format"));
return false;
}
free(eeprom); // TODO free in all cases ...
file.close();
if(setCurrent) setCurrentFile(myFile);
return true;
return result;
}
QString MdiChild::userFriendlyCurrentFile()
QString MdiChild::userFriendlyCurrentFile() const
{
return strippedName(curFile);
return QFileInfo(curFile).fileName();
}
void MdiChild::closeEvent(QCloseEvent *event)
@ -501,45 +521,47 @@ void MdiChild::setCurrentFile(const QString &fileName)
files.removeAll(fileName);
files.prepend(fileName);
while (files.size() > MaxRecentFiles)
files.removeLast();
g.recentFiles( files );
}
QString MdiChild::strippedName(const QString &fullFileName)
{
return QFileInfo(fullFileName).fileName();
files.removeLast();
g.recentFiles(files);
}
void MdiChild::writeEeprom() // write to Tx
{
QString tempFile = generateProcessUniqueTempFileName("temp.bin");
saveFile(tempFile, false);
if(!QFileInfo(tempFile).exists()) {
if (!QFileInfo(tempFile).exists()) {
QMessageBox::critical(this, tr("Error"), tr("Cannot write temporary file!"));
return;
}
FlashEEpromDialog *cd = new FlashEEpromDialog(this, tempFile);
FlashEEpromDialog * cd = new FlashEEpromDialog(this, tempFile);
cd->exec();
}
void MdiChild::simulate()
void MdiChild::on_radioSettings_clicked()
{
if (getCurrentRow() > 0) {
startSimulation(this, radioData, getCurrentRow()-1);
}
generalEdit();
}
void MdiChild::print(int model, QString filename)
void MdiChild::radioSimulate()
{
startSimulation(this, radioData, -1);
}
void MdiChild::modelSimulate()
{
startSimulation(this, radioData, getCurrentRow());
}
void MdiChild::print(int model, const QString & filename)
{
// TODO
PrintDialog * pd = NULL;
if (model>=0 && !filename.isEmpty()) {
pd = new PrintDialog(this, GetCurrentFirmware()/*firmware*/, radioData.generalSettings, radioData.models[model], filename);
}
else if (getCurrentRow() > 0) {
pd = new PrintDialog(this, GetCurrentFirmware()/*firmware*/, radioData.generalSettings, radioData.models[getCurrentRow()-1]);
else if (getCurrentRow()) {
pd = new PrintDialog(this, GetCurrentFirmware()/*firmware*/, radioData.generalSettings, radioData.models[getCurrentRow()]);
}
if (pd) {
@ -553,19 +575,14 @@ void MdiChild::viableModelSelected(bool viable)
emit copyAvailable(viable);
}
void MdiChild::setEEpromAvail(int eavail)
{
EEPromAvail=eavail;
}
int MdiChild::getCurrentRow() const
{
return ui->modelsList->currentRow();
return modelsListModel->getModelIndex(ui->modelsList->currentIndex());
}
bool MdiChild::loadBackup()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open backup Models and Settings file"), g.eepromDir(),tr(EEPROM_FILES_FILTER));
QString fileName = QFileDialog::getOpenFileName(this, tr("Open backup Models and Settings file"), g.eepromDir(), tr(EEPROM_FILES_FILTER));
if (fileName.isEmpty())
return false;
QFile file(fileName);
@ -574,8 +591,8 @@ bool MdiChild::loadBackup()
QMessageBox::critical(this, tr("Error"), tr("Unable to find file %1!").arg(fileName));
return false;
}
if(getCurrentRow() < 1) return false;
int index = getCurrentRow() - 1;
// TODO int index = getCurrentRow();
int eeprom_size = file.size();
if (!file.open(QFile::ReadOnly)) { //reading binary file - TODO HEX support
@ -597,17 +614,20 @@ bool MdiChild::loadBackup()
return false;
}
#if 0
std::bitset<NUM_ERRORS> errorsEeprom((unsigned long long)LoadBackup(radioData, (uint8_t *)eeprom.data(), eeprom_size, index));
if (!errorsEeprom.test(ALL_OK)) {
ShowEepromErrors(this, tr("Error"), tr("Invalid binary backup File %1").arg(fileName), (errorsEeprom).to_ulong());
return false;
}
if (errorsEeprom.test(HAS_WARNINGS)) {
ShowEepromWarnings(this, tr("Warning"), errorsEeprom.to_ulong());
}
std::bitset<NUM_ERRORS> errorsEeprom((unsigned long long)LoadBackup(radioData, (uint8_t *)eeprom.data(), eeprom_size, index));
if (!errorsEeprom.test(ALL_OK)) {
ShowEepromErrors(this, tr("Error"), tr("Invalid binary backup File %1").arg(fileName), (errorsEeprom).to_ulong());
return false;
}
if (errorsEeprom.test(HAS_WARNINGS)) {
ShowEepromWarnings(this, tr("Warning"), errorsEeprom.to_ulong());
}
ui->modelsList->refreshList();
refresh(true);
return true;
#else
return false;
#endif
}

View file

@ -23,6 +23,7 @@
#include <QtGui>
#include "eeprominterface.h"
#include "modelslist.h"
namespace Ui {
class MdiChild;
@ -44,52 +45,62 @@ class MdiChild : public QWidget
void newFile();
bool loadFile(const QString & fileName, bool resetCurrentFile=true);
bool loadBackup();
bool save();
bool saveAs(bool isNew=false);
bool saveFile(const QString & fileName, bool setCurrent=true);
bool hasSelection();
QString userFriendlyCurrentFile();
QString currentFile() { return curFile; }
bool hasPasteData();
bool hasSelection() const;
bool hasPasteData() const;
QString userFriendlyCurrentFile() const;
QString currentFile() const { return curFile; }
void viableModelSelected(bool viable);
void eepromInterfaceChanged();
void setEEpromAvail(int eavail);
int getCurrentRow() const;
void refresh(bool expand=false);
void keyPressEvent(QKeyEvent * event);
signals:
void copyAvailable(bool val);
protected:
void closeEvent(QCloseEvent * event);
private slots:
protected slots:
void documentWasModified();
void on_simulateButton_clicked();
void on_radioSettings_clicked();
void setDefault();
public slots:
void showModelsListContextMenu(const QPoint & pos);
void checkAndInitModel(int row);
void generalEdit();
void modelEdit();
void wizardEdit();
void openEditWindow();
void openModelEditWindow();
bool loadBackup();
void confirmDelete();
void deleteSelectedModels();
void cut();
void copy();
void paste();
void writeEeprom();
void simulate();
void print(int model=-1, QString filename="");
void modelSimulate();
void radioSimulate();
void print(int model=-1, const QString & filename="");
void setModified();
void updateTitle();
private:
bool maybeSave();
void setCurrentFile(const QString & fileName);
QString strippedName(const QString & fullFileName);
bool loadOtxFile(const QString & fileName);
void doCopy(QByteArray * gmData);
void doPaste(QByteArray * gmData, int index);
Ui::MdiChild * ui;
TreeModel * modelsListModel;
QString curFile;
@ -98,7 +109,6 @@ class MdiChild : public QWidget
bool isUntitled;
bool fileChanged;
int EEPromAvail;
};
#endif // _MDICHILD_H_

View file

@ -15,7 +15,14 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="ModelsListWidget" name="modelsList"/>
<widget class="QPushButton" name="radioSettings">
<property name="text">
<string>Radio settings</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="modelsList"/>
</item>
<item>
<widget class="QPushButton" name="simulateButton">
@ -26,13 +33,6 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ModelsListWidget</class>
<extends>QListWidget</extends>
<header location="global">modelslist.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="companion.qrc"/>
</resources>

View file

@ -28,7 +28,7 @@ LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row
displayStep(0.1)
{
BoardEnum board = firmware->getBoard();
bool allowGVars = (IS_TARANIS(board) || IS_HORUS(board));
bool allowGVars = IS_HORUS_OR_TARANIS(board);
int internalStep = 1;
spinbox->setProperty("index", row);

View file

@ -62,7 +62,7 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge
ui->countdownBeep->addItem(tr("Voice"), TimerData::COUNTDOWN_VOICE);
ui->countdownBeep->addItem(tr("Haptic"), TimerData::COUNTDOWN_HAPTIC);
}
ui->value->setMaximumTime(firmware->getMaxTimerStart());
ui->persistent->setField(timer.persistent, this);
@ -165,7 +165,7 @@ ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & modul
ui->trainerMode->setItemData(TRAINER_MODE_MASTER_BATTERY_COMPARTMENT, 0, Qt::UserRole - 1);
}
ui->trainerMode->setCurrentIndex(model.trainerMode);
if (!IS_TARANIS(firmware->getBoard())) {
if (!IS_HORUS_OR_TARANIS(firmware->getBoard())) {
ui->label_trainerMode->hide();
ui->trainerMode->hide();
}
@ -174,7 +174,7 @@ ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & modul
ui->label_trainerMode->hide();
ui->trainerMode->hide();
if (firmware->getCapability(NumModules) > 1) {
if (IS_TARANIS(firmware->getBoard()) || IS_HORUS(firmware->getBoard())) {
if (IS_HORUS_OR_TARANIS(firmware->getBoard())) {
if (moduleIdx == 0)
label = tr("Internal Radio System");
else
@ -294,7 +294,7 @@ void ModulePanel::update()
break;
}
}
else if (IS_TARANIS(firmware->getBoard()) || IS_HORUS(firmware->getBoard())) {
else if (IS_HORUS_OR_TARANIS(firmware->getBoard())) {
if (model->trainerMode == TRAINER_SLAVE_JACK) {
mask |= MASK_PPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT;
}
@ -644,7 +644,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
if (!firmware->getCapability(HasDisplayText)) {
ui->displayText->hide();
}
if (!firmware->getCapability(GlobalFunctions)) {
ui->gfEnabled->hide();
}
@ -675,7 +675,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
// Startup switches warnings
for (int i=0; i<firmware->getCapability(Switches); i++) {
Firmware::Switch sw = firmware->getSwitch(i);
if (IS_TARANIS(board) || IS_HORUS(board)) {
if (IS_HORUS_OR_TARANIS(board)) {
sw.type = Firmware::SwitchType(generalSettings.switchConfig[i]);
}
if (sw.type == Firmware::SWITCH_NONE || sw.type == Firmware::SWITCH_TOGGLE) {
@ -711,7 +711,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
QWidget::setTabOrder(slider, cb);
prevFocus = cb;
}
// Pot warnings
prevFocus = ui->potWarningMode;
if (IS_TARANIS(board)) {
@ -906,7 +906,7 @@ void SetupPanel::updateBeepCenter()
void SetupPanel::updateStartupSwitches()
{
lock = true;
uint64_t switchStates = model->switchWarningStates;
uint64_t value;
@ -955,7 +955,7 @@ void SetupPanel::startupSwitchEdited(int value)
}
model->switchWarningStates &= ~mask;
if (IS_TARANIS(GetEepromInterface()->getBoard()) && generalSettings.switchConfig[index] != Firmware::SWITCH_3POS) {
if (value == 1) {
value = 2;

View file

@ -787,7 +787,7 @@ TelemetryPanel::~TelemetryPanel()
void TelemetryPanel::update()
{
if (IS_TARANIS(firmware->getBoard())) {
if (IS_HORUS_OR_TARANIS(firmware->getBoard())) {
if (model->moduleData[0].protocol == PULSES_OFF && model->moduleData[1].protocol == PULSES_PPM) {
ui->telemetryProtocol->setEnabled(true);
}
@ -820,7 +820,7 @@ void TelemetryPanel::setup()
if (IS_ARM(firmware->getBoard())) {
ui->telemetryProtocol->addItem(tr("FrSky S.PORT"), 0);
ui->telemetryProtocol->addItem(tr("FrSky D"), 1);
if (IS_9XRPRO(firmware->getBoard()) ||
if (IS_9XRPRO(firmware->getBoard()) ||
(IS_TARANIS(firmware->getBoard()) && generalSettings.hw_uartMode == 2)) {
ui->telemetryProtocol->addItem(tr("FrSky D (cable)"), 2);
}
@ -835,7 +835,7 @@ void TelemetryPanel::setup()
ui->rssiAlarm1SB->setValue(model->frsky.rssiAlarms[0].value);
ui->rssiAlarm2SB->setValue(model->frsky.rssiAlarms[1].value);
if (!IS_TARANIS(firmware->getBoard())) {
if (!IS_HORUS_OR_TARANIS(firmware->getBoard())) {
ui->rssiAlarm1CB->setCurrentIndex(model->frsky.rssiAlarms[0].level);
ui->rssiAlarm2CB->setCurrentIndex(model->frsky.rssiAlarms[1].level);
}
@ -890,7 +890,7 @@ void TelemetryPanel::setup()
else {
ui->frskyProtoCB->addItem(tr("Winged Shadow How High (not supported)"));
}
ui->variousGB->hide();
if (!IS_ARM(firmware->getBoard())) {
if (!(firmware->getCapability(HasFasOffset)) && !(firmware_id.contains("fasoffset"))) {

View file

@ -21,6 +21,7 @@
#include "helpers.h"
#include "modelprinter.h"
#include <QPainter>
#include <QFile>
QString changeColor(const QString & input, const QString & to, const QString & from)
{

View file

@ -19,138 +19,314 @@
*/
#include "modelslist.h"
#include "mdichild.h"
#include "helpers.h"
class DragDropHeader {
public:
DragDropHeader():
general_settings(false),
models_count(0)
{
}
bool general_settings;
uint8_t models_count;
uint8_t models[CPN_MAX_MODELS];
};
TreeItem::TreeItem(const QVector<QVariant> & itemData):
itemData(itemData),
parentItem(NULL),
modelIndex(-1)
{
}
ModelsListWidget::ModelsListWidget(QWidget * parent):
QTreeWidget(parent)
TreeItem::TreeItem(TreeItem * parent, int modelIndex):
itemData(parent->columnCount()),
parentItem(parent),
modelIndex(modelIndex)
{
}
TreeItem::~TreeItem()
{
qDeleteAll(childItems);
}
TreeItem * TreeItem::child(int number)
{
return childItems.value(number);
}
int TreeItem::childCount() const
{
return childItems.count();
}
int TreeItem::childNumber() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
return 0;
}
int TreeItem::columnCount() const
{
return itemData.count();
}
QVariant TreeItem::data(int column) const
{
return itemData.value(column);
}
TreeItem * TreeItem::appendChild(int modelIndex)
{
TreeItem * item = new TreeItem(this, modelIndex);
childItems.insert(childItems.size(), item);
return item;
}
TreeItem * TreeItem::parent()
{
return parentItem;
}
bool TreeItem::removeChildren(int position, int count)
{
if (position < 0 || position + count > childItems.size())
return false;
for (int row = 0; row < count; ++row)
delete childItems.takeAt(position);
return true;
}
bool TreeItem::setData(int column, const QVariant & value)
{
if (column < 0 || column >= itemData.size())
return false;
itemData[column] = value;
return true;
}
TreeModel::TreeModel(RadioData * radioData, QObject * parent):
QAbstractItemModel(parent),
radioData(radioData),
availableEEpromSize(-1)
{
BoardEnum board = GetCurrentFirmware()->getBoard();
QStringList labels;
labels << tr("Index") << tr("Name");
QVector<QVariant> labels;
if (!IS_HORUS(board))
labels << tr("Index");
labels << tr("Name");
if (!(IS_HORUS(board) || IS_SKY9X(board))) {
labels << tr("Size");
}
setColumnCount(labels.size());
setHeaderLabels(labels);
rootItem = new TreeItem(labels);
refresh();
}
TreeModel::~TreeModel()
{
delete rootItem;
}
int TreeModel::columnCount(const QModelIndex & /* parent */) const
{
return rootItem->columnCount();
}
QVariant TreeModel::data(const QModelIndex & index, int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::FontRole) {
return QVariant();
}
TreeItem * item = getItem(index);
if (role == Qt::FontRole) {
if (item->getModelIndex() == (int)radioData->generalSettings.currModelIndex) {
QFont font;
font.setBold(true);
return font;
}
}
return item->data(index.column());
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
}
TreeItem * TreeModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
TreeItem * item = static_cast<TreeItem*>(index.internalPointer());
if (item) {
return item;
}
}
return rootItem;
}
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return rootItem->data(section);
return QVariant();
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid() && parent.column() != 0)
return QModelIndex();
TreeItem * parentItem = getItem(parent);
TreeItem * childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = getItem(index);
TreeItem *parentItem = childItem->parent();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->childNumber(), 0, parentItem);
}
bool TreeModel::removeRows(int position, int rows, const QModelIndex &parent)
{
TreeItem * parentItem = getItem(parent);
bool success = true;
beginRemoveRows(parent, position, position + rows - 1);
success = parentItem->removeChildren(position, rows);
endRemoveRows();
return success;
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem = getItem(parent);
return parentItem->childCount();
}
bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::EditRole)
return false;
TreeItem *item = getItem(index);
bool result = item->setData(index.column(), value);
if (result)
emit dataChanged(index, index);
return result;
}
void TreeModel::refresh()
{
EEPROMInterface * eepromInterface = GetEepromInterface();
BoardEnum board = eepromInterface->getBoard();
if (!IS_SKY9X(board) && !IS_HORUS(board)) {
availableEEpromSize = getEEpromSize(board) - 64; // let's consider fat
availableEEpromSize -= 16 * ((eepromInterface->getSize(radioData->generalSettings) + 14) / 15);
}
removeRows(0, rowCount());
TreeItem * defaultCategoryItem = NULL;
if (IS_HORUS(board)) {
for (unsigned int i = 0; i < radioData->categories.size(); i++) {
TreeItem * current = rootItem->appendChild(-1);
current->setData(0, QString(radioData->categories[i].name));
}
}
for (unsigned int i=0; i<(unsigned)GetCurrentFirmware()->getCapability(Models) && i<radioData->models.size(); i++) {
ModelData & model = radioData->models[i];
int currentColumn = 0;
TreeItem * current;
if (IS_HORUS(board)) {
if (!model.isEmpty()) {
TreeItem * categoryItem;
// TODO category should be set to -1 if not Horus
if (model.category >= 0 && model.category < rootItem->childCount()) {
categoryItem = rootItem->child(model.category);
}
else {
if (!defaultCategoryItem) {
defaultCategoryItem = rootItem->appendChild(-1);
defaultCategoryItem->setData(0, QObject::tr("Models"));
}
categoryItem = defaultCategoryItem;
}
current = categoryItem->appendChild(i);
}
}
else {
current = rootItem->appendChild(i);
current->setData(currentColumn++, QString().sprintf("%02d", i + 1));
}
if (!model.isEmpty()) {
QString modelName;
if (strlen(model.name) > 0)
modelName = model.name;
else
modelName = QString().sprintf("Model%02d", i+1);
current->setData(currentColumn++, modelName);
if (!IS_SKY9X(board) && !IS_HORUS(board)) {
int size = eepromInterface->getSize(model);
current->setData(currentColumn++, QString().sprintf("%5d", size));
size = 16 * ((size + 14) / 15);
availableEEpromSize -= size;
if (i == radioData->generalSettings.currModelIndex) {
// Because we need this space for a TEMP model each time we have to write it again
availableEEpromSize -= size;
}
}
/* TODO if (i == radioData->generalSettings.currModelIndex) {
QFont font = item->font(0);
font.setBold(true);
for (int j=0; j<columnCount(); j++) {
item->setFont(j, font);
}
} */
}
// addTopLevelItem(item);
}
if (!IS_SKY9X(board) && !IS_HORUS(board)) {
availableEEpromSize = (availableEEpromSize / 16) * 15;
}
}
#if 0
ModelsListWidget::ModelsListWidget(QWidget * parent):
QTreeView(parent),
radioData(NULL)
{
setColumnWidth(0, 50);
setColumnWidth(2, 100);
connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OpenEditWindow()));
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(ShowContextMenu(const QPoint&)));
connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(onCurrentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
setContextMenuPolicy(Qt::CustomContextMenu);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setDragEnabled(true);
setAcceptDrops(true);
setDragDropOverwriteMode(true);
setDropIndicatorShown(true);
if (!IS_HORUS(board)) {
setIndentation(0);
}
active_highlight_color = palette().color(QPalette::Active, QPalette::Highlight);
radioData = &((MdiChild *)parent)->radioData;
refreshList();
for (int i=0; i<labels.size(); i++) {
resizeColumnToContents(i);
}
}
void ModelsListWidget::ShowContextMenu(const QPoint& pos)
{
QPoint globalPos = this->mapToGlobal(pos);
QMenu contextMenu;
if (((MdiChild *)parent())->getCurrentRow() > 0) {
// context menu for model
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
bool hasData = mimeData->hasFormat("application/x-companion");
contextMenu.addAction(CompanionIcon("edit.png"), tr("&Edit"), this, SLOT(EditModel()));
contextMenu.addAction(CompanionIcon("open.png"), tr("&Restore from backup"), this, SLOT(LoadBackup()));
contextMenu.addAction(CompanionIcon("wizard.png"), tr("&Model Wizard"), this, SLOT(OpenWizard()));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("clear.png"), tr("&Delete"), this, SLOT(confirmDelete()), tr("Delete"));
contextMenu.addAction(CompanionIcon("copy.png"), tr("&Copy"), this, SLOT(copy()), tr("Ctrl+C"));
contextMenu.addAction(CompanionIcon("cut.png"), tr("&Cut"), this, SLOT(cut()), tr("Ctrl+X"));
contextMenu.addAction(CompanionIcon("paste.png"), tr("&Paste"), this, SLOT(paste()), tr("Ctrl+V"))->setEnabled(hasData);
contextMenu.addAction(CompanionIcon("duplicate.png"), tr("D&uplicate"), this, SLOT(duplicate()), tr("Ctrl+U"));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("currentmodel.png"), tr("&Use as default"), this, SLOT(setdefault()));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("print.png"), tr("P&rint model"), this, SLOT(print()),QKeySequence(tr("Ctrl+P")));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("simulate.png"), tr("&Simulate model"), this, SLOT(simulate()), tr("Alt+S"));
}
else {
// context menu for radio settings
contextMenu.addAction(CompanionIcon("edit.png"), tr("&Edit"), this, SLOT(EditModel()));
}
contextMenu.exec(globalPos);
}
int ModelsListWidget::currentRow() const
{
return indexOfTopLevelItem(currentItem());
}
void ModelsListWidget::EditModel()
{
((MdiChild *)parent())->modelEdit();
}
void ModelsListWidget::OpenEditWindow()
{
((MdiChild *)parent())->openEditWindow();
}
void ModelsListWidget::OpenWizard()
{
((MdiChild *)parent())->wizardEdit();
}
void ModelsListWidget::LoadBackup()
{
((MdiChild *)parent())->loadBackup();
}
void ModelsListWidget::simulate()
{
((MdiChild *)parent())->simulate();
}
void ModelsListWidget::print()
{
((MdiChild *)parent())->print();
}
void ModelsListWidget::setdefault()
{
if (currentRow() > 0) {
unsigned int currModel = currentRow() - 1;
if (!radioData->models[currModel].isEmpty() && radioData->generalSettings.currModelIndex != currModel) {
radioData->setCurrentModel(currModel);
refreshList();
((MdiChild *) parent())->setModified();
}
}
}
void ModelsListWidget::mousePressEvent(QMouseEvent *event)
@ -158,7 +334,7 @@ void ModelsListWidget::mousePressEvent(QMouseEvent *event)
if (event->button() == Qt::LeftButton)
dragStartPosition = event->pos();
QTreeWidget::mousePressEvent(event);
QTreeView::mousePressEvent(event);
}
void ModelsListWidget::mouseMoveEvent(QMouseEvent *event)
@ -188,18 +364,18 @@ void ModelsListWidget::mouseMoveEvent(QMouseEvent *event)
void ModelsListWidget::saveSelection()
{
currentSelection.current_item = currentItem();
for (int i=0; i<GetEepromInterface()->getMaxModels()+1; ++i) {
/*currentSelection.current_item = currentItem();
for (int i=0; i<GetCurrentFirmware()->getCapability(Models)+1; ++i) {
currentSelection.selected[i] = selectionModel()->isSelected(model()->index(i, 0));
}
}*/
}
void ModelsListWidget::restoreSelection()
{
setCurrentItem(currentSelection.current_item);
for (int i=0; i<GetEepromInterface()->getMaxModels()+1; ++i) {
/*setCurrentItem(currentSelection.current_item);
for (int i=0; i<GetCurrentFirmware()->getCapability(Models)+1; ++i) {
selectionModel()->select(model()->index(i, 0), currentSelection.selected[i] ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
}
}*/
}
void ModelsListWidget::dragEnterEvent(QDragEnterEvent *event)
@ -227,7 +403,7 @@ void ModelsListWidget::dragMoveEvent(QDragMoveEvent *event)
if (row >= 0) {
if (header->general_settings)
selectionModel()->select(model()->index(0, 0), QItemSelectionModel::Select);
for (int i=row, end=std::min(GetEepromInterface()->getMaxModels()+1, row+header->models_count); i<end; i++)
for (int i=row, end=std::min(GetCurrentFirmware()->getCapability(Models)+1, row+header->models_count); i<end; i++)
selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select);
}
}
@ -248,11 +424,11 @@ void ModelsListWidget::dropEvent(QDropEvent *event)
((ModelsListWidget*)event->source())->doCut(&gmData);
doPaste(&gmData, row);
clearSelection();
setCurrentItem(topLevelItem(row));
// setCurrentItem(topLevelItem(row));
DragDropHeader * header = (DragDropHeader *)gmData.data();
if (header->general_settings)
selectionModel()->select(model()->index(0, 0), QItemSelectionModel::Select);
for (int i=row, end=std::min(GetEepromInterface()->getMaxModels()+1, row+header->models_count); i<end; i++)
for (int i=row, end=std::min(GetCurrentFirmware()->getCapability(Models)+1, row+header->models_count); i<end; i++)
selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select);
}
event->acceptProposedAction();
@ -261,7 +437,7 @@ void ModelsListWidget::dropEvent(QDropEvent *event)
#ifndef WIN32
void ModelsListWidget::focusInEvent ( QFocusEvent * event )
{
QTreeWidget::focusInEvent(event);
QTreeView::focusInEvent(event);
QPalette palette = this->palette();
palette.setColor(QPalette::Active, QPalette::Highlight, active_highlight_color);
palette.setColor(QPalette::Inactive, QPalette::Highlight, active_highlight_color);
@ -270,7 +446,7 @@ void ModelsListWidget::focusInEvent ( QFocusEvent * event )
void ModelsListWidget::focusOutEvent ( QFocusEvent * event )
{
QTreeWidget::focusOutEvent(event);
QTreeView::focusOutEvent(event);
QPalette palette = this->palette();
palette.setColor(QPalette::Active, QPalette::Highlight, palette.color(QPalette::Active, QPalette::Midlight));
palette.setColor(QPalette::Inactive, QPalette::Highlight, palette.color(QPalette::Active, QPalette::Midlight));
@ -278,113 +454,6 @@ void ModelsListWidget::focusOutEvent ( QFocusEvent * event )
}
#endif
void ModelsListWidget::refreshList()
{
int current = std::max(0, indexOfTopLevelItem(currentItem()));
clear();
QTreeWidgetItem * item = new QTreeWidgetItem();
item->setText(1, tr("General Settings"));
addTopLevelItem(item);
EEPROMInterface * eepromInterface = GetEepromInterface();
BoardEnum board = eepromInterface->getBoard();
// TODO here we calculate the size used by the RLE format, this is clearly not the right place to do that...
int availableEEpromSize = eepromInterface->getEEpromSize() - 64; // let's consider fat
availableEEpromSize -= 16 * ((eepromInterface->getSize(radioData->generalSettings) + 14) / 15);
for (uint8_t i=0; i<GetEepromInterface()->getMaxModels(); i++) {
QTreeWidgetItem * item = new QTreeWidgetItem();
item->setTextAlignment(0, Qt::AlignLeft);
item->setText(0, QString().sprintf("%02d", i+1));
if (!radioData->models[i].isEmpty()) {
QString modelName;
if (strlen(radioData->models[i].name) > 0)
modelName = radioData->models[i].name;
else
modelName = QString().sprintf("Model%02d", i+1);
item->setText(1, modelName);
if (!IS_SKY9X(board) && !IS_HORUS(board)) {
int size = eepromInterface->getSize(radioData->models[i]);
item->setText(2, QString().sprintf("%5d", size));
size = 16 * ((size + 14) / 15);
availableEEpromSize -= size;
if (i == radioData->generalSettings.currModelIndex) {
// Because we need this space for a TEMP model each time we have to write it again
availableEEpromSize -= size;
}
}
if (i == radioData->generalSettings.currModelIndex) {
QFont font = item->font(0);
font.setBold(true);
for (int j=0; j<columnCount(); j++) {
item->setFont(j, font);
}
}
}
addTopLevelItem(item);
}
selectionModel()->select(model()->index(current, 0), QItemSelectionModel::Current | QItemSelectionModel::Select | QItemSelectionModel::Rows);
setCurrentItem(topLevelItem(current));
if (!IS_SKY9X(board) && !IS_HORUS(board)) {
((MdiChild*)parent())->setEEpromAvail((availableEEpromSize/16)*15);
}
}
void ModelsListWidget::cut()
{
copy();
deleteSelected(false);
}
void ModelsListWidget::confirmDelete()
{
deleteSelected(true);
}
void ModelsListWidget::deleteSelected(bool ask=true)
{
bool isModel=false;
unsigned int selModel;
QMessageBox::StandardButton ret = QMessageBox::Yes;
if (ask) {
foreach (QModelIndex index, this->selectionModel()->selectedIndexes()) {
if (index.row()>0 && !radioData->models[index.row()-1].isEmpty()) {
isModel = true;
selModel=index.row()-1;
}
}
if (isModel) {
if (radioData->generalSettings.currModelIndex != selModel) {
ret = QMessageBox::warning(this, "Companion", tr("Delete Selected Models?"), QMessageBox::Yes | QMessageBox::No);
}
else {
ret = QMessageBox::warning(this, "Companion", tr("Cannot delete default model."), QMessageBox::Ok);
}
}
}
if (ret == QMessageBox::Yes) {
foreach (QModelIndex index, this->selectionModel()->selectedIndexes()) {
if (index.row() > 0 && radioData->generalSettings.currModelIndex != (unsigned int)(index.row()-1)) {
radioData->models[index.row()-1].clear();
((MdiChild *)parent())->setModified();
}
else if (index.row()>0) {
if (ask) {
QMessageBox::warning(this, "Companion", tr("Cannot delete default model."), QMessageBox::Ok);
}
else {
QMessageBox::warning(this, "Companion", tr("Cannot cut default model."), QMessageBox::Ok);
}
}
}
}
}
void ModelsListWidget::doCut(QByteArray * gmData)
{
bool modified = false;
@ -400,127 +469,12 @@ void ModelsListWidget::doCut(QByteArray * gmData)
}
}
void ModelsListWidget::doCopy(QByteArray * gmData)
{
DragDropHeader header;
qDebug() << selectionModel()->selectedIndexes();
foreach(QModelIndex index, selectionModel()->selectedIndexes()) {
char column = index.column();
if (column == 0) {
char row = index.row();
if (!row) {
header.general_settings = true;
gmData->append('G');
gmData->append((char *) &radioData->generalSettings, sizeof(GeneralSettings));
}
else {
header.models[header.models_count++] = row;
gmData->append('M');
gmData->append((char *) &radioData->models[row - 1], sizeof(ModelData));
}
}
}
gmData->prepend((char *)&header, sizeof(header));
}
void ModelsListWidget::copy()
{
QByteArray gmData;
doCopy(&gmData);
QMimeData * mimeData = new QMimeData;
mimeData->setData("application/x-companion", gmData);
QClipboard * clipboard = QApplication::clipboard();
clipboard->setMimeData(mimeData, QClipboard::Clipboard);
}
void ModelsListWidget::doPaste(QByteArray * gmData, int index)
{
// QByteArray gmData = mimeD->data("application/x-companion");
char * gData = gmData->data() + sizeof(DragDropHeader); // new char[gmData.size() + 1];
int i = sizeof(DragDropHeader);
int id = index;
int ret, modified=0;
if(!id) id++;
while (i<gmData->size() && id<=GetEepromInterface()->getMaxModels()) {
qDebug() << i << gmData->size();
char c = *gData;
i++;
gData++;
if (c == 'G') {
// General settings
ret = QMessageBox::question(this, "Companion", tr("Do you want to overwrite radio general settings?"),
QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
radioData->generalSettings = *((GeneralSettings *)gData);
modified = 1;
}
gData += sizeof(GeneralSettings);
i += sizeof(GeneralSettings);
}
else {
// Model data
if (!radioData->models[id-1].isEmpty()) {
ret = QMessageBox::question(this, "Companion", tr("You are pasting on an not empty model, are you sure?"),
QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
radioData->models[id-1] = *((ModelData *)gData);
strcpy(radioData->models[id-1].filename, radioData->getNextModelFilename().toStdString().c_str());
gData += sizeof(ModelData);
i += sizeof(ModelData);
id++;
modified = 1;
}
else {
gData += sizeof(ModelData);
i += sizeof(ModelData);
id++;
}
}
else {
radioData->models[id-1] = *((ModelData *)gData);
strcpy(radioData->models[id-1].filename, radioData->getNextModelFilename().toStdString().c_str());
gData += sizeof(ModelData);
i += sizeof(ModelData);
id++;
modified=1;
}
}
}
if (modified==1) {
((MdiChild *)parent())->setModified();
}
}
bool ModelsListWidget::hasPasteData()
{
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
return mimeData->hasFormat("application/x-companion");
}
void ModelsListWidget::paste()
{
if (hasPasteData()) {
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
QByteArray gmData = mimeData->data("application/x-companion");
doPaste(&gmData, currentRow());
}
}
void ModelsListWidget::duplicate()
{
int i = this->currentRow();
if (i && i<GetEepromInterface()->getMaxModels()) {
if (i && i<GetCurrentFirmware()->getCapability(Models)) {
ModelData * model = &radioData->models[i-1];
while (i<GetEepromInterface()->getMaxModels()) {
while (i<GetCurrentFirmware()->getCapability(Models)) {
if (radioData->models[i].isEmpty()) {
radioData->models[i] = *model;
strcpy(radioData->models[i].filename, radioData->getNextModelFilename().toStdString().c_str());
@ -529,54 +483,20 @@ void ModelsListWidget::duplicate()
}
i++;
}
if (i==GetEepromInterface()->getMaxModels()) {
if (i==GetCurrentFirmware()->getCapability(Models)) {
QMessageBox::warning(this, "Companion", tr("No free slot available, cannot duplicate"), QMessageBox::Ok);
}
}
}
bool ModelsListWidget::hasSelection()
{
return (this->selectionModel()->hasSelection());
}
void ModelsListWidget::keyPressEvent(QKeyEvent *event)
{
if (event->matches(QKeySequence::Delete)) {
deleteSelected();
return;
}
if (event->matches(QKeySequence::Cut)) {
cut();
return;
}
if (event->matches(QKeySequence::Copy)) {
copy();
return;
}
if (event->matches(QKeySequence::Paste)) {
paste();
return;
}
if (event->matches(QKeySequence::Underline)) {
duplicate();
return;
}
QTreeWidget::keyPressEvent(event);//run the standard event in case we didn't catch an action
}
void ModelsListWidget::onCurrentItemChanged(QTreeWidgetItem * current, QTreeWidgetItem *)
{
int index = indexOfTopLevelItem(current);
/*int index = indexOfTopLevelItem(current);
if (!isVisible())
((MdiChild*)parent())->viableModelSelected(false);
else if (index<1)
((MdiChild*)parent())->viableModelSelected(false);
else
((MdiChild*)parent())->viableModelSelected(!radioData->models[currentRow()-1].isEmpty());
((MdiChild*)parent())->viableModelSelected(!radioData->models[currentRow()-1].isEmpty()); */
}
#endif

View file

@ -21,8 +21,8 @@
#ifndef _MODELSLIST_H_
#define _MODELSLIST_H_
#include <QtWidgets>
#include "eeprominterface.h"
#include <QtWidgets>
struct CurrentSelection
{
@ -30,13 +30,84 @@ struct CurrentSelection
bool selected[CPN_MAX_MODELS+1];
};
class ModelsListWidget : public QTreeWidget
class TreeItem
{
public:
explicit TreeItem(const QVector<QVariant> & itemData);
explicit TreeItem(TreeItem * parent, int modelIndex = -1);
~TreeItem();
TreeItem *child(int number);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
TreeItem * appendChild(int modelIndex);
TreeItem * parent();
bool removeChildren(int position, int count);
int childNumber() const;
bool setData(int column, const QVariant &value);
int getModelIndex() const { return modelIndex; }
private:
QList<TreeItem*> childItems;
QVector<QVariant> itemData;
TreeItem * parentItem;
int modelIndex;
};
class TreeModel : public QAbstractItemModel
{
// Q_OBJECT
public:
TreeModel(RadioData * radioData, QObject *parent = 0);
~TreeModel();
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) Q_DECL_OVERRIDE;
bool removeRows(int position, int rows,
const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE;
void refresh();
int getAvailableEEpromSize() { return availableEEpromSize; }
int getModelIndex(const QModelIndex & index) const {
return getItem(index)->getModelIndex();
}
private:
TreeItem * getItem(const QModelIndex & index) const;
TreeItem * rootItem;
RadioData * radioData;
int availableEEpromSize;
};
#if 0
class ModelsListWidget : public QTreeView
{
Q_OBJECT
public:
ModelsListWidget(QWidget * parent = 0);
void setRadioData(RadioData * radioData);
bool hasSelection();
void keyPressEvent(QKeyEvent * event);
bool hasPasteData();
@ -56,18 +127,16 @@ protected:
public slots:
void refreshList();
void ShowContextMenu(const QPoint& pos);
void cut();
void copy();
void paste();
void print();
void EditModel();
void OpenEditWindow();
void LoadBackup();
void OpenWizard();
void simulate();
void duplicate();
void setdefault();
void deleteSelected(bool ask);
void confirmDelete();
void onCurrentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *);
@ -79,12 +148,13 @@ private:
void saveSelection();
void restoreSelection();
RadioData *radioData;
RadioData * radioData;
QPoint dragStartPosition;
CurrentSelection currentSelection;
QColor active_highlight_color;
};
#endif
#endif // _MODELSLIST_H_

View file

@ -20,11 +20,11 @@
#include "process_flash.h"
#include "progresswidget.h"
#include "eeprominterface.h"
#include <QFile>
#include <QMessageBox>
#include <QProcess>
#include "eeprominterface.h"
//#include "firmwareinterface.h"
#include <QEventLoop>
#if defined _MSC_VER || !defined __GNUC__
#include <Windows.h>

1321
companion/src/radiodata.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,7 @@
#include "releasenotesdialog.h"
#include "ui_htmldialog.h"
#include <QFile>
ReleaseNotesDialog::ReleaseNotesDialog(QWidget * parent) :
QDialog(parent),

View file

@ -1,14 +1,18 @@
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
set(storage_NAMES
storage_eeprom
storage_sdcard
hexinterface
# xmlinterface
mountlist
rlefile
appdata
firmwareinterface
storage
bineeprom
eepe
hexeeprom
categorized
sdcard
otx
)
set(storage_SRCS

View file

@ -0,0 +1,103 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "bineeprom.h"
#include "eeprominterface.h"
#include <QFile>
#include <bitset>
// TODO should be RleFormat, RawFormat etc.
bool BinEepromFormat::load(RadioData & radioData)
{
QFile file(filename);
int size = file.size();
if (!file.open(QFile::ReadOnly)) {
qDebug() << "Unable to open" << filename << file.errorString();
return false;
}
QByteArray eeprom(size, 0);
int result = file.read((char *)eeprom.data(), size);
if (result != size) {
setError(QObject::tr("Error reading %1: %2").arg(filename).arg(file.errorString()));
return false;
}
return extract(radioData, eeprom);
}
bool BinEepromFormat::write(const RadioData & radioData)
{
bool result;
EEPROMInterface * eepromInterface = GetEepromInterface();
uint8_t * eeprom = (uint8_t *)malloc(getEEpromSize(eepromInterface->getBoard()));
int eeprom_size = eepromInterface->save(eeprom, radioData, 0, GetCurrentFirmware()->getVariantNumber());
if (eeprom_size) {
result = writeToFile(eeprom, eeprom_size);
}
else {
setError(QObject::tr("Cannot save EEPROM"));
result = false;
}
free(eeprom);
return result;
}
bool BinEepromFormat::writeToFile(const uint8_t * eeprom, uint32_t size)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
setError(QObject::tr("Cannot open file %1:\n%2.").arg(filename).arg(file.errorString()));
return false;
}
QTextStream outputStream(&file);
long len = file.write((char *)eeprom, size);
if (len != size) {
setError(QObject::tr("Error writing file %1:\n%2.").arg(filename).arg(file.errorString()));
return false;
}
return true;
}
bool BinEepromFormat::extract(RadioData & radioData, const QByteArray & eeprom)
{
std::bitset<NUM_ERRORS> errors;
foreach(EEPROMInterface * eepromInterface, eepromInterfaces) {
std::bitset<NUM_ERRORS> result((unsigned long long)eepromInterface->load(radioData, (uint8_t *)eeprom.data(), eeprom.size()));
if (result.test(ALL_OK)) {
if (errors.test(HAS_WARNINGS)) {
// TODO ShowEepromWarnings(this, QObject::tr("Warning"), errors.to_ulong());
}
return true;
}
else {
errors |= result;
}
}
setError(QObject::tr("Invalid EEPROM File %1: %2").arg(filename).arg(errors.to_ulong()));
return false;
}

View file

@ -18,16 +18,25 @@
* GNU General Public License for more details.
*/
#ifndef _STORAGE_EEPROM_H_
#define _STORAGE_EEPROM_H_
#ifndef _BINEEPROM_H_
#define _BINEEPROM_H_
#include <QDomDocument>
#include <QString>
#include "eeprominterface.h"
#include "storage.h"
unsigned long LoadBackup(RadioData &radioData, uint8_t *eeprom, int esize, int index);
unsigned long LoadEeprom(RadioData &radioData, const uint8_t *eeprom, int size);
unsigned long LoadEepromXml(RadioData &radioData, QDomDocument &doc);
bool convertEEprom(const QString &sourceEEprom, const QString &destinationEEprom, const QString &firmware);
class BinEepromFormat : public StorageFormat
{
public:
BinEepromFormat(const QString & filename):
StorageFormat(filename)
{
}
virtual bool load(RadioData & radioData);
virtual bool write(const RadioData & radioData);
protected:
bool extract(RadioData & radioData, const QByteArray & eeprom);
virtual bool writeToFile(const uint8_t * eeprom, uint32_t size);
};
#endif // _STORAGE_EEPROM_H_
#endif // _BINEEPROM_H_

View file

@ -0,0 +1,114 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "categorized.h"
#include "firmwares/opentx/opentxinterface.h"
bool CategorizedStorageFormat::load(RadioData & radioData)
{
QByteArray radioSettingsBuffer;
if (!loadFile(radioSettingsBuffer, "RADIO/radio.bin")) {
setError(QObject::tr("Can't extract RADIO/radio.bin"));
return false;
}
if (!loadRadioSettingsFromByteArray(radioData.generalSettings, radioSettingsBuffer)) {
return false;
}
QByteArray modelsListBuffer;
if (!loadFile(modelsListBuffer, "RADIO/models.txt")) {
setError(QObject::tr("Can't extract RADIO/models.txt"));
return false;
}
QList<QByteArray> lines = modelsListBuffer.split('\n');
int modelIndex = 0;
int categoryIndex = -1;
foreach (const QByteArray & line, lines) {
if (!line.isEmpty()) {
if (line.startsWith('[') && line.endsWith(']')) {
QString name = line.mid(1, line.size() - 2);
CategoryData category(name.toStdString().c_str());
radioData.categories.push_back(category);
categoryIndex++;
}
else {
qDebug() << "Loading" << line;
QByteArray modelBuffer;
if (!loadFile(modelBuffer, QString("MODELS/%1").arg(QString(line)))) {
setError(QObject::tr("Can't extract %1").arg(QString(line)));
return false;
}
if (!loadModelFromByteArray(radioData.models[modelIndex], modelBuffer)) {
return false;
}
strncpy(radioData.models[modelIndex].filename, line.data(), sizeof(radioData.models[modelIndex].filename));
if (strcmp(radioData.generalSettings.currModelFilename, line.data()) == 0) {
radioData.generalSettings.currModelIndex = modelIndex;
}
radioData.models[modelIndex].category = categoryIndex;
radioData.models[modelIndex].used = true;
modelIndex++;
}
}
}
return true;
}
bool CategorizedStorageFormat::write(const RadioData & radioData)
{
// models.txt
QByteArray modelsList;
int currentCategoryIndex = -1;
// radio.bin
QByteArray radioSettingsData;
writeRadioSettingsToByteArray(radioData.generalSettings, radioSettingsData);
if (!writeFile(radioSettingsData, "RADIO/radio.bin")) {
return false;
}
// all models
for (unsigned int i=0; i<radioData.models.size(); i++) {
const ModelData & model = radioData.models[i];
if (!model.isEmpty()) {
QString modelFilename = QString("MODELS/%1").arg(model.filename);
QByteArray modelData;
writeModelToByteArray(model, modelData);
if (!writeFile(modelData, modelFilename)) {
return false;
}
int categoryIndex = model.category;
if (currentCategoryIndex != categoryIndex) {
modelsList.append(QString().sprintf("[%s]\n", radioData.categories[model.category].name));
currentCategoryIndex = categoryIndex;
}
modelsList.append(QString(model.filename) + "\n");
}
}
if (!writeFile(modelsList, "RADIO/models.txt")) {
return false;
}
return true;
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _CATEGORIZED_H_
#define _CATEGORIZED_H_
#include "storage.h"
class CategorizedStorageFormat : public StorageFormat
{
public:
CategorizedStorageFormat(const QString & filename):
StorageFormat(filename)
{
}
virtual bool load(RadioData & radioData);
virtual bool write(const RadioData & radioData);
protected:
virtual bool loadFile(QByteArray & fileData, const QString & fileName) = 0;
virtual bool writeFile(const QByteArray & fileData, const QString & fileName) = 0;
};
#endif // _CATEGORIZED_H_

View file

@ -0,0 +1,53 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "eepe.h"
#include <QFile>
#include "eeprominterface.h"
#include "hexinterface.h"
bool EepeFormat::load(RadioData & radioData)
{
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Unable to open" << filename << file.errorString();
return false;
}
QTextStream inputStream(&file);
QString hline = inputStream.readLine();
if (hline != EEPE_EEPROM_FILE_HEADER) {
qDebug() << "No EEPE header";
return false;
}
qDebug() << "EEPE header found";
QByteArray eeprom(EESIZE_MAX, 0);
int eeprom_size = HexInterface(inputStream).load((uint8_t *)eeprom.data(), EESIZE_MAX);
if (!eeprom_size) {
setError(QObject::tr("Invalid EEPROM File %1").arg(filename));
return false;
}
eeprom.resize(eeprom_size);
return extract(radioData, eeprom);
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _EEPE_H_
#define _EEPE_H_
#include "hexeeprom.h"
class EepeFormat : public HexEepromFormat
{
public:
EepeFormat(const QString & filename):
HexEepromFormat(filename)
{
}
virtual bool load(RadioData & radioData);
};
#endif // _EEPE_H_

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
// TODO not enabled for now
bool EepeXmlFormat::load(RadioData & radioData)
{
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Unable to open" << filename << file.errorString();
return false;
}
QDomDocument doc(ER9X_EEPROM_FILE_TYPE);
bool xmlOK = doc.setContent(&file);
if (xmlOK) {
std::bitset<NUM_ERRORS> errors((unsigned long long) LoadEepromXml(radioData, doc));
if (errors.test(ALL_OK)) {
return true;
}
else {
qDebug() << "XML parsing error";
}
}
else {
qDebug() << "No XML content";
}
return false;
}

View file

@ -18,11 +18,12 @@
* GNU General Public License for more details.
*/
#include <QtGui>
#include "hexinterface.h"
#include "splash.h"
#include "firmwareinterface.h"
#include "helpers.h"
#include "storage.h"
#include <QtGui>
#define FW_MARK "FW"
#define VERS_MARK "VERS"
@ -30,25 +31,6 @@
#define TIME_MARK "TIME"
#define EEPR_MARK "EEPR"
int getFileType(const QString &fullFileName)
{
QString suffix = QFileInfo(fullFileName).suffix().toUpper();
if (suffix == "HEX")
return FILE_TYPE_HEX;
else if (suffix == "BIN")
return FILE_TYPE_BIN;
else if (suffix == "EEPM")
return FILE_TYPE_EEPM;
else if (suffix == "EEPE")
return FILE_TYPE_EEPE;
else if (suffix == "XML")
return FILE_TYPE_XML;
else if (suffix == "OTX")
return FILE_TYPE_OTX;
else
return 0;
}
FirmwareInterface::FirmwareInterface(const QString &filename):
flash(FSIZE_MAX, 0),
flashSize(0),
@ -355,9 +337,9 @@ unsigned int FirmwareInterface::save(QString fileName)
memcpy(binflash, flash.constData(), flashSize);
QFile file(fileName);
int fileType = getFileType(fileName);
int fileType = getStorageType(fileName);
if (fileType == FILE_TYPE_HEX) {
if (fileType == STORAGE_TYPE_HEX) {
if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { //reading HEX TEXT file
free(binflash);
return -1;

View file

@ -36,15 +36,6 @@
#define ERSKY9X_SPE "SPE"
#define ERSKY9X_OFFSET (7)
#define FILE_TYPE_BIN 1
#define FILE_TYPE_HEX 2
#define FILE_TYPE_EEPE 3
#define FILE_TYPE_EEPM 4
#define FILE_TYPE_XML 5
#define FILE_TYPE_OTX 6
int getFileType(const QString &fullFileName);
class FirmwareInterface
{
public:

View file

@ -0,0 +1,63 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "hexeeprom.h"
#include "eeprominterface.h"
#include "hexinterface.h"
#include <QFile>
bool HexEepromFormat::load(RadioData & radioData)
{
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Unable to open" << filename << file.errorString();
return false;
}
QTextStream inputStream(&file);
// TODO remove EESIZE_MAX
QByteArray eeprom(EESIZE_MAX, 0);
int eeprom_size = HexInterface(inputStream).load((uint8_t *)eeprom.data(), EESIZE_MAX);
if (!eeprom_size) {
setError(QObject::tr("Invalid EEPROM File %1").arg(filename));
return false;
}
eeprom.resize(eeprom_size);
return extract(radioData, eeprom);
}
bool HexEepromFormat::writeToFile(const uint8_t * eeprom, uint32_t size)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
setError(QObject::tr("Cannot open file %1:\n%2.").arg(filename).arg(file.errorString()));
return false;
}
QTextStream outputStream(&file);
if (!HexInterface(outputStream).save(eeprom, size)) {
setError(QObject::tr("Error writing file %1:\n%2.").arg(filename).arg(file.errorString()));
return false;
}
return true;
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _HEXEEPROM_H_
#define _HEXEEPROM_H_
#include "bineeprom.h"
class HexEepromFormat : public BinEepromFormat
{
public:
HexEepromFormat(const QString & filename):
BinEepromFormat(filename)
{
}
virtual bool load(RadioData & radioData);
protected:
virtual bool writeToFile(const uint8_t * eeprom, uint32_t size);
};
#endif // _HEXEEPROM_H_

View file

@ -84,7 +84,7 @@ int HexInterface::load(uint8_t *data, int maxsize)
}
bool HexInterface::save(uint8_t *data, const int size)
bool HexInterface::save(const uint8_t * data, const int size)
{
int addr = 0;
int nextbank = 1;
@ -103,7 +103,7 @@ bool HexInterface::save(uint8_t *data, const int size)
return true;
}
QString HexInterface::iHEXLine(quint8 * data, quint32 addr, quint8 len)
QString HexInterface::iHEXLine(const quint8 * data, quint32 addr, quint8 len)
{
unsigned int bankaddr;
bankaddr=addr&0xffff;

View file

@ -28,13 +28,13 @@ class HexInterface {
public:
HexInterface(QTextStream &stream);
int load(uint8_t *output, int maxsize);
bool save(uint8_t *data, const int size);
int load(uint8_t * output, int maxsize);
bool save(const uint8_t * data, const int size);
protected:
int getValueFromLine(const QString &line, int pos, int len=2);
QString iHEXLine(quint8 * data, quint32 addr, quint8 len);
QString iHEXLine(const quint8 * data, quint32 addr, quint8 len);
QString iHEXExtRec(quint8 bank);
QTextStream & stream;

View file

@ -0,0 +1,116 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "otx.h"
#include "miniz.c"
#include <QFile>
#define MZ_ALLOCATION_SIZE (32*1024)
bool OtxFormat::load(RadioData & radioData)
{
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
setError(QObject::tr("Error opening file %1:\n%2.").arg(filename).arg(file.errorString()));
return false;
}
QByteArray archiveContents = file.readAll();
qDebug() << "File" << filename << "read, size:" << archiveContents.size();
// open zip file
memset(&zip_archive, 0, sizeof(zip_archive));
if (!mz_zip_reader_init_mem(&zip_archive, archiveContents.data(), archiveContents.size(), 0)) {
qDebug() << QObject::tr("Error opening OTX archive %1").arg(filename);
return false;
}
bool result = CategorizedStorageFormat::load(radioData);
mz_zip_reader_end(&zip_archive);
return result;
}
bool OtxFormat::write(const RadioData & radioData)
{
qDebug() << "Saving to archive" << filename;
memset(&zip_archive, 0, sizeof(zip_archive));
if (!mz_zip_writer_init_heap(&zip_archive, 0, MZ_ALLOCATION_SIZE)) {
setError(QObject::tr("Error initializing OTX archive writer"));
return false;
}
bool result = CategorizedStorageFormat::write(radioData);
if (result) {
// finalize archive and get contents
char * archiveContents;
size_t archiveSize;
if (mz_zip_writer_finalize_heap_archive(&zip_archive, (void **)&archiveContents, &archiveSize)) {
qDebug() << "Archive size" << archiveSize;
// write contents to file
QFile file(filename);
if (file.open(QIODevice::WriteOnly)) {
qint64 len = file.write(archiveContents, archiveSize);
if (len != (qint64)archiveSize) {
setError(QObject::tr("Error writing file %1:\n%2.").arg(filename).arg(file.errorString()));
result = false;
}
}
else {
setError(QObject::tr("Error creating OTX file %1:\n%2.").arg(filename).arg(file.errorString()));
result = false;
}
}
else {
setError(QObject::tr("Error creating OTX archive"));
result = false;
}
}
mz_zip_writer_end(&zip_archive);
return result;
}
bool OtxFormat::loadFile(QByteArray & filedata, const QString & filename)
{
size_t size;
void * data = mz_zip_reader_extract_file_to_heap(&zip_archive, filename.toStdString().c_str(), &size, 0);
if (!data) {
return false;
}
qDebug() << QString("Extracted file %1, size=%2").arg(filename).arg(size);
filedata.clear();
filedata.append((char *)data, size);
mz_free(data);
return true;
}
bool OtxFormat::writeFile(const QByteArray & filedata, const QString & filename)
{
if (!mz_zip_writer_add_mem(&zip_archive, filename.toStdString().c_str(), filedata.data(), filedata.size(), MZ_DEFAULT_LEVEL)) {
setError(QObject::tr("Error adding %1 to OTX archive").arg(filename));
return false;
}
return true;
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _OTX_H_
#define _OTX_H_
#include "categorized.h"
#define MINIZ_HEADER_FILE_ONLY
#include "miniz.c"
#undef MINIZ_HEADER_FILE_ONLY
class OtxFormat : public CategorizedStorageFormat
{
public:
OtxFormat(const QString & filename):
CategorizedStorageFormat(filename)
{
}
virtual bool load(RadioData & radioData);
virtual bool write(const RadioData & radioData);
protected:
virtual bool loadFile(QByteArray & fileData, const QString & fileName);
virtual bool writeFile(const QByteArray & fileData, const QString & fileName);
mz_zip_archive zip_archive;
};
#endif // _OTX_H_

View file

@ -20,21 +20,6 @@
// TODO should be rle
/*
* Author - Bertrand Songis <bsongis@gmail.com>
*
* Based on th9x -> http://code.google.com/p/th9x/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _RLEFILE_H_
#define _RLEFILE_H_

View file

@ -0,0 +1,65 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "sdcard.h"
#include <QFile>
#include <QDir>
bool SdcardFormat::write(const RadioData & radioData)
{
QDir dir(filename);
dir.mkdir("RADIO");
dir.mkdir("MODELS");
return CategorizedStorageFormat::write(radioData);
}
bool SdcardFormat::loadFile(QByteArray & filedata, const QString & filename)
{
QString path = this->filename + "/" + filename;
QFile file(path);
if (!file.open(QFile::ReadOnly)) {
setError(QObject::tr("Error opening file %1:\n%2.").arg(path).arg(file.errorString()));
return false;
}
filedata = file.readAll();
qDebug() << "File" << path << "read, size:" << filedata.size();
return true;
}
bool SdcardFormat::writeFile(const QByteArray & data, const QString & filename)
{
QString path = this->filename + "/" + filename;
QFile file(path);
if (!file.open(QFile::WriteOnly)) {
setError(QObject::tr("Error opening file %1:\n%2.").arg(path).arg(file.errorString()));
qDebug() << "File" << path << "write error";
return false;
}
file.write(data.data(), data.size());
file.close();
qDebug() << "File" << path << "written, size:" << data.size();
return true;
}
bool SdcardStorageFactory::probe(const QString & path)
{
return QDir(path).exists();
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _SDCARD_H2_
#define _SDCARD_H2_
#include "categorized.h"
class SdcardFormat : public CategorizedStorageFormat
{
public:
SdcardFormat(const QString & filename):
CategorizedStorageFormat(filename)
{
}
virtual bool write(const RadioData & radioData);
protected:
virtual bool loadFile(QByteArray & fileData, const QString & fileName);
virtual bool writeFile(const QByteArray & fileData, const QString & fileName);
};
class SdcardStorageFactory : public DefaultStorageFactory<SdcardFormat>
{
public:
SdcardStorageFactory():
DefaultStorageFactory<SdcardFormat>("sdcard")
{
}
virtual bool probe(const QString & name);
};
#endif // _SDCARD_H2_

View file

@ -0,0 +1,170 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "bineeprom.h"
#include "eepe.h"
#include "otx.h"
#include "sdcard.h"
#include "firmwareinterface.h"
#include "eeprominterface.h"
#include <QFileInfo>
StorageType getStorageType(const QString & filename)
{
QString suffix = QFileInfo(filename).suffix().toUpper();
if (suffix == "HEX")
return STORAGE_TYPE_HEX;
else if (suffix == "BIN")
return STORAGE_TYPE_BIN;
else if (suffix == "EEPM")
return STORAGE_TYPE_EEPM;
else if (suffix == "EEPE")
return STORAGE_TYPE_EEPE;
else if (suffix == "XML")
return STORAGE_TYPE_XML;
else if (suffix == "OTX")
return STORAGE_TYPE_OTX;
else
return STORAGE_TYPE_UNKNOWN;
}
void registerStorageFactory(StorageFactory * factory);
QList<StorageFactory *> registeredStorageFactories;
void registerStorageFactory(StorageFactory * factory)
{
qDebug() << "register storage" << factory->name();
registeredStorageFactories.push_back(factory);
}
void registerStorageFactories()
{
registerStorageFactory(new DefaultStorageFactory<BinEepromFormat>("bin"));
registerStorageFactory(new DefaultStorageFactory<EepeFormat>("eepe"));
registerStorageFactory(new DefaultStorageFactory<HexEepromFormat>("hex"));
registerStorageFactory(new DefaultStorageFactory<OtxFormat>("otx"));
registerStorageFactory(new SdcardStorageFactory());
}
bool Storage::load(RadioData & radioData)
{
QFile file(filename);
if (!file.exists()) {
setError(QObject::tr("Unable to find file %1!").arg(filename));
return false;
}
foreach(StorageFactory * factory, registeredStorageFactories) {
StorageFormat * format = factory->instance(filename);
if (format->load(radioData)) {
setWarning(format->warning());
return true;
}
else {
setError(format->error());
}
}
return false;
}
bool Storage::write(const RadioData & radioData)
{
foreach(StorageFactory * factory, registeredStorageFactories) {
if (factory->probe(filename)) {
return factory->instance(filename)->write(radioData);
}
}
return false;
}
bool convertEEprom(const QString & sourceEEprom, const QString & destinationEEprom, const QString & firmwareFilename)
{
Firmware * currentFirmware = GetCurrentFirmware();
FirmwareInterface firmware(firmwareFilename);
if (!firmware.isValid())
return false;
uint8_t version = firmware.getEEpromVersion();
unsigned int variant = firmware.getEEpromVariant();
QSharedPointer<RadioData> radioData = QSharedPointer<RadioData>(new RadioData());
Storage storage(sourceEEprom);
if (!storage.load(*radioData))
return false;
QByteArray eeprom(EESIZE_MAX, 0);
int size = currentFirmware->saveEEPROM((uint8_t *)eeprom.data(), *radioData, version, variant);
if (size == 0) {
return false;
}
QFile destinationFile(destinationEEprom);
if (!destinationFile.open(QIODevice::WriteOnly))
return false;
int result = destinationFile.write(eeprom.constData(), size);
destinationFile.close();
return (result == size);
}
#if 0
unsigned long LoadBackup(RadioData & radioData, uint8_t * eeprom, int size, int index)
{
std::bitset<NUM_ERRORS> errors;
foreach(EEPROMInterface *eepromInterface, eepromInterfaces) {
std::bitset<NUM_ERRORS> result((unsigned long long)eepromInterface->loadBackup(radioData, eeprom, size, index));
if (result.test(ALL_OK)) {
return result.to_ulong();
}
else {
errors |= result;
}
}
if (errors.none()) {
errors.set(UNKNOWN_ERROR);
}
return errors.to_ulong();
}
unsigned long LoadEepromXml(RadioData & radioData, QDomDocument & doc)
{
std::bitset<NUM_ERRORS> errors;
foreach(EEPROMInterface *eepromInterface, eepromInterfaces) {
std::bitset<NUM_ERRORS> result((unsigned long long)eepromInterface->loadxml(radioData, doc));
if (result.test(ALL_OK)) {
return result.to_ulong();
}
else {
errors |= result;
}
}
if (errors.none()) {
errors.set(UNKNOWN_ERROR);
}
return errors.to_ulong();
}
#endif

View file

@ -0,0 +1,143 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _STORAGE_H_
#define _STORAGE_H_
#include "radiodata.h"
#include <QString>
#include <QDebug>
enum StorageType
{
STORAGE_TYPE_UNKNOWN,
STORAGE_TYPE_BIN,
STORAGE_TYPE_HEX,
STORAGE_TYPE_EEPE,
STORAGE_TYPE_EEPM,
STORAGE_TYPE_XML,
STORAGE_TYPE_SDCARD,
STORAGE_TYPE_OTX
};
StorageType getStorageType(const QString & filename);
class StorageFormat
{
public:
StorageFormat(const QString & filename, uint8_t version=0):
filename(filename),
version(version)
{
}
virtual bool load(RadioData & radioData) = 0;
virtual bool write(const RadioData & radioData) = 0;
QString error() {
return _error;
}
QString warning() {
return _warning;
}
protected:
void setError(const QString & error)
{
qDebug() << "error:" << error;
_error = error;
}
void setWarning(const QString & warning)
{
if (!warning.isEmpty())
qDebug() << "warning:" << warning;
_warning = warning;
}
QString filename;
uint8_t version;
QString _error;
QString _warning;
};
class StorageFactory
{
public:
StorageFactory()
{
}
virtual QString name() = 0;
virtual bool probe(const QString & filename) = 0;
virtual StorageFormat * instance(const QString & filename) = 0;
};
template <class T>
class DefaultStorageFactory : public StorageFactory
{
public:
DefaultStorageFactory(const QString & name):
StorageFactory(),
_name(name)
{
}
virtual QString name()
{
return _name;
}
virtual bool probe(const QString & filename)
{
return filename.toLower().endsWith("." + _name);
}
virtual StorageFormat * instance(const QString & filename)
{
return new T(filename);
}
QString _name;
};
class Storage : public StorageFormat
{
public:
Storage(const QString & filename):
StorageFormat(filename)
{
}
virtual bool load(RadioData & radioData);
virtual bool write(const RadioData & radioData);
};
void registerStorageFactories();
#if 0
unsigned long LoadBackup(RadioData &radioData, uint8_t *eeprom, int esize, int index);
unsigned long LoadEeprom(RadioData &radioData, const uint8_t *eeprom, int size);
unsigned long LoadEepromXml(RadioData &radioData, QDomDocument &doc);
#endif
bool convertEEprom(const QString & sourceEEprom, const QString & destinationEEprom, const QString & firmware);
#endif // _STORAGE_H_

View file

@ -1,124 +0,0 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <QFile>
#include "firmwareinterface.h"
#include "storage_eeprom.h"
unsigned long LoadEeprom(RadioData & radioData, const uint8_t * eeprom, const int size)
{
std::bitset<NUM_ERRORS> errors;
foreach(EEPROMInterface * eepromInterface, eepromInterfaces) {
std::bitset<NUM_ERRORS> result((unsigned long long)eepromInterface->load(radioData, eeprom, size));
if (result.test(ALL_OK)) {
return result.to_ulong();
}
else {
errors |= result;
}
}
if (errors.none()) {
errors.set(UNKNOWN_ERROR);
}
return errors.to_ulong();
}
unsigned long LoadBackup(RadioData & radioData, uint8_t * eeprom, int size, int index)
{
std::bitset<NUM_ERRORS> errors;
foreach(EEPROMInterface *eepromInterface, eepromInterfaces) {
std::bitset<NUM_ERRORS> result((unsigned long long)eepromInterface->loadBackup(radioData, eeprom, size, index));
if (result.test(ALL_OK)) {
return result.to_ulong();
}
else {
errors |= result;
}
}
if (errors.none()) {
errors.set(UNKNOWN_ERROR);
}
return errors.to_ulong();
}
unsigned long LoadEepromXml(RadioData & radioData, QDomDocument & doc)
{
std::bitset<NUM_ERRORS> errors;
foreach(EEPROMInterface *eepromInterface, eepromInterfaces) {
std::bitset<NUM_ERRORS> result((unsigned long long)eepromInterface->loadxml(radioData, doc));
if (result.test(ALL_OK)) {
return result.to_ulong();
}
else {
errors |= result;
}
}
if (errors.none()) {
errors.set(UNKNOWN_ERROR);
}
return errors.to_ulong();
}
bool convertEEprom(const QString & sourceEEprom, const QString & destinationEEprom, const QString & firmwareFilename)
{
Firmware * currentFirmware = GetCurrentFirmware();
FirmwareInterface firmware(firmwareFilename);
if (!firmware.isValid())
return false;
uint8_t version = firmware.getEEpromVersion();
unsigned int variant = firmware.getEEpromVariant();
QFile sourceFile(sourceEEprom);
int eeprom_size = sourceFile.size();
if (!eeprom_size)
return false;
if (!sourceFile.open(QIODevice::ReadOnly))
return false;
QByteArray eeprom(eeprom_size, 0);
long result = sourceFile.read(eeprom.data(), eeprom_size);
sourceFile.close();
QSharedPointer<RadioData> radioData = QSharedPointer<RadioData>(new RadioData());
std::bitset<NUM_ERRORS> errors((unsigned long long)LoadEeprom(*radioData, (uint8_t *)eeprom.data(), eeprom_size));
if (!errors.test(ALL_OK) || !currentFirmware->saveEEPROM((uint8_t *)eeprom.data(), *radioData, version, variant)) {
return false;
}
QFile destinationFile(destinationEEprom);
if (!destinationFile.open(QIODevice::WriteOnly))
return false;
result = destinationFile.write(eeprom.constData(), eeprom_size);
destinationFile.close();
if (result != eeprom_size)
return false;
return true;
}

View file

@ -1,300 +0,0 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <QFileInfo>
#include <QObject>
#include <QDebug>
#include <QRegularExpression>
#include "storage_sdcard.h"
// defines for miniz
#include "miniz.c"
int readFromArchive(QByteArray & data, mz_zip_archive * zip_archive, unsigned int index)
{
size_t uncompSize;
void * uncompData = mz_zip_reader_extract_to_heap(zip_archive, index, &uncompSize, 0);
if (!uncompData) {
return -1;
}
qDebug() << "Extracted file with index" << index << ",size" << uncompSize;
data.append((const char *)uncompData, uncompSize);
mz_free(uncompData);
return 0;
}
int StorageSdcard::read(const QString & path)
{
return QFileInfo(path).isDir() ? readSdcard(path) : readOtx(path);
}
int StorageSdcard::write(const QString & path)
{
return QFileInfo(path).isDir() ? writeSdcard(path) : writeOtx(path);
}
int StorageSdcard::readOtx(const QString & path)
{
QFile file(path);
if (!file.open(QFile::ReadOnly)) {
lastErrorMessage = QObject::tr("Error opening file %1:\n%2.").arg(path).arg(file.errorString());
return -1;
}
QByteArray archiveContents = file.readAll();
file.close();
qDebug() << "File" << path << "read, size:" << archiveContents.size();
// open zip file
mz_zip_archive zip_archive;
memset(&zip_archive, 0, sizeof(zip_archive));
if (!mz_zip_reader_init_mem(&zip_archive, archiveContents.data(), archiveContents.size(), 0)) {
lastErrorMessage = QObject::tr("Error opening OTX archive %1.").arg(path);
qDebug() << lastErrorMessage;
return -1;
}
// go trough all files in an archive
QRegularExpression regexModel("MODELS/\\w+.bin", QRegularExpression::CaseInsensitiveOption);
for (unsigned int i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) {
mz_zip_archive_file_stat file_stat;
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) {
lastErrorMessage = QObject::tr("mz_zip_reader_file_stat() failed!");
qDebug() << lastErrorMessage;
mz_zip_reader_end(&zip_archive);
return -1;
}
// printf("Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u, Is Dir: %u\n", file_stat.m_filename, file_stat.m_comment, (uint)file_stat.m_uncomp_size, (uint)file_stat.m_comp_size, mz_zip_reader_is_file_a_directory(&zip_archive, i));
if (mz_zip_reader_is_file_a_directory(&zip_archive, i)) continue;
QString filename(file_stat.m_filename);
if (regexModel.match(filename).hasMatch()) {
qDebug() << "found model:" << filename;
models.append(ModelFile());
ModelFile & newModel = models.last();
newModel.filename = QFileInfo(filename).fileName();
if (readFromArchive(newModel.data, &zip_archive, i) < 0) {
lastErrorMessage = QObject::tr("Can't extract file %1").arg(filename);
qDebug() << lastErrorMessage;
mz_zip_reader_end(&zip_archive);
return -1;
}
}
else if (QString::compare(filename, "RADIO/radio.bin", Qt::CaseInsensitive) == 0) {
if (readFromArchive(radio, &zip_archive, i) < 0) {
lastErrorMessage = QObject::tr("Can't extract file %1").arg(filename);
qDebug() << lastErrorMessage;
mz_zip_reader_end(&zip_archive);
return -1;
}
}
else if (QString::compare(filename, "RADIO/models.txt", Qt::CaseInsensitive) == 0) {
if (readFromArchive(modelList, &zip_archive, i) < 0) {
lastErrorMessage = QObject::tr("Can't extract file %1").arg(filename);
qDebug() << lastErrorMessage;
mz_zip_reader_end(&zip_archive);
return -1;
}
}
else {
qDebug() << "Unknown file " << filename;
}
}
mz_zip_reader_end(&zip_archive);
return 0;
}
void StorageSdcard::readFile(QByteArray & data, const QString & path)
{
QFile file(path);
if (!file.open(QFile::ReadOnly)) {
lastErrorMessage = QObject::tr("Error opening file %1:\n%2.").arg(path).arg(file.errorString());
throw StorageSdcardReadFileError();
}
data = file.readAll();
file.close();
qDebug() << "File" << path << "read, size:" << data.size();
}
int StorageSdcard::readSdcard(const QString & path)
{
try {
readFile(radio, path + "/RADIO/radio.bin");
readFile(modelList, path + "RADIO/models.txt");
QDir dir(path + "MODELS/");
QStringList filters;
filters << "*.bin";
foreach(QString filename, dir.entryList(filters, QDir::Files)) {
qDebug() << "found" << filename;
models.append(ModelFile());
ModelFile & newModel = models.last();
newModel.filename = QFileInfo(filename).fileName();
readFile(newModel.data, path + "MODELS/" + filename);
}
}
catch (StorageSdcardReadFileError) {
return -1;
}
return 0;
}
void StorageSdcard::writeFile(const QByteArray & data, const QString & path)
{
QFile file(path);
if (!file.open(QFile::WriteOnly)) {
lastErrorMessage = QObject::tr("Error opening file %1:\n%2.").arg(path).arg(file.errorString());
qDebug() << "File" << path << "write error";
throw StorageSdcardWriteFileError();
}
file.write(data.data(), data.size());
file.close();
qDebug() << "File" << path << "written, size:" << data.size();
}
int StorageSdcard::writeSdcard(const QString & path)
{
try {
QDir dir(path);
dir.mkdir("RADIO");
writeFile(radio, path + "/RADIO/radio.bin");
writeFile(modelList, path + "/RADIO/models.txt");
dir.mkdir("MODELS");
for (QList<ModelFile>::const_iterator i = models.begin(); i != models.end(); ++i) {
qDebug() << "writing" << i->filename;
writeFile(i->data, path + "/MODELS/" + i->filename);
}
}
catch (StorageSdcardWriteFileError) {
return -1;
}
return 0;
}
#define MZ_ALLOCATION_SIZE (32*1024)
int StorageSdcard::writeOtx(const QString & path)
{
qDebug() << "Saving to archive" << path;
mz_zip_archive zip_archive;
memset(&zip_archive, 0, sizeof(zip_archive));
if (!mz_zip_writer_init_heap(&zip_archive, 0, MZ_ALLOCATION_SIZE)) {
lastErrorMessage = QObject::tr("Error initializing OTX archive writer");
qDebug() << lastErrorMessage;
return -1;
}
// add radio.bin
if (!mz_zip_writer_add_mem(&zip_archive, "RADIO/radio.bin", radio.data(), radio.size(), MZ_DEFAULT_LEVEL)) {
lastErrorMessage = QObject::tr("Error adding %1 to OTX archive").arg("RADIO/radio.bin");
qDebug() << lastErrorMessage;
mz_zip_writer_end(&zip_archive);
return -1;
}
// add models.txt
if (!mz_zip_writer_add_mem(&zip_archive, "RADIO/models.txt", modelList.data(), modelList.size(), MZ_DEFAULT_LEVEL)) {
lastErrorMessage = QObject::tr("Error adding %1 to OTX archive").arg("RADIO/models.txt");
qDebug() << lastErrorMessage;
mz_zip_writer_end(&zip_archive);
return -1;
}
// add all models
for (QList<ModelFile>::iterator i = models.begin(); i != models.end(); ++i) {
QString filename = "MODELS/" + i->filename;
qDebug() << "\tadding model:" << filename << "size" << i->data.size();
if (!mz_zip_writer_add_mem(&zip_archive, filename.toLatin1(), i->data.data(), i->data.size(), MZ_DEFAULT_LEVEL)) {
lastErrorMessage = QObject::tr("Error adding %1 to OTX archive").arg(filename);
qDebug() << lastErrorMessage;
mz_zip_writer_end(&zip_archive);
return -1;
}
}
// finalize archive and get contents
char * archiveContents;
size_t archiveSize;
if (!mz_zip_writer_finalize_heap_archive(&zip_archive, (void **)&archiveContents, &archiveSize)) {
lastErrorMessage = QObject::tr("Error creating OTX archive");
qDebug() << lastErrorMessage;
mz_zip_writer_end(&zip_archive);
return -1;
}
qDebug() << "Archive size" << archiveSize;
// write contents to file
QFile file(path);
if (!file.open(QIODevice::WriteOnly)) {
lastErrorMessage = QObject::tr("Error creating OTX file %1:\n%2.").arg(path).arg(file.errorString());
qDebug() << lastErrorMessage;
mz_zip_writer_end(&zip_archive);
return -1;
}
qint64 result = file.write(archiveContents, archiveSize);
if(result != (qint64)archiveSize) {
lastErrorMessage = QObject::tr("Error writing file %1:\n%2.").arg(path).arg(file.errorString());
mz_zip_writer_end(&zip_archive);
return -1;
}
file.close();
mz_zip_writer_end(&zip_archive);
return 0;
}
const QByteArray & StorageSdcard::getModelData(const QString & filename) const
{
for (QList<ModelFile>::const_iterator i = models.begin(); i != models.end(); ++i) {
if (filename == i->filename) return i->data;
}
throw StorageSdcardModelNotFound();
}
QList<ModelFile>::iterator StorageSdcard::getModelIterator(const QString & filename)
{
for (QList<ModelFile>::iterator i = models.begin(); i != models.end(); ++i) {
if (filename == i->filename) return i;
}
return models.end();
}
QList<ModelFile>::const_iterator StorageSdcard::getModelIterator(const QString & filename) const
{
for (QList<ModelFile>::const_iterator i = models.begin(); i != models.end(); ++i) {
if (filename == i->filename) return i;
}
return models.end();
}
QList<QString> StorageSdcard::getModelsFileNames() const
{
QList<QString> result;
for (QList<ModelFile>::const_iterator i = models.begin(); i != models.end(); ++i) {
result.append(i->filename);
}
return result;
}

View file

@ -1,99 +0,0 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _STORAGE_SDCARD_H_
#define _STORAGE_SDCARD_H_
#include <QList>
#include <QByteArray>
#include <QString>
#include <QDir>
/*
This class implements a storage model used for radios that
store all settings on the SD card (currently only Hours)
Disk storage format:
* uses extension OTX
* its a normal ZIP archive containing the same directory
structure as the SD card in the radio. Only the relevant
directories and files are included.
*/
class StorageSdcardModelNotFound {};
class StorageSdcardReadFileError {};
class StorageSdcardWriteFileError {};
// representation of modelXX.bin file
struct ModelFile {
QString filename; // file name (without path)
QByteArray data; // file contents
};
class StorageSdcard {
public:
/*
Reads models and radio settings from:
* if a file is specified in path, from that file (OTX archive format)
* if a path is specified, from that path (where it expects to find files like on the SD card)
*/
int read(const QString & path);
/*
|Writes models and radio settings to:
* if a file is specified in path, to that file (OTX archive format)
* if a path is specified, to that path (layout created is like on the SD card)
*/
int write(const QString & path);
/*
Returns a model data for specified model
*/
const QByteArray & getModelData(const QString & filename) const;
/*
Returns a model iterator for specified model
*/
QList<ModelFile>::iterator getModelIterator(const QString & filename);
QList<ModelFile>::const_iterator getModelIterator(const QString & filename) const;
/*
Returns a list of all model bin files (their filenames)
*/
QList<QString> getModelsFileNames() const;
QString lastErrorMessage;
QByteArray radio; // radio settings (radio.bin)
QByteArray modelList; // model names and categories (models.txt)
QList<ModelFile> models; // collection of model data (modelXX.bin)
private:
int readOtx(const QString & path);
int readSdcard(const QString & path);
int writeOtx(const QString & path);
int writeSdcard(const QString & path);
void readFile(QByteArray & data, const QString & path);
void writeFile(const QByteArray & data, const QString & path);
};
#endif // _STORAGE_SDCARD_H_

View file

@ -195,7 +195,7 @@ bool XmlInterface::save(RadioData &radioData)
// the models
models xml_models;
models::model_sequence & model_sequence (xml_models.model());
for (int i=0; i<CPN_MAX_MODELS; i++) {
for (int i=0; i<radioData.models.size(); i++) {
ModelData & m = radioData.models[i];
if (m.used) {
model xm(m.name);

View file

@ -23,6 +23,7 @@
#include "eeprominterface.h"
#include <QTextStream>
#include <QDomDocument>
class XmlInterface
{
@ -32,7 +33,7 @@ class XmlInterface
bool load(RadioData &);
virtual bool loadxml(RadioData &radioData, QDomDocument &doc);
virtual bool loadxml(RadioData & radioData, QDomDocument & doc);
bool save(RadioData &radioData);