]> rtime.felk.cvut.cz Git - hydro.git/blobdiff - app-bohyn/src/html_layer.cc
Added monitoring web system. Minor changes in regulator and in control.
[hydro.git] / 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 (file)
index 0000000..3fb3c7b
--- /dev/null
@@ -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("<div id=\"crumbs\"><span>");
+    while (it.hasNext()) {
+        QStringList crumb = it.next();
+        _print(QString("<a href=\"%1\">%2</a>").arg(crumb[0]).arg(crumb[1]));
+        if (it.hasNext()) _print(" &gt;&gt; ");
+    }
+    _print("</span></div>\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("<html>\r\n\t<head>\r\n\t\t<title>404 - Not Found</title>\r\n\t<body>\r\n\t\t<h1>404 - Not Found</h1>\r\n\t</body>\r\n</html>\r\n");
+}
+
+void HtmlLayer::printDocumentStart() {
+    _print(
+        "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n"
+        "<html>\r\n"
+        "<head>\n"
+        "\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n"
+        "\t<script type=\"text/javascript\" src=\"" VAR_IMG_URL_BASE "js/jquery.js\"></script>\r\n"
+        "\t<script type=\"text/javascript\" src=\"" VAR_IMG_URL_BASE "js/nodevar.js\"></script>\r\n"
+        "\t<link rel=\"stylesheet\" type=\"text/css\" href=\"" VAR_IMG_URL_BASE "styles/main.css\" media=\"screen\" />\r\n"
+        "\t<title>Hydroponie FastCGI echo</title>\r\n"
+        "</head>\n"
+        "<body><div id=\"body\">\r\n\t<h1>Hydroponie web interface</h1>\r\n"
+    );
+    _print(QString("<a id=\"configLink\" href=\"%1xml_config/\">Správa konfigurace</a>").arg(documentRoot()));
+}
+
+void HtmlLayer::printDocumentEnd() {
+    _print("</div></body></html>\r\n");
+}
+
+void HtmlLayer::printContentStart() {
+    _print("<div id=\"main\"><div id=\"mainInner\">\n");
+}
+
+void HtmlLayer::printContentEnd() {
+    _print("</div></div><div class=\"cleaner\"></div>\n");
+}
+
+void HtmlLayer::printEnvVars() {
+    int i;
+    _print("<hr />\n"
+           "<div id=\"envVars\" style=\"color: grey;\">\n");
+    for(i = 0; environ[i] != NULL; i++)
+        _print(QString("%1<br />\n").arg(environ[i]));
+    _print("</div>\n");
+}
+
+/* funkce pro prezentaci nodu */
+void HtmlLayer::home(NodeMap &nodes, ValueCache &valueCache) {
+    _print(QString("<div class=\"title\"><span>Vyberte zařízení...</span></div>\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("<img src=\"" VAR_IMG_URL_BASE "%1\" />\n").arg(filename));
+
+}
+
+void HtmlLayer::printNodes(NodeMap &nodes) {
+    QByteArray ba;
+    NodeMapKeysIterator it(nodes.keys());
+    _print("<div id=\"nodes\"><div class=\"title\"><span>Dostupná zařízení</span></div><ul>\n");
+    while (it.hasNext()) {
+        int addr = it.next();
+        if (nodes.contains(addr)) {
+            Node &tmpNode = nodes.getNodeRef(addr);
+            _print(QString("\t<li><a href=\"%1%2/\" title=\"%3\">%4</a></li>\n").arg(documentRoot()).arg(tmpNode.getAddr()).arg(tmpNode.getIdStr()).arg(tmpNode.getCustomName()));
+        }
+    }
+    _print("</ul></div>\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<UploadedFile>();
+            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("<div class=\"title\"><span>Nahrání konfigurace</span></div>");
+    _print("<form action=\"./\" method=\"post\" enctype=\"multipart/form-data\"><table class=\"configTable\">\n");
+    _print(conf_form.renderField("config_file", "<tr><td>%1</td><td>%2 %3</td></tr>\n"));
+    _print("\t<tr><td colspan=\"2\"><input type=\"submit\" value=\"Uložit\" /></td></tr>\n</table></form>\n");
+    _print("<div class=\"spacer\"></div><p><a href=\"./download/\">Stáhnout aktuální konfguraci</a></p>\n");
+}
+
+void HtmlLayer::printNode(Node node) {
+//     addCrumb("./", node.getIdStr());
+    NodeVarIterator it = node.getIterator();
+    _print(QString("<div class=\"title\"><span>%1</span></div>").arg(node.getCustomName()));
+    _print(QString("<a href=\"%1%2/configure_vars/\">Konfigurace zařízení</a>").arg(documentRoot()).arg(node.getAddr()));
+    _print("<ul class=\"nodeVars\">\n");
+    while (it.hasNext()) {
+        it.next();
+        NodeVar tmpVar = it.value();
+//         if (!tmpVar.isVisible()) continue;
+        if (tmpVar.isSystemVar()) continue;
+        _print(QString("\t<li><a href=\"./%1/\">%2</a></li>\n").arg(tmpVar.getName()).arg(tmpVar.getCustomDescription()));
+    }
+    _print("</ul>\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("<div class=\"title\"><span>%1 (konfigurace zařízení)</span></div>").arg(node.getCustomName()));
+
+    /* formular pro konfiguraci nodu */
+    _print("<form action=\"./\" method=\"post\"><table class=\"configTable\">\n");
+    _print(node_form.renderField("node_name", "<tr><td>%1</td><td>%2 %3</td></tr>\n"));
+//     _print(node_form.renderField("config_file", "<tr><td>%1</td><td>%2 %3</td></tr>\n"));
+    _print("\t<tr><td colspan=\"2\"><input type=\"submit\" value=\"Uložit\" name=\"configure_node\" /></td></tr>\n</table></form>\n");
+
+    _print("<div class=\"spacer cleaner\"></div>");
+
+    /* formular pro konfiguraci promennych */
+    _print("<form action=\"./\" method=\"post\"><table class=\"configTable\">\n"
+           "\t<tr>"
+           "<th>Systémový název</th>"
+           "<th>Uživatelský název</th>"
+           "<th>Nastavit hodnotu</th>"
+           "<th>Aktuální hodnota</th>"
+           "<th>Barva</th>"
+           "<th>Spol. graf</th>"
+           "<th>Detail</th>"
+           "</tr>\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("<td>%1%2%3</td>");
+        QString errTemplate("<span class=\"errors\">%1</td>");
+        if (!tmpVar.isVisible()) continue;
+
+        _print("\t<tr>");
+//         _print(QString("<td>%1 (W:%2 R:%3)</td>").arg(name).arg(tmpVar.isWriteable()).arg(tmpVar.isReadable()));
+        _print(QString("<td>%1</td>").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("<td></td>");
+        
+        /* odkaz pro aktualizaci hodnoty */
+        if (tmpVar.isReadable())
+            _print(QString("<td><span id=\"%5\">%1</span> <a onclick=\"refreshVarValue(this.href, '%5'); return false;\" href=\"%2%3/%4/refresh_value/\"><img border=\"0\" style=\"margin-bottom: -3px;\" src=\"" VAR_IMG_URL_BASE "arrow_refresh.png\" /></a></td>").arg(tmpVar.getFloatValue(conn)).arg(documentRoot()).arg(node.getAddr()).arg(name).arg(tmpVar.getId()));
+        else
+            _print("<td></td>");
+
+        /* 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("<td><a href=\"%1%2/%3/\">Detail</a></td>").arg(documentRoot()).arg(node.getAddr()).arg(name));
+        } else
+            _print("<td></td><td></td><td></td>");
+
+        _print("</tr>\n");
+    }
+
+    _print("\t<tr><td colspan=\"7\"><input type=\"submit\" name =\"configure_vars\" value=\"Uložit\"></td></tr>\n"
+           "</table></form>\n");
+}
+
+void HtmlLayer::printVar(NodeVar &var, ValueCache &valueCache) {
+    
+    _print(QString("<div class=\"title\"><span>%1 (historie hodnot)</span></div>").arg(var.getCustomDescription()));
+    QString filename = QString("%1-%2.png").arg(var.getNodeSN()).arg(var.getId());
+
+    QPair<GraphData, GraphData> 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("<img src=\"" VAR_IMG_URL_BASE "%1\" />\n").arg(filename));
+    if (values.first.size() > 0) {
+        _print("<table border=\"1\"><tr>\n");
+        for (int i = 0; i < values.first.size(); i++) {
+            _print(QString("\t<td width=\"30\">%1</td>\n").arg(values.first[i].y()));
+        }
+        _print("</tr></table>\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<SelectOption> colorOptions = getColorOptions();
+    QList<SelectOption> onOffOptions = getOnOffOptions();
+    QList<SelectOption> 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("<h2 class=\"error\">%1</h2>\n").arg(err.getErrorMsg()));
+}
+
+void HtmlLayer::invalidNodeVar(NodeVarError err, QString/* nodeVarName*/) {
+    _print(QString("<h2 class=\"error\">%1</h2>\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;
+}