monitoring: improvements
authorJosé Bollo <jose.bollo@iot.bzh>
Sun, 27 Aug 2017 11:35:37 +0000 (13:35 +0200)
committerJosé Bollo <jose.bollo@iot.bzh>
Sun, 27 Aug 2017 12:41:45 +0000 (14:41 +0200)
- use flex/css for correct layout
- handles disconnection nicely
- handles initial token
- fix typo in afb-api-so

Change-Id: I386b98f9d6a2182029d39a373a9820478f97cfa6
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
src/afb-api-so.c
test/monitoring/AFB.js
test/monitoring/background-iot.bzh.jpeg [new file with mode: 0644]
test/monitoring/content-background-car-wide.png [deleted file]
test/monitoring/monitor-base.css
test/monitoring/monitor-demo.css
test/monitoring/monitor-pastel.css
test/monitoring/monitor.html
test/monitoring/monitor.js

index 9d8d9c0..55e474c 100644 (file)
@@ -86,7 +86,7 @@ static int adddirs(char path[PATH_MAX], size_t end, struct afb_apiset *apiset, i
        DIR *dir;
        struct dirent *dent;
        size_t len;
-       int rc=0;
+       int rc = 0;
 
        /* open the DIR now */
        dir = opendir(path);
index db03d9e..30fb982 100644 (file)
@@ -21,7 +21,7 @@ if (typeof base != "object")
 
 var initial = {
        base: base.base || "api",
-       token: base.token || "hello",
+       token: base.token || initialtoken || "hello",
        host: base.host || window.location.host,
        url: base.url || undefined
 };
diff --git a/test/monitoring/background-iot.bzh.jpeg b/test/monitoring/background-iot.bzh.jpeg
new file mode 100644 (file)
index 0000000..2edcfac
Binary files /dev/null and b/test/monitoring/background-iot.bzh.jpeg differ
diff --git a/test/monitoring/content-background-car-wide.png b/test/monitoring/content-background-car-wide.png
deleted file mode 100644 (file)
index 073d1b9..0000000
Binary files a/test/monitoring/content-background-car-wide.png and /dev/null differ
index 1318c91..f2de1d9 100644 (file)
 body {
        position: fixed;
        top: 0px;
-       bottom: 0px;
        left: 0px;
        right: 0px;
+       bottom: 0px;
 }
 
-body.on #params, body.off #main { display: none; }
+body.on #params, body.on #connect, body.off #disconnect { display: none; }
 
 /*******************************************************************/
-/* head */
-#head {
-       position: relative;
+/* utilities */
+.-flex-h {
+       display: flex;
+       flex-flow: row nowrap;
 }
-
-#logo {
-       float: left;
+.-flex-v {
+       display: flex;
+       flex-flow: column nowrap;
 }
-
-#connected {
-       float: right;
+.-flex-v > .-flex-fill {
+       height: 100%;
 }
-
-/*******************************************************************/
-/* connection area */
-
-/*******************************************************************/
-/* main area */
-
-#work {
-       position: relative;
-       top: 0px;
-       bottom: 0px;
-       left: 0px;
-       right: 0px;
+.-flex-h > .-flex-fill {
+       width: 100%;
 }
-
-#main {
+.-box-out {
        position: relative;
-       top: 0px;
-       bottom: 0px;
-       left: 0px;
-       right: 0px;
 }
-
-.fillfix {
-       width: 100%;
-       height: 100%;
-}
-
-#controls {
+.-box-in {
        position: absolute;
-       width: 250px;
        left: 0px;
-       top: 0px;
-       bottom: 0px;
-       overflow: auto;
-       margin-bottom: 75px;
-}
-
-#logmsg-box {
-       position: absolute;
-       width: 250px;
        right: 0px;
        top: 0px;
        bottom: 0px;
-/*
-       font-size: smaller;
        overflow: auto;
-       margin-bottom: 75px;
-*/
 }
 
-#logmsgs-inner-box {
+/*******************************************************************/
+/* head */
+#head {
        position: relative;
-       left: 0px;
-       right: 0px;
-       top: 0px;
-       bottom: 0px;
 }
 
