/* * 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 "helpers.h" #include "modelprinter.h" #include "multiprotocols.h" #include #include #include #include #include "multiprotocols.h" QString changeColor(const QString & input, const QString & to, const QString & from) { QString result = input; return result.replace("color="+from, "color="+to); } ModelPrinter::ModelPrinter(Firmware * firmware, const GeneralSettings & generalSettings, const ModelData & model): firmware(firmware), generalSettings(generalSettings), model(model) { } ModelPrinter::~ModelPrinter() { } void debugHtml(const QString & html) { QFile file("foo.html"); file.open(QIODevice::Truncate | QIODevice::WriteOnly); file.write(html.toUtf8()); file.close(); } QString addFont(const QString & input, const QString & color, const QString & size, const QString & face) { QString colorStr; if (!color.isEmpty()) { colorStr = "color=" + color; } QString sizeStr; if (!size.isEmpty()) { sizeStr = "size=" + size; } QString faceStr; if (!face.isEmpty()) { faceStr = "face='" + face + "'"; } return "" + input + ""; } QString ModelPrinter::printEEpromSize() { return tr("%1 bytes").arg(getCurrentEEpromInterface()->getSize(model)); } QString ModelPrinter::printChannelName(int idx) { QString str = RawSource(SOURCE_TYPE_CH, idx).toString(&model, &generalSettings); if (firmware->getCapability(ChannelsName)) { str = str.leftJustified(firmware->getCapability(ChannelsName) + 5, ' ', false); } str.append(' '); return str.toHtmlEscaped(); } QString ModelPrinter::printTrimIncrementMode() { switch (model.trimInc) { case -2: return tr("Exponential"); case -1: return tr("Extra Fine"); case 0: return tr("Fine"); case 1: return tr("Medium"); case 2: return tr("Coarse"); default: return tr("Unknown"); } } QString ModelPrinter::printThrottleTrimMode() { return model.thrTrim ? tr("Enabled") : tr("Disabled"); } QString ModelPrinter::printModuleProtocol(unsigned int protocol) { static const char * strings[] = { "OFF", "PPM", "Silverlit A", "Silverlit B", "Silverlit C", "CTP1009", "LP45", "DSM2", "DSMX", "PPM16", "PPMsim", "FrSky XJT (D16)", "FrSky XJT (D8)", "FrSky XJT (LR12)", "FrSky DJT", "Crossfire", "DIY Multiprotocol Module", "FrSky R9M Module" }; return CHECK_IN_ARRAY(strings, protocol); } QString ModelPrinter::printMultiRfProtocol(int rfProtocol, bool custom) { static const char *strings[] = { "FlySky", "Hubsan", "FrSky", "Hisky", "V2x2", "DSM", "Devo", "YD717", "KN", "SymaX", "SLT", "CX10", "CG023", "Bayang", "ESky", "MT99XX", "MJXQ", "Shenqi", "FY326", "SFHSS", "J6 PRO","FQ777","Assan","Hontai","OLRS", "FlySky AFHDS2A", "Q2x2", "Walkera", "Q303", "GW008", "DM002" }; if (custom) return "Custom - proto " + QString::number(rfProtocol); else return CHECK_IN_ARRAY(strings, rfProtocol); } QString ModelPrinter::printMultiSubType(int rfProtocol, bool custom, unsigned int subType) { /* custom protocols */ if (custom) rfProtocol = MM_RF_CUSTOM_SELECTED; Multiprotocols::MultiProtocolDefinition pdef = multiProtocols.getProtocol(rfProtocol); if (subType < (unsigned int) pdef.subTypeStrings.size()) return qApp->translate("Multiprotocols", qPrintable(pdef.subTypeStrings[subType])); else return "???"; } QString ModelPrinter::printModule(int idx) { const ModuleData &module = model.moduleData[idx]; if (module.protocol == PULSES_OFF) return printModuleProtocol(module.protocol); else if (module.protocol == PULSES_PPM) return tr("%1, Channels(%2-%3), PPM delay(%4usec), Pulse polarity(%5)").arg(printModuleProtocol(module.protocol)).arg(module.channelsStart + 1).arg(module.channelsStart + module.channelsCount).arg(module.ppm.delay).arg(module.polarityToString()); else { QString result = tr("%1, Channels(%2-%3)").arg(printModuleProtocol(module.protocol)).arg(module.channelsStart+1).arg(module.channelsStart+module.channelsCount); if (module.protocol != PULSES_PXX_XJT_D8) { result += " " + tr("Receiver number(%1)").arg(module.modelId); } if (module.protocol == PULSES_MULTIMODULE) result += " " + tr("radio Protocol %1, subType %2, option value %3").arg(printMultiRfProtocol(module.multi.rfProtocol, module.multi.customProto)).arg(printMultiSubType(module.multi.rfProtocol, module.multi.customProto, module.subType)).arg(module.multi.optionValue); return result; } } QString ModelPrinter::printTrainerMode() { QString result; switch (model.trainerMode) { case 1: result = tr("Slave/Jack"); // TODO + tr(": Channel start: %1, %2 Channels, %3usec Delay, Pulse polarity %4").arg(module.channelsStart+1).arg(module.channelsCount).arg(module.ppm.delay).arg(module.polarityToString()); break; case 2: result = tr("Master/SBUS Module"); break; case 3: result = tr("Master/CPPM Module"); break; case 4: result = tr("Master/SBUS in battery compartment"); break; default: result = tr("Master/Jack"); } return result; } QString ModelPrinter::printHeliSwashType () { switch (model.swashRingData.type) { case HELI_SWASH_TYPE_90: return tr("90"); case HELI_SWASH_TYPE_120: return tr("120"); case HELI_SWASH_TYPE_120X: return tr("120X"); case HELI_SWASH_TYPE_140: return tr("140"); case HELI_SWASH_TYPE_NONE: return tr("Off"); default: return "???"; } } QString ModelPrinter::printCenterBeep() { QStringList strl; if (model.beepANACenter & 0x01) strl << tr("Rudder"); if (model.beepANACenter & 0x02) strl << tr("Elevator"); if (model.beepANACenter & 0x04) strl << tr("Throttle"); if (model.beepANACenter & 0x08) strl << tr("Aileron"); if (IS_HORUS(firmware->getBoard())) { // TODO qDebug() << "ModelPrinter::printCenterBeep() TODO"; } else if (IS_TARANIS(firmware->getBoard())) { if (model.beepANACenter & 0x10) strl << "S1"; if (model.beepANACenter & 0x20) strl << "S2"; if (model.beepANACenter & 0x40) strl << "S3"; if (model.beepANACenter & 0x80) strl << "LS"; if (model.beepANACenter & 0x100) strl << "RS"; } else { if (model.beepANACenter & 0x10) strl << "P1"; if (model.beepANACenter & 0x20) strl << "P2"; if (model.beepANACenter & 0x40) strl << "P3"; } return strl.join(", "); } QString ModelPrinter::printTimer(int idx) { return printTimer(model.timers[idx]); } QString ModelPrinter::printTimer(const TimerData & timer) { QStringList result; if (firmware->getCapability(TimersName) && timer.name[0]) result += tr("Name(%1)").arg(timer.name); result += QString("%1:%2").arg(timer.val/60, 2, 10, QChar('0')).arg(timer.val%60, 2, 10, QChar('0')); result += timer.mode.toString(); if (timer.persistent) result += tr("Persistent"); if (timer.minuteBeep) result += tr("MinuteBeep"); if (timer.countdownBeep == TimerData::COUNTDOWN_BEEPS) result += tr("CountDown(Beeps)"); else if (timer.countdownBeep == TimerData::COUNTDOWN_VOICE) result += tr("CountDown(Voice)"); else if (timer.countdownBeep == TimerData::COUNTDOWN_HAPTIC) result += tr("CountDown(Haptic)"); return result.join(", "); } QString ModelPrinter::printTrim(int flightModeIndex, int stickIndex) { const FlightModeData & fm = model.flightModeData[flightModeIndex]; if (fm.trimMode[stickIndex] == -1) { return tr("Off"); } else { if (fm.trimRef[stickIndex] == flightModeIndex) { return QString("%1").arg(fm.trim[stickIndex]); } else { if (fm.trimMode[stickIndex] == 0) { return tr("FM%1").arg(fm.trimRef[stickIndex]); } else { if (fm.trim[stickIndex] < 0) return tr("FM%1%2").arg(fm.trimRef[stickIndex]).arg(fm.trim[stickIndex]); else return tr("FM%1+%2").arg(fm.trimRef[stickIndex]).arg(fm.trim[stickIndex]); } } } } QString ModelPrinter::printGlobalVar(int flightModeIndex, int gvarIndex) { const FlightModeData & fm = model.flightModeData[flightModeIndex]; if (fm.gvars[gvarIndex] <= 1024) { return QString("%1").arg(fm.gvars[gvarIndex]); } else { int num = fm.gvars[gvarIndex] - 1025; if (num >= flightModeIndex) num++; return tr("FM%1").arg(num); } } QString ModelPrinter::printRotaryEncoder(int flightModeIndex, int reIndex) { const FlightModeData & fm = model.flightModeData[flightModeIndex]; if (fm.rotaryEncoders[reIndex] <= 1024) { return QString("%1").arg(fm.rotaryEncoders[reIndex]); } else { int num = fm.rotaryEncoders[reIndex] - 1025; if (num >= flightModeIndex) num++; return tr("FM%1").arg(num); } } QString ModelPrinter::printInputName(int idx) { RawSourceType srcType = (firmware->getCapability(VirtualInputs) ? SOURCE_TYPE_VIRTUAL_INPUT : SOURCE_TYPE_STICK); return RawSource(srcType, idx).toString(&model, &generalSettings).toHtmlEscaped(); } QString ModelPrinter::printInputLine(int idx) { return printInputLine(model.expoData[idx]); } QString ModelPrinter::printInputLine(const ExpoData & input) { QString str = " "; switch (input.mode) { case (1): str += "<- "; break; case (2): str += "-> "; break; default: str += "   "; break; } if (firmware->getCapability(VirtualInputs)) { str += input.srcRaw.toString(&model, &generalSettings).toHtmlEscaped(); } str += " " + tr("Weight").toHtmlEscaped() + QString("(%1)").arg(Helpers::getAdjustmentString(input.weight, &model, true).toHtmlEscaped()); if (input.curve.value) str += " " + input.curve.toString(&model).toHtmlEscaped(); QString flightModesStr = printFlightModes(input.flightModes); if (!flightModesStr.isEmpty()) str += " " + flightModesStr.toHtmlEscaped(); if (input.swtch.type != SWITCH_TYPE_NONE) str += " " + tr("Switch").toHtmlEscaped() + QString("(%1)").arg(input.swtch.toString(getCurrentBoard(), &generalSettings)).toHtmlEscaped(); if (firmware->getCapability(VirtualInputs)) { if (input.carryTrim>0) str += " " + tr("NoTrim").toHtmlEscaped(); else if (input.carryTrim<0) str += " " + RawSource(SOURCE_TYPE_TRIM, (-(input.carryTrim)-1)).toString(&model, &generalSettings).toHtmlEscaped(); } if (input.offset) str += " " + tr("Offset(%1)").arg(Helpers::getAdjustmentString(input.offset, &model)).toHtmlEscaped(); if (firmware->getCapability(HasExpoNames) && input.name[0]) str += QString(" [%1]").arg(input.name).toHtmlEscaped(); return str; } QString ModelPrinter::printMixerLine(const MixData & mix, bool showMultiplex, int highlightedSource) { QString str = " "; if (showMultiplex) { switch(mix.mltpx) { case (1): str += "*="; break; case (2): str += ":="; break; default: str += "+="; break; } } else { str += "  "; } // highlight source if needed QString source = mix.srcRaw.toString(&model, &generalSettings).toHtmlEscaped(); if ( (mix.srcRaw.type == SOURCE_TYPE_CH) && (mix.srcRaw.index+1 == (int)highlightedSource) ) { source = "" + source + ""; } str += " " + source; if (mix.mltpx == MLTPX_MUL && !showMultiplex) str += " " + QString("MULT!").toHtmlEscaped(); else str += " " + tr("Weight(%1)").arg(Helpers::getAdjustmentString(mix.weight, &model, true)).toHtmlEscaped(); QString flightModesStr = printFlightModes(mix.flightModes); if (!flightModesStr.isEmpty()) str += " " + flightModesStr.toHtmlEscaped(); if (mix.swtch.type != SWITCH_TYPE_NONE) str += " " + tr("Switch(%1)").arg(mix.swtch.toString(getCurrentBoard(), &generalSettings)).toHtmlEscaped(); if (mix.carryTrim > 0) str += " " + tr("NoTrim").toHtmlEscaped(); else if (mix.carryTrim < 0) str += " " + RawSource(SOURCE_TYPE_TRIM, (-(mix.carryTrim)-1)).toString(&model, &generalSettings); if (firmware->getCapability(HasNoExpo) && mix.noExpo) str += " " + tr("No DR/Expo").toHtmlEscaped(); if (mix.sOffset) str += " " + tr("Offset(%1)").arg(Helpers::getAdjustmentString(mix.sOffset, &model)).toHtmlEscaped(); if (mix.curve.value) str += " " + mix.curve.toString(&model).toHtmlEscaped(); int scale = firmware->getCapability(SlowScale); if (scale == 0) scale = 1; if (mix.delayDown || mix.delayUp) str += " " + tr("Delay(u%1:d%2)").arg((double)mix.delayUp/scale).arg((double)mix.delayDown/scale).toHtmlEscaped(); if (mix.speedDown || mix.speedUp) str += " " + tr("Slow(u%1:d%2)").arg((double)mix.speedUp/scale).arg((double)mix.speedDown/scale).toHtmlEscaped(); if (mix.mixWarn) str += " " + tr("Warn(%1)").arg(mix.mixWarn).toHtmlEscaped(); if (firmware->getCapability(HasMixerNames) && mix.name[0]) str += QString(" [%1]").arg(mix.name).toHtmlEscaped(); return str; } QString ModelPrinter::printFlightModeSwitch(const RawSwitch & swtch) { return swtch.toString(getCurrentBoard(), &generalSettings); } QString ModelPrinter::printFlightModeName(int index) { const FlightModeData & fm = model.flightModeData[index]; if (strlen(fm.name) > 0) { return QString("%1").arg(fm.name); } else { return tr("FM%1").arg(index); } } QString ModelPrinter::printFlightModes(unsigned int flightModes) { int numFlightModes = firmware->getCapability(FlightModes); if (numFlightModes && flightModes) { if (flightModes == (unsigned int)(1< 1) return tr("Flight modes(%1)").arg(list.join(", ")); else return tr("Flight mode(%1)").arg(list.join(", ")); } } else return ""; } QString ModelPrinter::printLogicalSwitchLine(int idx) { QString result = ""; const LogicalSwitchData & ls = model.logicalSw[idx]; const QString sw1Name = RawSwitch(ls.val1).toString(getCurrentBoard(), &generalSettings); const QString sw2Name = RawSwitch(ls.val2).toString(getCurrentBoard(), &generalSettings); if (ls.isEmpty()) return result; if (ls.andsw!=0) { result +="( "; } switch (ls.getFunctionFamily()) { case LS_FAMILY_EDGE: result += tr("Edge(%1, [%2:%3])").arg(sw1Name).arg(ValToTim(ls.val2)).arg(ls.val3<0 ? tr("instant") : QString("%1").arg(ValToTim(ls.val2+ls.val3))); break; case LS_FAMILY_STICKY: result += tr("Sticky(%1, %2)").arg(sw1Name).arg(sw2Name); break; case LS_FAMILY_TIMER: result += tr("Timer(%1, %2)").arg(ValToTim(ls.val1)).arg(ValToTim(ls.val2)); break; case LS_FAMILY_VOFS: { RawSource source = RawSource(ls.val1); RawSourceRange range = source.getRange(&model, generalSettings); QString res; if (ls.val1) res += source.toString(&model, &generalSettings); else res += "0"; res.remove(" "); if (ls.func == LS_FN_APOS || ls.func == LS_FN_ANEG) res = "|" + res + "|"; else if (ls.func == LS_FN_DAPOS) res = "|d(" + res + ")|"; else if (ls.func == LS_FN_DPOS) result = "d(" + res + ")"; result += res; if (ls.func == LS_FN_VEQUAL) result += " = "; else if (ls.func == LS_FN_APOS || ls.func == LS_FN_VPOS || ls.func == LS_FN_DPOS || ls.func == LS_FN_DAPOS) result += " > "; else if (ls.func == LS_FN_ANEG || ls.func == LS_FN_VNEG) result += " < "; else if (ls.func == LS_FN_VALMOSTEQUAL) result += " ~ "; else result += " missing"; result += QString::number(range.step * (ls.val2 /*TODO+ source.getRawOffset(model)*/) + range.offset); break; } case LS_FAMILY_VBOOL: result += sw1Name; switch (ls.func) { case LS_FN_AND: result += " AND "; break; case LS_FN_OR: result += " OR "; break; case LS_FN_XOR: result += " XOR "; break; default: result += " bar "; break; } result += sw2Name; break; case LS_FAMILY_VCOMP: if (ls.val1) result += RawSource(ls.val1).toString(&model, &generalSettings); else result += "0"; switch (ls.func) { case LS_FN_EQUAL: case LS_FN_VEQUAL: result += " = "; break; case LS_FN_NEQUAL: result += " != "; break; case LS_FN_GREATER: result += " > "; break; case LS_FN_LESS: result += " < "; break; case LS_FN_EGREATER: result += " >= "; break; case LS_FN_ELESS: result += " <= "; break; default: result += " foo "; break; } if (ls.val2) result += RawSource(ls.val2).toString(&model, &generalSettings); else result += "0"; break; } if (ls.andsw != 0) { result +=" ) AND "; result += RawSwitch(ls.andsw).toString(getCurrentBoard(), &generalSettings); } if (firmware->getCapability(LogicalSwitchesExt)) { if (ls.duration) result += " " + tr("Duration(%1s)").arg(ls.duration/10.0); if (ls.delay) result += " " + tr("Delay(%1s)").arg(ls.delay/10.0); } return result; } QString ModelPrinter::printCustomFunctionLine(int idx) { QString result; const CustomFunctionData & cf = model.customFn[idx]; if (cf.swtch.type == SWITCH_TYPE_NONE) return result; result += cf.swtch.toString(getCurrentBoard(), &generalSettings) + " - "; result += cf.funcToString(&model) + " ("; result += cf.paramToString(&model) + ")"; if (!cf.repeatToString().isEmpty()) result += " " + cf.repeatToString(); if (!cf.enabledToString().isEmpty()) result += " " + cf.enabledToString(); return result; } QString ModelPrinter::printCurveName(int idx) { return model.curves[idx].nameToString(idx).toHtmlEscaped(); } QString ModelPrinter::printCurve(int idx) { QString result; const CurveData & curve = model.curves[idx]; result += (curve.type == CurveData::CURVE_TYPE_CUSTOM) ? tr("Custom") : tr("Standard"); result += ", ["; if (curve.type == CurveData::CURVE_TYPE_CUSTOM) { for (int j=0; jaddResource(QTextDocument::ImageResource, QUrl(filename), image.get()); // qDebug() << "ModelPrinter::createCurveImage()" << idx << filename; return filename; }