--- /dev/null
+
+#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(" >> ");
+ }
+ _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;
+}