-#logmsgs {
-       position: absolute;
-       left: 0px;
-       right: 0px;
-       top: 0px;
-       bottom: 0px;
-       font-size: smaller;
-       overflow: auto;
-       margin-bottom: 380px;
+#logo {
+       float: left;
 }
 
-
-#trace-events {
-       overflow: auto;
-       position: absolute;
-       right: 250px;
-       left: 250px;
-       top: 0px;
-       bottom: 0px;
-       margin-bottom: 75px;
+#connected {
+       float: right;
 }
 
+/*******************************************************************/
+/* connection area */
+
+/*******************************************************************/
+/* main area */
+
+#controls, #menu { flex: 1; }
+#middle { flex: 3; }
+#controls { min-width: 300px; }
+#menu { min-width: 200px; }
+
 /*******************************************************************/
 /* setting for apis */
 
@@ -140,6 +99,8 @@ body.on #params, body.off #main { display: none; }
 /*******************************************************************/
 /* display of logmsg */
 
+#experts.closed ~ #expert-pane { visibility: hidden; }
+
 /*******************************************************************/
 /* close box */
 .close {
@@ -167,7 +128,7 @@ body.on #params, body.off #main { display: none; }
 }
 
 .traceevent .time {
-       float: left;
+       float: right;
 }
 
 .traceevent.closed:hover {
@@ -183,9 +144,26 @@ body.on #params, body.off #main { display: none; }
 .traceevent.closed:hover .content {
        display: block;
        background: inherit;
+       overflow: hidden;
        left: 40%;
        max-width: 55%;
        position: absolute;
+       animation-name: display-block;
+       animation-duration: 2s;
+       -webkit-animation-name: display-block;
+       -webkit-animation-duration: 2s;
+}
+
+@-webkit-keyframes display-block {
+       from { opacity: 0; }
+       50% { opacity: 0; }
+       to { opacity: 1; }
+}
+
+@keyframes display-block {
+       from { opacity: 0; }
+       50% { opacity: 0; }
+       to { opacity: 1; }
 }
 
 .traceevent .content {
index f1455b7..87cf0a0 100644 (file)
  */
 /*******************************************************************/
 /* top */
-body {
+html {
        margin: 2px;
-       background-color: darkslategray;
-       background-image: url(content-background-car-wide.png);
-       background-position: center;
-       background-size: cover;
+       background: darkslategray url(background-iot.bzh.jpeg) center/cover;
        color: lightsteelblue;
        font-family: "Lucida Console", Monaco, monospace;
+       min-height: 100%;
 }
 
 /*******************************************************************/
 /* head */
 
 #logo {
-       background: url(iot-bzh-logo-small.png) center/contain no-repeat;
+       background: url(iot-bzh-logo-small.png) center/50% no-repeat;
        height: 60px;
        width: 200px;
 }
@@ -64,11 +62,6 @@ body {
        border-radius: 0px 50px;
 }
 
-#connect {
-       float: right;
-       margin: 20px;
-}
-
 /*******************************************************************/
 /* main area */
 
@@ -206,6 +199,8 @@ body {
        height: 10;
        background-image: url(cross.png);
        background-size: contain;
+       background-repeat: no-repeat;
+       background-position: center;
 }
 
 /*******************************************************************/
@@ -235,41 +230,42 @@ body {
 }
 
 .separator {
-       margin: 15px 100px;
+       margin: 25px 100px;
        padding: 0px;
        height: 2px;
-       background-color: lightsteelblue;
+       background-color: #ee3;
 }
 
 .traceevent.opened {
        background-color: rgba(0,0,0,0.6);
 }
 
-/*
-.traceevent.request, .trace-box.request { background: #ffd: }
-.traceevent.daemon, .trace-box.daemon { background: #fdf; }
-.traceevent.service, .trace-box.service { background: #ddf; }
-.traceevent.event, .trace-box.event { background: #dfd; }
-*/
 .traceevent.request, .trace-box.request { color: lightsteelblue; }
-.traceevent.daemon, .trace-box.daemon { color: #66f; }
+.traceevent.daemon, .trace-box.daemon { color: #5af; }
 .traceevent.service, .trace-box.service { color: #0f0; }
-.traceevent.event, .trace-box.event { color: #f0d; }
+.traceevent.event, .trace-box.event { color: #f52; }
 
 .traceevent.closed {
        max-height: 16px;
 }
 
+.traceevent .close {
+       height: 16px;
+       margin: -1px 0px 0px 0px;
+       padding: 1px;
+       background-color: grey;
+}
+
 .traceevent .time {
        height: 16px;
-       margin: -2px 8px 2px -2px;
+       margin: -1px 0px 0px 8px;
        padding: 1px 3px;
-       background: black;
-       color: white;
-       font-weight: bolder;
+       background-color: grey;
+       color: black;
 }
 
 .traceevent .tag {
+       margin-left: 10px;
        font-weight: bolder;
 }
 
@@ -280,10 +276,10 @@ body {
 .traceevent.closed:hover .content {
        margin: 5px;
        padding: 5px;
-       border: solid 1px;
-       border-radius: 0px 5px 5px 5px;
+       border: solid 4px;
+       border-radius: 0px 20px 20px 20px;
        box-shadow: 10px 10px #dd8;
-       background-color: rgba(0,0,0,0.8);
+       background-color: rgba(0,0,0,0.6);
 }
 
 .traceevent table.object tr td:nth-child(1) {
@@ -293,11 +289,9 @@ body {
 }
 
 .traceevent table.object tr td:nth-child(2) {
-/*
-       border: solid 1px black;
-*/
        background-color: rgba(0,0,0,0.4);
        padding: 0px 4px;
+       overflow: auto;
 }
 
 .traceevent table {
@@ -312,7 +306,7 @@ body {
 
 a:link { color: cyan; }
 a:link:hover, a:active { color: white; }
-a:visited { color: lightsteelblue; }
+a:visited { color: lightgrey; }
 
 /*******************************************************************/
 /* json format */
index 21bccd7..0264bf5 100644 (file)
@@ -57,11 +57,6 @@ body {
        border-radius: 0px 50px;
 }
 
-#connect {
-       float: right;
-       margin: 20px;
-}
-
 /*******************************************************************/
 /* main area */
 
index 9e35bc7..f22a62b 100644 (file)
     <script type="text/javascript" src="AFB.js"></script>
     <script type="text/javascript" src="monitor.js"></script>
 
-<body id="root" class="on" onload="init();">
+<body id="root" class="-flex-v on" onload="init();">
   <div id="head" class="clearfix">
     <div id="logo"></div>
     <div id="connected">Not Connected</div>
     <div id="title">Monitoring</div>
   </div>
-  <div id="work">
-    <div id="params" class="clearfix">
-      <div id="connect" class="x-button">connect</div>
-      <div>host: <input type="text" id="param-host" size="50" value="localhost"></input></div>
-      <div>port: <input type="text" id="param-port" size="10" value="1234"></input></div>
-      <div>token: <input type="text" id="param-token" size="33" value="hello"></input></div>
-    </div>
-    <div id="main">
-      <div class="fillfix"></div>
-      <div id="controls">
+  <div id="work" class="-flex-fill -flex-h">
+    <div id="controls" class="-box-out">
+      <div class="-box-in">
         <div id="all" class="api opened" data-api="*">
           <div class="opclo"></div>
           <div class="name">{ALL}</div>
         <div id="apis">
         </div>
       </div>
-      <div id="logmsg-box">
+    </div>
+    <div id="middle" class="-flex-v">
+      <div id="params" class="clearfix">
+        <div>host: <input type="text" id="param-host" size="50" value="localhost"></input></div>
+        <div>port: <input type="text" id="param-port" size="10" value="1234"></input></div>
+        <div>token: <input type="text" id="param-token" size="33" value="hello"></input></div>
+      </div>
+      <div class="-flex-fill -box-out">
+        <div id="trace-events" class="-box-in">
+        </div>
+      </div>
+    </div>
+
+      <div id="menu" class="-flex-v">
+        <div id="connect" class="x-button">Connect</div>
         <div id="disconnect" class="x-button">Disconnect</div>
         <div id="autoscroll" class="x-button">Stop scroll</div>
         <div id="addsep" class="x-button">Add separator</div>
         <div id="droptracevts" class="x-button">Clear traces</div>
-        <div id="expert-pane" class="closed">
+        <div id="experts" class="x-button closed">
           <div class="opclo"></div>
-          <div class="expert">{EXPERT}</div>
-          <div class="closedoff">
-            <div id="stopmsgs" class="x-button">Stop logs</div>
-            <div id="dropmsgs" class="x-button">Clear logs</div>
-            <div id="logmsgs-inner-box">
-              <div class="fillfix"></div>
-              <div id="logmsgs"></div>
-            </div>
+          {EXPERTS}
+        </div>
+        <div id="expert-pane" class="-flex-fill -flex-v">
+          <div id="stopmsgs" class="x-button">Stop logs</div>
+          <div id="dropmsgs" class="x-button">Clear logs</div>
+          <div class="-flex-fill -box-out">
+              <div id="logmsgs" class="-box-in"></div>
           </div>
         </div>
       </div>
-      <div id="trace-events">
-      </div>
-    </div>
   </div>
 
 <!-- template for APIS -->
index 2c53256..ecf1096 100644 (file)
@@ -47,34 +47,38 @@ _.templateSettings = { interpolate: /\{\{(.+?)\}\}/g };
 
 function untrace_all() {
        do_call("monitor/trace", {drop: true});
-       for_all_nodes(null, ".trace-item input[type=radio]", function(n){n.checked = n.value == "no";});
 }
 
-function disconnect() {
-       untrace_all();
-       apis = {};
-       apis_node.innerHTML = "";
-       root_node.className = "off";
+function disconnect(status) {
+       class_toggle(root_node, { on: "off" }, "off");
        connected_node.innerHTML = "Connection Closed";
-       connected_node.className = "ok";
-       ws && ws.close();
-       afb = null;
+       connected_node.className = status;
+       if (ws) {
+               untrace_all();
+               ws.onclose = ws.onabort = null;
+               ws.close();
+       }
        ws = null;
+       if (afb)
+               at("param-token").value = afb.context.token;
+       afb = null;
 }
 
-function connect(args) {
-       drop_all_trace_events();
-       drop_all_logmsgs();
-       ws && ws.close();
-       afb = new AFB(args);
-       ws = new afb.ws(onopen, onabort);
+function on_disconnect() {
+       disconnect("ok");
 }
 
-function on_connect(evt) {
-       connect({
+function connect() {
+       ws && ws.close();
+       afb = new AFB({
                host: at("param-host").value + ":" + at("param-port").value,
                token: at("param-token").value
        });
+       ws = new afb.ws(onopen, onabort);
+}
+
+function on_connect(evt) {
+       connect();
 }
 
 function init() {
@@ -103,7 +107,7 @@ function init() {
        for_all_nodes(root_node, ".opclo ~ :not(.closedoff)", function(n){n.onclick = on_toggle_opclo});
        for_all_nodes(root_node, ".verbosity select", function(n){n.onchange = set_verbosity});
        for_all_nodes(root_node, ".trace-item input", function(n){n.onchange = on_trace_change});
-       at("disconnect").onclick = disconnect;
+       at("disconnect").onclick = on_disconnect;
        at("connect").onclick = on_connect;
        at("droptracevts").onclick = drop_all_trace_events;
        at("dropmsgs").onclick = drop_all_logmsgs;
@@ -113,6 +117,14 @@ function init() {
        at("autoscroll").onclick = toggle_autoscroll;
        start_autoscroll(true);
        at("addsep").onclick = add_separator;
+       at("experts").onclick = toggle_experts;
+
+       at("param-host").value = document.location.hostname;
+       at("param-port").value = document.location.port;
+       var args = new URLSearchParams(document.location.search.substring(1));
+       at("param-token").value = args.get("x-afb-token") || args.get("token") || "hello";
+
+       document.onbeforeunload = on_disconnect;
 
        connect();
 }
@@ -137,17 +149,18 @@ function plug(target, sel, node) {
 }
 
 function onopen() {
-       root_node.className = "on";
+       class_toggle(root_node, { off: "on" }, "on");
        connected_node.innerHTML = "Connected " + ws.url;
        connected_node.className = "ok";
        ws.onevent("*", gotevent);
        ws.onclose = onabort;
+       untrace_all();
+       for_all_nodes(all_node, ".trace-box", update_trace_box);
        do_call("monitor/get", {apis:true,verbosity:true}, on_got_apis, on_error_apis);
 }
+
 function onabort() {
-       root_node.className = "off";
-       connected_node.innerHTML = "Connection Closed";
-       connected_node.className = "error";
+       disconnect("error");
 }
 
 function start_autoscroll(val) {
@@ -163,6 +176,12 @@ function add_separator() {
        trace_events_node.append(x);
        if (autoscroll)
                x.scrollIntoView();
+       if (msgs) {
+               x = document.importNode(t_separator, true);
+               logmsgs_node.append(x);
+               if (autoscroll)
+                       x.scrollIntoView();
+       }
 }
 
 function start_logmsgs(val) {
@@ -249,10 +268,14 @@ function set_verbosity(evt) {
 /* show all apis */
 function on_got_apis(obj) {
        inhibit = true;
+       var saved_apis = apis;
+       apis = {};
+       apis_node.innerHTML = "";
        _.each(obj.response.apis, function(api_desc, api_name){
                if (api_name == "monitor") return;
-               var api = apis[api_name];
+               var api = saved_apis[api_name];
                if (!api) {
+                       /* create the node */
                        api = {
                                node: document.importNode(t_api, true),
                                verbs: {},
@@ -261,42 +284,45 @@ function on_got_apis(obj) {
                        api.node.API = api;
                        api.node.dataset.api = api_name;
                        api.vnode = get(".verbs", api.node);
-                       apis[api_name] = api;
                        get(".name", api.node).textContent = api_name;
-                       get(".desc", api.node).textContent = api_desc.info.description || "";
-                       for_all_nodes(api.node, ".opclo", function(n){n.onclick = on_toggle_opclo});
-                       for_all_nodes(api.node, ".opclo ~ :not(.closedoff)", function(n){n.onclick = on_toggle_opclo});
-                       for_all_nodes(api.node, ".trace-item input", function(n){n.onchange = on_trace_change});
-                       apis_node.append(api.node);
-                       _.each(api_desc.paths, function(verb_desc, path_name){
-                               var verb_name = path_name.substring(1);
-                               var verb = api.verbs[verb_name];
-                               if (!verb) {
-                                       verb = {
-                                               node: document.importNode(t_verb, true),
-                                               name: verb_name,
-                                               api: api
-                                       };
-                                       verb.node.VERB = verb;
-                                       verb.node.dataset.verb = verb_name;
-                                       api.verbs[verb_name] = verb;
-                                       get(".name", verb.node).textContent = verb_name;
-                                       var g = verb_desc.get ||{};
-                                       var r = g["responses"] || {};
-                                       var t = r["200"] || {};
-                                       var d = t.description || "";
-                                       get(".desc", verb.node).textContent = d;
-                                       if (show_perms) {
-                                               var p = g["x-permissions"] || "";
-                                               get(".perm", verb.node).textContent = p ? JSON.stringify(p, null, 1) : "";
-                                       }
-                                       api.vnode.append(verb.node);
-                               }
-                       });
                        var s = get(".verbosity select", api.node);
                        s.API = api;
                        s.onchange = set_verbosity;
+                       for_all_nodes(api.node, ".opclo", function(n){n.onclick = on_toggle_opclo});
+                       for_all_nodes(api.node, ".opclo ~ :not(.closedoff)", function(n){n.onclick = on_toggle_opclo});
+                       for_all_nodes(api.node, ".trace-item input", function(n){n.onchange = on_trace_change});
+               } else {
+                       /* reactivate the expected traces */
+                       for_all_nodes(api.node, ".trace-box", update_trace_box);
                }
+               apis[api_name] = api;
+               get(".desc", api.node).textContent = api_desc.info.description || "";
+               _.each(api_desc.paths, function(verb_desc, path_name){
+                       var verb_name = path_name.substring(1);
+                       var verb = api.verbs[verb_name];
+                       if (!verb) {
+                               verb = {
+                                       node: document.importNode(t_verb, true),
+                                       name: verb_name,
+                                       api: api
+                               };
+                               verb.node.VERB = verb;
+                               verb.node.dataset.verb = verb_name;
+                               api.verbs[verb_name] = verb;
+                               get(".name", verb.node).textContent = verb_name;
+                               var g = verb_desc.get ||{};
+                               var r = g["responses"] || {};
+                               var t = r["200"] || {};
+                               var d = t.description || "";
+                               get(".desc", verb.node).textContent = d;
+                               if (show_perms) {
+                                       var p = g["x-permissions"] || "";
+                                       get(".perm", verb.node).textContent = p ? JSON.stringify(p, null, 1) : "";
+                               }
+                               api.vnode.append(verb.node);
+                       }
+               });
+               apis_node.append(api.node);
        });
        inhibit = false;
        on_got_verbosities(obj);
@@ -306,37 +332,42 @@ function on_toggle_opclo(evt) {
        toggle_opened_closed(evt.target.parentElement);
 }
 
-function on_trace_change(evt) {
-       var obj = evt.target;
-       var tra = obj.parentElement;
-       while (tra && !tra.dataset.trace)
-               tra = tra.parentElement;
-       var api = tra;
+function toggle_experts(evt) {
+       toggle_opened_closed(evt.target);
+}
+
+function update_trace_box(node) {
+       set_trace_box(node, false);
+}
+
+function set_trace_box(node, clear) {
+       var api = node;
        while (api && !api.dataset.api)
                api = api.parentElement;
-       var tag = api.dataset.api + "/" + tra.dataset.trace;
-       if (tra) {
-               var drop = false;
-               for_all_nodes(tra, "input", function(n){
-                       if (n.checked) {
-                               n.checked = false;
-                               if (n != obj && n.value != "no")
-                                       drop = true;
-                       }
-               });
-               if (drop)
-                       do_call("monitor/trace", {drop: {tag: tag}});
-               obj.checked = true;
-               if (obj.value != "no") {
-                       var spec = {tag: tag, name: "trace"};
-                       spec[tra.dataset.trace] = obj.value;
-                       if (api.dataset.api != "*")
-                               spec.api = api.dataset.api;
-                       do_call("monitor/trace", {add: spec});
-               }
+       var tag = api.dataset.api + "/" + node.dataset.trace;
+       var value = false;
+       for_all_nodes(node, "input", function(n){ if (n.checked) value = n.value; });
+       if (clear)
+               do_call("monitor/trace", {drop: {tag: tag}});
+       if (value != "no") {
+               var spec = {tag: tag, name: "trace"};
+               spec[node.dataset.trace] = value;
+               if (api.dataset.api != "*")
+                       spec.api = api.dataset.api;
+               do_call("monitor/trace", {add: spec});
        }
 }
 
+function on_trace_change(evt) {
+       var obj = evt.target;
+       var box = obj.parentElement;
+       while (box && !box.dataset.trace)
+               box = box.parentElement;
+       for_all_nodes(box, "input", function(n){n.checked = false;});
+       obj.checked = true;
+       set_trace_box(box, true);
+}
+
 function makecontent(node, deep, val) {
        if (--deep > 0) {
                if (_.isObject(val)) {
@@ -348,7 +379,7 @@ function makecontent(node, deep, val) {
                        return;
                }
        }
-       node.innerHTML = obj2html(val);
+       node.innerHTML = '<pre>' + obj2html(val) + '</pre>';
 }
 
 function makearritem(tbl, deep, val) {
@@ -424,23 +455,25 @@ function gottraceevent(obj) {
                x.scrollIntoView();
 }
 
-function toggle_opened_closed(node, defval) {
+function class_toggle(node, assoc, defval) {
        var matched = false;
        var cs = node.className.split(" ").map(
                function(x){
-                       if (!matched) {
-                               switch(x) {
-                               case "closed": matched = true; return "opened";
-                               case "opened": matched = true; return "closed";
-                               }
+                       if (!matched && (x in assoc)) {
+                               matched = true;
+                               return assoc[x];
                        }
-                       return x;
+                       return x == defval ? "" : x;
                }).join(" ");
-       if (!matched)
-               cs = cs + " " + (defval || "closed");
+       if (!matched && defval)
+               cs = cs + " " + defval;
        node.className = cs;
 }
 
+function toggle_opened_closed(node, defval) {
+       class_toggle(node, { closed: "opened", opened: "closed" }, defval);
+}
+
 function on_toggle_traceevent(evt) {
        if (getSelection() != "") return;
        var node = evt.target;