mirror of
https://github.com/cesanta/mongoose.git
synced 2025-01-18 23:53:15 +08:00
Add chart example
This commit is contained in:
parent
b3a4c12d67
commit
c2d2d085cd
6
Makefile
6
Makefile
@ -9,7 +9,7 @@ INCS ?= -Isrc -I.
|
||||
SSL ?= MBEDTLS
|
||||
CWD ?= $(realpath $(CURDIR))
|
||||
DOCKER ?= docker run --rm -e Tmp=. -e WINEDEBUG=-all -v $(CWD):$(CWD) -w $(CWD)
|
||||
VCFLAGS = /nologo /W3 /O2 /I. $(DEFS) $(TFLAGS)
|
||||
VCFLAGS = /nologo /W3 /O2 /MD /I. $(DEFS) $(TFLAGS)
|
||||
IPV6 ?= 1
|
||||
ASAN ?= -fsanitize=address,undefined -fno-sanitize-recover=all
|
||||
ASAN_OPTIONS ?= detect_leaks=1
|
||||
@ -116,6 +116,10 @@ vc2017: Makefile mongoose.h $(SRCS)
|
||||
$(DOCKER) mdashnet/vc2017 wine64 cl $(SRCS) $(VCFLAGS) ws2_32.lib /Fe$@.exe
|
||||
$(DOCKER) mdashnet/vc2017 wine64 $@.exe
|
||||
|
||||
vc22: Makefile mongoose.h $(SRCS)
|
||||
$(DOCKER) mdashnet/vc22 wine64 cl $(SRCS) $(VCFLAGS) ws2_32.lib /Fe$@.exe
|
||||
$(DOCKER) mdashnet/vc22 wine64 $@.exe
|
||||
|
||||
mingw: Makefile mongoose.h $(SRCS)
|
||||
$(DOCKER) mdashnet/mingw i686-w64-mingw32-gcc $(SRCS) -W -Wall -Werror -I. $(DEFS) -lwsock32 -o test.exe
|
||||
$(DOCKER) mdashnet/vc98 wine test.exe
|
||||
|
@ -7,6 +7,7 @@ void device_dashboard_fn(struct mg_connection *, int, void *, void *);
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
mg_log_set("3");
|
||||
mg_mgr_init(&mgr);
|
||||
mg_http_listen(&mgr, "http://0.0.0.0:8000", device_dashboard_fn, &mgr);
|
||||
for (;;) mg_mgr_poll(&mgr, 1000);
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 309 KiB After Width: | Height: | Size: 196 KiB |
@ -70,15 +70,25 @@ static void send_notification(struct mg_mgr *mgr, const char *name,
|
||||
struct mg_connection *c;
|
||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||
if (c->label[0] == 'W')
|
||||
mg_http_printf_chunk(c, "{\"name\": \"%s\", \"data\": \"%s\"}", name,
|
||||
data == NULL ? "" : data);
|
||||
mg_http_printf_chunk(c, "{\"name\": \"%s\", \"data\": %s}", name, data);
|
||||
}
|
||||
}
|
||||
|
||||
// Send simulated metrics data to the dashboard, for chart rendering
|
||||
static void timer_func(void *param) {
|
||||
char buf[50];
|
||||
mg_snprintf(buf, sizeof(buf), "[ %lu, %d ]", (unsigned long) time(NULL),
|
||||
10 + (int) ((double) rand() * 10 / RAND_MAX));
|
||||
// MG_INFO(("%s", buf));
|
||||
send_notification(param, "metrics", buf);
|
||||
}
|
||||
|
||||
// HTTP request handler function
|
||||
void device_dashboard_fn(struct mg_connection *c, int ev, void *ev_data,
|
||||
void *fn_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
if (ev == MG_EV_OPEN && c->is_listening) {
|
||||
mg_timer_add(c->mgr, 1000, MG_TIMER_REPEAT, timer_func, c->mgr);
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
struct user *u = getuser(hm);
|
||||
// MG_INFO(("%p [%.*s] auth %s", c->fd, (int) hm->uri.len, hm->uri.ptr,
|
||||
@ -97,14 +107,15 @@ void device_dashboard_fn(struct mg_connection *c, int ev, void *ev_data,
|
||||
// Admins only
|
||||
if (strcmp(u->name, "admin") == 0) {
|
||||
if (update_config(hm, &s_config))
|
||||
send_notification(fn_data, "config", NULL);
|
||||
send_notification(fn_data, "config", "null");
|
||||
mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
|
||||
} else {
|
||||
mg_printf(c, "%s", "HTTP/1.1 403 Denied\r\nContent-Length: 0\r\n\r\n");
|
||||
}
|
||||
} else if (mg_http_match_uri(hm, "/api/message/send")) {
|
||||
char buf[256];
|
||||
if (mg_http_get_var(&hm->body, "message", buf, sizeof(buf)) > 0) {
|
||||
if (mg_http_get_var(&hm->body, "message", buf + 1, sizeof(buf) - 2) > 0) {
|
||||
buf[0] = buf[strlen(buf)] = '"';
|
||||
send_notification(fn_data, "message", buf);
|
||||
}
|
||||
mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
|
||||
@ -118,7 +129,7 @@ void device_dashboard_fn(struct mg_connection *c, int ev, void *ev_data,
|
||||
mg_http_printf_chunk(c, "");
|
||||
} else {
|
||||
struct mg_http_serve_opts opts = {0};
|
||||
#if 1
|
||||
#if 0
|
||||
opts.root_dir = "/web_root";
|
||||
opts.fs = &mg_fs_packed;
|
||||
#else
|
||||
|
1
examples/device-dashboard/web_root/chartist.min.css
vendored
Normal file
1
examples/device-dashboard/web_root/chartist.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
10
examples/device-dashboard/web_root/chartist.min.js
vendored
Normal file
10
examples/device-dashboard/web_root/chartist.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -5,8 +5,10 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="chartist.min.css" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body></body>
|
||||
<script src="chartist.min.js"></script>
|
||||
<script type="module" src="main.js"></script>
|
||||
</html>
|
||||
|
@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
import {Component, h, html, render, useEffect, useState} from './preact.min.js';
|
||||
import {Component, h, html, render, useEffect, useState, useRef} from './preact.min.js';
|
||||
|
||||
const Nav = props => html`
|
||||
<div style="background: #333; padding: 0.5em; color: #fff;">
|
||||
@ -50,7 +50,7 @@ const Hero = props => html`
|
||||
<div><code>curl localhost:8000/api/config/get</code> - get current device configuration</div>
|
||||
<div><code>curl localhost:8000/api/config/set -d 'value1=7&value2=hello'</code> - set device configuration</div>
|
||||
<div><code>curl localhost:8000/api/message/send -d 'msg=hello'</code> - send chat message</div>
|
||||
<div><code>curl localhost:8000/api/watch</code> - get notifications on config changes or chat messages</div>
|
||||
<div><code>curl localhost:8000/api/watch</code> - get notifications: chat, config, metrics</div>
|
||||
|
||||
</div>`;
|
||||
|
||||
@ -182,8 +182,31 @@ const Chat = function(props) {
|
||||
</div>`;
|
||||
};
|
||||
|
||||
const datefmt = unix => (new Date(unix * 1000)).toISOString().substr(14, 5);
|
||||
|
||||
const Chart = function(props) {
|
||||
const chartdiv = useRef(null);
|
||||
const refresh = () => {
|
||||
const labels = props.metrics.map(el => datefmt(el[0]));
|
||||
const series = props.metrics.map(el => el[1]);
|
||||
const options = {low: 0, high: 20, showArea: true};
|
||||
// console.log([labels, [series]]);
|
||||
new Chartist.Line(chartdiv.current, {labels, series: [series]}, options);
|
||||
};
|
||||
useEffect(() => {chartdiv && refresh()}, [props.metrics]);
|
||||
|
||||
return html`
|
||||
<div style="margin: 0 0.3em;">
|
||||
<h3 style="background: #ec3; color: #fff; padding: 0.4em;">Data Chart</h3>
|
||||
<div style="height: 14em; overflow: auto; padding: 0.5em; " class="border">
|
||||
<div ref=${chartdiv} style="height: 100%;" />
|
||||
</div>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
const App = function(props) {
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [metrics, setMetrics] = useState([]);
|
||||
const [user, setUser] = useState('');
|
||||
const [config, setConfig] = useState({});
|
||||
|
||||
@ -206,12 +229,15 @@ const App = function(props) {
|
||||
var f = function(reader) {
|
||||
return reader.read().then(function(result) {
|
||||
var data = String.fromCharCode.apply(null, result.value);
|
||||
var notification = JSON.parse(data);
|
||||
if (notification.name == 'config') refresh();
|
||||
if (notification.name == 'message') {
|
||||
setMessages(m => m.concat([notification.data]))
|
||||
var msg = JSON.parse(data);
|
||||
if (msg.name == 'config') {
|
||||
refresh();
|
||||
} else if (msg.name == 'message') {
|
||||
setMessages(m => m.concat([msg.data]));
|
||||
} else if (msg.name == 'metrics') {
|
||||
setMetrics(m => m.concat([msg.data]).splice(-10));
|
||||
}
|
||||
// console.log(notification);
|
||||
// console.log(msg);
|
||||
if (!result.done) return f(reader);
|
||||
});
|
||||
};
|
||||
@ -232,16 +258,16 @@ const App = function(props) {
|
||||
|
||||
if (!user) return html`<${Login} login=${login} />`;
|
||||
const admin = user == 'admin';
|
||||
const colsize = admin ? 'c4' : 'c6';
|
||||
const cs = admin ? html`<${ChangeSettings} config=${config} />` : '';
|
||||
return html`
|
||||
<${Nav} user=${user} logout=${logout} />
|
||||
<div class="container">
|
||||
<${Hero} />
|
||||
<div class="row">
|
||||
<div class="col ${colsize}"><${ShowSettings} config=${config} /></div>
|
||||
<div class="col ${colsize}">${cs}</div>
|
||||
<div class="col ${colsize}"><${Chat} messages=${messages} /></div>
|
||||
<div class="col c6"><${Chart} metrics=${metrics} /></div>
|
||||
<div class="col c6"><${ShowSettings} config=${config} /></div>
|
||||
<div class="col c6"><${Chat} messages=${messages} /></div>
|
||||
<div class="col c6">${cs}</div>
|
||||
</div>
|
||||
<${Footer} />
|
||||
</div>`;
|
||||
|
Loading…
Reference in New Issue
Block a user