X-Git-Url: https://rtime.felk.cvut.cz/gitweb/hydro.git/blobdiff_plain/1af32f4239d82371f7f42e68d62ec8c96952bd12..d11f406f40c86b7139dccf68bbade44e51e2c8d0:/app-bohyn/src/html_layer.cc diff --git a/app-bohyn/src/html_layer.cc b/app-bohyn/src/html_layer.cc new file mode 100644 index 0000000..3fb3c7b --- /dev/null +++ b/app-bohyn/src/html_layer.cc @@ -0,0 +1,520 @@ + +#include "html_layer.h" + +extern char** environ; + +using namespace std; + +void HtmlLayer::_print(const QString &msg) { + if (this->buffering) { + this->buffer.append(msg); + } else { + QByteArray ba = msg.toUtf8(); + printf("%s", ba.constData()); + } +} + +HtmlLayer::HtmlLayer(HttpRequest &request) { + this->addCrumb(documentRoot(), "Home"); + this->buffering = false; + this->request = request; +} + +/* buffer funkce */ +void HtmlLayer::startBuffering() { + this->buffering = true; + this->buffer = ""; +} + +void HtmlLayer::stopBuffering() { this->buffering = false; } + +void HtmlLayer::printBuffer() const { + QByteArray ba = this->buffer.toUtf8(); + printf("%s", ba.constData()); +} + +/* drobky */ +void HtmlLayer::addCrumb(QString url, QString link) { + QStringList crumb; + crumb.append(url); + crumb.append(link); + crumbs.append(crumb); +} + +void HtmlLayer::printCrumbs() { + CrumbsIterator it(crumbs); + QByteArray url; + QByteArray link; + _print("
"); + while (it.hasNext()) { + QStringList crumb = it.next(); + _print(QString("%2").arg(crumb[0]).arg(crumb[1])); + if (it.hasNext()) _print(" >> "); + } + _print("
\n"); +} + +/* zakladni html funkce */ +QString HtmlLayer::documentRoot() const { + return QString(getenv("DOCUMENT_ROOT")); +} + +void HtmlLayer::printHTTPHeaders() { + _print("Status: 200 OK\r\n"); + _print("Pragma: no-cache\r\n"); + _print("Content-Type: text/html; charset=UTF-8\r\n\r\n"); +} + +void HtmlLayer::printHTTPRefreshHeaders(int seconds) { + _print("Status: 200 OK\r\n"); + _print(QString("Refresh: %1\r\n").arg(seconds)); + _print("Pragma: no-cache\r\n"); + _print("Content-Type: text/html; charset=UTF-8\r\n\r\n"); +} + +void HtmlLayer::printHTTPRedirectHeaders(QString location) { + _print("Status: 302 Found\r\n"); + _print(QString("Location: %1\r\n").arg(location)); + _print("Connection: close\r\n\r\n"); +} + +void HtmlLayer::printHTTP404Headers() { + _print("Status: 404 Not Found\r\n"); + _print("Pragma: no-cache\r\n"); + _print("Content-Type: text/html; charset=UTF-8\r\n\r\n"); +} + +void HtmlLayer::print404Error() { + _print("\r\n\t\r\n\t\t404 - Not Found\r\n\t\r\n\t\t

404 - Not Found

\r\n\t\r\n\r\n"); +} + +void HtmlLayer::printDocumentStart() { + _print( + "\r\n" + "\r\n" + "\n" + "\t\r\n" + "\t\r\n" + "\t\r\n" + "\t\r\n" + "\tHydroponie FastCGI echo\r\n" + "\n" + "
\r\n\t

Hydroponie web interface

