diff options
author | Andy Green <andy.green@linaro.org> | 2012-10-06 20:04:42 +0800 |
---|---|---|
committer | Andy Green <andy.green@linaro.org> | 2012-10-15 07:37:49 +0800 |
commit | 68d6c70d6b330941a5f22a35269d63d9d920064a (patch) | |
tree | ae26a7615239a3a67b30bb7d80e99dbee8e33a92 | |
parent | e1d63c1dfceef029bff15eda57fce6255bd159eb (diff) |
introduce aepd
Signed-off-by: Andy Green <andy.green@linaro.org>
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | README | 8 | ||||
-rw-r--r-- | aepd/Makefile.am | 25 | ||||
-rw-r--r-- | aepd/aepd.h | 65 | ||||
-rw-r--r-- | aepd/main.c | 313 | ||||
-rw-r--r-- | aepd/share/aepscope.html | 475 | ||||
-rw-r--r-- | aepd/share/favicon.ico | bin | 0 -> 1150 bytes | |||
-rw-r--r-- | aepd/share/linaro-logo-32.png | bin | 0 -> 5126 bytes | |||
-rw-r--r-- | aepd/websocket-protocol.c | 218 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | libarmep/sample.c | 2 | ||||
-rw-r--r-- | libarmep/service.c | 18 |
12 files changed, 1122 insertions, 7 deletions
diff --git a/Makefile.am b/Makefile.am index 6fff323..0a19074 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,2 +1,2 @@ +SUBDIRS = libarmep aepd arm-probe -SUBDIRS=libarmep arm-probe @@ -16,6 +16,14 @@ Make sure you have packages installed. +To build the websockets-based daemon, you will need to build libwebsockets, +built and installed on your box, which you can get from here + +git clone git://git.warmcat.com/libwebsockets + +It uses the same autotools based build approach. + + To initially configure the sources for your system, or if you change the autotools-related content: diff --git a/aepd/Makefile.am b/aepd/Makefile.am new file mode 100644 index 0000000..9096ae3 --- /dev/null +++ b/aepd/Makefile.am @@ -0,0 +1,25 @@ +bin_PROGRAMS=aepd +aepd_SOURCES=main.c websocket-protocol.c +aepd_CFLAGS=-fPIC -Wall -Werror -std=gnu99 -pedantic -DINSTALL_DATADIR=\"${datarootdir}\" +aepd_LDFLAGS=-fPIC +aepd_LDADD=-L../libarmep -larmep -lwebsockets + +# +# cook a random test cert and key +# notice your real cert and key will want to be 0600 permissions +# +aepd.pem aepd.key.pem: + printf "GB\nErewhon\nAll around\naepd-test-cert\n\nlocalhost\nnone@invalid.org\n" | \ + openssl req -new -newkey rsa:1024 -days 10000 -nodes -x509 -keyout \ + ./aepd.key.pem -out ./aepd.pem >/dev/null 2>&1 && \ + chmod 644 ./aepd.key.pem \ + ./aepd.pem + +clean-local: + rm -f ./aepd.key.pem ./aepd.pem + +install-data-local: aepd.pem aepd.key.pem + mkdir -p $(DESTDIR)$(datadir)/aepd + cp share/aepscope.html share/favicon.ico aepd.pem aepd.key.pem share/linaro-logo-32.png \ + $(DESTDIR)$(datadir)/aepd + diff --git a/aepd/aepd.h b/aepd/aepd.h new file mode 100644 index 0000000..cb3ed3d --- /dev/null +++ b/aepd/aepd.h @@ -0,0 +1,65 @@ +/* + * Author: Andy Green <andy.green@linaro.org> + * Copyright (C) 2012 Linaro, LTD + * Libwebsocket demo code (C) 2010-2012 Andy Green <andy@warmcat.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#include <string.h> +#include <sys/time.h> +#include <sys/mman.h> + +#include <libwebsockets.h> + +#include "../libarmep/libarmep.h" + +#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/aepd" + +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP, + + PROTOCOL_AEPD, + + /* always last */ + DEMO_PROTOCOL_COUNT +}; + +#define MAX_FIFO_EXTENT_SECONDS 10 + +#define DOUBLES_PER_CH_SAMPLE 3 +#define MAX_FIFO_EXTENT (MAX_PROBES * CHANNELS_PER_PROBE * sizeof(double) * \ + DOUBLES_PER_CH_SAMPLE * 10000 * MAX_FIFO_EXTENT_SECONDS) + +struct aepd_shared { + char fifo_filepath_stg[L_tmpnam + 1]; + char *fifo_filepath; + int fd_fifo_write; + int fd_fifo_read; + int fifo_wrapped; + int chans; + unsigned long fifo_pos; + double fifo_head_time; + unsigned long modulo_integer_chan_size; +}; + +extern struct aepd_shared *aepd_shared; diff --git a/aepd/main.c b/aepd/main.c new file mode 100644 index 0000000..c11c91e --- /dev/null +++ b/aepd/main.c @@ -0,0 +1,313 @@ +/* + * Author: Andy Green <andy.green@linaro.org> + * Copyright (C) 2012 Linaro, LTD + * Libwebsocket demo code (C) 2010-2012 Andy Green <andy@warmcat.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "aepd.h" + +struct aepd_shared *aepd_shared; +static double sam[MAX_PROBES * CHANNELS_PER_PROBE * DOUBLES_PER_CH_SAMPLE]; +extern struct libwebsocket_protocols protocols[]; + +void zero_fifo(void) +{ + int n; + + lseek(aepd_shared->fd_fifo_write, 0, SEEK_SET); + aepd_shared->fifo_pos = 0; + aepd_shared->fifo_wrapped = 0; + for (n = 0; n < sizeof(sam) / sizeof(sam[0]); n++) + sam[n] = 0.0; + if (aepd_shared->chans > 0) + aepd_shared->modulo_integer_chan_size = + (MAX_FIFO_EXTENT / (aepd_shared->chans * sizeof(double) * 3)) * + (aepd_shared->chans * sizeof(double) * 3); +} + +int add_fifo(void *data, unsigned int length) +{ + long l; + + l = write(aepd_shared->fd_fifo_write, data, length); + if (l < length) { + fprintf(stderr, "fifo write failed...\n"); + return (int)l; + } + + /* + * make sure the other processes can see it... + */ + fdatasync(aepd_shared->fd_fifo_write); + + aepd_shared->fifo_pos += length; + if (aepd_shared->fifo_pos >= aepd_shared->modulo_integer_chan_size) { + aepd_shared->fifo_wrapped = 1; + aepd_shared->fifo_pos = 0; + lseek(aepd_shared->fd_fifo_write, 0, SEEK_SET); + } + + return 0; +} + +struct aep_context aep_context = { + .config_filepath = "./config", + .highest = -1, + .decimate = 1, + .mv_min = 400, + .trigger_filter_us = 400, + .end_trigger_filter_us = 500000, + .average_len = 1, + .configuration_name = "default_configuration", + .verbose = 0, +}; + + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "interface", required_argument, NULL, 'i' }, + { NULL, 0, 0, 0 } +}; + +static int loop = 1; + +void sighandler(int sig) +{ + loop = 0; +} + +int main(int argc, char *argv[]) +{ + int n = 0, m; + const char *cert_path = + LOCAL_RESOURCE_PATH"/aepd.pem"; + const char *key_path = + LOCAL_RESOURCE_PATH"/aepd.key.pem"; + int port = 15164; + int use_ssl = 0; + struct libwebsocket_context *context; + int opts = 0; + char interface_name[128] = ""; + const char *interface_ptr = NULL; + struct aep_result *aep_result; + struct timeval tv; + unsigned long last = 0; + unsigned long ms10 = -1; + + fprintf(stderr, + "ARM Energy Probe Daemon (C) Copyright 2012 Linaro, LTD\n" + "licensed under LGPL2.1\n"); + + signal(SIGINT, sighandler); + + /* + * create our lump of shared memory + * which is usable by all forked processes + */ + + n = open("/dev/zero", O_RDWR); + aepd_shared = mmap(NULL, sizeof (struct aepd_shared), + PROT_READ | PROT_WRITE, MAP_SHARED, n, 0); + close(n); + + /* + * open spool fifo for result data we can serve + * it's a large ring buffer to deal with long term pretrigger + * websocket clients can make requests for variously-zoomed + * parts of this buffer. + */ + + aepd_shared->fifo_filepath = tmpnam(aepd_shared->fifo_filepath_stg); + aepd_shared->fd_fifo_write = open(aepd_shared->fifo_filepath, + O_CREAT | O_RDWR, 0600); + if (aepd_shared->fd_fifo_write < 0) { + fprintf(stderr, "Unable to open sample fifo file %s\n", + aepd_shared->fifo_filepath); + return -1; + } + aepd_shared->fd_fifo_read = open(aepd_shared->fifo_filepath, O_RDONLY); + if (aepd_shared->fd_fifo_read < 0) { + fprintf(stderr, "Unable to open sample fifo file %s for read\n", + aepd_shared->fifo_filepath); + return -1; + } + + zero_fifo(); + + while (n >= 0) { + n = getopt_long(argc, argv, "si:p:", options, NULL); + if (n < 0) + continue; + switch (n) { + + case 's': + use_ssl = 1; + break; + case 'p': + port = atoi(optarg); + break; + case 'i': + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; + break; + + default: + case 'h': + fprintf(stderr, "Usage: arm-probe \n" + " [--ssl -s] Listen using SSL / Encrypted link\n" + " [--interface -i <if>] Listen on specific " + "interface, eg, eth1\n" + " [--port -p -<port>] Port to listen on " + "(default %u)\n", port + ); + exit(1); + } + } + + /* libwebsockets context */ + + if (!use_ssl) + cert_path = key_path = NULL; + + context = libwebsocket_create_context(port, interface_ptr, protocols, + NULL, + cert_path, key_path, -1, -1, opts); + if (context == NULL) { + fprintf(stderr, "libwebsocket init failed\n"); + return -1; + } + + aep_context.original_count_channel_names = aep_context.count_channel_names; + + configure(&aep_context, NULL, "xx", aep_context.config_filepath, NULL); + + + /* + * fork off the AEP service process + * runs in its own process to exploit SMP to dedicate one core for that + * what happens to samples is decoupled from capture process with large + * shared-memory buffer to allow for jitter + */ + + if (aep_init_and_fork(&aep_context, argv) < 1) + return 0; /* child process exit */ + + + n = fork(); + if (n < 0) { + fprintf(stderr, "websockets service fork failed\n"); + return n; + } + if (!n) { + strcpy(argv[0] + strlen(argv[0]), " - websockets server"); + + /* websockets service process */ + + while (1) { + if (libwebsocket_service(context, 100)) + break; + if (getppid() == 1) + break; + + gettimeofday(&tv, NULL); + + ms10 = (tv.tv_sec * 10) + (tv.tv_usec / 100000); + + if (ms10 > last) { + last = ms10; + libwebsocket_callback_on_writable_all_protocol(&protocols[1]); + } + } + + libwebsocket_context_destroy(context); + return 0; + } + + /* + * this process just has to deal with servicing AEP output from the + * AEP service process and making it available to the websocket + * services via the fifo ring file + */ + + aepd_shared->chans = -1; + + while (loop) { + + aep_result = aep_wait_for_next_result(&aep_context); + if (!aep_result) { + if (aep_context.aep_shared->finished) + loop = 0; + + continue; + } + + /* + * we have the next result in aep_result.. + * voltage and current per channel + */ + + m = 0; + for (n = 0; n < aep_result->chans * 2; n += 2) { + + /* + * accumulate in V, A, W per-channel. + * We do summing like that so that we can compute + * averages over any distance without iteration, by + * (sample(x + distance) - sample(x)) / distance. + * We can recover individual intersample delta still + * by sample(x + 1) - sample(x) if we want it. + * Doubles are used to maximize dynamic range. + */ + + sam[m++] += aep_result->buf[n]; + sam[m++] += aep_result->buf[n + 1]; + sam[m++] += aep_result->buf[n] * aep_result->buf[n + 1]; + } + + /* + * notice we have expanded the V/A 2 sample data + * into W as well. That's because average of instantaneous W + * is not at all the same as average V * average A + */ + + add_fifo(&sam[0], aep_result->chans * 3 * sizeof(double)); + aepd_shared->fifo_head_time = aep_result->samtime; + if (aepd_shared->chans != aep_result->chans) { + aepd_shared->chans = aep_result->chans; + zero_fifo(); + } + + /* done with it */ + + aep_free_result(&aep_context); + } + + close(aepd_shared->fd_fifo_write); + close(aepd_shared->fd_fifo_read); + unlink(aepd_shared->fifo_filepath); + + sem_close(aep_context.semaphore); + + fprintf(stderr, "exited\n"); + + return 0; +} + diff --git a/aepd/share/aepscope.html b/aepd/share/aepscope.html new file mode 100644 index 0000000..c66e82b --- /dev/null +++ b/aepd/share/aepscope.html @@ -0,0 +1,475 @@ +<!DOCTYPE html> +<!-- +/* + * Author: Andy Green <andy.green@linaro.org> + * Copyright (C) 2012 Linaro, LTD + * Libwebsocket demo code (C) 2010-2012 Andy Green <andy@warmcat.com> + * Repo: http://git.linaro.org/gitweb?p=tools/arm-probe.git;a=summary + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ +--> +<html lang="en"> +<head> +<meta charset=utf-8 /> +<title>ARM Energy Probe aepd UI</title> +</head> +<body> +<header> +</header> +<article> +<table> + <tr> + <td> + <table> + <tr> + <td> + <img src="linaro-logo-32.png"> + </td> + <td id=wsdi_statustd align=center> + <div id=wsdi_status>Not initialized</div> + </td> + <td> + Resolution<br> + <select id=rate onchange="update_options();"> + <option value=1>100us</option> + <option value=2>200us</option> + <option value=5>500us</option> + <option value=10>1ms</option> + <option value=20>2ms</option> + <option value=50>5ms</option> + <option value=100>10ms</option> + <option value=200>20ms</option> + <option value=500>50ms</option> + <option value=1000>100ms</option> + <option value=5000>500ms</option> + <option value=10000>1s</option> + </select> + </td> + <td> + <div id=val>-</div> + </td> + </tr> + </table> + </td> + </tr> + <tr> + <td> + <table style="background-color:#000000"> + <tr> + <td id=pane0> + </td> + </tr> + </table> + </td> + <td> + <div id="cha"></div> + </td> + </tr> +</table> + +<script> +var ring_size = 8192; +var max_chans = 8 * 3; +var ringV = new Array(max_chans); +var ringA = new Array(max_chans); +var ringW = new Array(max_chans); +var ring_head = 0; +var total = 0; + +var connection = 0; +var chans = 0; + +var start = 10; +var main_start = 25; + +var rate; +var watts_fullscale = 4.0; +var canvas = new Array(3); +var ctx = new Array(3); + +var socket; + +var xlen = 600; +var ylen = 400; + +var bounce = 0; +var last_head = -1; + +var colours = new Array(); +colours[0] = "#ff0000"; +colours[1] = "#c00000"; +colours[2] = "#a00000"; +colours[3] = "#e0e000"; +colours[4] = "#0000c0"; +colours[5] = "#a0c0e0"; +colours[6] = "#e000e0"; +colours[7] = "#0000ff"; +colours[8] = "#000000"; + + +function get_appropriate_ws_url() +{ + var pcol; + var u = document.URL; + + /* + * We open the websocket encrypted if this page came on an + * https:// url itself, otherwise unencrypted + */ + + if (u.substring(0, 5) == "https") { + pcol = "wss://"; + u = u.substr(8); + } else { + pcol = "ws://"; + if (u.substring(0, 4) == "http") + u = u.substr(7); + } + + u = u.split('/'); + + return pcol + u[0]; +} + +function conn_retry() +{ + var r1, r2, r3, x, y, chan, z, block; + + if (connection) + return; + + socket = new WebSocket(get_appropriate_ws_url(), "linaro.aepd"); + + try { + socket.onopen = function() { + grayOut(false); + document.getElementById("wsdi_statustd").style.backgroundColor = "#40ff40"; + document.getElementById("wsdi_status").textContent = " Connected "; + connection = 1; + socket.send("r"+(rate)+"\n"); + } + + socket.onmessage = function got_packet(msg) { + + z = msg.data.split(';'); + + block = 0; + while (block < (z.length - 1)) { + + y = z[block].split(','); + chan = 0; + while (chan < (y.length - 1)) { + + x = y[chan].split(' '); + if (x.length != 3) { + chan++; + continue; + } + + if (x[0].charAt(0) == '-') + r1 = (-x[0].substring(1)); + else + r1 = (+x[0]); + + if (x[1].charAt(0) == '-') + r2 = (-x[1].substring(1)); + else + r2 = (+x[1]); + + if (x[2].charAt(0) == '-') + r3 = (-x[2].substring(1)); + else + r3 = (+x[2]); + + + ringV[chan][ring_head] = r1; + ringA[chan][ring_head] = r2; + ringW[chan][ring_head] = r3; + + chan++; + } + + chans = chan; + + ring_head++; + if (ring_head == ring_size) + ring_head = 0; + + total++; + + block++; + } + } + + socket.onclose = function(){ + document.getElementById("wsdi_statustd").style.backgroundColor = "#ff4040"; + document.getElementById("wsdi_status").textContent = " disconnected "; + connection = 0; + grayOut(true,{'zindex':'499'}); + setTimeout("conn_retry();", 1000); + } + } catch(exception) { +// alert('<p>Error' + exception); +// setTimeout("conn_retry", 1000); + } +} + +function units(n) { + var s = ''; + + if (n < 0) { + s = '-'; + n = -n; + } + + if (n >= 1000000000) { + b = n % 1000000; + return (s + (n - b) / 1000000000); + } else + if (n >= 1000000) { + b = n % 1000; + return (s + (n - b) / 1000000 + 'm'); + } else + if (n >= 1000) + return (s + (n / 1000).toFixed(3) + 'u'); + else + if (n != 0) + return (s + n.toFixed(1) + 'n'); + + return ('0'); +} + +function show(cv) +{ + var n, p, y, chan, q; + + if (!connection) + return; + + if (last_head == ring_head) + return; + + ctx = canvas[0].getContext("2d"); + ctx.save(); + ctx.globalCompositeOperation = "copy"; + ctx.globalAlpha = 1.0; + + q = ring_head; + last_head = q; + + //document.getElementById("val").textContent = q + " " + total + " "; + + for (n = xlen - 1; n >= 0; n--) { + + q--; + if (q == 0) + q = ring_size - 1; + + y = ylen - 2; + chan = 0; + while (chan < chans) { + + ctx.lineWidth = 1; + ctx.strokeStyle = colours[chan]; + + ctx.beginPath(); + + p = (ringW[chan][q] * ylen) / watts_fullscale; + + ctx.moveTo(n + 0.5, y); + ctx.lineTo(n + 0.5, y - p); + + y -= p; + + ctx.stroke(); + chan++; + } + + ctx.lineWidth = 1; + ctx.strokeStyle = '#ffffff'; + ctx.beginPath(); + ctx.moveTo(n, y); + ctx.lineTo(n, 1); + ctx.stroke(); + } + + ctx.restore(); +} + + +function update_options() +{ + rate = document.getElementById("rate").value; + localStorage["ivmon.rate"] = rate; + socket.send("r"+(rate)+"\n"); +} + +/* + * This section around grayOut came from here: + * http://www.codingforums.com/archive/index.php/t-151720.html + * Assumed public domain + */ + +function grayOut(vis, options) { + var options = options || {}; + var zindex = options.zindex || 50; + var opacity = options.opacity || 70; + var opaque = (opacity / 100); + var bgcolor = options.bgcolor || '#000000'; + var dark = document.getElementById('darkenScreenObject'); + + if (!dark) { + var tbody = document.getElementsByTagName("body")[0]; + var tnode = document.createElement('div'); + tnode.style.position = 'absolute'; + tnode.style.top = '0px'; + tnode.style.left = '0px'; + tnode.style.overflow = 'hidden'; + tnode.style.display ='none'; + tnode.id = 'darkenScreenObject'; + tbody.appendChild(tnode); + dark = document.getElementById('darkenScreenObject'); + } + if (vis) { + dark.style.opacity = opaque; + dark.style.MozOpacity = opaque; + dark.style.filter ='alpha(opacity='+opacity+')'; + dark.style.zIndex = zindex; + dark.style.backgroundColor = bgcolor; + dark.style.width = gsize(1); + dark.style.height = gsize(0); + dark.style.display ='block'; + addEvent(window, "resize", + function() { + dark.style.height = gsize(0); + dark.style.width = gsize(1); + } + ); + } else { + dark.style.display = 'none'; + removeEvent(window, "resize", + function() { + dark.style.height = gsize(0); + dark.style.width = gsize(1); + } + ); + } +} + +function gsize(ptype) +{ + var h = document.compatMode == 'CSS1Compat' && + !window.opera ? + document.documentElement.clientHeight : + document.body.clientHeight; + var w = document.compatMode == 'CSS1Compat' && + !window.opera ? + document.documentElement.clientWidth : + document.body.clientWidth; + if (document.body && + (document.body.scrollWidth || document.body.scrollHeight)) { + var pageWidth = (w > (t = document.body.scrollWidth)) ? + ("" + w + "px") : ("" + (t) + "px"); + var pageHeight = (h > (t = document.body.scrollHeight)) ? + ("" + h + "px") : ("" + (t) + "px"); + } else if (document.body.offsetWidth) { + var pageWidth = (w > (t = document.body.offsetWidth)) ? + ("" + w + "px") : ("" + (t) + "px"); + var pageHeight =(h > (t = document.body.offsetHeight)) ? + ("" + h + "px") : ("" + (t) + "px"); + } else { + var pageWidth = '100%'; + var pageHeight = '100%'; + } + return (ptype == 1) ? pageWidth : pageHeight; +} + +function addEvent( obj, type, fn ) { + if ( obj.attachEvent ) { + obj['e' + type + fn] = fn; + obj[type+fn] = function() { obj['e' + type+fn]( window.event );} + obj.attachEvent('on' + type, obj[type + fn]); + } else + obj.addEventListener(type, fn, false); +} + +function removeEvent( obj, type, fn ) { + if ( obj.detachEvent ) { + obj.detachEvent('on' + type, obj[type + fn]); + obj[type + fn] = null; + } else + obj.removeEventListener(type, fn, false); +} + +/* + * end of grayOut related stuff + * + * requestAnimFrame stuff came from here + * http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + * presumed public domain + */ + +window.requestAnimFrame = (function(){ + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(/* function */ callback, /* DOMElement */ element){ + window.setTimeout(callback, 1000 / 24); + }; +})(); + +/* + * end of requestAnimFrame stuff + */ + +for (n = 0; n < max_chans; n++) { + ringV[n] = new Array(ring_size); + ringA[n] = new Array(ring_size); + ringW[n] = new Array(ring_size); +} + +for (n = 0; n < 1; n++) { + canvas[n] = document.createElement('canvas'); + canvas[n].height = ylen; + canvas[n].width = xlen; + + document.getElementById('pane0').appendChild(canvas[n]); +} + + +if (localStorage["ivmon.rate"]) { + rate = document.getElementById("rate").value = + parseInt(localStorage["ivmon.rate"]); + document.getElementById("rate").value = rate; +} + +grayOut(true,{'zindex':'499'}); +conn_retry(); + +(function animloop(){ + requestAnimFrame(animloop); + show(); +})(); + +</script> +</article> +</body> +</html> + diff --git a/aepd/share/favicon.ico b/aepd/share/favicon.ico Binary files differnew file mode 100644 index 0000000..576d338 --- /dev/null +++ b/aepd/share/favicon.ico diff --git a/aepd/share/linaro-logo-32.png b/aepd/share/linaro-logo-32.png Binary files differnew file mode 100644 index 0000000..6b245d0 --- /dev/null +++ b/aepd/share/linaro-logo-32.png diff --git a/aepd/websocket-protocol.c b/aepd/websocket-protocol.c new file mode 100644 index 0000000..cd81512 --- /dev/null +++ b/aepd/websocket-protocol.c @@ -0,0 +1,218 @@ +/* + * Author: Andy Green <andy.green@linaro.org> + * Copyright (C) 2012 Linaro, LTD + * Libwebsocket demo code (C) 2010-2012 Andy Green <andy@warmcat.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "aepd.h" + +/* + * We take a strict whitelist approach to stop ../ attacks + */ + +struct serveable { + const char *urlpath; + const char *mimetype; +}; + +static struct serveable whitelist[] = { + { "/favicon.ico", "image/x-icon" }, + { "/linaro-logo-32.png", "image/png" }, + + /* last one is the default served if no match */ + { "/aepscope.html", "text/html" }, +}; + +/* this protocol server (always the first one) just knows how to do HTTP */ + +static int callback_http(struct libwebsocket_context *context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, void *user, + void *in, size_t len) +{ + int n; + char buf[512]; + + switch (reason) { + case LWS_CALLBACK_HTTP: + + for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++) + if (in && strcmp(in, whitelist[n].urlpath) == 0) + break; + + sprintf(buf, LOCAL_RESOURCE_PATH"%s", whitelist[n].urlpath); + + if (libwebsockets_serve_http_file(wsi, buf, whitelist[n].mimetype)) + fprintf(stderr, "Failed to send HTTP file\n"); + break; + + default: + break; + } + + return 0; +} + + + +/* linaro_aepd_protocol */ + +struct per_session_data__linaro_aepd { + struct libwebsocket *wsi; + unsigned long ringbuffer_tail; + double sam[MAX_PROBES * CHANNELS_PER_PROBE * 3]; + int sam_valid; + int stride; +}; + + +static int +callback_linaro_aepd(struct libwebsocket_context *context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len) +{ + int n, m; + struct per_session_data__linaro_aepd *pss = user; + double sam[MAX_PROBES * CHANNELS_PER_PROBE * 3]; + double d[10]; + char buf[LWS_SEND_BUFFER_PRE_PADDING + 16384 + LWS_SEND_BUFFER_POST_PADDING]; + char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING]; + int budget = 10; + unsigned long extent; + + switch (reason) { + + case LWS_CALLBACK_ESTABLISHED: + pss->ringbuffer_tail = aepd_shared->fifo_pos; + pss->sam_valid = 0; + pss->wsi = wsi; + pss->stride = 100; + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + + /* + * aggregate up to 'budget' results in one websocket message + */ + + while (budget--) { + + if (pss->ringbuffer_tail <= aepd_shared->fifo_pos) + extent = aepd_shared->fifo_pos - pss->ringbuffer_tail; + else + extent = (aepd_shared->modulo_integer_chan_size - pss->ringbuffer_tail) + aepd_shared->fifo_pos; + + if (extent < (pss->stride * aepd_shared->chans * sizeof(double) * 3)) { + budget = 0; + continue; + } + + lseek(aepd_shared->fd_fifo_read, pss->ringbuffer_tail, SEEK_SET); + if (read(aepd_shared->fd_fifo_read, &sam[0], aepd_shared->chans * sizeof(double) * 3) < 0) + fprintf(stderr, "fifo read fail\n"); + + pss->ringbuffer_tail += (pss->stride * aepd_shared->chans * sizeof(double) * 3 ); + if (pss->ringbuffer_tail > aepd_shared->modulo_integer_chan_size) + pss->ringbuffer_tail -= aepd_shared->modulo_integer_chan_size; + + if (pss->sam_valid) { + + /* + * Javascript can't cope with binary, so we must ascii-fy it + */ + + m = 0; + for (n = 0; n < aepd_shared->chans; n++) { + p += sprintf(p, "%f %f %f", + (sam[m] - pss->sam[m]) / (double)pss->stride, + (sam[m + 1] - pss->sam[m + 1]) / (double)pss->stride, + (sam[m + 2] - pss->sam[m + 2]) / (double)pss->stride + ); + + if (n + 1 != aepd_shared->chans) { + *p++ = ','; + *p = '\0'; + } + m += 3; + } + + *p++ = ';'; + *p = '\0'; + } + + memcpy(&pss->sam[0], &sam[0], aepd_shared->chans * sizeof(double) * 3); + pss->sam_valid = 1; + } + + /* + * if we generated something, send it. We are guaranteed not to block + * because we got here by POLLOUT seen on the socket + */ + + if (p != &buf[LWS_SEND_BUFFER_PRE_PADDING]) { + n = libwebsocket_write(wsi, (unsigned char *) + &buf[LWS_SEND_BUFFER_PRE_PADDING], + p - &buf[LWS_SEND_BUFFER_PRE_PADDING], LWS_WRITE_TEXT); + if (n < 0) { + fprintf(stderr, "ERROR writing to socket"); + return 1; + } + libwebsocket_callback_on_writable(context, wsi); + } + + break; + + case LWS_CALLBACK_RECEIVE: + + switch (*(char *)in) { + case 'r': + if (sscanf(((char *)in) + 1, "%lf\n", &d[0]) == 1) + pss->stride = (int)d[0]; + break; + } + break; + + default: + break; + } + + return 0; +} + +/* list of supported protocols and callbacks */ + +struct libwebsocket_protocols protocols[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + callback_http, /* callback */ + 0 /* per_session_data_size */ + }, + { + "linaro.aepd", + callback_linaro_aepd, + sizeof(struct per_session_data__linaro_aepd), + }, + { + NULL, NULL, 0 /* End of list */ + } +}; + diff --git a/configure.ac b/configure.ac index 03a6e94..65140bd 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,8 @@ AC_CHECK_FUNCS([poll memset ]) AC_CONFIG_FILES([Makefile libarmep/Makefile - arm-probe/Makefile]) + arm-probe/Makefile + aepd/Makefile]) AC_OUTPUT diff --git a/libarmep/sample.c b/libarmep/sample.c index d3f5a25..f108438 100644 --- a/libarmep/sample.c +++ b/libarmep/sample.c @@ -336,7 +336,7 @@ unripe: } } - if (aep_shared->head == (sizeof(aep_shared->aep_result) / sizeof(*aep_result)) - 1) + if (aep_shared->head == (sizeof(aep_shared->aep_result) / sizeof(aep_shared->aep_result[0])) - 1) aep_shared->head = 0; else aep_shared->head++; diff --git a/libarmep/service.c b/libarmep/service.c index c8d0208..2f85cff 100644 --- a/libarmep/service.c +++ b/libarmep/service.c @@ -563,8 +563,6 @@ int aep_init_and_fork(struct aep_context *aep_context, char *argv[]) loop = 1; - signal(SIGINT, sighandler); - init_interpolation(); /* @@ -583,6 +581,10 @@ int aep_init_and_fork(struct aep_context *aep_context, char *argv[]) sprintf(semname, "linaro.aep.%u\n", getpid()); aep_context->semaphore = sem_open(semname, O_CREAT | O_RDWR, 0600, 0); + if (aep_context->semaphore == SEM_FAILED) { + fprintf(stderr, "Failed to open sem %s\n", semname); + return -1; + } /* fork off aep service loop */ @@ -590,6 +592,8 @@ int aep_init_and_fork(struct aep_context *aep_context, char *argv[]) if (n) return n; + signal(SIGINT, sighandler); + /* * child process runs independently * fills the named pipe fifo with sample packets @@ -598,12 +602,16 @@ int aep_init_and_fork(struct aep_context *aep_context, char *argv[]) if (argv) strcpy(argv[0] + strlen(argv[0]), " - AEP server"); - aep_context->semaphore = sem_open(semname, O_CREAT | O_RDWR, 0600, 0); + aep_context->semaphore = sem_open(semname, O_RDWR, 0600, 0); + if (aep_context->semaphore == SEM_FAILED) { + fprintf(stderr, "Child failed to open sem %s\n", semname); + return -1; + } aep_context->aep_shared->finished = 0; aep_context->poll_timeout_ms = 10; - while (!aep_context->aep_shared->finished) { + while (!aep_context->aep_shared->finished && loop && getppid() != 1) { n = poll(aep_context->pollfds, aep_context->count_pollfds, aep_context->poll_timeout_ms); if (n < 0) @@ -674,6 +682,8 @@ struct aep_result * aep_wait_for_next_result(struct aep_context *aep_context) if (aep_shared->tail == aep_shared->head) return NULL; +// fprintf(stderr, "%d %d, ", aep_shared->tail, aep_shared->head); + return &aep_shared->aep_result[aep_shared->tail]; } |