mirror of
https://github.com/cesanta/mongoose.git
synced 2025-06-07 01:07:19 +08:00
Add examples for mongoose blog article
PUBLISHED_FROM=54f54211919bb2276e02b4d10306ffa5540a9313
This commit is contained in:
parent
93beff3b65
commit
776103068f
2
examples/connected_device_1/Makefile
Normal file
2
examples/connected_device_1/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
PROG = server
|
||||
include ../examples.mk
|
43
examples/connected_device_1/server.c
Normal file
43
examples/connected_device_1/server.c
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2015 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *p) {
|
||||
if (ev == MG_EV_HTTP_REQUEST) {
|
||||
mg_serve_http(nc, p, s_http_server_opts); // Serves static content
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
cs_stat_t st;
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||
if (nc == NULL) {
|
||||
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up HTTP server parameters
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||
|
||||
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Starting web server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
39
examples/connected_device_1/web_root/index.shtml
Normal file
39
examples/connected_device_1/web_root/index.shtml
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Connected device demo</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||
<h1>Connected device demo - static content only</h1>
|
||||
|
||||
<p>
|
||||
This page shows initial steps on how to make a device connected.
|
||||
Mongoose serves static content from the <code>web_root</code>
|
||||
directory. Images, CSS, HTML, JavaScript files could be dropped into the
|
||||
<code>web_root</code> directory - and they'll be served happily
|
||||
by Mongoose.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Below is a placeholder for the
|
||||
settings section, and a real-time dashboard, which will be
|
||||
implemented in the following examples.
|
||||
Both settings section, and a dashboard section, could be organised
|
||||
differently on this UI - for example, in different tabs. But, to
|
||||
make the demonstration clearer, we show everything on one page.
|
||||
</p>
|
||||
|
||||
<h1>Settings section</h1>
|
||||
<p>Nothing here yet</p>
|
||||
|
||||
<h1>Dashboard section</h1>
|
||||
<p>Nothing here yet</p>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
examples/connected_device_1/web_root/logo.png
Normal file
BIN
examples/connected_device_1/web_root/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
31
examples/connected_device_1/web_root/style.css
Normal file
31
examples/connected_device_1/web_root/style.css
Normal file
@ -0,0 +1,31 @@
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #36c;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
div.content {
|
||||
width: 800px;
|
||||
margin: 1em auto;
|
||||
padding: 2em;
|
||||
background-color: #fff;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 0 0.3em;
|
||||
color: #396;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: #69c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
2
examples/connected_device_2/Makefile
Normal file
2
examples/connected_device_2/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
PROG = server
|
||||
include ../examples.mk
|
81
examples/connected_device_2/server.c
Normal file
81
examples/connected_device_2/server.c
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2015 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
struct device_settings {
|
||||
char setting1[100];
|
||||
char setting2[100];
|
||||
};
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
static struct device_settings s_settings = { "value1", "value2" };
|
||||
|
||||
static void handle_save(struct mg_connection *nc, struct http_message *hm) {
|
||||
// Get form variables and store settings values
|
||||
mg_get_http_var(&hm->body, "setting1", s_settings.setting1,
|
||||
sizeof(s_settings.setting1));
|
||||
mg_get_http_var(&hm->body, "setting2", s_settings.setting2,
|
||||
sizeof(s_settings.setting2));
|
||||
|
||||
// Send response
|
||||
mg_printf(nc, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
|
||||
}
|
||||
|
||||
static void handle_ssi_call(struct mg_connection *nc, const char *param) {
|
||||
if (strcmp(param, "setting1") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting1);
|
||||
} else if (strcmp(param, "setting2") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting2);
|
||||
}
|
||||
}
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
|
||||
switch (ev) {
|
||||
case MG_EV_HTTP_REQUEST:
|
||||
if (mg_vcmp(&hm->uri, "/save") == 0) {
|
||||
handle_save(nc, hm);
|
||||
} else {
|
||||
mg_serve_http(nc, hm, s_http_server_opts); // Serve static content
|
||||
}
|
||||
break;
|
||||
case MG_EV_SSI_CALL:
|
||||
handle_ssi_call(nc, ev_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
cs_stat_t st;
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||
if (nc == NULL) {
|
||||
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up HTTP server parameters
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||
|
||||
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Starting web server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
46
examples/connected_device_2/web_root/index.shtml
Normal file
46
examples/connected_device_2/web_root/index.shtml
Normal file
@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Connected device demo</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||
<h1>Connected device demo - settings section</h1>
|
||||
|
||||
<p>
|
||||
This page is one of the series of examples that show how to make
|
||||
devices connected with Mongoose. Here, we create a settings section
|
||||
that expose some internal device variables, and allow user to change
|
||||
them. They could be some stored in a database, in a file, or in
|
||||
firmware RAM. In this example, we store them in RAM, and use two
|
||||
variables, one integer and one string. We use SSI
|
||||
<code><!--#call parameter_name --></code> directive to fetch
|
||||
variable values from Mongoose.
|
||||
</p>
|
||||
|
||||
<h1>Settings section</h1>
|
||||
<form method="POST" action="/save">
|
||||
<fieldset>
|
||||
<legend>Device settings</legend>
|
||||
<label>Setting 1:</label> <input type="text"
|
||||
name="setting1" value="<!--#call setting1 -->" >
|
||||
<label>Setting 2:</label> <input type="text"
|
||||
name="setting2" value="<!--#call setting2 -->" >
|
||||
|
||||
<div>
|
||||
<button type="submit">Save Settings</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<h1>Dashboard section</h1>
|
||||
<p>Nothing here yet</p>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
examples/connected_device_2/web_root/logo.png
Normal file
BIN
examples/connected_device_2/web_root/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
67
examples/connected_device_2/web_root/style.css
Normal file
67
examples/connected_device_2/web_root/style.css
Normal file
@ -0,0 +1,67 @@
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #36c;
|
||||
color: #555;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
div.content {
|
||||
width: 800px;
|
||||
margin: 1em auto;
|
||||
padding: 2em;
|
||||
background-color: #fff;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #ccc;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
fieldset legend {
|
||||
font-style: italic;
|
||||
color: silver;
|
||||
}
|
||||
|
||||
fieldset label {
|
||||
text-align: right;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
font-size: 100%;
|
||||
padding: 0.2em;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
color: #666;
|
||||
background: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.2em 2em;
|
||||
font-size: 100%;
|
||||
cursor: pointer;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 0 0.3em;
|
||||
color: #396;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: #69c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
2
examples/connected_device_3/Makefile
Normal file
2
examples/connected_device_3/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
PROG = server
|
||||
include ../examples.mk
|
98
examples/connected_device_3/server.c
Normal file
98
examples/connected_device_3/server.c
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright (c) 2015 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
struct device_settings {
|
||||
char setting1[100];
|
||||
char setting2[100];
|
||||
};
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
static struct device_settings s_settings = { "value1", "value2" };
|
||||
|
||||
static void handle_save(struct mg_connection *nc, struct http_message *hm) {
|
||||
// Get form variables and store settings values
|
||||
mg_get_http_var(&hm->body, "setting1", s_settings.setting1,
|
||||
sizeof(s_settings.setting1));
|
||||
mg_get_http_var(&hm->body, "setting2", s_settings.setting2,
|
||||
sizeof(s_settings.setting2));
|
||||
|
||||
// Send response
|
||||
mg_printf(nc, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
|
||||
}
|
||||
|
||||
static void handle_get_cpu_usage(struct mg_connection *nc) {
|
||||
// Generate random value, as an example of changing CPU usage
|
||||
// Getting real CPU usage depends on the OS.
|
||||
int cpu_usage = (double) rand() / RAND_MAX * 100.0;
|
||||
|
||||
// Use chunked encoding in order to avoid calculating Content-Length
|
||||
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||
|
||||
// Output JSON object which holds CPU usage data
|
||||
mg_printf_http_chunk(nc, "{ \"result\": %d }", cpu_usage);
|
||||
|
||||
// Send empty chunk, the end of response
|
||||
mg_send_http_chunk(nc, "", 0);
|
||||
}
|
||||
|
||||
static void handle_ssi_call(struct mg_connection *nc, const char *param) {
|
||||
if (strcmp(param, "setting1") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting1);
|
||||
} else if (strcmp(param, "setting2") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting2);
|
||||
}
|
||||
}
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
|
||||
switch (ev) {
|
||||
case MG_EV_HTTP_REQUEST:
|
||||
if (mg_vcmp(&hm->uri, "/save") == 0) {
|
||||
handle_save(nc, hm);
|
||||
} else if (mg_vcmp(&hm->uri, "/get_cpu_usage") == 0) {
|
||||
handle_get_cpu_usage(nc);
|
||||
} else {
|
||||
mg_serve_http(nc, hm, s_http_server_opts); // Serve static content
|
||||
}
|
||||
break;
|
||||
case MG_EV_SSI_CALL:
|
||||
handle_ssi_call(nc, ev_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
cs_stat_t st;
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||
if (nc == NULL) {
|
||||
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up HTTP server parameters
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||
|
||||
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Starting web server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
44
examples/connected_device_3/web_root/index.shtml
Normal file
44
examples/connected_device_3/web_root/index.shtml
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Connected device demo</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||
<h1>Connected device demo - RESTful call</h1>
|
||||
|
||||
<p>
|
||||
This page is one of the series of examples that show how to make
|
||||
devices connected with Mongoose. Here we add a RESTful call
|
||||
to the Mongoose to show CPU usage stats on a dashboard.
|
||||
RESTful call is done using Jquery's <code>ajax</code> call.
|
||||
</p>
|
||||
|
||||
<h1>Settings section</h1>
|
||||
<form method="POST" action="/save">
|
||||
<fieldset>
|
||||
<legend>Device settings</legend>
|
||||
<label>Setting 1:</label> <input type="text"
|
||||
name="setting1" value="<!--#call setting1 -->" >
|
||||
<label>Setting 2:</label> <input type="text"
|
||||
name="setting2" value="<!--#call setting2 -->" >
|
||||
|
||||
<div>
|
||||
<button type="submit">Save Settings</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<h1>Dashboard section</h1>
|
||||
<label>CPU usage:</label> <span id="cpu_usage"> </span>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
5
examples/connected_device_3/web_root/jquery-1.11.3.min.js
vendored
Normal file
5
examples/connected_device_3/web_root/jquery-1.11.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
examples/connected_device_3/web_root/logo.png
Normal file
BIN
examples/connected_device_3/web_root/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
12
examples/connected_device_3/web_root/main.js
Normal file
12
examples/connected_device_3/web_root/main.js
Normal file
@ -0,0 +1,12 @@
|
||||
$(document).ready(function() {
|
||||
// Start 1-second timer to call RESTful endpoint
|
||||
setInterval(function() {
|
||||
$.ajax({
|
||||
url: '/get_cpu_usage',
|
||||
dataType: 'json',
|
||||
success: function(json) {
|
||||
$('#cpu_usage').text(json.result + '% ');
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
});
|
66
examples/connected_device_3/web_root/style.css
Normal file
66
examples/connected_device_3/web_root/style.css
Normal file
@ -0,0 +1,66 @@
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #36c;
|
||||
color: #555;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
div.content {
|
||||
width: 800px;
|
||||
margin: 1em auto;
|
||||
padding: 2em;
|
||||
background-color: #fff;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #ccc;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
fieldset legend {
|
||||
font-style: italic;
|
||||
color: silver;
|
||||
}
|
||||
|
||||
fieldset label {
|
||||
text-align: right;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
font-size: 100%;
|
||||
padding: 0.2em;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
color: #666;
|
||||
background: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.2em 2em;
|
||||
font-size: 100%;
|
||||
cursor: pointer;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 0 0.3em;
|
||||
color: #396;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: #69c;
|
||||
text-decoration: none;
|
||||
}
|
2
examples/connected_device_4/Makefile
Normal file
2
examples/connected_device_4/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
PROG = server
|
||||
include ../examples.mk
|
115
examples/connected_device_4/server.c
Normal file
115
examples/connected_device_4/server.c
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2015 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
struct device_settings {
|
||||
char setting1[100];
|
||||
char setting2[100];
|
||||
};
|
||||
|
||||
static const char *s_http_port = "8000";
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
static struct device_settings s_settings = { "value1", "value2" };
|
||||
|
||||
static void handle_save(struct mg_connection *nc, struct http_message *hm) {
|
||||
// Get form variables and store settings values
|
||||
mg_get_http_var(&hm->body, "setting1", s_settings.setting1,
|
||||
sizeof(s_settings.setting1));
|
||||
mg_get_http_var(&hm->body, "setting2", s_settings.setting2,
|
||||
sizeof(s_settings.setting2));
|
||||
|
||||
// Send response
|
||||
mg_printf(nc, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
|
||||
}
|
||||
|
||||
static void handle_get_cpu_usage(struct mg_connection *nc) {
|
||||
// Generate random value, as an example of changing CPU usage
|
||||
// Getting real CPU usage depends on the OS.
|
||||
int cpu_usage = (double) rand() / RAND_MAX * 100.0;
|
||||
|
||||
// Use chunked encoding in order to avoid calculating Content-Length
|
||||
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||
|
||||
// Output JSON object which holds CPU usage data
|
||||
mg_printf_http_chunk(nc, "{ \"result\": %d }", cpu_usage);
|
||||
|
||||
// Send empty chunk, the end of response
|
||||
mg_send_http_chunk(nc, "", 0);
|
||||
}
|
||||
|
||||
static void handle_ssi_call(struct mg_connection *nc, const char *param) {
|
||||
if (strcmp(param, "setting1") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting1);
|
||||
} else if (strcmp(param, "setting2") == 0) {
|
||||
mg_printf_html_escape(nc, "%s", s_settings.setting2);
|
||||
}
|
||||
}
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
|
||||
switch (ev) {
|
||||
case MG_EV_HTTP_REQUEST:
|
||||
if (mg_vcmp(&hm->uri, "/save") == 0) {
|
||||
handle_save(nc, hm);
|
||||
} else if (mg_vcmp(&hm->uri, "/get_cpu_usage") == 0) {
|
||||
handle_get_cpu_usage(nc);
|
||||
} else {
|
||||
mg_serve_http(nc, hm, s_http_server_opts); // Serve static content
|
||||
}
|
||||
break;
|
||||
case MG_EV_SSI_CALL:
|
||||
handle_ssi_call(nc, ev_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void push_data_to_all_websocket_connections(struct mg_mgr *m) {
|
||||
struct mg_connection *c;
|
||||
int memory_usage = (double) rand() / RAND_MAX * 100.0;
|
||||
|
||||
for (c = mg_next(m, NULL); c != NULL; c = mg_next(m, c)) {
|
||||
if (c->flags & MG_F_IS_WEBSOCKET) {
|
||||
mg_printf_websocket_frame(c, WEBSOCKET_OP_TEXT, "%d", memory_usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
cs_stat_t st;
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||
if (nc == NULL) {
|
||||
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up HTTP server parameters
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||
|
||||
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Starting web server on port %s\n", s_http_port);
|
||||
for (;;) {
|
||||
static time_t last_time;
|
||||
time_t now = time(NULL);
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
if (now - last_time > 0) {
|
||||
push_data_to_all_websocket_connections(&mgr);
|
||||
last_time = now;
|
||||
}
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
46
examples/connected_device_4/web_root/index.shtml
Normal file
46
examples/connected_device_4/web_root/index.shtml
Normal file
@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Connected device demo</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
|
||||
<script src="jquery.flot.min.js"></script>
|
||||
<script src="jquery.flot.time.js"></script>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||
<h1>Connected device demo - Websocket push</h1>
|
||||
|
||||
<p>
|
||||
This page is one of the series of examples that show how to make
|
||||
devices connected with Mongoose. Here we add a real-time Websocket
|
||||
server push, and update dashboard graph as data arrives.
|
||||
</p>
|
||||
|
||||
<h1>Settings section</h1>
|
||||
<form method="POST" action="/save">
|
||||
<fieldset>
|
||||
<legend>Device settings</legend>
|
||||
<label>Setting 1:</label> <input type="text"
|
||||
name="setting1" value="<!--#call setting1 -->" >
|
||||
<label>Setting 2:</label> <input type="text"
|
||||
name="setting2" value="<!--#call setting2 -->" >
|
||||
|
||||
<div>
|
||||
<button type="submit">Save Settings</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<h1>Dashboard section</h1>
|
||||
<label>CPU usage:</label> <span id="cpu_usage"> </span>
|
||||
<div id="graphs"></div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
5
examples/connected_device_4/web_root/jquery-1.11.3.min.js
vendored
Normal file
5
examples/connected_device_4/web_root/jquery-1.11.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
8
examples/connected_device_4/web_root/jquery.flot.min.js
vendored
Normal file
8
examples/connected_device_4/web_root/jquery.flot.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
432
examples/connected_device_4/web_root/jquery.flot.time.js
Normal file
432
examples/connected_device_4/web_root/jquery.flot.time.js
Normal file
@ -0,0 +1,432 @@
|
||||
/* Pretty handling of time axes.
|
||||
|
||||
Copyright (c) 2007-2014 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
Set axis.mode to "time" to enable. See the section "Time series data" in
|
||||
API.txt for details.
|
||||
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
var options = {
|
||||
xaxis: {
|
||||
timezone: null, // "browser" for local to the client or timezone for timezone-js
|
||||
timeformat: null, // format string to use
|
||||
twelveHourClock: false, // 12 or 24 time in time mode
|
||||
monthNames: null // list of names of months
|
||||
}
|
||||
};
|
||||
|
||||
// round to nearby lower multiple of base
|
||||
|
||||
function floorInBase(n, base) {
|
||||
return base * Math.floor(n / base);
|
||||
}
|
||||
|
||||
// Returns a string with the date d formatted according to fmt.
|
||||
// A subset of the Open Group's strftime format is supported.
|
||||
|
||||
function formatDate(d, fmt, monthNames, dayNames) {
|
||||
|
||||
if (typeof d.strftime == "function") {
|
||||
return d.strftime(fmt);
|
||||
}
|
||||
|
||||
var leftPad = function(n, pad) {
|
||||
n = "" + n;
|
||||
pad = "" + (pad == null ? "0" : pad);
|
||||
return n.length == 1 ? pad + n : n;
|
||||
};
|
||||
|
||||
var r = [];
|
||||
var escape = false;
|
||||
var hours = d.getHours();
|
||||
var isAM = hours < 12;
|
||||
|
||||
if (monthNames == null) {
|
||||
monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
}
|
||||
|
||||
if (dayNames == null) {
|
||||
dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
}
|
||||
|
||||
var hours12;
|
||||
|
||||
if (hours > 12) {
|
||||
hours12 = hours - 12;
|
||||
} else if (hours == 0) {
|
||||
hours12 = 12;
|
||||
} else {
|
||||
hours12 = hours;
|
||||
}
|
||||
|
||||
for (var i = 0; i < fmt.length; ++i) {
|
||||
|
||||
var c = fmt.charAt(i);
|
||||
|
||||
if (escape) {
|
||||
switch (c) {
|
||||
case 'a': c = "" + dayNames[d.getDay()]; break;
|
||||
case 'b': c = "" + monthNames[d.getMonth()]; break;
|
||||
case 'd': c = leftPad(d.getDate()); break;
|
||||
case 'e': c = leftPad(d.getDate(), " "); break;
|
||||
case 'h': // For back-compat with 0.7; remove in 1.0
|
||||
case 'H': c = leftPad(hours); break;
|
||||
case 'I': c = leftPad(hours12); break;
|
||||
case 'l': c = leftPad(hours12, " "); break;
|
||||
case 'm': c = leftPad(d.getMonth() + 1); break;
|
||||
case 'M': c = leftPad(d.getMinutes()); break;
|
||||
// quarters not in Open Group's strftime specification
|
||||
case 'q':
|
||||
c = "" + (Math.floor(d.getMonth() / 3) + 1); break;
|
||||
case 'S': c = leftPad(d.getSeconds()); break;
|
||||
case 'y': c = leftPad(d.getFullYear() % 100); break;
|
||||
case 'Y': c = "" + d.getFullYear(); break;
|
||||
case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
|
||||
case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
|
||||
case 'w': c = "" + d.getDay(); break;
|
||||
}
|
||||
r.push(c);
|
||||
escape = false;
|
||||
} else {
|
||||
if (c == "%") {
|
||||
escape = true;
|
||||
} else {
|
||||
r.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r.join("");
|
||||
}
|
||||
|
||||
// To have a consistent view of time-based data independent of which time
|
||||
// zone the client happens to be in we need a date-like object independent
|
||||
// of time zones. This is done through a wrapper that only calls the UTC
|
||||
// versions of the accessor methods.
|
||||
|
||||
function makeUtcWrapper(d) {
|
||||
|
||||
function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
|
||||
sourceObj[sourceMethod] = function() {
|
||||
return targetObj[targetMethod].apply(targetObj, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
var utc = {
|
||||
date: d
|
||||
};
|
||||
|
||||
// support strftime, if found
|
||||
|
||||
if (d.strftime != undefined) {
|
||||
addProxyMethod(utc, "strftime", d, "strftime");
|
||||
}
|
||||
|
||||
addProxyMethod(utc, "getTime", d, "getTime");
|
||||
addProxyMethod(utc, "setTime", d, "setTime");
|
||||
|
||||
var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"];
|
||||
|
||||
for (var p = 0; p < props.length; p++) {
|
||||
addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
|
||||
addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
|
||||
}
|
||||
|
||||
return utc;
|
||||
};
|
||||
|
||||
// select time zone strategy. This returns a date-like object tied to the
|
||||
// desired timezone
|
||||
|
||||
function dateGenerator(ts, opts) {
|
||||
if (opts.timezone == "browser") {
|
||||
return new Date(ts);
|
||||
} else if (!opts.timezone || opts.timezone == "utc") {
|
||||
return makeUtcWrapper(new Date(ts));
|
||||
} else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
|
||||
var d = new timezoneJS.Date();
|
||||
// timezone-js is fickle, so be sure to set the time zone before
|
||||
// setting the time.
|
||||
d.setTimezone(opts.timezone);
|
||||
d.setTime(ts);
|
||||
return d;
|
||||
} else {
|
||||
return makeUtcWrapper(new Date(ts));
|
||||
}
|
||||
}
|
||||
|
||||
// map of app. size of time units in milliseconds
|
||||
|
||||
var timeUnitSize = {
|
||||
"second": 1000,
|
||||
"minute": 60 * 1000,
|
||||
"hour": 60 * 60 * 1000,
|
||||
"day": 24 * 60 * 60 * 1000,
|
||||
"month": 30 * 24 * 60 * 60 * 1000,
|
||||
"quarter": 3 * 30 * 24 * 60 * 60 * 1000,
|
||||
"year": 365.2425 * 24 * 60 * 60 * 1000
|
||||
};
|
||||
|
||||
// the allowed tick sizes, after 1 year we use
|
||||
// an integer algorithm
|
||||
|
||||
var baseSpec = [
|
||||
[1, "second"], [2, "second"], [5, "second"], [10, "second"],
|
||||
[30, "second"],
|
||||
[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
|
||||
[30, "minute"],
|
||||
[1, "hour"], [2, "hour"], [4, "hour"],
|
||||
[8, "hour"], [12, "hour"],
|
||||
[1, "day"], [2, "day"], [3, "day"],
|
||||
[0.25, "month"], [0.5, "month"], [1, "month"],
|
||||
[2, "month"]
|
||||
];
|
||||
|
||||
// we don't know which variant(s) we'll need yet, but generating both is
|
||||
// cheap
|
||||
|
||||
var specMonths = baseSpec.concat([[3, "month"], [6, "month"],
|
||||
[1, "year"]]);
|
||||
var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"],
|
||||
[1, "year"]]);
|
||||
|
||||
function init(plot) {
|
||||
plot.hooks.processOptions.push(function (plot, options) {
|
||||
$.each(plot.getAxes(), function(axisName, axis) {
|
||||
|
||||
var opts = axis.options;
|
||||
|
||||
if (opts.mode == "time") {
|
||||
axis.tickGenerator = function(axis) {
|
||||
|
||||
var ticks = [];
|
||||
var d = dateGenerator(axis.min, opts);
|
||||
var minSize = 0;
|
||||
|
||||
// make quarter use a possibility if quarters are
|
||||
// mentioned in either of these options
|
||||
|
||||
var spec = (opts.tickSize && opts.tickSize[1] ===
|
||||
"quarter") ||
|
||||
(opts.minTickSize && opts.minTickSize[1] ===
|
||||
"quarter") ? specQuarters : specMonths;
|
||||
|
||||
if (opts.minTickSize != null) {
|
||||
if (typeof opts.tickSize == "number") {
|
||||
minSize = opts.tickSize;
|
||||
} else {
|
||||
minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < spec.length - 1; ++i) {
|
||||
if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]]
|
||||
+ spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
|
||||
&& spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var size = spec[i][0];
|
||||
var unit = spec[i][1];
|
||||
|
||||
// special-case the possibility of several years
|
||||
|
||||
if (unit == "year") {
|
||||
|
||||
// if given a minTickSize in years, just use it,
|
||||
// ensuring that it's an integer
|
||||
|
||||
if (opts.minTickSize != null && opts.minTickSize[1] == "year") {
|
||||
size = Math.floor(opts.minTickSize[0]);
|
||||
} else {
|
||||
|
||||
var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10));
|
||||
var norm = (axis.delta / timeUnitSize.year) / magn;
|
||||
|
||||
if (norm < 1.5) {
|
||||
size = 1;
|
||||
} else if (norm < 3) {
|
||||
size = 2;
|
||||
} else if (norm < 7.5) {
|
||||
size = 5;
|
||||
} else {
|
||||
size = 10;
|
||||
}
|
||||
|
||||
size *= magn;
|
||||
}
|
||||
|
||||
// minimum size for years is 1
|
||||
|
||||
if (size < 1) {
|
||||
size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
axis.tickSize = opts.tickSize || [size, unit];
|
||||
var tickSize = axis.tickSize[0];
|
||||
unit = axis.tickSize[1];
|
||||
|
||||
var step = tickSize * timeUnitSize[unit];
|
||||
|
||||
if (unit == "second") {
|
||||
d.setSeconds(floorInBase(d.getSeconds(), tickSize));
|
||||
} else if (unit == "minute") {
|
||||
d.setMinutes(floorInBase(d.getMinutes(), tickSize));
|
||||
} else if (unit == "hour") {
|
||||
d.setHours(floorInBase(d.getHours(), tickSize));
|
||||
} else if (unit == "month") {
|
||||
d.setMonth(floorInBase(d.getMonth(), tickSize));
|
||||
} else if (unit == "quarter") {
|
||||
d.setMonth(3 * floorInBase(d.getMonth() / 3,
|
||||
tickSize));
|
||||
} else if (unit == "year") {
|
||||
d.setFullYear(floorInBase(d.getFullYear(), tickSize));
|
||||
}
|
||||
|
||||
// reset smaller components
|
||||
|
||||
d.setMilliseconds(0);
|
||||
|
||||
if (step >= timeUnitSize.minute) {
|
||||
d.setSeconds(0);
|
||||
}
|
||||
if (step >= timeUnitSize.hour) {
|
||||
d.setMinutes(0);
|
||||
}
|
||||
if (step >= timeUnitSize.day) {
|
||||
d.setHours(0);
|
||||
}
|
||||
if (step >= timeUnitSize.day * 4) {
|
||||
d.setDate(1);
|
||||
}
|
||||
if (step >= timeUnitSize.month * 2) {
|
||||
d.setMonth(floorInBase(d.getMonth(), 3));
|
||||
}
|
||||
if (step >= timeUnitSize.quarter * 2) {
|
||||
d.setMonth(floorInBase(d.getMonth(), 6));
|
||||
}
|
||||
if (step >= timeUnitSize.year) {
|
||||
d.setMonth(0);
|
||||
}
|
||||
|
||||
var carry = 0;
|
||||
var v = Number.NaN;
|
||||
var prev;
|
||||
|
||||
do {
|
||||
|
||||
prev = v;
|
||||
v = d.getTime();
|
||||
ticks.push(v);
|
||||
|
||||
if (unit == "month" || unit == "quarter") {
|
||||
if (tickSize < 1) {
|
||||
|
||||
// a bit complicated - we'll divide the
|
||||
// month/quarter up but we need to take
|
||||
// care of fractions so we don't end up in
|
||||
// the middle of a day
|
||||
|
||||
d.setDate(1);
|
||||
var start = d.getTime();
|
||||
d.setMonth(d.getMonth() +
|
||||
(unit == "quarter" ? 3 : 1));
|
||||
var end = d.getTime();
|
||||
d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
|
||||
carry = d.getHours();
|
||||
d.setHours(0);
|
||||
} else {
|
||||
d.setMonth(d.getMonth() +
|
||||
tickSize * (unit == "quarter" ? 3 : 1));
|
||||
}
|
||||
} else if (unit == "year") {
|
||||
d.setFullYear(d.getFullYear() + tickSize);
|
||||
} else {
|
||||
d.setTime(v + step);
|
||||
}
|
||||
} while (v < axis.max && v != prev);
|
||||
|
||||
return ticks;
|
||||
};
|
||||
|
||||
axis.tickFormatter = function (v, axis) {
|
||||
|
||||
var d = dateGenerator(v, axis.options);
|
||||
|
||||
// first check global format
|
||||
|
||||
if (opts.timeformat != null) {
|
||||
return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
|
||||
}
|
||||
|
||||
// possibly use quarters if quarters are mentioned in
|
||||
// any of these places
|
||||
|
||||
var useQuarters = (axis.options.tickSize &&
|
||||
axis.options.tickSize[1] == "quarter") ||
|
||||
(axis.options.minTickSize &&
|
||||
axis.options.minTickSize[1] == "quarter");
|
||||
|
||||
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
|
||||
var span = axis.max - axis.min;
|
||||
var suffix = (opts.twelveHourClock) ? " %p" : "";
|
||||
var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
|
||||
var fmt;
|
||||
|
||||
if (t < timeUnitSize.minute) {
|
||||
fmt = hourCode + ":%M:%S" + suffix;
|
||||
} else if (t < timeUnitSize.day) {
|
||||
if (span < 2 * timeUnitSize.day) {
|
||||
fmt = hourCode + ":%M" + suffix;
|
||||
} else {
|
||||
fmt = "%b %d " + hourCode + ":%M" + suffix;
|
||||
}
|
||||
} else if (t < timeUnitSize.month) {
|
||||
fmt = "%b %d";
|
||||
} else if ((useQuarters && t < timeUnitSize.quarter) ||
|
||||
(!useQuarters && t < timeUnitSize.year)) {
|
||||
if (span < timeUnitSize.year) {
|
||||
fmt = "%b";
|
||||
} else {
|
||||
fmt = "%b %Y";
|
||||
}
|
||||
} else if (useQuarters && t < timeUnitSize.year) {
|
||||
if (span < timeUnitSize.year) {
|
||||
fmt = "Q%q";
|
||||
} else {
|
||||
fmt = "Q%q %Y";
|
||||
}
|
||||
} else {
|
||||
fmt = "%Y";
|
||||
}
|
||||
|
||||
var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
|
||||
|
||||
return rt;
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: options,
|
||||
name: 'time',
|
||||
version: '1.0'
|
||||
});
|
||||
|
||||
// Time-axis support used to be in Flot core, which exposed the
|
||||
// formatDate function on the plot object. Various plugins depend
|
||||
// on the function, so we need to re-expose it here.
|
||||
|
||||
$.plot.formatDate = formatDate;
|
||||
$.plot.dateGenerator = dateGenerator;
|
||||
|
||||
})(jQuery);
|
BIN
examples/connected_device_4/web_root/logo.png
Normal file
BIN
examples/connected_device_4/web_root/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
75
examples/connected_device_4/web_root/main.js
Normal file
75
examples/connected_device_4/web_root/main.js
Normal file
@ -0,0 +1,75 @@
|
||||
$(document).ready(function() {
|
||||
// Start 1-second timer to call RESTful endpoint
|
||||
setInterval(function() {
|
||||
$.ajax({
|
||||
url: '/get_cpu_usage',
|
||||
dataType: 'json',
|
||||
success: function(json) {
|
||||
$('#cpu_usage').text(json.result + '% ');
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
// Initialize graph
|
||||
var $c = $('<div class="graph"/>').appendTo('#graphs');
|
||||
var dur = 120;
|
||||
var past = Date.now() - dur * 1000;
|
||||
var options = {
|
||||
lines: {
|
||||
fill: true,
|
||||
lineWidth: 0,
|
||||
fillColor: {colors: [ { opacity: 0 }, { opacity: 1 } ] }
|
||||
},
|
||||
grid: { borderWidth: 1, borderColor: '#ccc'},
|
||||
xaxis: { mode: 'time', ticks: 5 },
|
||||
legend: { labelBoxBorderColor: '#fff' },
|
||||
colors: [ '#fec', '#396', '#e39', '9e2' ],
|
||||
hooks: {
|
||||
draw: [function(plot, canvas) {
|
||||
canvas.font = '13px sans-serif';
|
||||
canvas.fillStyle = '#aaa';
|
||||
canvas.fillText('Resource Usage', 35, 25);
|
||||
}]
|
||||
}
|
||||
};
|
||||
var plot = $.plot($c, [], options);
|
||||
|
||||
var updateGraph = function(counter) {
|
||||
var data = plot.getData();
|
||||
var now = Date.now();
|
||||
var oldest = now - dur * 1000;
|
||||
data[0] = {
|
||||
show: false,
|
||||
data: [[oldest, null], [now, null]]
|
||||
};
|
||||
|
||||
console.log(data);
|
||||
|
||||
// Remove old points
|
||||
$.each(data, function(di, d) {
|
||||
while (d.data.length > 0 && d.data[0][0] < oldest) {
|
||||
d.data.shift();
|
||||
}
|
||||
});
|
||||
|
||||
// Add new points
|
||||
var new_datapoint = [now, counter];
|
||||
if (data[1]) {
|
||||
data[1].label = 'memory';
|
||||
data[1].data.push(new_datapoint);
|
||||
} else {
|
||||
data[1] = [{ data: [new_datapoint] }];
|
||||
}
|
||||
|
||||
// Redraw the graph
|
||||
plot.setData(data);
|
||||
plot.setupGrid();
|
||||
plot.draw();
|
||||
};
|
||||
|
||||
// Create Websocket connection. For simplicity, no reconnect logic is here.
|
||||
var ws = new WebSocket('ws://' + location.host);
|
||||
ws.onmessage = function(ev) {
|
||||
updateGraph(ev.data);
|
||||
};
|
||||
});
|
87
examples/connected_device_4/web_root/style.css
Normal file
87
examples/connected_device_4/web_root/style.css
Normal file
@ -0,0 +1,87 @@
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #36c;
|
||||
color: #555;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
div.content {
|
||||
width: 800px;
|
||||
margin: 1em auto;
|
||||
padding: 2em;
|
||||
background-color: #fff;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #ccc;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
fieldset legend {
|
||||
font-style: italic;
|
||||
color: silver;
|
||||
}
|
||||
|
||||
fieldset label {
|
||||
text-align: right;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
font-size: 100%;
|
||||
padding: 0.2em;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
color: #666;
|
||||
background: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.2em 2em;
|
||||
font-size: 100%;
|
||||
cursor: pointer;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 0 0.3em;
|
||||
color: #396;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: #69c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#graphs {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.graph {
|
||||
min-height: 12em;
|
||||
border: 0px solid #ccc;
|
||||
position: relative;
|
||||
margin: 0 1em 1em 0;
|
||||
width: 36em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.graph_title {
|
||||
position: absolute;
|
||||
top: 0.5em; left: 1em;
|
||||
color: #ccc;
|
||||
font-weight: bold;
|
||||
z-index: 999;
|
||||
}
|
13
examples/examples.mk
Normal file
13
examples/examples.mk
Normal file
@ -0,0 +1,13 @@
|
||||
SOURCES = $(PROG).c ../../mongoose.c
|
||||
CFLAGS = -W -Wall -I../.. -Wno-unused-function $(CFLAGS_EXTRA) $(MODULE_CFLAGS)
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(SOURCES)
|
||||
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||
|
||||
$(PROG).exe: $(SOURCES)
|
||||
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||
|
||||
clean:
|
||||
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
Loading…
Reference in New Issue
Block a user