\r\n" + ); + _print(QString("Správa konfigurace").arg(documentRoot())); +} + +void HtmlLayer::printDocumentEnd() { + _print("
\r\n"); +} + +void HtmlLayer::printContentStart() { + _print("
\n"); +} + +void HtmlLayer::printContentEnd() { + _print("
\n"); +} + +void HtmlLayer::printEnvVars() { + int i; + _print("
\n" + "
\n"); + for(i = 0; environ[i] != NULL; i++) + _print(QString("%1
\n").arg(environ[i])); + _print("
\n"); +} + +/* funkce pro prezentaci nodu */ +void HtmlLayer::home(NodeMap &nodes, ValueCache &valueCache) { + _print(QString("
Vyberte zařízení...
\n")); + + QString filename("common_graph.png"); + GraphPainter gp(QSize(VAR_IMG_WIDTH, VAR_IMG_HEIGHT), VAR_IMG_BORDER_TOP, VAR_IMG_BORDER_RIGHT, VAR_IMG_BORDER_BOTTOM, VAR_IMG_BORDER_LEFT); + NodeMapIterator itm(nodes); + while (itm.hasNext()) { + itm.next(); + NodeVarIterator itv = itm.value()->getIterator(); + while (itv.hasNext()) { + itv.next(); + if (itv.value().getCommonGraphDisplay()) { + GraphData gdata = valueCache.getValues(itv.value().getNodeSN(), itv.value().getId(), HISTORY_LEN).first; + gp.addGraph(Graph(gdata, itv.value().getGraphColor(), Qt::SolidLine, false)); + } + } + } + QDateTime current_datetime = currentDateTime(); + gp.drawGraphs(current_datetime.toTime_t() - HISTORY_LEN, current_datetime.toTime_t(), getTimeLabels(HISTORY_LEN)); + gp.saveGraphs(QString(VAR_IMG_FILE_PATH).append(filename)); + + _print(QString("\n").arg(filename)); + +} + +void HtmlLayer::printNodes(NodeMap &nodes) { + QByteArray ba; + NodeMapKeysIterator it(nodes.keys()); + _print("
Dostupná zařízení
\n"); +} + +void HtmlLayer::printNodesConfig(NodeMap &nodes) { + Form conf_form; + conf_form.addField("config_file", new FileField("Konfigurace", true)); + + if (request.method() == "POST") { + conf_form.bindFiles(request.FILES()); + + if (conf_form.isValid()) { + bool parseErr = false; + UploadedFile config_file = conf_form.getCleanedDataRef()["config_file"].value(); + if (!config_file.isNull()) { + QDomDocument config; + if (config.setContent(config_file.contentRef())) { + QDomElement confElem = config.documentElement(); + if (confElem.tagName() != "configuration") { + parseErr = true; + } else { + QDomElement nodesElem = confElem.firstChildElement("nodes"); + if (!nodesElem.isNull()) + nodes.setXMLConfig(nodesElem); + } + } + } else { + parseErr = true; + } + + if (parseErr) { + conf_form.setError("config_file", "Chyba při parsování konfiguračního souboru"); + } else { + throw HttpRedirect("./"); + } + + } + } + + _print("
Nahrání konfigurace
"); + _print("
\n"); + _print(conf_form.renderField("config_file", "\n")); + _print("\t\n
%1%2 %3
\n"); + _print("

Stáhnout aktuální konfguraci

