Compilation fix
[opentx.git] / companion / src / multimodelprinter.cpp
blob864970418b534bb1c5232e9c2916561288e82d60
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
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.
21 #include "helpers.h"
22 #include "helpers_html.h"
23 #include "multimodelprinter.h"
24 #include <algorithm>
26 MultiModelPrinter::MultiColumns::MultiColumns(int count):
27 count(count),
28 compareColumns(NULL)
30 columns = new QString[count];
33 MultiModelPrinter::MultiColumns::~MultiColumns()
35 delete[] columns;
38 void MultiModelPrinter::MultiColumns::append(const QString & str)
40 for (int i=0; i<count; i++) {
41 append(i, str);
45 void MultiModelPrinter::MultiColumns::appendTitle(const QString & name)
47 append("<b>" + name + "</b>&nbsp;");
50 void MultiModelPrinter::MultiColumns::append(int idx, const QString & str)
52 if (compareColumns)
53 compareColumns[idx].append(str);
54 else
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])
68 cellColor = "green";
69 else if (i>0 && compareColumns[i]!=compareColumns[0])
70 cellColor = "red";
71 columns[i].append(QString("<font color='%1'>%2</font>").arg(cellColor).arg(compareColumns[i]));
73 delete[] compareColumns;
74 compareColumns = NULL;
77 template <class T>
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>");
90 return result;
93 bool MultiModelPrinter::MultiColumns::isEmpty()
95 for (int i=0; i<count; i++) {
96 if (!columns[i].isEmpty())
97 return false;
99 return true;
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):
118 firmware(firmware)
122 MultiModelPrinter::~MultiModelPrinter()
124 clearModels();
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;'>";
158 str += printSetup();
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))
168 str += printGvars();
169 str += printLogicalSwitches();
170 str += printCustomFunctions();
171 str += printTelemetry();
172 str += "</table>";
173 return str;
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());
211 return str;
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;
222 if (!heliEnabled)
223 return "";
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());
262 return str;
265 QString MultiModelPrinter::printFlightModes()
267 QString str = printTitle(tr("Flight modes"));
269 // Trims
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>&nbsp;");
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("Flight mode") + "</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><br/>");
314 COMPARE(model->gvars_names[i]);
315 columns.append("</td>");
318 for (int i=0; i<firmware->getCapability(RotaryEncoders); i++) {
319 columns.append("<td><b>" + tr("RE%1").arg(i+1) + "</b></td>");
321 columns.append("</tr>");
322 for (int i=0; i<firmware->getCapability(FlightModes); i++) {
323 columns.append("<tr><td><b>" + tr("FM%1").arg(i) + "</b>&nbsp;");
324 COMPARE(model->flightModeData[i].name);
325 columns.append("</td>");
326 if (firmware->getCapability(GvarsFlightModes)) {
327 for (int k=0; k<gvars; k++) {
328 columns.append("<td>");
329 COMPARE(modelPrinter->printGlobalVar(i, k));
330 columns.append("</td>");
333 for (int k=0; k<firmware->getCapability(RotaryEncoders); k++) {
334 columns.append("<td>");
335 COMPARE(modelPrinter->printRotaryEncoder(i, k));
336 columns.append("</td>");
338 columns.append("</tr>");
340 columns.append("</table>");
341 str.append(columns.print());
344 return str;
347 QString MultiModelPrinter::printLimits()
349 QString str = printTitle(tr("Limits"));
350 MultiColumns columns(modelPrinterMap.size());
351 columns.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'>" \
352 "<tr>" \
353 " <td><b>" + tr("Channel") + "</b></td>" \
354 " <td><b>" + tr("Offset") + "</b></td>" \
355 " <td><b>" + tr("Min") + "</b></td>" \
356 " <td><b>" + tr("Max") + "</b></td>" \
357 " <td><b>" + tr("Invert") + "</b></td>" \
358 "</tr>");
359 for (int i=0; i<firmware->getCapability(Outputs); i++) {
360 int count = 0;
361 for (int k=0; k < modelPrinterMap.size(); k++)
362 count = std::max(count, modelPrinterMap.value(k).first->mixes(i).size());
363 if (!count)
364 continue;
365 columns.append("<tr><td><b>");
366 COMPARE(modelPrinter->printChannelName(i));
367 columns.append("</td><td>");
368 COMPARE(model->limitData[i].offsetToString());
369 columns.append("</td><td>");
370 COMPARE(model->limitData[i].minToString());
371 columns.append("</td><td>");
372 COMPARE(model->limitData[i].maxToString());
373 columns.append("</td><td>");
374 COMPARE(model->limitData[i].revertToString());
375 columns.append("</td></tr>");
378 columns.append("</table>");
379 str.append(columns.print());
380 return str;
383 QString MultiModelPrinter::printGvars()
385 QString str = printTitle(tr("Global Variables"));
386 int gvars = firmware->getCapability(Gvars);
387 MultiColumns columns(modelPrinterMap.size());
388 columns.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'><tr>");
389 for (int i=0; i<gvars; i++) {
390 columns.append(QString("<td><b>") + tr("GV%1").arg(i+1) + "</b></td>");
392 columns.append("</tr><tr>");
393 for (int i=0; i<gvars; i++) {
394 columns.append("<td>");
395 COMPARE(model->flightModeData[0].gvars[i]);
396 columns.append("</td>");
398 columns.append("</tr>");
399 str.append(columns.print());
400 return str;
403 QString MultiModelPrinter::printInputs()
405 QString str = printTitle(tr("Inputs"));
406 MultiColumns columns(modelPrinterMap.size());
407 columns.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
408 for (int i=0; i<std::max(4, firmware->getCapability(VirtualInputs)); i++) {
409 int count = 0;
410 for (int k=0; k < modelPrinterMap.size(); k++) {
411 count = std::max(count, modelPrinterMap.value(k).first->expos(i).size());
413 if (count > 0) {
414 columns.append("<tr><td width='20%'><b>");
415 COMPARE(modelPrinter->printInputName(i));
416 columns.append("</b></td><td>");
417 for (int j=0; j<count; j++) {
418 if (j > 0)
419 columns.append("<br/>");
420 COMPARE(j<model->expos(i).size() ? modelPrinter->printInputLine(*model->expos(i)[j]) : "");
422 columns.append("</td></tr>");
425 str.append(columns.print());
426 return str;
429 QString MultiModelPrinter::printMixers()
431 QString str = printTitle(tr("Mixers"));
432 MultiColumns columns(modelPrinterMap.size());
433 columns.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
434 for (int i=0; i<firmware->getCapability(Outputs); i++) {
435 int count = 0;
436 for (int k=0; k < modelPrinterMap.size(); k++) {
437 count = std::max(count, modelPrinterMap.value(k).first->mixes(i).size());
439 if (count > 0) {
440 columns.append("<tr><td width='20%'><b>");
441 COMPARE(modelPrinter->printChannelName(i));
442 columns.append("</b></td><td>");
443 for (int j=0; j<count; j++) {
444 if (j > 0)
445 columns.append("<br/>");
446 COMPARE((j < model->mixes(i).size()) ? modelPrinter->printMixerLine(*model->mixes(i)[j], (j>0)) : "&nbsp;");
448 columns.append("</td></tr>");
451 str.append(columns.print());
452 return str;
455 QString MultiModelPrinter::printCurves(QTextDocument * document)
457 QString str;
458 MultiColumns columns(modelPrinterMap.size());
459 int count = 0;
460 columns.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
461 for (int i=0; i<firmware->getCapability(NumCurves); i++) {
462 bool curveEmpty = true;
463 for (int k=0; k < modelPrinterMap.size(); k++) {
464 if (!modelPrinterMap.value(k).first->curves[i].isEmpty()) {
465 curveEmpty = false;
466 break;
469 if (!curveEmpty) {
470 count++;
471 columns.append("<tr><td width='20%'><b>");
472 COMPARE(modelPrinter->printCurveName(i));
473 columns.append("</b></td><td>");
474 COMPARE(modelPrinter->printCurve(i));
475 for (int k=0; k < modelPrinterMap.size(); k++)
476 columns.append(k, QString("<br/><img src='%1' border='0' />").arg(modelPrinterMap.value(k).second->createCurveImage(i, document)));
477 columns.append("</td></tr>");
480 columns.append("</table><br/>");
481 if (count > 0) {
482 str.append(printTitle(tr("Curves")));
483 str.append(columns.print());
485 return str;
488 QString MultiModelPrinter::printLogicalSwitches()
490 QString str;
491 MultiColumns columns(modelPrinterMap.size());
492 int count = 0;
493 columns.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
494 for (int i=0; i<firmware->getCapability(LogicalSwitches); i++) {
495 bool lsEmpty = true;
496 for (int k=0; k < modelPrinterMap.size(); k++) {
497 if (!modelPrinterMap.value(k).first->logicalSw[i].isEmpty()) {
498 lsEmpty = false;
499 break;
502 if (!lsEmpty) {
503 count++;
504 columns.append("<tr><td width='20%'><b>" + tr("L%1").arg(i+1) + "</b></td><td>");
505 COMPARE(modelPrinter->printLogicalSwitchLine(i));
506 columns.append("</td></tr>");
509 columns.append("</table>");
510 if (count > 0) {
511 str.append(printTitle(tr("Logical Switches")));
512 str.append(columns.print());
514 return str;
517 QString MultiModelPrinter::printCustomFunctions()
519 QString str;
520 MultiColumns columns(modelPrinterMap.size());
521 int count = 0;
522 columns.append("<table cellspacing='0' cellpadding='1' width='100%' border='0' style='border-collapse:collapse'>");
523 for (int i=0; i < firmware->getCapability(CustomFunctions); i++) {
524 bool sfEmpty = true;
525 for (int k=0; k < modelPrinterMap.size(); k++) {
526 if (!modelPrinterMap.value(k).first->customFn[i].isEmpty()) {
527 sfEmpty = false;
528 break;
531 if (!sfEmpty) {
532 count++;
533 columns.append("<tr><td width='20%'><b>" + tr("SF%1").arg(i+1) + "</b></td><td>");
534 COMPARE(modelPrinter->printCustomFunctionLine(i));
535 columns.append("</td></tr>");
538 columns.append("</table>");
539 if (count > 0) {
540 str.append(printTitle(tr("Special Functions")));
541 str.append(columns.print());
543 return str;
546 QString MultiModelPrinter::printTelemetry()
548 QString str = printTitle(tr("Telemetry Settings"));
550 // Analogs on non ARM boards
551 if (!IS_ARM(firmware->getBoard())) {
552 MultiColumns columns(modelPrinterMap.size());
553 columns.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'>" \
554 "<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>");
555 for (int i=0; i<2; i++) {
556 columns.append("<tr><td><b>"+tr("A%1").arg(i+1)+"</b></td><td>");
557 COMPARE(getFrSkyUnits(model->frsky.channels[i].type));
558 columns.append("</td><td>");
559 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)));
560 columns.append("</td><td>");
561 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)));
562 columns.append("</td></tr>");
564 columns.append("</table><br/>");
565 str.append(columns.print());
566 // TODO I remove the analogs alarms for now
569 // RSSI alarms
571 MultiColumns columns(modelPrinterMap.size());
572 columns.append("<table border='0' cellspacing='0' cellpadding='1' width='100%'>");
573 for (int i=0; i<2; i++) {
574 columns.append("<tr><td><b>" + QString(i==0 ? tr("RSSI Alarms") : "") + "</b></td><td>");
575 if (IS_ARM(getCurrentBoard())) {
576 COMPARE(i==0 ? tr("Low Alarm") : tr("Critical Alarm"));
578 else {
579 COMPARE(getFrSkyAlarmType(model->rssiAlarms.level[i]));
581 columns.append("</td><td>&lt;</td><td>");
582 if (i == 0) {
583 COMPARE(QString::number(model->rssiAlarms.warning, 10));
585 else {
586 COMPARE(QString::number(model->rssiAlarms.critical, 10));
588 columns.append("</td></tr>");
590 columns.append("</table><br/>");
591 str.append(columns.print());
594 return str;