diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index bad82790c..21e4c29a6 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -185,7 +185,6 @@ set(companion_MOC_HDRS flashfirmwaredialog.h flasheepromdialog.h downloaddialog.h - modelslist.h mdichild.h mainwindow.h radionotfound.h diff --git a/companion/src/companion.cpp b/companion/src/companion.cpp index 433fc059a..54aa3ce57 100644 --- a/companion/src/companion.cpp +++ b/companion/src/companion.cpp @@ -20,21 +20,16 @@ #include #include -#include -#include #include -#include #include -#include -#include #if defined(JOYSTICKS) || defined(SIMU_AUDIO) #include #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 @@ -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(); diff --git a/companion/src/contributorsdialog.cpp b/companion/src/contributorsdialog.cpp index 8c95001d4..dc882be81 100644 --- a/companion/src/contributorsdialog.cpp +++ b/companion/src/contributorsdialog.cpp @@ -21,6 +21,7 @@ #include "contributorsdialog.h" #include "ui_htmldialog.h" #include "helpers.h" +#include ContributorsDialog::ContributorsDialog(QWidget * parent): QDialog(parent), diff --git a/companion/src/eepromimportexport.h b/companion/src/eepromimportexport.h index 721ef328a..6555e1d22 100644 --- a/companion/src/eepromimportexport.h +++ b/companion/src/eepromimportexport.h @@ -22,6 +22,7 @@ #define _EEPROMIMPORTEXPORT_H_ #include "customdebug.h" +#include #define DIM(arr) (sizeof((arr))/sizeof((arr)[0])) diff --git a/companion/src/eeprominterface.cpp b/companion/src/eeprominterface.cpp index c308dafff..44650a67b 100644 --- a/companion/src/eeprominterface.cpp +++ b/companion/src/eeprominterface.cpp @@ -18,11 +18,6 @@ * GNU General Public License for more details. */ -#include -#include -#include -#include -#include #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 +#include +#include +#include +#include +#include std::list 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 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); diff --git a/companion/src/eeprominterface.h b/companion/src/eeprominterface.h index 3e9088a2f..e1856b8c2 100644 --- a/companion/src/eeprominterface.h +++ b/companion/src/eeprominterface.h @@ -21,17 +21,13 @@ #ifndef _EEPROMINTERFACE_H_ #define _EEPROMINTERFACE_H_ -#include -#include -#include -#include -#include // This should be removed from here, and remove Xml dependency from all libs which don't need it. -#include -#include -#include -#include "constants.h" +#include "radiodata.h" #include "../../radio/src/definitions.h" #include "simulatorinterface.h" +#include +#include +#include +#include #define EESIZE_STOCK 2048 #define EESIZE_M128 4096 @@ -55,1305 +51,27 @@ #define IS_2560(board) (board==BOARD_GRUVIN9X || board==BOARD_MEGA2560) #define IS_SKY9X(board) (board==BOARD_SKY9X || board==BOARD_9XRPRO || board==BOARD_AR9X) #define IS_9XRPRO(board) (board==BOARD_9XRPRO) -#define IS_TARANIS(board) (board==BOARD_TARANIS_X9D || board==BOARD_TARANIS_X9DP || board==BOARD_TARANIS_X9E || board==BOARD_TARANIS_X7) +#define IS_TARANIS_X7(board) (board==BOARD_TARANIS_X7) #define IS_TARANIS_PLUS(board) (board==BOARD_TARANIS_X9DP || board==BOARD_TARANIS_X9E) #define IS_TARANIS_X9E(board) (board==BOARD_TARANIS_X9E) +#define IS_TARANIS(board) (board==BOARD_TARANIS_X9D || board==BOARD_TARANIS_X9DP || board==BOARD_TARANIS_X9E || board==BOARD_TARANIS_X7) #define IS_HORUS(board) (board==BOARD_HORUS) +#define IS_HORUS_OR_TARANIS(board) (IS_HORUS(board) || IS_TARANIS(board)) #define IS_FLAMENCO(board) (board==BOARD_FLAMENCO) #define IS_STM32(board) (IS_TARANIS(board) || IS_HORUS(board) || IS_FLAMENCO(board)) #define IS_ARM(board) (IS_STM32(board) || IS_SKY9X(board)) #define HAS_LARGE_LCD(board) (IS_HORUS(board) || (IS_TARANIS(board) && board != BOARD_TARANIS_X7)) +QString RotaryEncoderString(int index); + const uint8_t modn12x3[4][4]= { {1, 2, 3, 4}, {1, 3, 2, 4}, {4, 2, 3, 1}, {4, 3, 2, 1} }; -enum Switches { - SWITCH_NONE, - - SWITCH_THR = SWITCH_NONE+1, - SWITCH_RUD, - SWITCH_ELE, - SWITCH_ID0, - SWITCH_ID1, - SWITCH_ID2, - SWITCH_AIL, - SWITCH_GEA, - SWITCH_TRN, - - SWITCH_SA0 = SWITCH_NONE+1, - SWITCH_SA1, - SWITCH_SA2, - SWITCH_SB0, - SWITCH_SB1, - SWITCH_SB2, - SWITCH_SC0, - SWITCH_SC1, - SWITCH_SC2, - SWITCH_SD0, - SWITCH_SD1, - SWITCH_SD2, - SWITCH_SE0, - SWITCH_SE1, - SWITCH_SE2, - SWITCH_SF0, - SWITCH_SF1, - SWITCH_SG0, - SWITCH_SG1, - SWITCH_SG2, - SWITCH_SH0, - SWITCH_SH2, - SWITCH_SI0, - SWITCH_SI2, - SWITCH_SJ0, - SWITCH_SJ2, - SWITCH_SK0, - SWITCH_SK2, - -}; - -enum TimerModes { - TMRMODE_NONE, - TMRMODE_ABS, - TMRMODE_THR, - TMRMODE_THR_REL, - TMRMODE_THR_TRG, - TMRMODE_FIRST_SWITCH -}; - -enum FailsafeModes { - FAILSAFE_NOT_SET, - FAILSAFE_HOLD, - FAILSAFE_CUSTOM, - FAILSAFE_NOPULSES, - FAILSAFE_RECEIVER, - FAILSAFE_LAST = FAILSAFE_RECEIVER -}; - -#define TRIM_LH_L 0 -#define TRIM_LH_R 1 -#define TRIM_LV_DN 2 -#define TRIM_LV_UP 3 -#define TRIM_RV_DN 4 -#define TRIM_RV_UP 5 -#define TRIM_RH_L 6 -#define TRIM_RH_R 7 -#define TRIM_T5_DN 8 -#define TRIM_T5_UP 9 -#define TRIM_T6_DN 10 -#define TRIM_T6_UP 11 -#define TRIM_NONE 12 - -#define CHAR_FOR_NAMES " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-." -#define CHAR_FOR_NAMES_REGEX "[ A-Za-z0-9_.-,]*" - -enum HeliSwashTypes { - HELI_SWASH_TYPE_NONE=0, - HELI_SWASH_TYPE_120, - HELI_SWASH_TYPE_120X, - HELI_SWASH_TYPE_140, - HELI_SWASH_TYPE_90 -}; - -class ModelData; -class GeneralSettings; - -enum TelemetrySource { - TELEMETRY_SOURCE_TX_BATT, - TELEMETRY_SOURCE_TX_TIME, - TELEMETRY_SOURCE_TIMER1, - TELEMETRY_SOURCE_TIMER2, - TELEMETRY_SOURCE_TIMER3, - TELEMETRY_SOURCE_SWR, - TELEMETRY_SOURCE_RSSI_TX, - TELEMETRY_SOURCE_RSSI_RX, - TELEMETRY_SOURCE_A1, - TELEMETRY_SOURCE_A2, - TELEMETRY_SOURCE_A3, - TELEMETRY_SOURCE_A4, - TELEMETRY_SOURCE_ALT, - TELEMETRY_SOURCE_RPM, - TELEMETRY_SOURCE_FUEL, - TELEMETRY_SOURCE_T1, - TELEMETRY_SOURCE_T2, - TELEMETRY_SOURCE_SPEED, - TELEMETRY_SOURCE_DIST, - TELEMETRY_SOURCE_GPS_ALT, - TELEMETRY_SOURCE_CELL, - TELEMETRY_SOURCE_CELLS_SUM, - TELEMETRY_SOURCE_VFAS, - TELEMETRY_SOURCE_CURRENT, - TELEMETRY_SOURCE_CONSUMPTION, - TELEMETRY_SOURCE_POWER, - TELEMETRY_SOURCE_ACCX, - TELEMETRY_SOURCE_ACCY, - TELEMETRY_SOURCE_ACCZ, - TELEMETRY_SOURCE_HDG, - TELEMETRY_SOURCE_VERTICAL_SPEED, - TELEMETRY_SOURCE_ASPEED, - TELEMETRY_SOURCE_DTE, - TELEMETRY_SOURCE_A1_MIN, - TELEMETRY_SOURCE_A2_MIN, - TELEMETRY_SOURCE_A3_MIN, - TELEMETRY_SOURCE_A4_MIN, - TELEMETRY_SOURCE_ALT_MIN, - TELEMETRY_SOURCE_ALT_MAX, - TELEMETRY_SOURCE_RPM_MAX, - TELEMETRY_SOURCE_T1_MAX, - TELEMETRY_SOURCE_T2_MAX, - TELEMETRY_SOURCE_SPEED_MAX, - TELEMETRY_SOURCE_DIST_MAX, - TELEMETRY_SOURCE_ASPEED_MAX, - TELEMETRY_SOURCE_CELL_MIN, - TELEMETRY_SOURCE_CELLS_MIN, - TELEMETRY_SOURCE_VFAS_MIN, - TELEMETRY_SOURCE_CURRENT_MAX, - TELEMETRY_SOURCE_POWER_MAX, - TELEMETRY_SOURCE_ACC, - TELEMETRY_SOURCE_GPS_TIME, - TELEMETRY_SOURCES_STATUS_COUNT = TELEMETRY_SOURCE_GPS_TIME+1, - TELEMETRY_SOURCES_DISPLAY_COUNT = TELEMETRY_SOURCE_POWER_MAX+1, - TELEMETRY_SOURCES_COUNT = TELEMETRY_SOURCE_POWER_MAX+1, - TELEMETRY_SOURCE_RESERVE = -1 -}; - -#define TM_HASTELEMETRY 0x01 -#define TM_HASOFFSET 0x02 -#define TM_HASWSHH 0x04 - -enum RawSourceType { - SOURCE_TYPE_NONE, - SOURCE_TYPE_VIRTUAL_INPUT, - SOURCE_TYPE_LUA_OUTPUT, - SOURCE_TYPE_STICK, // and POTS - SOURCE_TYPE_ROTARY_ENCODER, - SOURCE_TYPE_TRIM, - SOURCE_TYPE_MAX, - SOURCE_TYPE_SWITCH, - SOURCE_TYPE_CUSTOM_SWITCH, - SOURCE_TYPE_CYC, - SOURCE_TYPE_PPM, - SOURCE_TYPE_CH, - SOURCE_TYPE_GVAR, - SOURCE_TYPE_SPECIAL, - SOURCE_TYPE_TELEMETRY, - MAX_SOURCE_TYPE -}; - -QString RotaryEncoderString(int index); - -class RawSourceRange -{ - public: - RawSourceRange(): - decimals(0), - min(0.0), - max(0.0), - step(1.0), - offset(0.0) - { - } - - float getValue(int value); - - int decimals; - double min; - double max; - double step; - double offset; - QString unit; -}; - -#define RANGE_SINGLE_PRECISION 1 -#define RANGE_DELTA_FUNCTION 2 -#define RANGE_DELTA_ABS_FUNCTION 4 - -class RawSource { - public: - RawSource(): - type(SOURCE_TYPE_NONE), - index(0) - { - } - - explicit RawSource(int value): - type(RawSourceType(abs(value)/65536)), - index(value >= 0 ? abs(value)%65536 : -(abs(value)%65536)) - { - } - - RawSource(RawSourceType type, int index=0): - type(type), - index(index) - { - } - - inline const int toValue() const - { - return index >= 0 ? (type * 65536 + index) : -(type * 65536 - index); - } - - QString toString(const ModelData * model = NULL) const; - - RawSourceRange getRange(const ModelData * model, const GeneralSettings & settings, unsigned int flags=0) const; - - bool operator == ( const RawSource & other) { - return (this->type == other.type) && (this->index == other.index); - } - - bool operator != ( const RawSource & other) { - return (this->type != other.type) || (this->index != other.index); - } - - bool isTimeBased() const; - bool isPot() const; - bool isSlider() const; - - RawSourceType type; - int index; -}; - -enum RawSwitchType { - SWITCH_TYPE_NONE, - SWITCH_TYPE_SWITCH, - SWITCH_TYPE_VIRTUAL, - SWITCH_TYPE_MULTIPOS_POT, - SWITCH_TYPE_TRIM, - SWITCH_TYPE_ROTARY_ENCODER, - SWITCH_TYPE_ON, - SWITCH_TYPE_OFF, - SWITCH_TYPE_ONE, - SWITCH_TYPE_FLIGHT_MODE, - SWITCH_TYPE_TIMER_MODE -}; - -class RawSwitch { - public: - RawSwitch(): - type(SWITCH_TYPE_NONE), - index(0) - { - } - - explicit RawSwitch(int value): - type(RawSwitchType(abs(value)/256)), - index(value >= 0 ? abs(value)%256 : -(abs(value)%256)) - { - } - - RawSwitch(RawSwitchType type, int index=0): - type(type), - index(index) - { - } - - inline const int toValue() const - { - return index >= 0 ? (type * 256 + index) : -(type * 256 - index); - } - - QString toString() const; - - bool operator== ( const RawSwitch& other) { - return (this->type == other.type) && (this->index == other.index); - } - - bool operator!= ( const RawSwitch& other) { - return (this->type != other.type) || (this->index != other.index); - } - - RawSwitchType type; - int index; -}; - -class CurveReference { - public: - enum CurveRefType { - CURVE_REF_DIFF, - CURVE_REF_EXPO, - CURVE_REF_FUNC, - CURVE_REF_CUSTOM - }; - - CurveReference() { clear(); } - - CurveReference(CurveRefType type, int value): - type(type), - value(value) - { - } - - void clear() { memset(this, 0, sizeof(CurveReference)); } - - CurveRefType type; - int value; - - QString toString() const; -}; - -enum InputMode { - INPUT_MODE_NONE, - INPUT_MODE_POS, - INPUT_MODE_NEG, - INPUT_MODE_BOTH -}; - -class ExpoData { - public: - ExpoData() { clear(); } - RawSource srcRaw; - unsigned int scale; - unsigned int mode; - unsigned int chn; - RawSwitch swtch; - unsigned int flightModes; // -5=!FP4, 0=normal, 5=FP4 - int weight; - int offset; - CurveReference curve; - int carryTrim; - char name[10+1]; - void clear() { memset(this, 0, sizeof(ExpoData)); } -}; - -class CurvePoint { - public: - int8_t x; - int8_t y; -}; - -class CurveData { - public: - enum CurveType { - CURVE_TYPE_STANDARD, - CURVE_TYPE_CUSTOM, - CURVE_TYPE_LAST = CURVE_TYPE_CUSTOM - }; - - CurveData(); - void clear(int count); - bool isEmpty() const; - CurveType type; - bool smooth; - int count; - CurvePoint points[CPN_MAX_POINTS]; - char name[6+1]; -}; - -class LimitData { - public: - LimitData() { clear(); } - int min; - int max; - bool revert; - int offset; - int ppmCenter; - bool symetrical; - char name[6+1]; - CurveReference curve; - QString minToString() const; - QString maxToString() const; - QString offsetToString() const; - QString revertToString() const; - void clear(); -}; - -enum MltpxValue { - MLTPX_ADD=0, - MLTPX_MUL=1, - MLTPX_REP=2 -}; - -#define MIXDATA_NAME_LEN 10 - -class MixData { - public: - MixData() { clear(); } - unsigned int destCh; // 1..CPN_MAX_CHNOUT - RawSource srcRaw; - int weight; - RawSwitch swtch; - CurveReference curve; //0=symmetrisch - unsigned int delayUp; - unsigned int delayDown; - unsigned int speedUp; // Servogeschwindigkeit aus Tabelle (10ms Cycle) - unsigned int speedDown; // 0 nichts - int carryTrim; - bool noExpo; - MltpxValue mltpx; // multiplex method 0=+ 1=* 2=replace - unsigned int mixWarn; // mixer warning - unsigned int flightModes; // -5=!FP4, 0=normal, 5=FP4 - int sOffset; - char name[MIXDATA_NAME_LEN+1]; - - void clear() { memset(this, 0, sizeof(MixData)); } -}; - -enum CSFunction { - LS_FN_OFF, - LS_FN_VPOS, - LS_FN_VNEG, - LS_FN_APOS, - LS_FN_ANEG, - LS_FN_AND, - LS_FN_OR, - LS_FN_XOR, - LS_FN_EQUAL, - LS_FN_NEQUAL, - LS_FN_GREATER, - LS_FN_LESS, - LS_FN_EGREATER, - LS_FN_ELESS, - LS_FN_DPOS, - LS_FN_DAPOS, - LS_FN_VEQUAL, // added at the end to avoid everything renumbered - LS_FN_VALMOSTEQUAL, - LS_FN_TIMER, - LS_FN_STICKY, - LS_FN_EDGE, - // later ... LS_FN_RANGE, - LS_FN_MAX -}; - -enum CSFunctionFamily { - LS_FAMILY_VOFS, - LS_FAMILY_VBOOL, - LS_FAMILY_VCOMP, - LS_FAMILY_TIMER, - LS_FAMILY_STICKY, - LS_FAMILY_EDGE, -}; - -class LogicalSwitchData { // Logical Switches data - public: - LogicalSwitchData(unsigned int func=0) - { - clear(); - this->func = func; - } - unsigned int func; - int val1; - int val2; - int val3; - unsigned int delay; - unsigned int duration; - int andsw; - void clear() { memset(this, 0, sizeof(LogicalSwitchData)); } - CSFunctionFamily getFunctionFamily() const; - unsigned int getRangeFlags() const; - QString funcToString() const; -}; - -enum AssignFunc { - FuncOverrideCH1 = 0, - FuncOverrideCH32 = FuncOverrideCH1+CPN_MAX_CHNOUT-1, - FuncTrainer, - FuncTrainerRUD, - FuncTrainerELE, - FuncTrainerTHR, - FuncTrainerAIL, - FuncInstantTrim, - FuncPlaySound, - FuncPlayHaptic, - FuncReset, - FuncSetTimer1, - FuncSetTimer2, - FuncSetTimer3, - FuncVario, - FuncPlayPrompt, - FuncPlayBoth, - FuncPlayValue, - FuncPlayScript, - FuncLogs, - FuncVolume, - FuncBacklight, - FuncScreenshot, - FuncBackgroundMusic, - FuncBackgroundMusicPause, - FuncAdjustGV1, - FuncAdjustGVLast = FuncAdjustGV1+CPN_MAX_GVARS-1, - FuncSetFailsafeInternalModule, - FuncSetFailsafeExternalModule, - FuncRangeCheckInternalModule, - FuncRangeCheckExternalModule, - FuncBindInternalModule, - FuncBindExternalModule, - FuncCount, - FuncReserve = -1 -}; - -enum GVarAdjustModes -{ - FUNC_ADJUST_GVAR_CONSTANT, - FUNC_ADJUST_GVAR_SOURCE, - FUNC_ADJUST_GVAR_GVAR, - FUNC_ADJUST_GVAR_INCDEC -}; - -class CustomFunctionData { // Function Switches data - public: - CustomFunctionData(AssignFunc func=FuncOverrideCH1) { clear(); this->func = func; } - RawSwitch swtch; - AssignFunc func; - int param; - char paramarm[10]; - unsigned int enabled; // TODO perhaps not any more the right name - unsigned int adjustMode; - int repeatParam; - void clear(); - QString funcToString() const; - QString paramToString(const ModelData * model) const; - QString repeatToString() const; - QString enabledToString() const; - - static void populateResetParams(const ModelData * model, QComboBox * b, unsigned int value); - static void populatePlaySoundParams(QStringList & qs); - static void populateHapticParams(QStringList & qs); - -}; - -class FlightModeData { - public: - FlightModeData() { clear(0); } - int trimMode[CPN_MAX_STICKS+CPN_MAX_AUX_TRIMS]; - int trimRef[CPN_MAX_STICKS+CPN_MAX_AUX_TRIMS]; - int trim[CPN_MAX_STICKS+CPN_MAX_AUX_TRIMS]; - RawSwitch swtch; - char name[10+1]; - unsigned int fadeIn; - unsigned int fadeOut; - int rotaryEncoders[CPN_MAX_ENCODERS]; - int gvars[CPN_MAX_GVARS]; - void clear(const int phase); -}; - -class SwashRingData { // Swash Ring data - public: - SwashRingData() { clear(); } - int elevatorWeight; - int aileronWeight; - int collectiveWeight; - unsigned int type; - RawSource collectiveSource; - RawSource aileronSource; - RawSource elevatorSource; - unsigned int value; - void clear() { memset(this, 0, sizeof(SwashRingData)); } -}; - -class FrSkyAlarmData { - public: - FrSkyAlarmData() { clear(); } - unsigned int level; // 0=none, 1=Yellow, 2=Orange, 3=Red - unsigned int greater; // 0=LT(<), 1=GT(>) - unsigned int value; // 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc. - - void clear() { memset(this, 0, sizeof(FrSkyAlarmData)); } -}; - -class FrSkyRSSIAlarm { - public: - FrSkyRSSIAlarm() { clear(0, 50); } - unsigned int level; - int value; - void clear(unsigned int level, int value) { this->level = level; this->value = value;} -}; - -class FrSkyChannelData { - public: - FrSkyChannelData() { clear(); } - unsigned int ratio; // 0.0 means not used, 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc. - unsigned int type; // future use: 0=volts, 1=ml... - int offset; - unsigned int multiplier; - FrSkyAlarmData alarms[2]; - - float getRatio() const; - RawSourceRange getRange() const; - void clear() { memset(this, 0, sizeof(FrSkyChannelData)); } -}; - -struct FrSkyBarData { - RawSource source; - unsigned int barMin; // minimum for bar display - unsigned int barMax; // ditto for max display (would usually = ratio) -}; - -struct FrSkyLineData { - RawSource source[3]; -}; - -struct TelemetryScriptData { - char filename[8+1]; -}; - -enum TelemetryScreenEnum { - TELEMETRY_SCREEN_NONE, - TELEMETRY_SCREEN_NUMBERS, - TELEMETRY_SCREEN_BARS, - TELEMETRY_SCREEN_SCRIPT -}; - -class FrSkyScreenData { - public: - FrSkyScreenData() { clear(); } - - typedef struct { - FrSkyBarData bars[4]; - FrSkyLineData lines[4]; - TelemetryScriptData script; - } FrSkyScreenBody; - - unsigned int type; - FrSkyScreenBody body; - - void clear(); -}; - -enum TelemetryVarioSources { - TELEMETRY_VARIO_SOURCE_ALTI, - TELEMETRY_VARIO_SOURCE_ALTI_PLUS, - TELEMETRY_VARIO_SOURCE_VSPEED, - TELEMETRY_VARIO_SOURCE_A1, - TELEMETRY_VARIO_SOURCE_A2, - TELEMETRY_VARIO_SOURCE_DTE, -}; - -enum TelemetryVoltsSources { - TELEMETRY_VOLTS_SOURCE_A1, - TELEMETRY_VOLTS_SOURCE_A2, - TELEMETRY_VOLTS_SOURCE_A3, - TELEMETRY_VOLTS_SOURCE_A4, - TELEMETRY_VOLTS_SOURCE_FAS, - TELEMETRY_VOLTS_SOURCE_CELLS -}; - -enum TelemetryCurrentSources { - TELEMETRY_CURRENT_SOURCE_NONE, - TELEMETRY_CURRENT_SOURCE_A1, - TELEMETRY_CURRENT_SOURCE_A2, - TELEMETRY_CURRENT_SOURCE_A3, - TELEMETRY_CURRENT_SOURCE_A4, - TELEMETRY_CURRENT_SOURCE_FAS -}; - -enum UartModes { - UART_MODE_NONE, - UART_MODE_TELEMETRY_MIRROR, - UART_MODE_TELEMETRY, - UART_MODE_SBUS_TRAINER, - UART_MODE_DEBUG -}; - -enum TrainerMode { - TRAINER_MODE_MASTER_TRAINER_JACK, - TRAINER_MODE_SLAVE, - TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE, - TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE, - TRAINER_MODE_MASTER_BATTERY_COMPARTMENT, -}; - -class FrSkyData { - public: - FrSkyData() { clear(); } - FrSkyChannelData channels[4]; - unsigned int usrProto; - int blades; - unsigned int voltsSource; - unsigned int altitudeSource; - unsigned int currentSource; - FrSkyScreenData screens[4]; - FrSkyRSSIAlarm rssiAlarms[2]; - unsigned int varioSource; - bool varioCenterSilent; - int varioMin; - int varioCenterMin; // if increment in 0.2m/s = 3.0m/s max - int varioCenterMax; - int varioMax; - bool mAhPersistent; - unsigned int storedMah; - int fasOffset; - bool ignoreSensorIds; - - void clear(); -}; - -class MavlinkData { - public: - MavlinkData() { clear();} - unsigned int rc_rssi_scale; - unsigned int pc_rssi_en; - void clear() { memset(this, 0, sizeof(MavlinkData)); } -}; - -#define TIMER_NAME_LEN 8 - -class TimerData { - public: - enum CountDownMode { - COUNTDOWN_SILENT, - COUNTDOWN_BEEPS, - COUNTDOWN_VOICE, - COUNTDOWN_HAPTIC - }; - TimerData() { clear(); } - RawSwitch mode; - char name[TIMER_NAME_LEN+1]; - bool minuteBeep; - unsigned int countdownBeep; - unsigned int val; - unsigned int persistent; - int pvalue; - void clear() { memset(this, 0, sizeof(TimerData)); mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); } -}; - -enum PulsesProtocol { - PULSES_OFF, - PULSES_PPM, - PULSES_SILV_A, - PULSES_SILV_B, - PULSES_SILV_C, - PULSES_CTP1009, - PULSES_LP45, - PULSES_DSM2, - PULSES_DSMX, - PULSES_PPM16, - PULSES_PPMSIM, - PULSES_PXX_XJT_X16, - PULSES_PXX_XJT_D8, - PULSES_PXX_XJT_LR12, - PULSES_PXX_DJT, - PULSES_CROSSFIRE, - PULSES_MULTIMODULE, - PULSES_PROTOCOL_LAST -}; - -enum MultiModuleRFProtocols { - MM_RF_PROTO_FLYSKY=0, - MM_RF_PROTO_FIRST=MM_RF_PROTO_FLYSKY, - MM_RF_PROTO_HUBSAN, - MM_RF_PROTO_FRSKY, - MM_RF_PROTO_HISKY, - MM_RF_PROTO_V2X2, - MM_RF_PROTO_DSM2, - MM_RF_PROTO_DEVO, - MM_RF_PROTO_YD717, - MM_RF_PROTO_KN, - MM_RF_PROTO_SYMAX, - MM_RF_PROTO_SLT, - MM_RF_PROTO_CX10, - MM_RF_PROTO_CG023, - MM_RF_PROTO_BAYANG, - MM_RF_PROTO_ESky, - MM_RF_PROTO_MT99XX, - MM_RF_PROTO_MJXQ, - MM_RF_PROTO_SHENQI, - MM_RF_PROTO_FY326, - MM_RF_PROTO_SFHSS, - MM_RF_PROTO_J6PRO, - MM_RF_PROTO_FQ777, - MM_RF_PROTO_ASSAN, - MM_RF_PROTO_HONTAI, - MM_RF_PROTO_OLRS, - MM_RF_PROTO_AFHDS2A, - MM_RF_PROTO_Q2X2, - MM_RF_PROTO_LAST= MM_RF_PROTO_Q2X2 -}; - -unsigned int getNumSubtypes(MultiModuleRFProtocols type); - -enum TrainerProtocol { - TRAINER_MASTER_JACK, - TRAINER_SLAVE_JACK, - TRAINER_MASTER_SBUS_MODULE, - TRAINER_MASTER_CPPM_MODULE, - TRAINER_MASTER_SBUS_BATT_COMPARTMENT -}; - -class ModuleData { - public: - ModuleData() { clear(); } - unsigned int modelId; - int protocol; - unsigned int subType; - bool invertedSerial; - unsigned int channelsStart; - int channelsCount; // 0=8 channels - unsigned int failsafeMode; - int failsafeChannels[CPN_MAX_CHNOUT]; - - - struct { - int delay; - bool pulsePol; // false = positive - bool outputType; // false = open drain, true = push pull - int frameLength; - } ppm; - - struct { - unsigned int rfProtocol; - bool autoBindMode; - bool lowPowerMode; - bool customProto; - int optionValue; - } multi; - - - - void clear() { memset(this, 0, sizeof(ModuleData)); } - QString polarityToString() const { return ppm.pulsePol ? QObject::tr("Positive") : QObject::tr("Negative"); } // TODO ModelPrinter -}; - -#define CPN_MAX_SCRIPTS 7 -#define CPN_MAX_SCRIPT_INPUTS 10 -class ScriptData { - public: - ScriptData() { clear(); } - char filename[10+1]; - char name[10+1]; - int inputs[CPN_MAX_SCRIPT_INPUTS]; - void clear() { memset(this, 0, sizeof(ScriptData)); } -}; - -#define CPN_MAX_SENSORS 32 -class SensorData { - - public: - - enum - { - TELEM_TYPE_CUSTOM, - TELEM_TYPE_CALCULATED - }; - - enum - { - TELEM_FORMULA_ADD, - TELEM_FORMULA_AVERAGE, - TELEM_FORMULA_MIN, - TELEM_FORMULA_MAX, - TELEM_FORMULA_MULTIPLY, - TELEM_FORMULA_TOTALIZE, - TELEM_FORMULA_CELL, - TELEM_FORMULA_CONSUMPTION, - TELEM_FORMULA_DIST, - TELEM_FORMULA_LAST = TELEM_FORMULA_DIST - }; - - enum { - TELEM_CELL_INDEX_LOWEST, - TELEM_CELL_INDEX_1, - TELEM_CELL_INDEX_2, - TELEM_CELL_INDEX_3, - TELEM_CELL_INDEX_4, - TELEM_CELL_INDEX_5, - TELEM_CELL_INDEX_6, - TELEM_CELL_INDEX_HIGHEST, - TELEM_CELL_INDEX_DELTA, - }; - - enum - { - UNIT_RAW, - UNIT_VOLTS, - UNIT_AMPS, - UNIT_MILLIAMPS, - UNIT_KTS, - UNIT_METERS_PER_SECOND, - UNIT_FEET_PER_SECOND, - UNIT_KMH, - UNIT_MPH, - UNIT_METERS, - UNIT_FEET, - UNIT_CELSIUS, - UNIT_FAHRENHEIT, - UNIT_PERCENT, - UNIT_MAH, - UNIT_WATTS, - UNIT_MILLIWATTS, - UNIT_DB, - UNIT_RPMS, - UNIT_G, - UNIT_DEGREE, - UNIT_RADIANS, - UNIT_MILLILITERS, - UNIT_FLOZ, - UNIT_HOURS, - UNIT_MINUTES, - UNIT_SECONDS, - // FrSky format used for these fields, could be another format in the future - UNIT_FIRST_VIRTUAL, - UNIT_CELLS = UNIT_FIRST_VIRTUAL, - UNIT_DATETIME, - UNIT_GPS, - UNIT_GPS_LONGITUDE, - UNIT_GPS_LATITUDE, - UNIT_GPS_LONGITUDE_EW, - UNIT_GPS_LATITUDE_NS, - UNIT_DATETIME_YEAR, - UNIT_DATETIME_DAY_MONTH, - UNIT_DATETIME_HOUR_MIN, - UNIT_DATETIME_SEC - }; - - SensorData() { clear(); } - unsigned int type; // custom / formula - unsigned int id; - unsigned int subid; - unsigned int instance; - unsigned int persistentValue; - unsigned int formula; - char label[4+1]; - unsigned int unit; - unsigned int prec; - bool autoOffset; - bool filter; - bool logs; - bool persistent; - bool onlyPositive; - - // for custom sensors - unsigned int ratio; - int offset; - - // for consumption - unsigned int amps; - - // for cell - unsigned int source; - unsigned int index; - - // for calculations - int sources[4]; - - // for GPS dist - unsigned int gps; - unsigned int alt; - - bool isAvailable() const { return strlen(label) > 0; } - void updateUnit(); - QString unitString() const; - void clear() { memset(this, 0, sizeof(SensorData)); } -}; - -/* - * TODO ... - */ -#if 0 -class CustomScreenOptionData { - public: - -}; - -class CustomScreenZoneData { - public: - char widgetName[10+1]; - WidgetOptionData widgetOptions[5]; -}; - -class CustomScreenData { - public: - CustomScreenData(); - - char layoutName[10+1]; - CustomScreenZoneData zones[]; - CustomScreenOptionData options[]; -}; -#else -typedef char CustomScreenData[610+1]; -typedef char TopbarData[216+1]; -#endif - -class ModelData { - public: - ModelData(); - ModelData(const ModelData & src); - ModelData & operator = (const ModelData & src); - - ExpoData * insertInput(const int idx); - void removeInput(const int idx); - - bool isInputValid(const unsigned int idx) const; - bool hasExpos(uint8_t inputIdx) const; - bool hasMixes(uint8_t output) const; - - QVector expos(int input) const; - QVector mixes(int channel) const; - - bool used; - char category[15+1]; - char name[15+1]; - char filename[16+1]; - TimerData timers[CPN_MAX_TIMERS]; - bool noGlobalFunctions; - bool thrTrim; // Enable Throttle Trim - int trimInc; // Trim Increments - unsigned int trimsDisplay; - bool disableThrottleWarning; - - unsigned int beepANACenter; // 1<<0->A1.. 1<<6->A7 - - bool extendedLimits; // TODO xml - bool extendedTrims; - bool throttleReversed; - FlightModeData flightModeData[CPN_MAX_FLIGHT_MODES]; - MixData mixData[CPN_MAX_MIXERS]; - LimitData limitData[CPN_MAX_CHNOUT]; - - char inputNames[CPN_MAX_INPUTS][4+1]; - ExpoData expoData[CPN_MAX_EXPOS]; - - CurveData curves[CPN_MAX_CURVES]; - LogicalSwitchData logicalSw[CPN_MAX_CSW]; - CustomFunctionData customFn[CPN_MAX_CUSTOM_FUNCTIONS]; - SwashRingData swashRingData; - unsigned int thrTraceSrc; - uint64_t switchWarningStates; - unsigned int switchWarningEnable; - unsigned int potsWarningMode; - bool potsWarningEnabled[CPN_MAX_POTS]; - int potPosition[CPN_MAX_POTS]; - bool displayChecklist; - // TODO structure - char gvars_names[CPN_MAX_GVARS][6+1]; - bool gvars_popups[CPN_MAX_GVARS]; - MavlinkData mavlink; - unsigned int telemetryProtocol; - FrSkyData frsky; - - char bitmap[10+1]; - - unsigned int trainerMode; - - ModuleData moduleData[CPN_MAX_MODULES+1/*trainer*/]; - - ScriptData scriptData[CPN_MAX_SCRIPTS]; - - SensorData sensorData[CPN_MAX_SENSORS]; - - unsigned int toplcdTimer; - - CustomScreenData customScreenData[5]; - - TopbarData topbarData; - - void clear(); - bool isEmpty() const; - void setDefaultInputs(const GeneralSettings & settings); - void setDefaultMixes(const GeneralSettings & settings); - void setDefaultValues(unsigned int id, const GeneralSettings & settings); - - int getTrimValue(int phaseIdx, int trimIdx); - void setTrimValue(int phaseIdx, int trimIdx, int value); - - bool isGVarLinked(int phaseIdx, int gvarIdx); - int getGVarFieldValue(int phaseIdx, int gvarIdx); - - ModelData removeGlobalVars(); - - void clearMixes(); - void clearInputs(); - - int getChannelsMax(bool forceExtendedLimits=false) const; - - protected: - void removeGlobalVar(int & var); -}; - - -class TrainerMix { - public: - TrainerMix() { clear(); } - unsigned int src; // 0-7 = ch1-8 - RawSwitch swtch; - int weight; - unsigned int mode; // off, add-mode, subst-mode - void clear() { memset(this, 0, sizeof(TrainerMix)); } -}; - -class TrainerData { - public: - TrainerData() { clear(); } - int calib[4]; - TrainerMix mix[4]; - void clear() { memset(this, 0, sizeof(TrainerData)); } -}; - -class GeneralSettings { - public: - - enum BeeperMode { - BEEPER_QUIET = -2, - BEEPER_ALARMS_ONLY = -1, - BEEPER_NOKEYS = 0, - BEEPER_ALL = 1 - }; - - enum PotConfig { - POT_NONE, - POT_WITH_DETENT, - POT_MULTIPOS_SWITCH, - POT_WITHOUT_DETENT - }; - - enum SliderConfig { - SLIDER_NONE, - SLIDER_WITH_DETENT - }; - - GeneralSettings(); - - int getDefaultStick(unsigned int channel) const; - RawSource getDefaultSource(unsigned int channel) const; - int getDefaultChannel(unsigned int stick) const; - - unsigned int version; - unsigned int variant; - int calibMid[CPN_MAX_STICKS+CPN_MAX_POTS+CPN_MAX_MOUSE_ANALOGS]; - int calibSpanNeg[CPN_MAX_STICKS+CPN_MAX_POTS+CPN_MAX_MOUSE_ANALOGS]; - int calibSpanPos[CPN_MAX_STICKS+CPN_MAX_POTS+CPN_MAX_MOUSE_ANALOGS]; - unsigned int currModelIndex; - char currModelFilename[16+1]; - unsigned int contrast; - unsigned int vBatWarn; - int txVoltageCalibration; - int txCurrentCalibration; - int vBatMin; - int vBatMax; - int backlightMode; - TrainerData trainer; - unsigned int view; // main screen view // TODO enum - bool disableThrottleWarning; - bool fai; - int switchWarning; // -1=down, 0=off, 1=up - bool disableMemoryWarning; - BeeperMode beeperMode; - bool disableAlarmWarning; - bool enableTelemetryAlarm; - BeeperMode hapticMode; - unsigned int stickMode; // TODO enum - int timezone; - bool adjustRTC; - bool optrexDisplay; - unsigned int inactivityTimer; - bool minuteBeep; - bool preBeep; - bool flashBeep; - bool disablePotScroll; - bool frskyinternalalarm; - bool disableBG; - unsigned int splashMode; - int splashDuration; - unsigned int backlightDelay; - bool blightinv; - bool stickScroll; - unsigned int templateSetup; //RETA order according to chout_ar array - int PPM_Multiplier; - int hapticLength; - unsigned int reNavigation; - unsigned int stickReverse; - bool hideNameOnSplash; - bool enablePpmsim; - unsigned int speakerPitch; - int hapticStrength; - unsigned int speakerMode; - unsigned int lightOnStickMove; /* er9x / ersky9x only */ - char ownerName[10+1]; - unsigned int switchWarningStates; - int beeperLength; - unsigned int gpsFormat; - int speakerVolume; - unsigned int backlightBright; - unsigned int backlightOffBright; - int switchesDelay; - int temperatureCalib; - int temperatureWarn; - unsigned int mAhWarn; - unsigned int mAhUsed; - unsigned int globalTimer; - bool bluetoothEnable; - char bluetoothName[10+1]; - unsigned int btBaudrate; - unsigned int sticksGain; - unsigned int rotarySteps; - unsigned int countryCode; - bool jitterFilter; - unsigned int imperial; - bool crosstrim; - char ttsLanguage[2+1]; - int beepVolume; - int wavVolume; - int varioVolume; - int varioPitch; - int varioRange; - int varioRepeat; - int backgroundVolume; - unsigned int mavbaud; - unsigned int switchUnlockStates; - unsigned int hw_uartMode; - unsigned int backlightColor; - CustomFunctionData customFn[CPN_MAX_CUSTOM_FUNCTIONS]; - char switchName[18][3+1]; - unsigned int switchConfig[18]; - char stickName[4][3+1]; - char potName[4][3+1]; - unsigned int potConfig[4]; - char sliderName[4][3+1]; - unsigned int sliderConfig[4]; - - char themeName[8+1]; - typedef uint8_t ThemeOptionData[8+1]; - ThemeOptionData themeOptionValue[5]; - - struct SwitchInfo { - SwitchInfo(unsigned int index, unsigned int position): - index(index), - position(position) - { - } - unsigned int index; - unsigned int position; - }; - - static SwitchInfo switchInfoFromSwitchPositionTaranis(unsigned int index); - bool switchPositionAllowedTaranis(int index) const; - bool switchSourceAllowedTaranis(int index) const; - bool isPotAvailable(int index) const; - bool isSliderAvailable(int index) const; -}; - -class RadioData { - public: - GeneralSettings generalSettings; - ModelData models[CPN_MAX_MODELS]; - - void setCurrentModel(unsigned int index) - { - generalSettings.currModelIndex = index; - strcpy(generalSettings.currModelFilename, models[index].filename); - } - - QString getNextModelFilename() - { - char filename[sizeof(ModelData::filename)]; - int index = 0; - bool found = true; - while (found) { - sprintf(filename, "model%d.bin", ++index); - found = false; - for (int i=0; i EEPROMWarnings; /* EEPROM string conversion functions */ @@ -1539,69 +246,6 @@ inline int applyStickMode(int stick, unsigned int mode) return stick; } -inline void applyStickModeToModel(ModelData &model, unsigned int mode) -{ - ModelData model_copy = model; - - // trims - for (int p=0; p firmwares; extern Firmware * default_firmware_variant; extern Firmware * current_firmware_variant; -Firmware * GetFirmware(QString id); +Firmware * GetFirmware(const QString & id); inline Firmware * GetCurrentFirmware() { @@ -1794,4 +438,7 @@ SimulatorInterface * GetCurrentFirmwareSimulator(); extern QList eepromInterfaces; +bool loadFile(RadioData & radioData, const QString & filename); + + #endif // _EEPROMINTERFACE_H_ diff --git a/companion/src/firmwares/er9x/er9xeeprom.cpp b/companion/src/firmwares/er9x/er9xeeprom.cpp index be84ef15e..3e75cb501 100644 --- a/companion/src/firmwares/er9x/er9xeeprom.cpp +++ b/companion/src/firmwares/er9x/er9xeeprom.cpp @@ -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) { diff --git a/companion/src/firmwares/er9x/er9xinterface.cpp b/companion/src/firmwares/er9x/er9xinterface.cpp index f5d8eb7d6..bdee523a4 100644 --- a/companion/src/firmwares/er9x/er9xinterface.cpp +++ b/companion/src/firmwares/er9x/er9xinterface.cpp @@ -18,11 +18,12 @@ * GNU General Public License for more details. */ -#include #include "er9xinterface.h" #include "er9xeeprom.h" #include "rlefile.h" -#include "appdata.h" +// #include "appdata.h" +// #include +#include #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 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; iopenRd(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; + } +} \ No newline at end of file diff --git a/companion/src/firmwares/er9x/er9xinterface.h b/companion/src/firmwares/er9x/er9xinterface.h index 645d4cb5f..639f2c461 100644 --- a/companion/src/firmwares/er9x/er9xinterface.h +++ b/companion/src/firmwares/er9x/er9xinterface.h @@ -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: diff --git a/companion/src/firmwares/ersky9x/ersky9xeeprom.cpp b/companion/src/firmwares/ersky9x/ersky9xeeprom.cpp index 5cf86badf..48d24cba6 100644 --- a/companion/src/firmwares/ersky9x/ersky9xeeprom.cpp +++ b/companion/src/firmwares/ersky9x/ersky9xeeprom.cpp @@ -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)); diff --git a/companion/src/firmwares/ersky9x/ersky9xinterface.cpp b/companion/src/firmwares/ersky9x/ersky9xinterface.cpp index d63787726..c5b50c88b 100644 --- a/companion/src/firmwares/ersky9x/ersky9xinterface.cpp +++ b/companion/src/firmwares/ersky9x/ersky9xinterface.cpp @@ -18,10 +18,11 @@ * GNU General Public License for more details. */ -#include #include "ersky9xinterface.h" #include "ersky9xeeprom.h" #include "rlefile.h" +#include +#include #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(&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; iopenRd(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; + } +} \ No newline at end of file diff --git a/companion/src/firmwares/ersky9x/ersky9xinterface.h b/companion/src/firmwares/ersky9x/ersky9xinterface.h index c39cbaf5c..3ebe36128 100644 --- a/companion/src/firmwares/ersky9x/ersky9xinterface.h +++ b/companion/src/firmwares/ersky9x/ersky9xinterface.h @@ -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: diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 095b86f75..74fb888ef 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -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 -#include #include "opentxinterface.h" #include "opentxeeprom.h" #include "rlefile.h" #include "appdata.h" -#include "storage_sdcard.h" +#include +#include +#include +#include #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 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 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(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 opentxEEpromInterfaces; +void registerOpenTxEEpromInterface(BoardEnum board) { - std::bitset 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(radioData.generalSettings, storage.radio); - - // Models - int modelIndex = 0; - QString modelList = QString(storage.modelList); - QList 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(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(radioData.generalSettings, storage.radio, version); - - // all models - for (int i=0; i(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 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(radioData.generalSettings, board, version, variant); + int result = saveRadioSettings((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(i, radioData.models[i], version, variant); + result = saveModel(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 +bool loadFromByteArray(T & dest, const QByteArray & data) +{ + foreach(OpenTxEepromInterface * eepromInterface, opentxEEpromInterfaces) { + if (eepromInterface->loadFromByteArray(dest, data)) { + return true; + } + } + return false; +} + +template +bool saveToByteArray(const T & dest, QByteArray & data) +{ + BoardEnum board = GetCurrentFirmware()->getBoard(); + foreach(OpenTxEepromInterface * eepromInterface, opentxEEpromInterfaces) { + if (eepromInterface->getBoard() == board) { + return eepromInterface->saveToByteArray(dest, data); + } + } + return false; +} + +bool loadModelFromByteArray(ModelData & model, const QByteArray & data) +{ + return loadFromByteArray(model, data); +} + +bool loadRadioSettingsFromByteArray(GeneralSettings & settings, const QByteArray & data) +{ + return loadFromByteArray(settings, data); +} + +bool writeModelToByteArray(const ModelData & model, QByteArray & data) +{ + return saveToByteArray(model, data); +} + +bool writeRadioSettingsToByteArray(const GeneralSettings & settings, QByteArray & data) +{ + return saveToByteArray(settings, data); +} diff --git a/companion/src/firmwares/opentx/opentxinterface.h b/companion/src/firmwares/opentx/opentxinterface.h index c07d29f09..29751cd3a 100644 --- a/companion/src/firmwares/opentx/opentxinterface.h +++ b/companion/src/firmwares/opentx/opentxinterface.h @@ -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 bool loadFromByteArray(T & dest, const QByteArray & data, uint8_t version, uint32_t variant=0); + public: template bool loadFromByteArray(T & dest, const QByteArray & data); template - 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 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_ diff --git a/companion/src/flasheepromdialog.cpp b/companion/src/flasheepromdialog.cpp index 249479736..d6ba8ca2a 100644 --- a/companion/src/flasheepromdialog.cpp +++ b/companion/src/flasheepromdialog.cpp @@ -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 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 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 = QSharedPointer(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; igetEEpromSize()); + 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())); diff --git a/companion/src/flashfirmwaredialog.cpp b/companion/src/flashfirmwaredialog.cpp index 4779aa68f..638032f30 100644 --- a/companion/src/flashfirmwaredialog.cpp +++ b/companion/src/flashfirmwaredialog.cpp @@ -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 @@ -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"); diff --git a/companion/src/generaledit/hardware.cpp b/companion/src/generaledit/hardware.cpp index e5875b678..82b146ce5 100644 --- a/companion/src/generaledit/hardware.cpp +++ b/companion/src/generaledit/hardware.cpp @@ -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); diff --git a/companion/src/helpers.cpp b/companion/src/helpers.cpp index d73eb0d8b..ff188aeb7 100644 --- a/companion/src/helpers.cpp +++ b/companion/src/helpers.cpp @@ -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; igetCapability(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); } diff --git a/companion/src/helpers.h b/companion/src/helpers.h index 2db44e334..8a6ad7d3b 100644 --- a/companion/src/helpers.h +++ b/companion/src/helpers.h @@ -21,13 +21,15 @@ #ifndef _HELPERS_H_ #define _HELPERS_H_ +#include "eeprominterface.h" +#include "modeledit/modeledit.h" #include #include #include #include #include -#include "eeprominterface.h" -#include "modeledit/modeledit.h" +#include +#include extern const QColor colors[CPN_MAX_CURVES]; diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index 7f1c89a11..ca53f96ab 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -18,10 +18,6 @@ * GNU General Public License for more details. */ -#include -#include -#include -#include #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 +#include +#include +#include #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 && modelgetMaxModels()) && !printfilename.isEmpty() )) { + if (!(printing && model >= 0 && modelgetCapability(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 models = storage.getModelsFileNames(); - qDebug() << "We have" << models.size() << "models:"; - foreach(QString filename, models) { - QList::const_iterator i = storage.getModelIterator(filename); - if (i != storage.models.end()) { - qDebug() << "\tModel:" << i->filename << "size" << i->data.size(); - } - } - - for (QList::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); diff --git a/companion/src/mainwindow.h b/companion/src/mainwindow.h index 1d9631ef4..79b6dcab0 100644 --- a/companion/src/mainwindow.h +++ b/companion/src/mainwindow.h @@ -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); diff --git a/companion/src/mdichild.cpp b/companion/src/mdichild.cpp index 1e16fb00d..f34dcf7f9 100644 --- a/companion/src/mdichild.cpp +++ b/companion/src/mdichild.cpp @@ -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 @@ -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 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 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 errorsEeprom((unsigned long long)LoadEeprom(radioData, eeprom, eeprom_size)); - if (!errorsEeprom.test(ALL_OK)) { - std::bitset 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 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 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 } diff --git a/companion/src/mdichild.h b/companion/src/mdichild.h index 34603e4f6..e459e58b9 100644 --- a/companion/src/mdichild.h +++ b/companion/src/mdichild.h @@ -23,6 +23,7 @@ #include #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_ diff --git a/companion/src/mdichild.ui b/companion/src/mdichild.ui index 88f20161c..0b8a6c1b3 100644 --- a/companion/src/mdichild.ui +++ b/companion/src/mdichild.ui @@ -15,7 +15,14 @@ - + + + Radio settings + + + + + @@ -26,13 +33,6 @@ - - - ModelsListWidget - QListWidget -
modelslist.h
-
-
diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index e96c02080..241e8476a 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -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); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index e17bb2f4c..61f475246 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -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; igetCapability(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; diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index 778d7c603..349703d51 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -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"))) { diff --git a/companion/src/modelprinter.cpp b/companion/src/modelprinter.cpp index 220e09ae1..24fd230a9 100644 --- a/companion/src/modelprinter.cpp +++ b/companion/src/modelprinter.cpp @@ -21,6 +21,7 @@ #include "helpers.h" #include "modelprinter.h" #include +#include QString changeColor(const QString & input, const QString & to, const QString & from) { diff --git a/companion/src/modelslist.cpp b/companion/src/modelslist.cpp index 1652f27e1..786e7e2ee 100644 --- a/companion/src/modelslist.cpp +++ b/companion/src/modelslist.cpp @@ -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 & 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(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 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(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) && imodels.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; jsetFont(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; imapToGlobal(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; igetMaxModels()+1; ++i) { + /*currentSelection.current_item = currentItem(); + for (int i=0; igetCapability(Models)+1; ++i) { currentSelection.selected[i] = selectionModel()->isSelected(model()->index(i, 0)); - } + }*/ } void ModelsListWidget::restoreSelection() { - setCurrentItem(currentSelection.current_item); - for (int i=0; igetMaxModels()+1; ++i) { + /*setCurrentItem(currentSelection.current_item); + for (int i=0; igetCapability(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); igetCapability(Models)+1, row+header->models_count); iselect(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); igetCapability(Models)+1, row+header->models_count); iselect(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; igetMaxModels(); 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; jsetFont(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 (isize() && 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 && igetMaxModels()) { + if (i && igetCapability(Models)) { ModelData * model = &radioData->models[i-1]; - while (igetMaxModels()) { + while (igetCapability(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 \ No newline at end of file diff --git a/companion/src/modelslist.h b/companion/src/modelslist.h index b4bc14e5c..1bebd7d0d 100644 --- a/companion/src/modelslist.h +++ b/companion/src/modelslist.h @@ -21,8 +21,8 @@ #ifndef _MODELSLIST_H_ #define _MODELSLIST_H_ -#include #include "eeprominterface.h" +#include 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 & 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 childItems; + QVector 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_ diff --git a/companion/src/process_flash.cpp b/companion/src/process_flash.cpp index e434cb429..c57bfe639 100644 --- a/companion/src/process_flash.cpp +++ b/companion/src/process_flash.cpp @@ -20,11 +20,11 @@ #include "process_flash.h" #include "progresswidget.h" +#include "eeprominterface.h" #include #include #include -#include "eeprominterface.h" -//#include "firmwareinterface.h" +#include #if defined _MSC_VER || !defined __GNUC__ #include diff --git a/companion/src/radiodata.h b/companion/src/radiodata.h new file mode 100644 index 000000000..07a0e2a58 --- /dev/null +++ b/companion/src/radiodata.h @@ -0,0 +1,1321 @@ +#ifndef _RADIODATA_H_ +#define _RADIODATA_H_ + +#include "constants.h" +#include +#include + +enum Switches { + SWITCH_NONE, + + SWITCH_THR = SWITCH_NONE+1, + SWITCH_RUD, + SWITCH_ELE, + SWITCH_ID0, + SWITCH_ID1, + SWITCH_ID2, + SWITCH_AIL, + SWITCH_GEA, + SWITCH_TRN, + + SWITCH_SA0 = SWITCH_NONE+1, + SWITCH_SA1, + SWITCH_SA2, + SWITCH_SB0, + SWITCH_SB1, + SWITCH_SB2, + SWITCH_SC0, + SWITCH_SC1, + SWITCH_SC2, + SWITCH_SD0, + SWITCH_SD1, + SWITCH_SD2, + SWITCH_SE0, + SWITCH_SE1, + SWITCH_SE2, + SWITCH_SF0, + SWITCH_SF1, + SWITCH_SG0, + SWITCH_SG1, + SWITCH_SG2, + SWITCH_SH0, + SWITCH_SH2, + SWITCH_SI0, + SWITCH_SI2, + SWITCH_SJ0, + SWITCH_SJ2, + SWITCH_SK0, + SWITCH_SK2, + +}; + +enum TimerModes { + TMRMODE_NONE, + TMRMODE_ABS, + TMRMODE_THR, + TMRMODE_THR_REL, + TMRMODE_THR_TRG, + TMRMODE_FIRST_SWITCH +}; + +enum FailsafeModes { + FAILSAFE_NOT_SET, + FAILSAFE_HOLD, + FAILSAFE_CUSTOM, + FAILSAFE_NOPULSES, + FAILSAFE_RECEIVER, + FAILSAFE_LAST = FAILSAFE_RECEIVER +}; + +#define TRIM_LH_L 0 +#define TRIM_LH_R 1 +#define TRIM_LV_DN 2 +#define TRIM_LV_UP 3 +#define TRIM_RV_DN 4 +#define TRIM_RV_UP 5 +#define TRIM_RH_L 6 +#define TRIM_RH_R 7 +#define TRIM_T5_DN 8 +#define TRIM_T5_UP 9 +#define TRIM_T6_DN 10 +#define TRIM_T6_UP 11 +#define TRIM_NONE 12 + +#define CHAR_FOR_NAMES " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-." +#define CHAR_FOR_NAMES_REGEX "[ A-Za-z0-9_.-,]*" + +enum HeliSwashTypes { + HELI_SWASH_TYPE_NONE=0, + HELI_SWASH_TYPE_120, + HELI_SWASH_TYPE_120X, + HELI_SWASH_TYPE_140, + HELI_SWASH_TYPE_90 +}; + +class ModelData; +class GeneralSettings; + +enum TelemetrySource { + TELEMETRY_SOURCE_TX_BATT, + TELEMETRY_SOURCE_TX_TIME, + TELEMETRY_SOURCE_TIMER1, + TELEMETRY_SOURCE_TIMER2, + TELEMETRY_SOURCE_TIMER3, + TELEMETRY_SOURCE_SWR, + TELEMETRY_SOURCE_RSSI_TX, + TELEMETRY_SOURCE_RSSI_RX, + TELEMETRY_SOURCE_A1, + TELEMETRY_SOURCE_A2, + TELEMETRY_SOURCE_A3, + TELEMETRY_SOURCE_A4, + TELEMETRY_SOURCE_ALT, + TELEMETRY_SOURCE_RPM, + TELEMETRY_SOURCE_FUEL, + TELEMETRY_SOURCE_T1, + TELEMETRY_SOURCE_T2, + TELEMETRY_SOURCE_SPEED, + TELEMETRY_SOURCE_DIST, + TELEMETRY_SOURCE_GPS_ALT, + TELEMETRY_SOURCE_CELL, + TELEMETRY_SOURCE_CELLS_SUM, + TELEMETRY_SOURCE_VFAS, + TELEMETRY_SOURCE_CURRENT, + TELEMETRY_SOURCE_CONSUMPTION, + TELEMETRY_SOURCE_POWER, + TELEMETRY_SOURCE_ACCX, + TELEMETRY_SOURCE_ACCY, + TELEMETRY_SOURCE_ACCZ, + TELEMETRY_SOURCE_HDG, + TELEMETRY_SOURCE_VERTICAL_SPEED, + TELEMETRY_SOURCE_ASPEED, + TELEMETRY_SOURCE_DTE, + TELEMETRY_SOURCE_A1_MIN, + TELEMETRY_SOURCE_A2_MIN, + TELEMETRY_SOURCE_A3_MIN, + TELEMETRY_SOURCE_A4_MIN, + TELEMETRY_SOURCE_ALT_MIN, + TELEMETRY_SOURCE_ALT_MAX, + TELEMETRY_SOURCE_RPM_MAX, + TELEMETRY_SOURCE_T1_MAX, + TELEMETRY_SOURCE_T2_MAX, + TELEMETRY_SOURCE_SPEED_MAX, + TELEMETRY_SOURCE_DIST_MAX, + TELEMETRY_SOURCE_ASPEED_MAX, + TELEMETRY_SOURCE_CELL_MIN, + TELEMETRY_SOURCE_CELLS_MIN, + TELEMETRY_SOURCE_VFAS_MIN, + TELEMETRY_SOURCE_CURRENT_MAX, + TELEMETRY_SOURCE_POWER_MAX, + TELEMETRY_SOURCE_ACC, + TELEMETRY_SOURCE_GPS_TIME, + TELEMETRY_SOURCES_STATUS_COUNT = TELEMETRY_SOURCE_GPS_TIME+1, + TELEMETRY_SOURCES_DISPLAY_COUNT = TELEMETRY_SOURCE_POWER_MAX+1, + TELEMETRY_SOURCES_COUNT = TELEMETRY_SOURCE_POWER_MAX+1, + TELEMETRY_SOURCE_RESERVE = -1 +}; + +#define TM_HASTELEMETRY 0x01 +#define TM_HASOFFSET 0x02 +#define TM_HASWSHH 0x04 + +enum RawSourceType { + SOURCE_TYPE_NONE, + SOURCE_TYPE_VIRTUAL_INPUT, + SOURCE_TYPE_LUA_OUTPUT, + SOURCE_TYPE_STICK, // and POTS + SOURCE_TYPE_ROTARY_ENCODER, + SOURCE_TYPE_TRIM, + SOURCE_TYPE_MAX, + SOURCE_TYPE_SWITCH, + SOURCE_TYPE_CUSTOM_SWITCH, + SOURCE_TYPE_CYC, + SOURCE_TYPE_PPM, + SOURCE_TYPE_CH, + SOURCE_TYPE_GVAR, + SOURCE_TYPE_SPECIAL, + SOURCE_TYPE_TELEMETRY, + MAX_SOURCE_TYPE +}; + +class RawSourceRange +{ + public: + RawSourceRange(): + decimals(0), + min(0.0), + max(0.0), + step(1.0), + offset(0.0) + { + } + + float getValue(int value); + + int decimals; + double min; + double max; + double step; + double offset; + QString unit; +}; + +#define RANGE_SINGLE_PRECISION 1 +#define RANGE_DELTA_FUNCTION 2 +#define RANGE_DELTA_ABS_FUNCTION 4 + +class RawSource { + public: + RawSource(): + type(SOURCE_TYPE_NONE), + index(0) + { + } + + explicit RawSource(int value): + type(RawSourceType(abs(value)/65536)), + index(value >= 0 ? abs(value)%65536 : -(abs(value)%65536)) + { + } + + RawSource(RawSourceType type, int index=0): + type(type), + index(index) + { + } + + inline const int toValue() const + { + return index >= 0 ? (type * 65536 + index) : -(type * 65536 - index); + } + + QString toString(const ModelData * model = NULL) const; + + RawSourceRange getRange(const ModelData * model, const GeneralSettings & settings, unsigned int flags=0) const; + + bool operator == ( const RawSource & other) { + return (this->type == other.type) && (this->index == other.index); + } + + bool operator != ( const RawSource & other) { + return (this->type != other.type) || (this->index != other.index); + } + + bool isTimeBased() const; + bool isPot() const; + bool isSlider() const; + + RawSourceType type; + int index; +}; + +enum RawSwitchType { + SWITCH_TYPE_NONE, + SWITCH_TYPE_SWITCH, + SWITCH_TYPE_VIRTUAL, + SWITCH_TYPE_MULTIPOS_POT, + SWITCH_TYPE_TRIM, + SWITCH_TYPE_ROTARY_ENCODER, + SWITCH_TYPE_ON, + SWITCH_TYPE_OFF, + SWITCH_TYPE_ONE, + SWITCH_TYPE_FLIGHT_MODE, + SWITCH_TYPE_TIMER_MODE +}; + +class RawSwitch { + public: + RawSwitch(): + type(SWITCH_TYPE_NONE), + index(0) + { + } + + explicit RawSwitch(int value): + type(RawSwitchType(abs(value)/256)), + index(value >= 0 ? abs(value)%256 : -(abs(value)%256)) + { + } + + RawSwitch(RawSwitchType type, int index=0): + type(type), + index(index) + { + } + + inline const int toValue() const + { + return index >= 0 ? (type * 256 + index) : -(type * 256 - index); + } + + QString toString() const; + + bool operator== ( const RawSwitch& other) { + return (this->type == other.type) && (this->index == other.index); + } + + bool operator!= ( const RawSwitch& other) { + return (this->type != other.type) || (this->index != other.index); + } + + RawSwitchType type; + int index; +}; + +class CurveReference { + public: + enum CurveRefType { + CURVE_REF_DIFF, + CURVE_REF_EXPO, + CURVE_REF_FUNC, + CURVE_REF_CUSTOM + }; + + CurveReference() { clear(); } + + CurveReference(CurveRefType type, int value): + type(type), + value(value) + { + } + + void clear() { memset(this, 0, sizeof(CurveReference)); } + + CurveRefType type; + int value; + + QString toString() const; +}; + +enum InputMode { + INPUT_MODE_NONE, + INPUT_MODE_POS, + INPUT_MODE_NEG, + INPUT_MODE_BOTH +}; + +class ExpoData { + public: + ExpoData() { clear(); } + RawSource srcRaw; + unsigned int scale; + unsigned int mode; + unsigned int chn; + RawSwitch swtch; + unsigned int flightModes; // -5=!FP4, 0=normal, 5=FP4 + int weight; + int offset; + CurveReference curve; + int carryTrim; + char name[10+1]; + void clear() { memset(this, 0, sizeof(ExpoData)); } +}; + +class CurvePoint { + public: + int8_t x; + int8_t y; +}; + +class CurveData { + public: + enum CurveType { + CURVE_TYPE_STANDARD, + CURVE_TYPE_CUSTOM, + CURVE_TYPE_LAST = CURVE_TYPE_CUSTOM + }; + + CurveData(); + void clear(int count); + bool isEmpty() const; + CurveType type; + bool smooth; + int count; + CurvePoint points[CPN_MAX_POINTS]; + char name[6+1]; +}; + +class LimitData { + public: + LimitData() { clear(); } + int min; + int max; + bool revert; + int offset; + int ppmCenter; + bool symetrical; + char name[6+1]; + CurveReference curve; + QString minToString() const; + QString maxToString() const; + QString offsetToString() const; + QString revertToString() const; + void clear(); +}; + +enum MltpxValue { + MLTPX_ADD=0, + MLTPX_MUL=1, + MLTPX_REP=2 +}; + +#define MIXDATA_NAME_LEN 10 + +class MixData { + public: + MixData() { clear(); } + unsigned int destCh; // 1..CPN_MAX_CHNOUT + RawSource srcRaw; + int weight; + RawSwitch swtch; + CurveReference curve; //0=symmetrisch + unsigned int delayUp; + unsigned int delayDown; + unsigned int speedUp; // Servogeschwindigkeit aus Tabelle (10ms Cycle) + unsigned int speedDown; // 0 nichts + int carryTrim; + bool noExpo; + MltpxValue mltpx; // multiplex method 0=+ 1=* 2=replace + unsigned int mixWarn; // mixer warning + unsigned int flightModes; // -5=!FP4, 0=normal, 5=FP4 + int sOffset; + char name[MIXDATA_NAME_LEN+1]; + + void clear() { memset(this, 0, sizeof(MixData)); } +}; + +enum CSFunction { + LS_FN_OFF, + LS_FN_VPOS, + LS_FN_VNEG, + LS_FN_APOS, + LS_FN_ANEG, + LS_FN_AND, + LS_FN_OR, + LS_FN_XOR, + LS_FN_EQUAL, + LS_FN_NEQUAL, + LS_FN_GREATER, + LS_FN_LESS, + LS_FN_EGREATER, + LS_FN_ELESS, + LS_FN_DPOS, + LS_FN_DAPOS, + LS_FN_VEQUAL, // added at the end to avoid everything renumbered + LS_FN_VALMOSTEQUAL, + LS_FN_TIMER, + LS_FN_STICKY, + LS_FN_EDGE, + // later ... LS_FN_RANGE, + LS_FN_MAX +}; + +enum CSFunctionFamily { + LS_FAMILY_VOFS, + LS_FAMILY_VBOOL, + LS_FAMILY_VCOMP, + LS_FAMILY_TIMER, + LS_FAMILY_STICKY, + LS_FAMILY_EDGE, +}; + +class LogicalSwitchData { // Logical Switches data + public: + LogicalSwitchData(unsigned int func=0) + { + clear(); + this->func = func; + } + unsigned int func; + int val1; + int val2; + int val3; + unsigned int delay; + unsigned int duration; + int andsw; + void clear() { memset(this, 0, sizeof(LogicalSwitchData)); } + CSFunctionFamily getFunctionFamily() const; + unsigned int getRangeFlags() const; + QString funcToString() const; +}; + +enum AssignFunc { + FuncOverrideCH1 = 0, + FuncOverrideCH32 = FuncOverrideCH1+CPN_MAX_CHNOUT-1, + FuncTrainer, + FuncTrainerRUD, + FuncTrainerELE, + FuncTrainerTHR, + FuncTrainerAIL, + FuncInstantTrim, + FuncPlaySound, + FuncPlayHaptic, + FuncReset, + FuncSetTimer1, + FuncSetTimer2, + FuncSetTimer3, + FuncVario, + FuncPlayPrompt, + FuncPlayBoth, + FuncPlayValue, + FuncPlayScript, + FuncLogs, + FuncVolume, + FuncBacklight, + FuncScreenshot, + FuncBackgroundMusic, + FuncBackgroundMusicPause, + FuncAdjustGV1, + FuncAdjustGVLast = FuncAdjustGV1+CPN_MAX_GVARS-1, + FuncSetFailsafeInternalModule, + FuncSetFailsafeExternalModule, + FuncRangeCheckInternalModule, + FuncRangeCheckExternalModule, + FuncBindInternalModule, + FuncBindExternalModule, + FuncCount, + FuncReserve = -1 +}; + +enum GVarAdjustModes +{ + FUNC_ADJUST_GVAR_CONSTANT, + FUNC_ADJUST_GVAR_SOURCE, + FUNC_ADJUST_GVAR_GVAR, + FUNC_ADJUST_GVAR_INCDEC +}; + +class CustomFunctionData { // Function Switches data + public: + CustomFunctionData(AssignFunc func=FuncOverrideCH1) { clear(); this->func = func; } + RawSwitch swtch; + AssignFunc func; + int param; + char paramarm[10]; + unsigned int enabled; // TODO perhaps not any more the right name + unsigned int adjustMode; + int repeatParam; + void clear(); + QString funcToString() const; + QString paramToString(const ModelData * model) const; + QString repeatToString() const; + QString enabledToString() const; + + static void populateResetParams(const ModelData * model, QComboBox * b, unsigned int value); + static void populatePlaySoundParams(QStringList & qs); + static void populateHapticParams(QStringList & qs); + +}; + +class FlightModeData { + public: + FlightModeData() { clear(0); } + int trimMode[CPN_MAX_STICKS+CPN_MAX_AUX_TRIMS]; + int trimRef[CPN_MAX_STICKS+CPN_MAX_AUX_TRIMS]; + int trim[CPN_MAX_STICKS+CPN_MAX_AUX_TRIMS]; + RawSwitch swtch; + char name[10+1]; + unsigned int fadeIn; + unsigned int fadeOut; + int rotaryEncoders[CPN_MAX_ENCODERS]; + int gvars[CPN_MAX_GVARS]; + void clear(const int phase); +}; + +class SwashRingData { // Swash Ring data + public: + SwashRingData() { clear(); } + int elevatorWeight; + int aileronWeight; + int collectiveWeight; + unsigned int type; + RawSource collectiveSource; + RawSource aileronSource; + RawSource elevatorSource; + unsigned int value; + void clear() { memset(this, 0, sizeof(SwashRingData)); } +}; + +class FrSkyAlarmData { + public: + FrSkyAlarmData() { clear(); } + unsigned int level; // 0=none, 1=Yellow, 2=Orange, 3=Red + unsigned int greater; // 0=LT(<), 1=GT(>) + unsigned int value; // 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc. + + void clear() { memset(this, 0, sizeof(FrSkyAlarmData)); } +}; + +class FrSkyRSSIAlarm { + public: + FrSkyRSSIAlarm() { clear(0, 50); } + unsigned int level; + int value; + void clear(unsigned int level, int value) { this->level = level; this->value = value;} +}; + +class FrSkyChannelData { + public: + FrSkyChannelData() { clear(); } + unsigned int ratio; // 0.0 means not used, 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc. + unsigned int type; // future use: 0=volts, 1=ml... + int offset; + unsigned int multiplier; + FrSkyAlarmData alarms[2]; + + float getRatio() const; + RawSourceRange getRange() const; + void clear() { memset(this, 0, sizeof(FrSkyChannelData)); } +}; + +struct FrSkyBarData { + RawSource source; + unsigned int barMin; // minimum for bar display + unsigned int barMax; // ditto for max display (would usually = ratio) +}; + +struct FrSkyLineData { + RawSource source[3]; +}; + +struct TelemetryScriptData { + char filename[8+1]; +}; + +enum TelemetryScreenEnum { + TELEMETRY_SCREEN_NONE, + TELEMETRY_SCREEN_NUMBERS, + TELEMETRY_SCREEN_BARS, + TELEMETRY_SCREEN_SCRIPT +}; + +class FrSkyScreenData { + public: + FrSkyScreenData() { clear(); } + + typedef struct { + FrSkyBarData bars[4]; + FrSkyLineData lines[4]; + TelemetryScriptData script; + } FrSkyScreenBody; + + unsigned int type; + FrSkyScreenBody body; + + void clear(); +}; + +enum TelemetryVarioSources { + TELEMETRY_VARIO_SOURCE_ALTI, + TELEMETRY_VARIO_SOURCE_ALTI_PLUS, + TELEMETRY_VARIO_SOURCE_VSPEED, + TELEMETRY_VARIO_SOURCE_A1, + TELEMETRY_VARIO_SOURCE_A2, + TELEMETRY_VARIO_SOURCE_DTE, +}; + +enum TelemetryVoltsSources { + TELEMETRY_VOLTS_SOURCE_A1, + TELEMETRY_VOLTS_SOURCE_A2, + TELEMETRY_VOLTS_SOURCE_A3, + TELEMETRY_VOLTS_SOURCE_A4, + TELEMETRY_VOLTS_SOURCE_FAS, + TELEMETRY_VOLTS_SOURCE_CELLS +}; + +enum TelemetryCurrentSources { + TELEMETRY_CURRENT_SOURCE_NONE, + TELEMETRY_CURRENT_SOURCE_A1, + TELEMETRY_CURRENT_SOURCE_A2, + TELEMETRY_CURRENT_SOURCE_A3, + TELEMETRY_CURRENT_SOURCE_A4, + TELEMETRY_CURRENT_SOURCE_FAS +}; + +enum UartModes { + UART_MODE_NONE, + UART_MODE_TELEMETRY_MIRROR, + UART_MODE_TELEMETRY, + UART_MODE_SBUS_TRAINER, + UART_MODE_DEBUG +}; + +enum TrainerMode { + TRAINER_MODE_MASTER_TRAINER_JACK, + TRAINER_MODE_SLAVE, + TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE, + TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE, + TRAINER_MODE_MASTER_BATTERY_COMPARTMENT, +}; + +class FrSkyData { + public: + FrSkyData() { clear(); } + FrSkyChannelData channels[4]; + unsigned int usrProto; + int blades; + unsigned int voltsSource; + unsigned int altitudeSource; + unsigned int currentSource; + FrSkyScreenData screens[4]; + FrSkyRSSIAlarm rssiAlarms[2]; + unsigned int varioSource; + bool varioCenterSilent; + int varioMin; + int varioCenterMin; // if increment in 0.2m/s = 3.0m/s max + int varioCenterMax; + int varioMax; + bool mAhPersistent; + unsigned int storedMah; + int fasOffset; + bool ignoreSensorIds; + + void clear(); +}; + +class MavlinkData { + public: + MavlinkData() { clear();} + unsigned int rc_rssi_scale; + unsigned int pc_rssi_en; + void clear() { memset(this, 0, sizeof(MavlinkData)); } +}; + +#define TIMER_NAME_LEN 8 + +class TimerData { + public: + enum CountDownMode { + COUNTDOWN_SILENT, + COUNTDOWN_BEEPS, + COUNTDOWN_VOICE, + COUNTDOWN_HAPTIC + }; + TimerData() { clear(); } + RawSwitch mode; + char name[TIMER_NAME_LEN+1]; + bool minuteBeep; + unsigned int countdownBeep; + unsigned int val; + unsigned int persistent; + int pvalue; + void clear() { memset(this, 0, sizeof(TimerData)); mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); } +}; + +enum PulsesProtocol { + PULSES_OFF, + PULSES_PPM, + PULSES_SILV_A, + PULSES_SILV_B, + PULSES_SILV_C, + PULSES_CTP1009, + PULSES_LP45, + PULSES_DSM2, + PULSES_DSMX, + PULSES_PPM16, + PULSES_PPMSIM, + PULSES_PXX_XJT_X16, + PULSES_PXX_XJT_D8, + PULSES_PXX_XJT_LR12, + PULSES_PXX_DJT, + PULSES_CROSSFIRE, + PULSES_MULTIMODULE, + PULSES_PROTOCOL_LAST +}; + +enum MultiModuleRFProtocols { + MM_RF_PROTO_FLYSKY=0, + MM_RF_PROTO_FIRST=MM_RF_PROTO_FLYSKY, + MM_RF_PROTO_HUBSAN, + MM_RF_PROTO_FRSKY, + MM_RF_PROTO_HISKY, + MM_RF_PROTO_V2X2, + MM_RF_PROTO_DSM2, + MM_RF_PROTO_DEVO, + MM_RF_PROTO_YD717, + MM_RF_PROTO_KN, + MM_RF_PROTO_SYMAX, + MM_RF_PROTO_SLT, + MM_RF_PROTO_CX10, + MM_RF_PROTO_CG023, + MM_RF_PROTO_BAYANG, + MM_RF_PROTO_ESky, + MM_RF_PROTO_MT99XX, + MM_RF_PROTO_MJXQ, + MM_RF_PROTO_SHENQI, + MM_RF_PROTO_FY326, + MM_RF_PROTO_SFHSS, + MM_RF_PROTO_J6PRO, + MM_RF_PROTO_FQ777, + MM_RF_PROTO_ASSAN, + MM_RF_PROTO_HONTAI, + MM_RF_PROTO_OLRS, + MM_RF_PROTO_AFHDS2A, + MM_RF_PROTO_Q2X2, + MM_RF_PROTO_LAST= MM_RF_PROTO_Q2X2 +}; + +unsigned int getNumSubtypes(MultiModuleRFProtocols type); + +enum TrainerProtocol { + TRAINER_MASTER_JACK, + TRAINER_SLAVE_JACK, + TRAINER_MASTER_SBUS_MODULE, + TRAINER_MASTER_CPPM_MODULE, + TRAINER_MASTER_SBUS_BATT_COMPARTMENT +}; + +class ModuleData { + public: + ModuleData() { clear(); } + unsigned int modelId; + int protocol; + unsigned int subType; + bool invertedSerial; + unsigned int channelsStart; + int channelsCount; // 0=8 channels + unsigned int failsafeMode; + int failsafeChannels[CPN_MAX_CHNOUT]; + + + struct { + int delay; + bool pulsePol; // false = positive + bool outputType; // false = open drain, true = push pull + int frameLength; + } ppm; + + struct { + unsigned int rfProtocol; + bool autoBindMode; + bool lowPowerMode; + bool customProto; + int optionValue; + } multi; + + + + void clear() { memset(this, 0, sizeof(ModuleData)); } + QString polarityToString() const { return ppm.pulsePol ? QObject::tr("Positive") : QObject::tr("Negative"); } // TODO ModelPrinter +}; + +#define CPN_MAX_SCRIPTS 7 +#define CPN_MAX_SCRIPT_INPUTS 10 +class ScriptData { + public: + ScriptData() { clear(); } + char filename[10+1]; + char name[10+1]; + int inputs[CPN_MAX_SCRIPT_INPUTS]; + void clear() { memset(this, 0, sizeof(ScriptData)); } +}; + +#define CPN_MAX_SENSORS 32 +class SensorData { + + public: + + enum + { + TELEM_TYPE_CUSTOM, + TELEM_TYPE_CALCULATED + }; + + enum + { + TELEM_FORMULA_ADD, + TELEM_FORMULA_AVERAGE, + TELEM_FORMULA_MIN, + TELEM_FORMULA_MAX, + TELEM_FORMULA_MULTIPLY, + TELEM_FORMULA_TOTALIZE, + TELEM_FORMULA_CELL, + TELEM_FORMULA_CONSUMPTION, + TELEM_FORMULA_DIST, + TELEM_FORMULA_LAST = TELEM_FORMULA_DIST + }; + + enum { + TELEM_CELL_INDEX_LOWEST, + TELEM_CELL_INDEX_1, + TELEM_CELL_INDEX_2, + TELEM_CELL_INDEX_3, + TELEM_CELL_INDEX_4, + TELEM_CELL_INDEX_5, + TELEM_CELL_INDEX_6, + TELEM_CELL_INDEX_HIGHEST, + TELEM_CELL_INDEX_DELTA, + }; + + enum + { + UNIT_RAW, + UNIT_VOLTS, + UNIT_AMPS, + UNIT_MILLIAMPS, + UNIT_KTS, + UNIT_METERS_PER_SECOND, + UNIT_FEET_PER_SECOND, + UNIT_KMH, + UNIT_MPH, + UNIT_METERS, + UNIT_FEET, + UNIT_CELSIUS, + UNIT_FAHRENHEIT, + UNIT_PERCENT, + UNIT_MAH, + UNIT_WATTS, + UNIT_MILLIWATTS, + UNIT_DB, + UNIT_RPMS, + UNIT_G, + UNIT_DEGREE, + UNIT_RADIANS, + UNIT_MILLILITERS, + UNIT_FLOZ, + UNIT_HOURS, + UNIT_MINUTES, + UNIT_SECONDS, + // FrSky format used for these fields, could be another format in the future + UNIT_FIRST_VIRTUAL, + UNIT_CELLS = UNIT_FIRST_VIRTUAL, + UNIT_DATETIME, + UNIT_GPS, + UNIT_GPS_LONGITUDE, + UNIT_GPS_LATITUDE, + UNIT_GPS_LONGITUDE_EW, + UNIT_GPS_LATITUDE_NS, + UNIT_DATETIME_YEAR, + UNIT_DATETIME_DAY_MONTH, + UNIT_DATETIME_HOUR_MIN, + UNIT_DATETIME_SEC + }; + + SensorData() { clear(); } + unsigned int type; // custom / formula + unsigned int id; + unsigned int subid; + unsigned int instance; + unsigned int persistentValue; + unsigned int formula; + char label[4+1]; + unsigned int unit; + unsigned int prec; + bool autoOffset; + bool filter; + bool logs; + bool persistent; + bool onlyPositive; + + // for custom sensors + unsigned int ratio; + int offset; + + // for consumption + unsigned int amps; + + // for cell + unsigned int source; + unsigned int index; + + // for calculations + int sources[4]; + + // for GPS dist + unsigned int gps; + unsigned int alt; + + bool isAvailable() const { return strlen(label) > 0; } + void updateUnit(); + QString unitString() const; + void clear() { memset(this, 0, sizeof(SensorData)); } +}; + +/* + * TODO ... + */ +#if 0 +class CustomScreenOptionData { + public: + +}; + +class CustomScreenZoneData { + public: + char widgetName[10+1]; + WidgetOptionData widgetOptions[5]; +}; + +class CustomScreenData { + public: + CustomScreenData(); + + char layoutName[10+1]; + CustomScreenZoneData zones[]; + CustomScreenOptionData options[]; +}; +#else +typedef char CustomScreenData[610+1]; +typedef char TopbarData[216+1]; +#endif + +class ModelData { + public: + ModelData(); + ModelData(const ModelData & src); + ModelData & operator = (const ModelData & src); + + ExpoData * insertInput(const int idx); + void removeInput(const int idx); + + bool isInputValid(const unsigned int idx) const; + bool hasExpos(uint8_t inputIdx) const; + bool hasMixes(uint8_t output) const; + + QVector expos(int input) const; + QVector mixes(int channel) const; + + bool used; + int category; + char name[15+1]; + char filename[16+1]; + TimerData timers[CPN_MAX_TIMERS]; + bool noGlobalFunctions; + bool thrTrim; // Enable Throttle Trim + int trimInc; // Trim Increments + unsigned int trimsDisplay; + bool disableThrottleWarning; + + unsigned int beepANACenter; // 1<<0->A1.. 1<<6->A7 + + bool extendedLimits; // TODO xml + bool extendedTrims; + bool throttleReversed; + FlightModeData flightModeData[CPN_MAX_FLIGHT_MODES]; + MixData mixData[CPN_MAX_MIXERS]; + LimitData limitData[CPN_MAX_CHNOUT]; + + char inputNames[CPN_MAX_INPUTS][4+1]; + ExpoData expoData[CPN_MAX_EXPOS]; + + CurveData curves[CPN_MAX_CURVES]; + LogicalSwitchData logicalSw[CPN_MAX_CSW]; + CustomFunctionData customFn[CPN_MAX_CUSTOM_FUNCTIONS]; + SwashRingData swashRingData; + unsigned int thrTraceSrc; + uint64_t switchWarningStates; + unsigned int switchWarningEnable; + unsigned int potsWarningMode; + bool potsWarningEnabled[CPN_MAX_POTS]; + int potPosition[CPN_MAX_POTS]; + bool displayChecklist; + // TODO structure + char gvars_names[CPN_MAX_GVARS][6+1]; + bool gvars_popups[CPN_MAX_GVARS]; + MavlinkData mavlink; + unsigned int telemetryProtocol; + FrSkyData frsky; + + char bitmap[10+1]; + + unsigned int trainerMode; + + ModuleData moduleData[CPN_MAX_MODULES+1/*trainer*/]; + + ScriptData scriptData[CPN_MAX_SCRIPTS]; + + SensorData sensorData[CPN_MAX_SENSORS]; + + unsigned int toplcdTimer; + + CustomScreenData customScreenData[5]; + + TopbarData topbarData; + + void clear(); + bool isEmpty() const; + void setDefaultInputs(const GeneralSettings & settings); + void setDefaultMixes(const GeneralSettings & settings); + void setDefaultValues(unsigned int id, const GeneralSettings & settings); + + int getTrimValue(int phaseIdx, int trimIdx); + void setTrimValue(int phaseIdx, int trimIdx, int value); + + bool isGVarLinked(int phaseIdx, int gvarIdx); + int getGVarFieldValue(int phaseIdx, int gvarIdx); + + ModelData removeGlobalVars(); + + void clearMixes(); + void clearInputs(); + + int getChannelsMax(bool forceExtendedLimits=false) const; + + protected: + void removeGlobalVar(int & var); +}; + + +class TrainerMix { + public: + TrainerMix() { clear(); } + unsigned int src; // 0-7 = ch1-8 + RawSwitch swtch; + int weight; + unsigned int mode; // off, add-mode, subst-mode + void clear() { memset(this, 0, sizeof(TrainerMix)); } +}; + +class TrainerData { + public: + TrainerData() { clear(); } + int calib[4]; + TrainerMix mix[4]; + void clear() { memset(this, 0, sizeof(TrainerData)); } +}; + +class GeneralSettings { + public: + + enum BeeperMode { + BEEPER_QUIET = -2, + BEEPER_ALARMS_ONLY = -1, + BEEPER_NOKEYS = 0, + BEEPER_ALL = 1 + }; + + enum PotConfig { + POT_NONE, + POT_WITH_DETENT, + POT_MULTIPOS_SWITCH, + POT_WITHOUT_DETENT + }; + + enum SliderConfig { + SLIDER_NONE, + SLIDER_WITH_DETENT + }; + + GeneralSettings(); + + int getDefaultStick(unsigned int channel) const; + RawSource getDefaultSource(unsigned int channel) const; + int getDefaultChannel(unsigned int stick) const; + + unsigned int version; + unsigned int variant; + int calibMid[CPN_MAX_STICKS+CPN_MAX_POTS+CPN_MAX_MOUSE_ANALOGS]; + int calibSpanNeg[CPN_MAX_STICKS+CPN_MAX_POTS+CPN_MAX_MOUSE_ANALOGS]; + int calibSpanPos[CPN_MAX_STICKS+CPN_MAX_POTS+CPN_MAX_MOUSE_ANALOGS]; + unsigned int currModelIndex; + char currModelFilename[16+1]; + unsigned int contrast; + unsigned int vBatWarn; + int txVoltageCalibration; + int txCurrentCalibration; + int vBatMin; + int vBatMax; + int backlightMode; + TrainerData trainer; + unsigned int view; // main screen view // TODO enum + bool disableThrottleWarning; + bool fai; + int switchWarning; // -1=down, 0=off, 1=up + bool disableMemoryWarning; + BeeperMode beeperMode; + bool disableAlarmWarning; + bool enableTelemetryAlarm; + BeeperMode hapticMode; + unsigned int stickMode; // TODO enum + int timezone; + bool adjustRTC; + bool optrexDisplay; + unsigned int inactivityTimer; + bool minuteBeep; + bool preBeep; + bool flashBeep; + unsigned int splashMode; + int splashDuration; + unsigned int backlightDelay; + unsigned int templateSetup; //RETA order according to chout_ar array + int PPM_Multiplier; + int hapticLength; + unsigned int reNavigation; + unsigned int stickReverse; + unsigned int speakerPitch; + int hapticStrength; + unsigned int speakerMode; + char ownerName[10+1]; + unsigned int switchWarningStates; + int beeperLength; + unsigned int gpsFormat; + int speakerVolume; + unsigned int backlightBright; + unsigned int backlightOffBright; + int switchesDelay; + int temperatureCalib; + int temperatureWarn; + unsigned int mAhWarn; + unsigned int mAhUsed; + unsigned int globalTimer; + bool bluetoothEnable; + char bluetoothName[10+1]; + unsigned int btBaudrate; + unsigned int sticksGain; + unsigned int rotarySteps; + unsigned int countryCode; + bool jitterFilter; + unsigned int imperial; + char ttsLanguage[2+1]; + int beepVolume; + int wavVolume; + int varioVolume; + int varioPitch; + int varioRange; + int varioRepeat; + int backgroundVolume; + unsigned int mavbaud; + unsigned int switchUnlockStates; + unsigned int hw_uartMode; + unsigned int backlightColor; + CustomFunctionData customFn[CPN_MAX_CUSTOM_FUNCTIONS]; + char switchName[18][3+1]; + unsigned int switchConfig[18]; + char stickName[4][3+1]; + char potName[4][3+1]; + unsigned int potConfig[4]; + char sliderName[4][3+1]; + unsigned int sliderConfig[4]; + + char themeName[8+1]; + typedef uint8_t ThemeOptionData[8+1]; + ThemeOptionData themeOptionValue[5]; + + struct SwitchInfo { + SwitchInfo(unsigned int index, unsigned int position): + index(index), + position(position) + { + } + unsigned int index; + unsigned int position; + }; + + static SwitchInfo switchInfoFromSwitchPositionTaranis(unsigned int index); + bool switchPositionAllowedTaranis(int index) const; + bool switchSourceAllowedTaranis(int index) const; + bool isPotAvailable(int index) const; + bool isSliderAvailable(int index) const; +}; + +class CategoryData { + public: + CategoryData(const char * name) { + strncpy(this->name, name, sizeof(CategoryData::name)); + } + char name[15+1]; +}; + +class RadioData { + public: + RadioData(); + + GeneralSettings generalSettings; + std::vector categories; + std::vector models; + + void setCurrentModel(unsigned int index) + { + generalSettings.currModelIndex = index; + strcpy(generalSettings.currModelFilename, models[index].filename); + } + + void fixModelFilename(unsigned int index) + { + ModelData & model = models[index]; + QString filename(model.filename); + bool ok = filename.endsWith(".bin"); + if (ok) { + if (filename.startsWith("model") && filename.mid(5, filename.length()-9).toInt() > 0) { + ok = false; + } + } + if (ok) { + for (unsigned i=0; i ReleaseNotesDialog::ReleaseNotesDialog(QWidget * parent) : QDialog(parent), diff --git a/companion/src/storage/CMakeLists.txt b/companion/src/storage/CMakeLists.txt index 69be3a8f6..76f940881 100644 --- a/companion/src/storage/CMakeLists.txt +++ b/companion/src/storage/CMakeLists.txt @@ -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 diff --git a/companion/src/storage/bineeprom.cpp b/companion/src/storage/bineeprom.cpp new file mode 100644 index 000000000..64b1007a6 --- /dev/null +++ b/companion/src/storage/bineeprom.cpp @@ -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 +#include + +// 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 errors; + + foreach(EEPROMInterface * eepromInterface, eepromInterfaces) { + std::bitset 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; +} diff --git a/companion/src/storage/storage_eeprom.h b/companion/src/storage/bineeprom.h similarity index 58% rename from companion/src/storage/storage_eeprom.h rename to companion/src/storage/bineeprom.h index 32f410d34..68085e164 100644 --- a/companion/src/storage/storage_eeprom.h +++ b/companion/src/storage/bineeprom.h @@ -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 -#include -#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_ diff --git a/companion/src/storage/categorized.cpp b/companion/src/storage/categorized.cpp new file mode 100644 index 000000000..e34ae7d22 --- /dev/null +++ b/companion/src/storage/categorized.cpp @@ -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 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 +#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); +} diff --git a/companion/src/storage/eepe.h b/companion/src/storage/eepe.h new file mode 100644 index 000000000..069676c70 --- /dev/null +++ b/companion/src/storage/eepe.h @@ -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_ diff --git a/companion/src/storage/eepexml.cpp b/companion/src/storage/eepexml.cpp new file mode 100644 index 000000000..77d351f10 --- /dev/null +++ b/companion/src/storage/eepexml.cpp @@ -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 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; +} \ No newline at end of file diff --git a/companion/src/storage/firmwareinterface.cpp b/companion/src/storage/firmwareinterface.cpp index e69448bd9..86febc2f1 100644 --- a/companion/src/storage/firmwareinterface.cpp +++ b/companion/src/storage/firmwareinterface.cpp @@ -18,11 +18,12 @@ * GNU General Public License for more details. */ -#include #include "hexinterface.h" #include "splash.h" #include "firmwareinterface.h" #include "helpers.h" +#include "storage.h" +#include #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; diff --git a/companion/src/storage/firmwareinterface.h b/companion/src/storage/firmwareinterface.h index 6ee15ccdb..29a3e3ea1 100644 --- a/companion/src/storage/firmwareinterface.h +++ b/companion/src/storage/firmwareinterface.h @@ -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: diff --git a/companion/src/storage/hexeeprom.cpp b/companion/src/storage/hexeeprom.cpp new file mode 100644 index 000000000..e49230be9 --- /dev/null +++ b/companion/src/storage/hexeeprom.cpp @@ -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 + +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; +} diff --git a/companion/src/storage/hexeeprom.h b/companion/src/storage/hexeeprom.h new file mode 100644 index 000000000..e3b426f24 --- /dev/null +++ b/companion/src/storage/hexeeprom.h @@ -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_ diff --git a/companion/src/storage/hexinterface.cpp b/companion/src/storage/hexinterface.cpp index ab718b93a..224757fea 100644 --- a/companion/src/storage/hexinterface.cpp +++ b/companion/src/storage/hexinterface.cpp @@ -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; diff --git a/companion/src/storage/hexinterface.h b/companion/src/storage/hexinterface.h index 4dcf915b2..c4866842c 100644 --- a/companion/src/storage/hexinterface.h +++ b/companion/src/storage/hexinterface.h @@ -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; diff --git a/companion/src/storage/otx.cpp b/companion/src/storage/otx.cpp new file mode 100644 index 000000000..4f0d3be61 --- /dev/null +++ b/companion/src/storage/otx.cpp @@ -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 + +#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; +} diff --git a/companion/src/storage/otx.h b/companion/src/storage/otx.h new file mode 100644 index 000000000..3d81cd4b4 --- /dev/null +++ b/companion/src/storage/otx.h @@ -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_ diff --git a/companion/src/storage/rlefile.h b/companion/src/storage/rlefile.h index 4569b0cbc..6b5d01551 100644 --- a/companion/src/storage/rlefile.h +++ b/companion/src/storage/rlefile.h @@ -20,21 +20,6 @@ // TODO should be rle -/* - * Author - Bertrand Songis - * - * 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_ diff --git a/companion/src/storage/sdcard.cpp b/companion/src/storage/sdcard.cpp new file mode 100644 index 000000000..d134f6613 --- /dev/null +++ b/companion/src/storage/sdcard.cpp @@ -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 +#include + +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(); +} + diff --git a/companion/src/storage/sdcard.h b/companion/src/storage/sdcard.h new file mode 100644 index 000000000..6ab8bbdea --- /dev/null +++ b/companion/src/storage/sdcard.h @@ -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 +{ + public: + SdcardStorageFactory(): + DefaultStorageFactory("sdcard") + { + } + + virtual bool probe(const QString & name); +}; + +#endif // _SDCARD_H2_ diff --git a/companion/src/storage/storage.cpp b/companion/src/storage/storage.cpp new file mode 100644 index 000000000..0a8e73065 --- /dev/null +++ b/companion/src/storage/storage.cpp @@ -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 + +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 registeredStorageFactories; + +void registerStorageFactory(StorageFactory * factory) +{ + qDebug() << "register storage" << factory->name(); + registeredStorageFactories.push_back(factory); +} + +void registerStorageFactories() +{ + registerStorageFactory(new DefaultStorageFactory("bin")); + registerStorageFactory(new DefaultStorageFactory("eepe")); + registerStorageFactory(new DefaultStorageFactory("hex")); + registerStorageFactory(new DefaultStorageFactory("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 = QSharedPointer(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 errors; + + foreach(EEPROMInterface *eepromInterface, eepromInterfaces) { + std::bitset 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 errors; + + foreach(EEPROMInterface *eepromInterface, eepromInterfaces) { + std::bitset 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 diff --git a/companion/src/storage/storage.h b/companion/src/storage/storage.h new file mode 100644 index 000000000..6fe665945 --- /dev/null +++ b/companion/src/storage/storage.h @@ -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 +#include + +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 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_ diff --git a/companion/src/storage/storage_eeprom.cpp b/companion/src/storage/storage_eeprom.cpp deleted file mode 100644 index aa3b0a18d..000000000 --- a/companion/src/storage/storage_eeprom.cpp +++ /dev/null @@ -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 -#include "firmwareinterface.h" -#include "storage_eeprom.h" - -unsigned long LoadEeprom(RadioData & radioData, const uint8_t * eeprom, const int size) -{ - std::bitset errors; - - foreach(EEPROMInterface * eepromInterface, eepromInterfaces) { - std::bitset 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 errors; - - foreach(EEPROMInterface *eepromInterface, eepromInterfaces) { - std::bitset 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 errors; - - foreach(EEPROMInterface *eepromInterface, eepromInterfaces) { - std::bitset 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 = QSharedPointer(new RadioData()); - std::bitset 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; -} diff --git a/companion/src/storage/storage_sdcard.cpp b/companion/src/storage/storage_sdcard.cpp deleted file mode 100644 index 11ccb87a8..000000000 --- a/companion/src/storage/storage_sdcard.cpp +++ /dev/null @@ -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 -#include -#include -#include -#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::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::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::const_iterator i = models.begin(); i != models.end(); ++i) { - if (filename == i->filename) return i->data; - } - throw StorageSdcardModelNotFound(); -} - - -QList::iterator StorageSdcard::getModelIterator(const QString & filename) -{ - for (QList::iterator i = models.begin(); i != models.end(); ++i) { - if (filename == i->filename) return i; - } - return models.end(); -} - -QList::const_iterator StorageSdcard::getModelIterator(const QString & filename) const -{ - for (QList::const_iterator i = models.begin(); i != models.end(); ++i) { - if (filename == i->filename) return i; - } - return models.end(); -} - -QList StorageSdcard::getModelsFileNames() const -{ - QList result; - for (QList::const_iterator i = models.begin(); i != models.end(); ++i) { - result.append(i->filename); - } - return result; -} - diff --git a/companion/src/storage/storage_sdcard.h b/companion/src/storage/storage_sdcard.h deleted file mode 100644 index 1360c8a89..000000000 --- a/companion/src/storage/storage_sdcard.h +++ /dev/null @@ -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 -#include -#include -#include - -/* - 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::iterator getModelIterator(const QString & filename); - QList::const_iterator getModelIterator(const QString & filename) const; - - /* - Returns a list of all model bin files (their filenames) - */ - QList getModelsFileNames() const; - - QString lastErrorMessage; - QByteArray radio; // radio settings (radio.bin) - QByteArray modelList; // model names and categories (models.txt) - QList 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_ diff --git a/companion/src/storage/xmlinterface.cpp b/companion/src/storage/xmlinterface.cpp index 1a13bd3de..225e23ca8 100644 --- a/companion/src/storage/xmlinterface.cpp +++ b/companion/src/storage/xmlinterface.cpp @@ -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 +#include 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);