\n"); +} + +void HtmlLayer::printNode(Node node) { +// addCrumb("./", node.getIdStr()); + NodeVarIterator it = node.getIterator(); + _print(QString("
%1
").arg(node.getCustomName())); + _print(QString("Konfigurace zařízení").arg(documentRoot()).arg(node.getAddr())); + _print("\n"); +} + +void HtmlLayer::printVarsConfig(NodeMap &nodes, Node &node, CidPool &cidPool) { + Form vars_form, node_form; + processVarsConfig(nodes, node, vars_form, cidPool); + processNodeConfig(node, node_form); + + _print(QString("
%1 (konfigurace zařízení)
").arg(node.getCustomName())); + + /* formular pro konfiguraci nodu */ + _print("
\n"); + _print(node_form.renderField("node_name", "\n")); +// _print(node_form.renderField("config_file", "\n")); + _print("\t\n
%1%2 %3
%1%2 %3
\n"); + + _print("
"); + + /* formular pro konfiguraci promennych */ + _print("
\n" + "\t" + "" + "" + "" + "" + "" + "" + "" + "\n"); + + QString descr_field("%1_description"); + QString value_field("%1_value"); + QString color_field("%1_color"); + QString displ_field("%1_display"); + + ULOIConnection conn(node.getAddr()); + + NodeVarIterator it = node.getIterator(); + while (it.hasNext()) { + it.next(); + NodeVar tmpVar = it.value(); + QString name = tmpVar.getName(); + QString tdTemplate(""); + QString errTemplate("%1"); + if (!tmpVar.isVisible()) continue; + + _print("\t"); +// _print(QString("").arg(name).arg(tmpVar.isWriteable()).arg(tmpVar.isReadable())); + _print(QString("").arg(name)); + + /* field pro popis */ + _print(vars_form.renderField(descr_field.arg(name), tdTemplate, "", errTemplate)); + + /* field pro hodnotu */ + if (vars_form[value_field.arg(name)]) + _print(vars_form.renderField(value_field.arg(name), tdTemplate, "", errTemplate)); + else + _print(""); + + /* odkaz pro aktualizaci hodnoty */ + if (tmpVar.isReadable()) + _print(QString("").arg(tmpVar.getFloatValue(conn)).arg(documentRoot()).arg(node.getAddr()).arg(name).arg(tmpVar.getId())); + else + _print(""); + + /* field pro barvu a pro zobrazeni ve spolecnem grafu, odkaz na detail */ + if (vars_form[color_field.arg(name)] && vars_form[displ_field.arg(name)]) { + _print(vars_form.renderField(color_field.arg(name), tdTemplate, "", errTemplate)); + _print(vars_form.renderField(displ_field.arg(name), tdTemplate, "", errTemplate)); + _print(QString("").arg(documentRoot()).arg(node.getAddr()).arg(name)); + } else + _print(""); + + _print("\n"); + } + + _print("\t\n" + "
Systémový názevUživatelský názevNastavit hodnotuAktuální hodnotaBarvaSpol. grafDetail
%1%2%3
%1 (W:%2 R:%3)%1%1 Detail
\n"); +} + +void HtmlLayer::printVar(NodeVar &var, ValueCache &valueCache) { + + _print(QString("
%1 (historie hodnot)
").arg(var.getCustomDescription())); + QString filename = QString("%1-%2.png").arg(var.getNodeSN()).arg(var.getId()); + + QPair values = valueCache.getValues(var.getNodeSN(), var.getId(), HISTORY_LEN); + Graph g1(values.first, var.getGraphColor(), Qt::SolidLine, false); + Graph g2(values.second, var.getGraphColor(), Qt::DashLine, false); + + GraphPainter gp(QSize(VAR_IMG_WIDTH, VAR_IMG_HEIGHT), VAR_IMG_BORDER_TOP, VAR_IMG_BORDER_RIGHT, VAR_IMG_BORDER_BOTTOM, VAR_IMG_BORDER_LEFT); + gp.addGraph(g1); + gp.addGraph(g2); + + QDateTime current_datetime = currentDateTime(); + gp.drawGraphs(current_datetime.toTime_t() - HISTORY_LEN, current_datetime.toTime_t(), getTimeLabels(HISTORY_LEN)); + gp.saveGraphs(QString(VAR_IMG_FILE_PATH).append(filename)); + + _print(QString("\n").arg(filename)); + if (values.first.size() > 0) { + _print("\n"); + for (int i = 0; i < values.first.size(); i++) { + _print(QString("\t\n").arg(values.first[i].y())); + } + _print("
%1
\n"); + } +} + +/* AJAX odezva */ +void HtmlLayer::printVarValueAjaxRespose(Node &node, NodeVar &var) { + if (var.isVisible() && var.isReadable()) { +#ifndef TEST_RUN + ULOIConnection conn(node.getAddr()); + _print(QString("%1").arg(var.getFloatValue(conn))); +#else + _print(QString("%1").arg(getRandom())); +#endif + } +} + +/* XML download a upload */ +void HtmlLayer::printXMLDownload(NodeMap &nodes) { + QDomDocument doc; + QDomElement configElem = doc.createElement("configuration"); + + doc.appendChild(configElem); + configElem.appendChild(nodes.getXMLConfig(doc)); + + _print("Status: 200 OK\r\n"); + _print("Pragma: no-cache\r\n"); + _print("Content-Type: text/html; charset=UTF-8\r\n"); + _print("Content-Disposition: attachment; filename=config.xml\r\n\r\n"); + _print(doc.toString(4)); +} + +/* zpracovani dat od uzivatele */ +void HtmlLayer::processVarsConfig(NodeMap &nodes, Node &node, Form &form, CidPool &cidPool) { + QList colorOptions = getColorOptions(); + QList onOffOptions = getOnOffOptions(); + QList slotOptions = nodes.getSlotOptions(node.getAddr()); + + QString descr_field("%1_description"); + QString value_field("%1_value"); + QString color_field("%1_color"); + QString displ_field("%1_display"); + + /*******************************/ + /* + Form form; + + form.addField("first_name", new CharField("Jméno", true, "...vyplňte jméno...")); + form.addField("surname", new CharField("Příjmení", true, "...vyplňte příjmení...")); + form.addField("age", new CharInteger("Věk", true, 18)); + + if (request.method() == "POST") { + + form.bindData(request.POST()); + if (form.isValid()) { + + QVariantMap cleaned_data = form.getCleanedDataRef(); + + } + + } + */ + /*******************************/ + + CidPool localCidCache; + ULOIConnection conn(node.getAddr()); + + /* vytvoreni formulare */ + NodeVarIterator it = node.getIterator(); + while (it.hasNext()) { + it.next(); + NodeVar tmpVar = it.value(); + QString name = tmpVar.getName(); + if (!tmpVar.isVisible()) continue; + + /* field pro popis */ + form.addField(descr_field.arg(name), new CharField("", true, tmpVar.getCustomDescription())); + + /* field pro hodnotu */ + if (tmpVar.isWriteable()) { + if (tmpVar.getUserType() == "cid") + form.addField(value_field.arg(name), new CidField("", true, cidPool, localCidCache, tmpVar.getFloatValue(conn))); + else if (tmpVar.getUserType() == "bool") + form.addField(value_field.arg(name), new ChoiceField("", onOffOptions, true, QVariant(tmpVar.getFloatValue(conn)).toString())); + else if (tmpVar.getUserType() == "slot") + form.addField(value_field.arg(name), new ChoiceField("", slotOptions, false, QVariant(tmpVar.getFloatValue(conn)).toString())); + else if (tmpVar.getDecimalPlaces() > 0) + form.addField(value_field.arg(name), new FloatField("", true, tmpVar.getFloatValue(conn))); + else + form.addField(value_field.arg(name), new IntegerField("", true, tmpVar.getFloatValue(conn))); + } + + /* field pro barvu a pro zobrazeni ve spolecnem grafu */ + if (!tmpVar.isSystemVar() && tmpVar.isReadable()) { + form.addField(color_field.arg(name), new ChoiceField("", colorOptions, true, tmpVar.getGraphColor().name())); + form.addField(displ_field.arg(name), new BooleanField("", false, tmpVar.getCommonGraphDisplay())); + } + } + + if (request.method() == "POST" && request.POST().contains("configure_vars")) { + form.bindData(request.POST()); + + if (form.isValid()) { + QVariantMap cleaned_data = form.getCleanedDataRef(); + NodeVarMutableIterator it = node.getMutableIterator(); + QByteArray qItemVal; + node.resetUsedCids(); + while (it.hasNext()) { + it.next(); + NodeVar &tmpVar = it.value(); + QString name = tmpVar.getName(); + if (!tmpVar.isVisible()) continue; + + /* nastaveni popisu */ + tmpVar.setCustomDescription(cleaned_data[descr_field.arg(name)].toString()); + + /* nastaveni hodnoty */ +// node.resetUsedCids(); + if (cleaned_data.contains(value_field.arg(name))) { + float val = cleaned_data.value(value_field.arg(name)).toDouble(); + if (tmpVar.getUserType() == "cid") { + if ((val == form[value_field.arg(name)]->getInitial().toDouble()) || tmpVar.setFloatValue(val, conn)) { + node.addUsedCid((int)val); + cidPool.freeCid(form[value_field.arg(name)]->getInitial().toInt()); + cidPool.blockCid((int)val); + logToFile("CID nastaven"); +// } else if (tmpVar.setFloatValue(val, conn)) { +// node.addUsedCid(val); +// cidPool.freeCid(form[value_field.arg(name)]->getInitial().toInt()); +// cidPool.blockCid(val); +// logToFile("CID nastaven"); + } else + logToFile("chyba pri nastavovani CIDu"); + } else if (val != form[value_field.arg(name)]->getInitial().toDouble()) + tmpVar.setFloatValue(val, conn); + } + + /* nastaveni barvy a zobrazeni ve spolecnem grafu */ + if (cleaned_data.contains(color_field.arg(name)) && cleaned_data.contains(displ_field.arg(name))) { + tmpVar.setGraphColor(QColor(cleaned_data.value(color_field.arg(name)).toString())); + tmpVar.setCommonGraphDisplay(cleaned_data.value(displ_field.arg(name)).toBool()); + } + + it.setValue(tmpVar); + } + throw HttpRedirect("./"); + } + + } +} + +void HtmlLayer::processNodeConfig(Node &node, Form &form) { + form.addField("node_name", new CharField("Název zařízení", true, node.getCustomName())); + + if (request.method() == "POST" && request.POST().contains("configure_node")) { + form.bindData(request.POST()); + if (form.isValid()) { + node.setCustomName(form.getCleanedDataRef()["node_name"].toString()); + throw HttpRedirect("./"); + } + } +} + +/* chybova hlaseni */ +void HtmlLayer::invalidNode(NodeError err, int/* nodeAddr*/) { + _print(QString("

%1

\n").arg(err.getErrorMsg())); +} + +void HtmlLayer::invalidNodeVar(NodeVarError err, QString/* nodeVarName*/) { + _print(QString("

%1

\n").arg(err.getErrorMsg())); +} + +/* obecne funkce */ +void HtmlLayer::print(const QString &msg) { + _print(msg); +} + + +/* na miru udelany form field kvuli validaci unikatnosti CIDu */ +CidField::CidField(QString _label, bool _required, CidPool &_cidPool, CidPool &_localCidCache, QVariant _initial, QObject *parent) + : IntegerField(_label, _required, _initial, parent), cidPool(_cidPool), localCidCache(_localCidCache) {} + +QVariant CidField::clean(QVariant value) const { + QVariant retval = IntegerField::clean(value); + if (retval == initial) + return retval; + int tmp_cid_val = retval.toInt(); + if (tmp_cid_val == 0) + return 0; + if (!cidPool.isFreeCid(tmp_cid_val)) + throw ValidationError("Tento CID je již používán"); + if (!localCidCache.isFreeCid(tmp_cid_val)) + throw ValidationError("CID musí být unikátní"); + localCidCache.blockCid(tmp_cid_val); + return retval; +}