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 < modelPrinterMap.size(); cc++) { \
105 ModelPrinter * modelPrinter = modelPrinterMap.value(cc).second; \
106 const ModelData * model = modelPrinterMap.value(cc).first; \
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(modelPrinterMap
.count()) + label
+ "</h2></td></tr>";
117 MultiModelPrinter::MultiModelPrinter(Firmware
* firmware
):
122 MultiModelPrinter::~MultiModelPrinter()
127 void MultiModelPrinter::setModel(int idx
, const ModelData
* model
, const GeneralSettings
* generalSettings
)
129 if (modelPrinterMap
.contains(idx
) && modelPrinterMap
.value(idx
).second
) {
130 // free existing model printer
131 delete modelPrinterMap
.value(idx
).second
;
132 modelPrinterMap
[idx
].second
= NULL
;
135 QPair
<const ModelData
*, ModelPrinter
*> pair(model
, new ModelPrinter(firmware
, *generalSettings
, *model
));
136 modelPrinterMap
.insert(idx
, pair
); // QMap.insert will replace any existing key
139 void MultiModelPrinter::setModel(int idx
, const ModelData
* model
)
141 setModel(idx
, model
, &defaultSettings
);
144 void MultiModelPrinter::clearModels()
146 for(int i
=0; i
< modelPrinterMap
.size(); i
++) {
147 if (modelPrinterMap
.value(i
).second
)
148 delete modelPrinterMap
.value(i
).second
;
150 modelPrinterMap
.clear();
153 QString
MultiModelPrinter::print(QTextDocument
* document
)
155 if (document
) document
->clear();
157 QString str
= "<table border='1' cellspacing='0' cellpadding='3' width='100%' style='font-family: monospace;'>";
159 if (firmware
->getCapability(Heli
))
160 str
+= printHeliSetup();
161 if (firmware
->getCapability(FlightModes
))
162 str
+= printFlightModes();
163 str
+= printInputs();
164 str
+= printMixers();
165 str
+= printLimits();
166 str
+= printCurves(document
);
167 if (firmware
->getCapability(Gvars
) && !firmware
->getCapability(GvarsFlightModes
))
169 str
+= printLogicalSwitches();
170 str
+= printCustomFunctions();
171 str
+= printTelemetry();
176 QString
MultiModelPrinter::printSetup()
178 QString str
= printTitle(tr("General Model Settings"));
180 MultiColumns
columns(modelPrinterMap
.size());
181 columns
.appendTitle(tr("Name:"));
182 COMPARE(model
->name
);
183 columns
.append("<br/>");
184 columns
.appendTitle(tr("EEprom Size:"));
185 COMPARE(modelPrinter
->printEEpromSize());
186 columns
.append("<br/>");
187 for (int i
=0; i
<firmware
->getCapability(Timers
); i
++) {
188 columns
.appendTitle(tr("Timer%1:").arg(i
+1));
189 COMPARE(modelPrinter
->printTimer(i
));
190 columns
.append("<br/>");
192 for (int i
=0; i
<firmware
->getCapability(NumModules
); i
++) {
193 columns
.appendTitle(firmware
->getCapability(NumModules
) > 1 ? tr("Module%1:").arg(i
+1) : tr("Module:"));
194 COMPARE(modelPrinter
->printModule(i
));
195 columns
.append("<br/>");
197 if (IS_HORUS_OR_TARANIS(firmware
->getBoard())) {
198 columns
.appendTitle(tr("Trainer port:"));
199 COMPARE(modelPrinter
->printTrainerMode());
200 columns
.append("<br/>");
202 columns
.appendTitle(tr("Throttle Trim:"));
203 COMPARE(modelPrinter
->printThrottleTrimMode());
204 columns
.append("<br/>");
205 columns
.appendTitle(tr("Trim Increment:"));
206 COMPARE(modelPrinter
->printTrimIncrementMode());
207 columns
.append("<br/>");
208 columns
.appendTitle(tr("Center Beep:"));
209 COMPARE(modelPrinter
->printCenterBeep());
210 str
.append(columns
.print());
215 QString
MultiModelPrinter::printHeliSetup()
217 bool heliEnabled
= false;
218 for (int k
=0; k
< modelPrinterMap
.size(); k
++) {
219 heliEnabled
= heliEnabled
|| modelPrinterMap
.value(k
).first
->swashRingData
.type
!= HELI_SWASH_TYPE_NONE
;
225 QString str
= printTitle(tr("Helicopter Setup"));
226 MultiColumns
columns(modelPrinterMap
.size());
227 columns
.appendTitle (tr("Swash Type:"));
228 COMPARE(modelPrinter
->printHeliSwashType());
229 columns
.append ("<br/>");
231 columns
.appendTitle (tr("Swash Ring:"));
232 COMPARE(model
->swashRingData
.value
);
234 columns
.append ("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
235 columns
.append("<tr>");
236 columns
.append("<td></td><td><b>" + tr("Input") + "</b></td><td><b>" + tr("Weight") + "</b></td>");
237 columns
.append("</tr>");
239 columns
.append("<tr><td><b>" + tr("Long. cyc") + "</b></td><td>");
240 COMPARE(model
->swashRingData
.elevatorSource
.toString(model
));
241 columns
.append("</td><td>");
242 COMPARE(model
->swashRingData
.elevatorWeight
)
243 columns
.append("</td></tr>");
245 columns
.append("<tr><td><b>" + tr("Lateral cyc") + "</b></td><td>");
246 COMPARE(model
->swashRingData
.aileronSource
.toString(model
));
247 columns
.append("</td><td>");
248 COMPARE(model
->swashRingData
.aileronWeight
)
249 columns
.append("</td></tr>");
252 columns
.append("<tr><td><b>" + tr("Collective") + "</b></td><td>");
253 COMPARE(model
->swashRingData
.collectiveSource
.toString(model
));
254 columns
.append("</td><td>");
255 COMPARE(model
->swashRingData
.collectiveWeight
)
256 columns
.append("</td></tr>");
257 columns
.append("</table>");
261 str
.append(columns
.print());
265 QString
MultiModelPrinter::printFlightModes()
267 QString str
= printTitle(tr("Flight modes"));
271 MultiColumns
columns(modelPrinterMap
.size());
272 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
273 columns
.append("<tr>");
274 columns
.append("<td><b>" + tr("Flight mode") + "</b></td>");
275 columns
.append("<td><b>" + tr("Switch") + "</b></td>");
276 columns
.append("<td><b>" + tr("Fade IN") + "</b></td>");
277 columns
.append("<td><b>" + tr("Fade OUT") + "</b></td>");
278 for (int i
=0; i
< getBoardCapability(getCurrentBoard(), Board::NumTrims
); i
++) {
279 columns
.append("<td><b>" + RawSource(SOURCE_TYPE_TRIM
, i
).toString() + "</b></td>");
281 columns
.append("</tr>");
283 for (int i
=0; i
<firmware
->getCapability(FlightModes
); i
++) {
284 columns
.append("<tr><td><b>" + tr("FM%1").arg(i
) + "</b> ");
285 COMPARE(model
->flightModeData
[i
].name
);
286 columns
.append("</td><td>");
287 COMPARE(modelPrinter
->printFlightModeSwitch(model
->flightModeData
[i
].swtch
));
288 columns
.append("</td><td>");
289 COMPARE(model
->flightModeData
[i
].fadeIn
);
290 columns
.append("</td><td>");
291 COMPARE(model
->flightModeData
[i
].fadeOut
);
292 columns
.append("</td>");
293 for (int k
=0; k
< getBoardCapability(getCurrentBoard(), Board::NumTrims
); k
++) {
294 columns
.append("<td>");
295 COMPARE(modelPrinter
->printTrim(i
, k
));
296 columns
.append("</td>");
298 columns
.append("</tr>");
301 columns
.append("</table>");
302 str
.append(columns
.print());
305 // GVars and Rotary Encoders
306 int gvars
= firmware
->getCapability(Gvars
);
307 if ((gvars
&& firmware
->getCapability(GvarsFlightModes
)) || firmware
->getCapability(RotaryEncoders
)) {
308 MultiColumns
columns(modelPrinterMap
.size());
309 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
310 columns
.append("<tr><td><b>" + tr("Global variables") + "</b></td>");
311 if (firmware
->getCapability(GvarsFlightModes
)) {
312 for (int i
=0; i
<gvars
; i
++) {
313 columns
.append("<td><b>" + tr("GV%1").arg(i
+1) + "</b></td>");
316 for (int i
=0; i
<firmware
->getCapability(RotaryEncoders
); i
++) {
317 columns
.append("<td><b>" + tr("RE%1").arg(i
+1) + "</b></td>");
319 columns
.append("</tr>");
321 if (firmware
->getCapability(GvarsFlightModes
)) {
322 columns
.append("<tr><td><b>Name</b></td>");
323 for (int i
=0; i
<gvars
; i
++) {
324 columns
.append("<td>");
325 COMPARE(model
->gvarData
[i
].name
);
326 columns
.append("</td>");
328 columns
.append("</tr>");
329 columns
.append("<tr><td><b>Unit</b></td>");
330 for (int i
=0; i
<gvars
; i
++) {
331 columns
.append("<td>");
332 COMPARE(modelPrinter
->printGlobalVarUnit(i
));
333 columns
.append("</td>");
335 columns
.append("</tr>");
336 columns
.append("<tr><td><b>Prec</b></td>");
337 for (int i
=0; i
<gvars
; i
++) {
338 columns
.append("<td>");
339 COMPARE(modelPrinter
->printGlobalVarPrec(i
));
340 columns
.append("</td>");
342 columns
.append("</tr>");
343 columns
.append("<tr><td><b>Min</b></td>");
344 for (int i
=0; i
<gvars
; i
++) {
345 columns
.append("<td>");
346 COMPARE(modelPrinter
->printGlobalVarMin(i
));
347 columns
.append("</td>");
349 columns
.append("</tr>");
350 columns
.append("<tr><td><b>Max</b></td>");
351 for (int i
=0; i
<gvars
; i
++) {
352 columns
.append("<td>");
353 COMPARE(modelPrinter
->printGlobalVarMax(i
));
354 columns
.append("</td>");
356 columns
.append("</tr>");
357 columns
.append("<tr><td><b>Popup</b></td>");
358 for (int i
=0; i
<gvars
; i
++) {
359 columns
.append("<td>");
360 COMPARE(modelPrinter
->printGlobalVarPopup(i
));
361 columns
.append("</td>");
363 columns
.append("</tr>");
366 columns
.append("<tr><td><b>" + tr("Flight mode") + "</b></td></tr>");
368 for (int i
=0; i
<firmware
->getCapability(FlightModes
); i
++) {
369 columns
.append("<tr><td><b>" + tr("FM%1").arg(i
) + "</b> ");
370 COMPARE(model
->flightModeData
[i
].name
);
371 columns
.append("</td>");
372 if (firmware
->getCapability(GvarsFlightModes
)) {
373 for (int k
=0; k
<gvars
; k
++) {
374 columns
.append("<td>");
375 COMPARE(modelPrinter
->printGlobalVar(i
, k
));
376 columns
.append("</td>");
379 for (int k
=0; k
<firmware
->getCapability(RotaryEncoders
); k
++) {
380 columns
.append("<td>");
381 COMPARE(modelPrinter
->printRotaryEncoder(i
, k
));
382 columns
.append("</td>");
384 columns
.append("</tr>");
386 columns
.append("</table>");
387 str
.append(columns
.print());
393 QString
MultiModelPrinter::printLimits()
395 QString str
= printTitle(tr("Limits"));
396 MultiColumns
columns(modelPrinterMap
.size());
397 columns
.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'>" \
399 " <td><b>" + tr("Channel") + "</b></td>" \
400 " <td><b>" + tr("Offset") + "</b></td>" \
401 " <td><b>" + tr("Min") + "</b></td>" \
402 " <td><b>" + tr("Max") + "</b></td>" \
403 " <td><b>" + tr("Invert") + "</b></td>" \
405 for (int i
=0; i
<firmware
->getCapability(Outputs
); i
++) {
407 for (int k
=0; k
< modelPrinterMap
.size(); k
++)
408 count
= std::max(count
, modelPrinterMap
.value(k
).first
->mixes(i
).size());
411 columns
.append("<tr><td><b>");
412 COMPARE(modelPrinter
->printChannelName(i
));
413 columns
.append("</td><td>");
414 COMPARE(model
->limitData
[i
].offsetToString());
415 columns
.append("</td><td>");
416 COMPARE(model
->limitData
[i
].minToString());
417 columns
.append("</td><td>");
418 COMPARE(model
->limitData
[i
].maxToString());
419 columns
.append("</td><td>");
420 COMPARE(model
->limitData
[i
].revertToString());
421 columns
.append("</td></tr>");
424 columns
.append("</table>");
425 str
.append(columns
.print());
429 QString
MultiModelPrinter::printGvars()
431 QString str
= printTitle(tr("Global Variables"));
432 int gvars
= firmware
->getCapability(Gvars
);
433 MultiColumns
columns(modelPrinterMap
.size());
434 columns
.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'><tr>");
435 for (int i
=0; i
<gvars
; i
++) {
436 columns
.append(QString("<td><b>") + tr("GV%1").arg(i
+1) + "</b></td>");
438 columns
.append("</tr><tr>");
439 for (int i
=0; i
<gvars
; i
++) {
440 columns
.append("<td>");
441 COMPARE(model
->flightModeData
[0].gvars
[i
]);
442 columns
.append("</td>");
444 columns
.append("</tr>");
445 str
.append(columns
.print());
449 QString
MultiModelPrinter::printInputs()
451 QString str
= printTitle(tr("Inputs"));
452 MultiColumns
columns(modelPrinterMap
.size());
453 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
454 for (int i
=0; i
<std::max(4, firmware
->getCapability(VirtualInputs
)); i
++) {
456 for (int k
=0; k
< modelPrinterMap
.size(); k
++) {
457 count
= std::max(count
, modelPrinterMap
.value(k
).first
->expos(i
).size());
460 columns
.append("<tr><td width='20%'><b>");
461 COMPARE(modelPrinter
->printInputName(i
));
462 columns
.append("</b></td><td>");
463 for (int j
=0; j
<count
; j
++) {
465 columns
.append("<br/>");
466 COMPARE(j
<model
->expos(i
).size() ? modelPrinter
->printInputLine(*model
->expos(i
)[j
]) : "");
468 columns
.append("</td></tr>");
471 str
.append(columns
.print());
475 QString
MultiModelPrinter::printMixers()
477 QString str
= printTitle(tr("Mixers"));
478 MultiColumns
columns(modelPrinterMap
.size());
479 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
480 for (int i
=0; i
<firmware
->getCapability(Outputs
); i
++) {
482 for (int k
=0; k
< modelPrinterMap
.size(); k
++) {
483 count
= std::max(count
, modelPrinterMap
.value(k
).first
->mixes(i
).size());
486 columns
.append("<tr><td width='20%'><b>");
487 COMPARE(modelPrinter
->printChannelName(i
));
488 columns
.append("</b></td><td>");
489 for (int j
=0; j
<count
; j
++) {
491 columns
.append("<br/>");
492 COMPARE((j
< model
->mixes(i
).size()) ? modelPrinter
->printMixerLine(*model
->mixes(i
)[j
], (j
>0)) : " ");
494 columns
.append("</td></tr>");
497 str
.append(columns
.print());
501 QString
MultiModelPrinter::printCurves(QTextDocument
* document
)
504 MultiColumns
columns(modelPrinterMap
.size());
506 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
507 for (int i
=0; i
<firmware
->getCapability(NumCurves
); i
++) {
508 bool curveEmpty
= true;
509 for (int k
=0; k
< modelPrinterMap
.size(); k
++) {
510 if (!modelPrinterMap
.value(k
).first
->curves
[i
].isEmpty()) {
517 columns
.append("<tr><td width='20%'><b>");
518 COMPARE(modelPrinter
->printCurveName(i
));
519 columns
.append("</b></td><td>");
520 COMPARE(modelPrinter
->printCurve(i
));
521 for (int k
=0; k
< modelPrinterMap
.size(); k
++)
522 columns
.append(k
, QString("<br/><img src='%1' border='0' />").arg(modelPrinterMap
.value(k
).second
->createCurveImage(i
, document
)));
523 columns
.append("</td></tr>");
526 columns
.append("</table><br/>");
528 str
.append(printTitle(tr("Curves")));
529 str
.append(columns
.print());
534 QString
MultiModelPrinter::printLogicalSwitches()
537 MultiColumns
columns(modelPrinterMap
.size());
539 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
540 for (int i
=0; i
<firmware
->getCapability(LogicalSwitches
); i
++) {
542 for (int k
=0; k
< modelPrinterMap
.size(); k
++) {
543 if (!modelPrinterMap
.value(k
).first
->logicalSw
[i
].isEmpty()) {
550 columns
.append("<tr><td width='20%'><b>" + tr("L%1").arg(i
+1) + "</b></td><td>");
551 COMPARE(modelPrinter
->printLogicalSwitchLine(i
));
552 columns
.append("</td></tr>");
555 columns
.append("</table>");
557 str
.append(printTitle(tr("Logical Switches")));
558 str
.append(columns
.print());
563 QString
MultiModelPrinter::printCustomFunctions()
566 MultiColumns
columns(modelPrinterMap
.size());
568 columns
.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
569 for (int i
=0; i
< firmware
->getCapability(CustomFunctions
); i
++) {
571 for (int k
=0; k
< modelPrinterMap
.size(); k
++) {
572 if (!modelPrinterMap
.value(k
).first
->customFn
[i
].isEmpty()) {
579 columns
.append("<tr><td width='20%'><b>" + tr("SF%1").arg(i
+1) + "</b></td><td>");
580 COMPARE(modelPrinter
->printCustomFunctionLine(i
));
581 columns
.append("</td></tr>");
584 columns
.append("</table>");
586 str
.append(printTitle(tr("Special Functions")));
587 str
.append(columns
.print());
592 QString
MultiModelPrinter::printTelemetry()
594 QString str
= printTitle(tr("Telemetry Settings"));
596 // Analogs on non ARM boards
597 if (!IS_ARM(firmware
->getBoard())) {
598 MultiColumns
columns(modelPrinterMap
.size());
599 columns
.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'>" \
600 "<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>");
601 for (int i
=0; i
<2; i
++) {
602 columns
.append("<tr><td><b>"+tr("A%1").arg(i
+1)+"</b></td><td>");
603 COMPARE(getFrSkyUnits(model
->frsky
.channels
[i
].type
));
604 columns
.append("</td><td>");
605 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)));
606 columns
.append("</td><td>");
607 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)));
608 columns
.append("</td></tr>");
610 columns
.append("</table><br/>");
611 str
.append(columns
.print());
612 // TODO I remove the analogs alarms for now
617 MultiColumns
columns(modelPrinterMap
.size());
618 columns
.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'>");
619 for (int i
=0; i
<2; i
++) {
620 columns
.append("<tr><td><b>" + QString(i
==0 ? tr("RSSI Alarms") : "") + "</b></td><td>");
621 if (IS_ARM(getCurrentBoard())) {
622 COMPARE(i
==0 ? tr("Low Alarm") : tr("Critical Alarm"));
625 COMPARE(getFrSkyAlarmType(model
->rssiAlarms
.level
[i
]));
627 columns
.append("</td><td><</td><td>");
629 COMPARE(QString::number(model
->rssiAlarms
.warning
, 10));
632 COMPARE(QString::number(model
->rssiAlarms
.critical
, 10));
634 columns
.append("</td></tr>");
636 columns
.append("</table><br/>");
637 str
.append(columns
.print());