5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
22 #include "helpers_html.h"
23 #include "multimodelprinter.h"
26 MultiModelPrinter::MultiColumns::MultiColumns(int count
):
30 columns
= new QString
[count
];
33 MultiModelPrinter::MultiColumns::~MultiColumns()
38 void MultiModelPrinter::MultiColumns::append(const QString
& str
)
40 for (int i
=0; i
<count
; i
++) {
45 void MultiModelPrinter::MultiColumns::appendTitle(const QString
& name
)
47 append("<b>" + name
+ "</b> ");
50 void MultiModelPrinter::MultiColumns::append(int idx
, const QString
& str
)
53 compareColumns
[idx
].append(str
);
55 columns
[idx
].append(str
);
58 void MultiModelPrinter::MultiColumns::beginCompare()
60 compareColumns
= new QString
[count
];
63 void MultiModelPrinter::MultiColumns::endCompare(const QString
& color
)
65 for (int i
=0; i
<count
; i
++) {
66 QString cellColor
= color
;
67 if (i
==0 && count
>1 && compareColumns
[0]!=compareColumns
[1])
69 else if (i
>0 && compareColumns
[i
]!=compareColumns
[0])
71 columns
[i
].append(QString("<font color='%1'>%2</font>").arg(cellColor
).arg(compareColumns
[i
]));
73 delete[] compareColumns
;
74 compareColumns
= NULL
;
78 void MultiModelPrinter::MultiColumns::append(int idx
, T val
)
80 append(idx
, QString("%1").arg(val
));
83 QString
MultiModelPrinter::MultiColumns::print()
85 QString result
= "<tr>";
86 for (int i
=0; i
<count
; i
++) {
87 result
.append(QString("<td width='%1%'>%2</td>").arg(100.0/count
).arg(columns
[i
]));
89 result
.append("</tr>");
93 bool MultiModelPrinter::MultiColumns::isEmpty()
95 for (int i
=0; i
<count
; i
++) {
96 if (!columns
[i
].isEmpty())
102 #define COMPARE(what) \
103 columns.beginCompare(); \
104 for (int cc=0; cc<models.size(); cc++) { \
105 ModelPrinter * modelPrinter = modelPrinters[cc]; \
106 ModelData * model = models[cc]; \
107 (void)(model); (void)(modelPrinter); \
108 columns.append(cc, (what)); \
110 columns.endCompare();
112 QString
MultiModelPrinter::printTitle(const QString
& label
)
114 return QString("<tr><td colspan='%1'><h2>").arg(modelPrinters
.count()) + label
+ "</h2></td></tr>";
117 MultiModelPrinter::MultiModelPrinter(Firmware
* firmware
):
122 MultiModelPrinter::~MultiModelPrinter()
124 for(int i
=0; i
<modelPrinters
.size(); i
++) {
125 delete modelPrinters
[i
];
129 void MultiModelPrinter::setModel(int idx
, const ModelData
& model
)
131 int count
= std::max(models
.size(), idx
+1);
132 models
.resize(count
);
133 modelPrinters
.resize(count
);
135 if (modelPrinters
[idx
]) {
136 // free existing model printer
137 delete modelPrinters
[idx
];
138 modelPrinters
[idx
] = 0;
141 models
[idx
] = (ModelData
*)&model
; // TODO remove cast
142 modelPrinters
[idx
] = new ModelPrinter(firmware
, defaultSettings
, model
);
145 QString
MultiModelPrinter::print(QTextDocument
* document
)
147 if (document
) document
->clear();
149 QString str
= "<table border='1' cellspacing='0' cellpadding='3' width='100%' style='font-family: monospace;'>";
151 if (firmware
->getCapability(Heli
))
152 str
+= printHeliSetup();
153 if (firmware
->getCapability(FlightModes
))
154 str
+= printFlightModes();
155 str
+= printInputs();
156 str
+= printMixers();
157 str
+= printLimits();
158 str
+= printCurves(document
);
159 if (firmware
->getCapability(Gvars
) && !firmware
->getCapability(GvarsFlightModes
))
161 str
+= printLogicalSwitches();
162 str
+= printCustomFunctions();
163 str
+= printTelemetry();
168 QString
MultiModelPrinter::printSetup()
170 QString str
= printTitle(tr("General Model Settings"));
172 MultiColumns
columns(models
.size());
173 columns
.appendTitle(tr("Name:"));
174 COMPARE(model
->name
);
175 columns
.append("<br/>");
176 columns
.appendTitle(tr("EEprom Size:"));
177 COMPARE(modelPrinter
->printEEpromSize());
178 columns
.append("<br/>");
179 for (int i
=0; i
<firmware
->getCapability(Timers
); i
++) {
180 columns
.appendTitle(tr("Timer%1:").arg(i
+1));
181 COMPARE(modelPrinter
->printTimer(i
));
182 columns
.append("<br/>");
184 for (int i
=0; i
<firmware
->getCapability(NumModules
); i
++) {
185 columns
.appendTitle(firmware
->getCapability(NumModules
) > 1 ? tr("Module%1:").arg(i
+1) : tr("Module:"));
186 COMPARE(modelPrinter
->printModule(i
));
187 columns
.append("<br/>");
189 if (IS_TARANIS(firmware
->getBoard())) {
190 columns
.appendTitle(tr("Trainer port:"));
191 COMPARE(modelPrinter
->printTrainerMode());
192 columns
.append("<br/>");
194 columns
.appendTitle(tr("Throttle Trim:"));
195 COMPARE(modelPrinter
->printThrottleTrimMode());
196 columns
.append("<br/>");
197 columns
.appendTitle(tr("Trim Increment:"));
198 COMPARE(modelPrinter
->printTrimIncrementMode());
199 columns
.append("<br/>");
200 columns
.appendTitle(tr("Center Beep:"));
201 COMPARE(modelPrinter
->printCenterBeep());
202 str
.append(columns
.print());
207 QString
MultiModelPrinter::printHeliSetup()
209 bool heliEnabled
= false;
210 for (int k
=0; k
<models
.size(); k
++) {
211 heliEnabled
= heliEnabled
|| models
[k
]->swashRingData
.type
!= HELI_SWASH_TYPE_NONE
;
217 QString str
= printTitle(tr("Helicopter Setup"));
218 MultiColumns
columns(models
.size());
219 columns
.appendTitle (tr("Swash Type:"));
220 COMPARE(modelPrinter
->printHeliSwashType());
221 columns
.append ("<br/>");
223 columns
.appendTitle (tr("Swash Ring:"));
224 COMPARE(model
->swashRingData
.value
);
226 columns
.append ("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
227 columns
.append("<tr>");
228 columns
.append("<td></td><td><b>" + tr("Input") + "</b></td><td><b>" + tr("Weight") + "</b></td>");
229 columns
.append("</tr>");
231 columns
.append("<tr><td><b>" + tr("Long. cyc") + "</b></td><td>");
232 COMPARE(model
->swashRingData
.elevatorSource
.toString(model
));
233 columns
.append("</td><td>");
234 COMPARE(model
->swashRingData
.elevatorWeight
)
235 columns
.append("</td></tr>");
237 columns
.append("<tr><td><b>" + tr("Lateral cyc") + "</b></td><td>");
238 COMPARE(model
->swashRingData
.aileronSource
.toString(model
));
239 columns
.append("</td><td>");
240 COMPARE(model
->swashRingData
.aileronWeight
)
241 columns
.append("</td></tr>");
244 columns
.append("<tr><td><b>" + tr("Collective") + "</b></td><td>");
245 COMPARE(model
->swashRingData
.collectiveSource
.toString(model
));
246 columns
.append("</td><td>");
247 COMPARE(model
->swashRingData
.collectiveWeight
)
248 columns
.append("</td></tr>");
249 columns
.append("</table>");
253 str
.append(columns
.print());
257 QString
MultiModelPrinter::printFlightModes()
259 QString str
= printTitle(tr("Flight modes"));
263 MultiColumns
columns(models
.size());
264 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
265 columns
.append("<tr>");
266 columns
.append("<td><b>" + tr("Flight mode") + "</b></td>");
267 columns
.append("<td><b>" + tr("Switch") + "</b></td>");
268 columns
.append("<td><b>" + tr("Fade IN") + "</b></td>");
269 columns
.append("<td><b>" + tr("Fade OUT") + "</b></td>");
270 for (int i
=0; i
<4; i
++) {
271 columns
.append("<td><b>" + getCurrentFirmware()->getAnalogInputName(i
) + " trim</b></td>");
273 columns
.append("</tr>");
275 for (int i
=0; i
<firmware
->getCapability(FlightModes
); i
++) {
276 columns
.append("<tr><td><b>" + tr("FM%1").arg(i
) + "</b> ");
277 COMPARE(model
->flightModeData
[i
].name
);
278 columns
.append("</td><td>");
279 COMPARE(model
->flightModeData
[i
].swtch
.toString());
280 columns
.append("</td><td>");
281 COMPARE(model
->flightModeData
[i
].fadeIn
);
282 columns
.append("</td><td>");
283 COMPARE(model
->flightModeData
[i
].fadeOut
);
284 columns
.append("</td>");
285 for (int k
=0; k
<CPN_MAX_STICKS
; k
++) {
286 columns
.append("<td>");
287 COMPARE(modelPrinter
->printTrim(i
, k
));
288 columns
.append("</td>");
290 columns
.append("</tr>");
293 columns
.append("</table>");
294 str
.append(columns
.print());
297 // GVars and Rotary Encoders
298 int gvars
= firmware
->getCapability(Gvars
);
299 if ((gvars
&& firmware
->getCapability(GvarsFlightModes
)) || firmware
->getCapability(RotaryEncoders
)) {
300 MultiColumns
columns(models
.size());
301 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
302 columns
.append("<tr><td><b>" + tr("Flight mode") + "</b></td>");
303 if (firmware
->getCapability(GvarsFlightModes
)) {
304 for (int i
=0; i
<gvars
; i
++) {
305 columns
.append("<td><b>" + tr("GV%1").arg(i
+1) + "</b><br/>");
306 COMPARE(model
->gvars_names
[i
]);
307 columns
.append("</td>");
310 for (int i
=0; i
<firmware
->getCapability(RotaryEncoders
); i
++) {
311 columns
.append("<td><b>" + tr("RE%1").arg(i
+1) + "</b></td>");
313 columns
.append("</tr>");
314 for (int i
=0; i
<firmware
->getCapability(FlightModes
); i
++) {
315 columns
.append("<tr><td><b>" + tr("FM%1").arg(i
) + "</b> ");
316 COMPARE(model
->flightModeData
[i
].name
);
317 columns
.append("</td>");
318 if (firmware
->getCapability(GvarsFlightModes
)) {
319 for (int k
=0; k
<gvars
; k
++) {
320 columns
.append("<td>");
321 COMPARE(modelPrinter
->printGlobalVar(i
, k
));
322 columns
.append("</td>");
325 for (int k
=0; k
<firmware
->getCapability(RotaryEncoders
); k
++) {
326 columns
.append("<td>");
327 COMPARE(modelPrinter
->printRotaryEncoder(i
, k
));
328 columns
.append("</td>");
330 columns
.append("</tr>");
332 columns
.append("</table>");
333 str
.append(columns
.print());
339 QString
MultiModelPrinter::printLimits()
341 QString str
= printTitle(tr("Limits"));
342 MultiColumns
columns(models
.size());
343 columns
.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'>" \
345 " <td><b>" + tr("Channel") + "</b></td>" \
346 " <td><b>" + (firmware
->getCapability(ChannelsName
) > 0 ? tr("Name") : "") + "</b></td>" \
347 " <td><b>" + tr("Offset") + "</b></td>" \
348 " <td><b>" + tr("Min") + "</b></td>" \
349 " <td><b>" + tr("Max") + "</b></td>" \
350 " <td><b>" + tr("Invert") + "</b></td>" \
352 for (int i
=0; i
<firmware
->getCapability(Outputs
); i
++) {
353 columns
.append("<tr><td><b>");
354 COMPARE(modelPrinter
->printChannelName(i
));
355 columns
.append("</b></td><td>");
356 COMPARE(modelPrinter
->printOutputName(i
));
357 columns
.append("</td><td>");
358 COMPARE(model
->limitData
[i
].offsetToString());
359 columns
.append("</td><td>");
360 COMPARE(model
->limitData
[i
].minToString());
361 columns
.append("</td><td>");
362 COMPARE(model
->limitData
[i
].maxToString());
363 columns
.append("</td><td>");
364 COMPARE(model
->limitData
[i
].revertToString());
365 columns
.append("</td></tr>");
367 columns
.append("</table>");
368 str
.append(columns
.print());
372 QString
MultiModelPrinter::printGvars()
374 QString str
= printTitle(tr("Global Variables"));
375 int gvars
= firmware
->getCapability(Gvars
);
376 MultiColumns
columns(models
.size());
377 columns
.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'><tr>");
378 for (int i
=0; i
<gvars
; i
++) {
379 columns
.append(QString("<td><b>") + tr("GV%1").arg(i
+1) + "</b></td>");
381 columns
.append("</tr><tr>");
382 for (int i
=0; i
<gvars
; i
++) {
383 columns
.append("<td>");
384 COMPARE(model
->flightModeData
[0].gvars
[i
]);
385 columns
.append("</td>");
387 columns
.append("</tr>");
388 str
.append(columns
.print());
392 QString
MultiModelPrinter::printInputs()
394 QString str
= printTitle(tr("Inputs"));
395 MultiColumns
columns(models
.size());
396 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
397 for (int i
=0; i
<std::max(4, firmware
->getCapability(VirtualInputs
)); i
++) {
399 for (int k
=0; k
<models
.size(); k
++) {
400 count
= std::max(count
, models
[k
]->expos(i
).size());
403 columns
.append("<tr><td width='20%'><b>");
404 COMPARE(modelPrinter
->printInputName(i
));
405 columns
.append("</b></td><td>");
406 for (int j
=0; j
<count
; j
++) {
408 columns
.append("<br/>");
409 COMPARE(j
<model
->expos(i
).size() ? modelPrinter
->printInputLine(*model
->expos(i
)[j
]) : "");
411 columns
.append("</td></tr>");
414 str
.append(columns
.print());
418 QString
MultiModelPrinter::printMixers()
420 QString str
= printTitle(tr("Mixers"));
421 MultiColumns
columns(models
.size());
422 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
423 for (int i
=0; i
<firmware
->getCapability(Outputs
); i
++) {
425 for (int k
=0; k
<models
.size(); k
++) {
426 count
= std::max(count
, models
[k
]->mixes(i
).size());
429 columns
.append("<tr><td width='20%'><b>");
430 COMPARE(modelPrinter
->printMixerName(i
+1));
431 columns
.append("</b></td><td>");
432 for (int j
=0; j
<count
; j
++) {
434 columns
.append("<br/>");
435 COMPARE((j
< model
->mixes(i
).size()) ? modelPrinter
->printMixerLine(*model
->mixes(i
)[j
], (j
>0)) : " ");
437 columns
.append("</td></tr>");
440 str
.append(columns
.print());
444 QString
MultiModelPrinter::printCurves(QTextDocument
* document
)
447 MultiColumns
columns(models
.size());
449 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
450 for (int i
=0; i
<firmware
->getCapability(NumCurves
); i
++) {
451 bool curveEmpty
= true;
452 for (int k
=0; k
<models
.size(); k
++) {
453 if (!models
[k
]->curves
[i
].isEmpty()) {
460 columns
.append("<tr><td width='20%'><b>" + tr("CV%1").arg(i
+1) + "</b></td><td>");
461 COMPARE(modelPrinter
->printCurve(i
));
462 for (int k
=0; k
<models
.size(); k
++)
463 columns
.append(k
, QString("<br/><img src='%1' border='0' />").arg(modelPrinters
[k
]->createCurveImage(i
, document
)));
464 columns
.append("</td></tr>");
467 columns
.append("</table><br/>");
469 str
.append(printTitle(tr("Curves")));
470 str
.append(columns
.print());
475 QString
MultiModelPrinter::printLogicalSwitches()
478 MultiColumns
columns(models
.size());
480 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
481 for (int i
=0; i
<firmware
->getCapability(LogicalSwitches
); i
++) {
483 for (int k
=0; k
<models
.size(); k
++) {
484 if (!modelPrinters
[k
]->printLogicalSwitchLine(i
).isEmpty()) {
491 columns
.append("<tr><td width='20%'><b>" + tr("L%1").arg(i
+1) + "</b></td><td>");
492 COMPARE(modelPrinter
->printLogicalSwitchLine(i
));
493 columns
.append("</td></tr>");
496 columns
.append("</table>");
498 str
.append(printTitle(tr("Logical Switches")));
499 str
.append(columns
.print());
504 QString
MultiModelPrinter::printCustomFunctions()
507 MultiColumns
columns(models
.size());
509 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
510 for (int i
=0; i
<firmware
->getCapability(CustomFunctions
); i
++) {
512 for (int k
=0; k
<models
.size(); k
++) {
513 if (!modelPrinters
[k
]->printCustomFunctionLine(i
).isEmpty()) {
520 columns
.append("<tr><td width='20%'><b>" + tr("SF%1").arg(i
+1) + "</b></td><td>");
521 COMPARE(modelPrinter
->printCustomFunctionLine(i
));
522 columns
.append("</td></tr>");
525 columns
.append("</table>");
527 str
.append(printTitle(tr("Special Functions")));
528 str
.append(columns
.print());
533 QString
MultiModelPrinter::printTelemetry()
535 QString str
= printTitle(tr("Telemetry Settings"));
537 // Analogs on non ARM boards
538 if (!IS_ARM(firmware
->getBoard())) {
539 MultiColumns
columns(models
.size());
540 columns
.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'>" \
541 "<tr><td width='22%'><b>" + tr("Analogs") + "</b></td><td width='26%'><b>" + tr("Unit") + "</b></td><td width='26%'><b>" + tr("Scale") + "</b></td><td width='26%'><b>" + tr("Offset") + "</b></td></tr>");
542 for (int i
=0; i
<2; i
++) {
543 columns
.append("<tr><td><b>"+tr("A%1").arg(i
+1)+"</b></td><td>");
544 COMPARE(getFrSkyUnits(model
->frsky
.channels
[i
].type
));
545 columns
.append("</td><td>");
546 COMPARE(QString::number((model
->frsky
.channels
[i
].ratio
/ (model
->frsky
.channels
[i
].type
==0 ? 10.0 : 1)), 10, (model
->frsky
.channels
[i
].type
==0 ? 1 : 0)));
547 columns
.append("</td><td>");
548 COMPARE(QString::number((model
->frsky
.channels
[i
].offset
*(model
->frsky
.channels
[i
].ratio
/ (model
->frsky
.channels
[i
].type
==0 ?10.0 : 1)))/255, 10, (model
->frsky
.channels
[i
].type
==0 ? 1 : 0)));
549 columns
.append("</td></tr>");
551 columns
.append("</table><br/>");
552 str
.append(columns
.print());
553 // TODO I remove the analogs alarms for now
558 MultiColumns
columns(models
.size());
559 columns
.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'>");
560 for (int i
=0; i
<2; i
++) {
561 columns
.append("<tr><td><b>" + QString(i
==0 ? tr("RSSI Alarms") : "") + "</b></td><td>");
562 if (IS_HORUS_OR_TARANIS(GetEepromInterface()->getBoard())) {
563 COMPARE(i
==0 ? tr("Low Alarm") : tr("Critical Alarm"));
566 COMPARE(getFrSkyAlarmType(model
->frsky
.rssiAlarms
[i
].level
));
568 columns
.append("</td><td><</td><td>");
569 COMPARE(QString::number(model
->frsky
.rssiAlarms
[i
].value
, 10));
570 columns
.append("</td></tr>");
572 columns
.append("</table><br/>");
573 str
.append(columns
.print());