├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── assets ├── README ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── epoch.0.5.2.min.css │ └── main.css ├── favicon.ico ├── fonts │ ├── CooperHewitt-Bold.eot │ ├── CooperHewitt-Bold.svg │ ├── CooperHewitt-Bold.woff │ ├── CooperHewitt-Medium.eot │ ├── CooperHewitt-Medium.svg │ ├── CooperHewitt-Medium.woff │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── img │ ├── messgeraet.png │ └── prototyp1.jpg └── js │ ├── app.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── d3.js │ ├── d3.v2.min.js │ ├── epoch.0.5.2.min.js │ ├── jquery-2.1.1.min.js │ ├── justgage.1.0.1.min.js │ └── raphael.2.1.0.min.js ├── contrib └── freebsd │ └── defluxiod ├── src └── github.com │ └── netzsinus │ └── defluxio-software │ ├── .travis.yml │ ├── api.go │ ├── cmd │ ├── defluxio-exporter │ │ └── main.go │ ├── defluxio-provider │ │ └── main.go │ └── defluxiod │ │ └── main.go │ ├── config.go │ ├── config_test.go │ ├── conn.go │ ├── contrib │ ├── analysis │ │ └── plot_frequenz.py │ └── freebsd │ │ ├── README │ │ └── influxdb │ ├── database.go │ ├── export.go │ ├── hardware │ ├── firmware │ │ ├── Makefile │ │ ├── freq_capture.c │ │ ├── freq_capture.h │ │ ├── log_formatter.c │ │ ├── log_formatter.h │ │ ├── main.c │ │ ├── status_led.c │ │ ├── status_led.h │ │ ├── uart.c │ │ └── uart.h │ └── images │ │ ├── ISR-Cycle-No-Noise-Canceler.csv │ │ ├── ISR-Cycle-No-Noise-Canceler.png │ │ ├── ISR-Cycle-No-Noise-Canceler.wfm │ │ ├── ISR-Cycle-With-Noise-Canceler.csv │ │ ├── ISR-Cycle-With-Noise-Canceler.png │ │ ├── ISR-Cycle-With-Noise-Canceler.wfm │ │ ├── OptoAmp-Before-After.bmp │ │ ├── OptoAmp-Before-After.csv │ │ ├── OptoAmp-Before-After.png │ │ ├── OptoAmp-Before-After.wfm │ │ ├── SchmittTrigger-Detail.csv │ │ ├── SchmittTrigger-Detail.png │ │ ├── SchmittTrigger-Detail.wfm │ │ ├── SchmittTrigger-ISR-Activation.csv │ │ ├── SchmittTrigger-ISR-Activation.png │ │ ├── SchmittTrigger-ISR-Activation.wfm │ │ ├── Sinus-Komparator.csv │ │ ├── Sinus-Komparator.png │ │ └── Sinus-Komparator.wfm │ ├── hub.go │ ├── meter.go │ ├── meter_test.go │ ├── readings.go │ └── readings_test.go ├── vendor ├── manifest └── src │ └── github.com │ ├── gorilla │ ├── context │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── context.go │ │ ├── context_test.go │ │ └── doc.go │ ├── mux │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bench_test.go │ │ ├── doc.go │ │ ├── mux.go │ │ ├── mux_test.go │ │ ├── old_test.go │ │ ├── regexp.go │ │ └── route.go │ └── websocket │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── client.go │ │ ├── client_server_test.go │ │ ├── client_test.go │ │ ├── conn.go │ │ ├── conn_test.go │ │ ├── doc.go │ │ ├── examples │ │ ├── autobahn │ │ │ ├── README.md │ │ │ ├── fuzzingclient.json │ │ │ └── server.go │ │ ├── chat │ │ │ ├── README.md │ │ │ ├── conn.go │ │ │ ├── home.html │ │ │ ├── hub.go │ │ │ └── main.go │ │ └── filewatch │ │ │ ├── README.md │ │ │ └── main.go │ │ ├── json.go │ │ ├── json_test.go │ │ ├── server.go │ │ ├── server_test.go │ │ └── util.go │ ├── influxdata │ └── influxdb │ │ └── client │ │ └── v2 │ │ ├── client.go │ │ ├── client_test.go │ │ └── example_test.go │ ├── influxdb │ └── influxdb │ │ ├── models │ │ ├── points.go │ │ ├── points_test.go │ │ ├── rows.go │ │ └── time.go │ │ └── pkg │ │ └── escape │ │ ├── bytes.go │ │ ├── bytes_test.go │ │ └── strings.go │ └── tarm │ └── goserial │ ├── LICENSE │ ├── README.md │ ├── basic_test.go │ ├── serial.go │ ├── serial_linux.go │ ├── serial_posix.go │ └── serial_windows.go └── views ├── _footer.html ├── _header.html ├── daten.html ├── impressum.html ├── index.html ├── meters.html └── projekt.html /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /pkg 3 | # 4 | # configuration files 5 | defluxio-provider.conf 6 | defluxio-exporter.conf 7 | defluxiod.conf 8 | 9 | #Mac 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - release 4 | env: 5 | - "PATH=/home/travis/gopath/bin:$PATH" 6 | script: go get github.com/constabulary/gb/... && gb build all 7 | notifications: 8 | email: 9 | on_success: change 10 | on_failure: always 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Mathias Dalheimer 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Netzsinus software components 2 | ============================= 3 | 4 | [![Build Status](https://travis-ci.org/netzsinus/defluxio-software.svg?branch=master)](https://travis-ci.org/netzsinus/defluxio-software) 5 | 6 | 7 | This repository contains the software components (excluding firmware) of 8 | the netzsin.us project. These are the following: 9 | 10 | * ``defluxiod``, the server component (serves HTML and deals with data 11 | sent by the sensors). Incoming sensor data will be stored in an 12 | [InfluxDB](https://influxdata.com) database, although this might change 13 | in the future. 14 | * ``defluxio-provider``: The reference implementation of the sensor 15 | software. It reads the measurements from the sensor and submits them 16 | to the server. 17 | * ``defluxio-exporter``: A small software component that extracts 18 | measurements from the database and exports them into text files for 19 | [data.netzsin.us](data.netzsin.us). 20 | 21 | ### Installing the software 22 | 23 | You need a working [Golang installation](http://golang.org) and the [GB 24 | build tool](http://getgb.io/) in order to compile your binary. Please 25 | install the Go compiler first. Afterwards you can install GB like this: 26 | 27 | go get github.com/constabulary/gb/... 28 | 29 | Clone this repository: 30 | 31 | git clone https://github.com/netzsinus/defluxio-software.git 32 | 33 | and build it: 34 | 35 | cd defluxio-software 36 | gb build all 37 | 38 | Now, there should be several binaries in the ````bin```` subfolder. 39 | 40 | #### Crosscompiling e.g. for Raspberry Pi 41 | 42 | Go has very good crosscompilation support. Typically, I develop under 43 | Mac OS and crosscompile a binary for my RPi. It is easy: 44 | 45 | # clear whatever old binaries I have 46 | rm -rf pkg bin 47 | # start crosscompilation 48 | GOOS=linux GOARCH=arm GOARM=5 gb build all 49 | 50 | You can then copy the binary from the ``bin`` subdirectory to the RPi 51 | and start it. Please note that ``defluxiod`` will not be built because 52 | it depends on libraries only available for the 64bit x86 architecture. 53 | 54 | ## Configuration 55 | 56 | All binaries can generate a default configuration file which serves as a template for your configuration. In order to set up a new frequency sensor, do the following: 57 | 58 | $ cd ~ 59 | $ mkdir etc 60 | $ cd etc 61 | $ defluxio-provider -genconfig=true 62 | 63 | Now, edit the file ``defluxio-provider.conf``. If you want to use the "official" netzsin.us server (that would make me happy!) please contact ``md@gonium.net`` for access credentials. On the typical raspberry pi installation, you can start the frequency provider daemon like this: 64 | 65 | $ sudo /home/pi/go/bin/defluxio-provider -config=/home/pi/etc/defluxio-provider.conf 66 | 67 | A typical startup sequence looks like this: 68 | 69 | ```` 70 | 2015/02/20 11:55:17 Received unknown data: 71 | 2015/02/20 11:55:17 Startup: Ignoring measurement 49.99652099609375 72 | 2015/02/20 11:55:17 Received unknown data: 73 | 2015/02/20 11:55:17 Info message: I;Frequency: 49.995566 Hz, delta: -4 mHz 74 | 2015/02/20 11:55:17 Received unknown data: 75 | 2015/02/20 11:55:17 Startup: Ignoring measurement 49.99557113647461 76 | 2015/02/20 11:55:17 Received unknown data: 77 | 2015/02/20 11:55:17 Info message: I;Frequency: 49.995868 Hz, delta: -4 mHz 78 | 2015/02/20 11:55:17 Received unknown data: 79 | 2015/02/20 11:55:17 Startup: Ignoring measurement 49.99586868286133 80 | 2015/02/20 11:55:17 Received unknown data: 81 | 2015/02/20 11:55:18 Info message: I;Frequency: 49.996924 Hz, delta: -3 mHz 82 | 2015/02/20 11:55:18 Startup: Ignoring measurement 49.99692153930664 83 | 2015/02/20 11:55:19 Info message: I;Freque995667 Hz, delta: -4 mHz 84 | 2015/02/20 11:55:19 Startup: Ignoring measurement 49.995670318603516 85 | 2015/02/20 11:55:20 Info message: I;Freque996321 Hz, delta: -4 mHz 86 | 2015/02/20 11:55:20 Frequency: 49.99632 87 | ```` 88 | 89 | ## Development Setup 90 | 91 | If you want to contribute (or test your new server design) you can run 92 | your own server. The server does not store any data by default, see 93 | "Installing InfluxDB" below. But incoming frequency measurements will be 94 | displayed on the webpage. In short, while in the directory 95 | ``defluxio-software``, try the following: 96 | 97 | Run an instance of ``defluxiod`` on your local machine: 98 | 99 | ```` 100 | $ ./bin/defluxiod -genconfig 101 | $ ./bin/defluxiod -config=defluxiod.conf 102 | 2016/01/29 10:13:29 Configured meters are: 103 | 2016/01/29 10:13:29 * meter1 (Meter 1) 104 | 2016/01/29 10:13:29 * meter2 (Meter 2) 105 | 2016/01/29 10:13:29 Starting meter surveilance routine 106 | 2016/01/29 10:13:29 Starting server at 127.0.0.1:8080 107 | ```` 108 | 109 | If you browse to [http://127.0.0.1:8080](http://127.0.0.1:8080), you 110 | should see a clone of the netzsin.us page. 111 | 112 | Start a simulated frequency sensor: 113 | 114 | ```` 115 | $ ./bin/defluxio-provider -genconfig 116 | $ ./bin/defluxio-provider -config=defluxio-provider.conf -sim 117 | 2016/01/29 10:17:27 Frequency: 49.98730 118 | 2016/01/29 10:17:27 Error posting data: Post http://127.0.0.1:8080/api/submit/meter1: EOF 119 | 2016/01/29 10:17:29 Frequency: 49.95660 120 | 2016/01/29 10:17:31 Frequency: 50.01041 121 | 2016/01/29 10:17:33 Frequency: 49.97088 122 | 2016/01/29 10:17:35 Frequency: 49.95438 123 | ```` 124 | 125 | The parameter ``-sim`` does enable the simulation mode - no frequency 126 | sensor hardware is needed. It just sends random frequency measurements 127 | to the server. 128 | 129 | If you want to submit values to the server using your own client, its 130 | rather easy: You can submit a JSON array using the POST verb. Using 131 | curl: 132 | 133 | $ curl -i -X POST -H "Content-Type: application/json" \ 134 | -H "X-API-KEY: secretkey1" \ 135 | -d "{\"Timestamp\": \"`date --rfc-3339=ns | sed 's/ /T/; s/\(\....\).*-/\1-/g'`\", \"Value\":49.9999}" \ 136 | http://127.0.0.1:8080/api/submit/meter1 137 | HTTP/1.1 200 OK 138 | Content-Type: application/json 139 | Date: Fri, 29 Jan 2016 10:42:43 GMT 140 | Content-Length: 0 141 | 142 | In plain text the body of the request looks like this: 143 | 144 | {"Timestamp":"2016-01-29T11:33:22.954022564+01:00","Value":49.9999} 145 | 146 | The linux date command does not format the date correctly according to 147 | ISO8601, so a little ``sed`` magic is applied. Please note: The server 148 | currently has a bug leading to failing goroutines on first submission. 149 | Subsequent calls will succeed, just call curl several times. 150 | 151 | If you're using an embedded sensor, it might be tricky to construct the 152 | ISO8601 date string. You can also specify a unix timestamp with 153 | millisecond resolution like this: 154 | 155 | $ curl -i -X POST -H "Content-Type: application/json" \ 156 | -H "X-API-KEY: secretkey1" \ 157 | -d "{\"Timestamp\": `date +%s`.12, \"Value\": 49.850}" \ 158 | http://127.0.0.1:8080/api/submit/meter1 159 | 160 | Please note that I appended ``.12`` after the date command to simulate 161 | subsecond resolution. 162 | 163 | #### Installing InfluxDB (only needed for the server) 164 | 165 | If you want to store frequency measurements you need to install the 166 | InfluxDB version 0.9 as outlined [on the influxdb 167 | website](http://influxdb.com/docs/v0.9/introduction/installation.html). 168 | 169 | To enable the database you need to set ``enabled: true`` in your 170 | ``defluxiod.conf`` file. You can also use the ``defluxio-exporter`` 171 | commandline tool to export the measurements from the database again. 172 | 173 | -------------------------------------------------------------------------------- /assets/README: -------------------------------------------------------------------------------- 1 | Please copy slide PDF files here manually. 2 | -------------------------------------------------------------------------------- /assets/css/main.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'CooperHewitt-Medium'; 3 | src: url('/fonts/CooperHewitt-Medium.woff') format('woff'), /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 4 | url('/fonts/CooperHewitt-Medium.ttf') format('truetype'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 5 | } 6 | 7 | @font-face { 8 | font-family: 'CooperHewitt-Bold'; 9 | src: url('/fonts/CooperHewitt-Bold.woff') format('woff'), /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 10 | url('/fonts/CooperHewitt-Bold.ttf') format('truetype'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 11 | } 12 | 13 | body { 14 | padding-top: 90px; 15 | padding-left: 30px; 16 | padding-right: 30px; 17 | font-family: 'CooperHewitt-Medium', arial, sans-serif; 18 | } 19 | 20 | h1,h2,h3,h4,h5,h6 { 21 | font-family: 'CooperHewitt-Bold', arial, sans-serif; 22 | } 23 | 24 | .navbar-inverse { 25 | background-color: #222; 26 | border-color: #080808; 27 | font-family: 'CooperHewitt-Bold', arial, sans-serif; 28 | } 29 | 30 | .center-block { 31 | display: block; 32 | margin-left: auto; 33 | margin-right: auto; 34 | } 35 | 36 | .vcenter { 37 | display: inline-block; 38 | vertical-align: bottom; 39 | float: none; 40 | } 41 | 42 | .top-buffer { margin-top:35px; } 43 | .left-buffer { margin-left:20px; } 44 | .right-buffer { margin-right:20px; } 45 | 46 | #freqgauge { 47 | width:320px; height:240px; 48 | } 49 | 50 | #rlgauge { 51 | width:320px; height:240px; 52 | } 53 | 54 | #freqchart .frequenz .line { 55 | fill: pink; 56 | stroke: pink; 57 | width:480px; height:220px; 58 | } 59 | -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/favicon.ico -------------------------------------------------------------------------------- /assets/fonts/CooperHewitt-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/fonts/CooperHewitt-Bold.eot -------------------------------------------------------------------------------- /assets/fonts/CooperHewitt-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/fonts/CooperHewitt-Bold.woff -------------------------------------------------------------------------------- /assets/fonts/CooperHewitt-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/fonts/CooperHewitt-Medium.eot -------------------------------------------------------------------------------- /assets/fonts/CooperHewitt-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/fonts/CooperHewitt-Medium.woff -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /assets/img/messgeraet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/img/messgeraet.png -------------------------------------------------------------------------------- /assets/img/prototyp1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/img/prototyp1.jpg -------------------------------------------------------------------------------- /assets/js/app.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | 4 | 5 | function date2string(date) { 6 | var hr = date.getHours(); 7 | var min = date.getMinutes(); 8 | if (min < 10) { 9 | min = "0" + min; 10 | } 11 | var sec = date.getSeconds(); 12 | if (sec < 10) { 13 | sec = "0" + sec; 14 | } 15 | return hr + ":" + min + ":" + sec; 16 | } 17 | 18 | var now = Math.round(new Date()/1000) 19 | var netfreqdata = [ 20 | { 21 | label: 'Netzfrequenz', 22 | values: [ {time: now, y: 0.0} ] 23 | } 24 | ]; 25 | var rldata = [ 26 | { 27 | label: 'Regelleistung', 28 | values: [ {time: now, y: 0.0} ] 29 | } 30 | ]; 31 | var freqChart = $('#freqchart').epoch({ 32 | type: 'time.line', 33 | label: "Frequenz", 34 | data: netfreqdata, 35 | width: 460, 36 | height: 240, 37 | tickFormats: { 38 | bottom: function(d) { 39 | return date2string(new Date(d*1000)); 40 | }, 41 | right: function(d) { 42 | return d + "mHz"; 43 | } 44 | }, 45 | axes: ['left', 'bottom', 'right'], 46 | windowSize: 100, 47 | historySize: 20, 48 | queueSize: 60 49 | }); 50 | var rlChart = $('#rlchart').epoch({ 51 | type: 'time.line', 52 | label: "Regelleistung", 53 | data: rldata, 54 | width: 460, 55 | height: 240, 56 | tickFormats: { 57 | bottom: function(d) { 58 | return date2string(new Date(d*1000)); 59 | }, 60 | right: function(d) { 61 | return d + "MW"; 62 | } 63 | }, 64 | axes: ['left', 'bottom', 'right'], 65 | windowSize: 100, 66 | historySize: 20, 67 | queueSize: 60 68 | }); 69 | var freqgauge = new JustGage({ 70 | id: "freqgauge", 71 | value: "n/a", 72 | min: -150, 73 | max: 150, 74 | levelColors: [ "#2A4026", "#B6D96C", "#2A4026" ], 75 | levelColorsGradient: true, 76 | title: "Frequenzabweichung", 77 | label: "mHz" 78 | }); 79 | var rlgauge = new JustGage({ 80 | id: "rlgauge", 81 | value: "n/a", 82 | min: -1000, 83 | max: 1000, 84 | levelColors: [ "#2A4026", "#B6D96C", "#2A4026" ], 85 | levelColorsGradient: true, 86 | title: "Regelleistung", 87 | label: "MW" 88 | }); 89 | 90 | if (window["WebSocket"]) { 91 | // get data from the websocket. 92 | var conn = new WebSocket(ws_endpoint); 93 | conn.onclose = function(evt) { 94 | console.log("Connection closed."); 95 | freqgauge.refresh("n/a"); 96 | rlgauge.refresh("n/a"); 97 | } 98 | conn.onmessage = function(evt) { 99 | data = JSON && JSON.parse(evt.data) || $.parseJSON(evt.data); 100 | freqgauge.refresh(Number((data.Value-50)*1000).toFixed(0)); 101 | var regelleistung=0.0; 102 | if (data.Value < 50-0.01) { 103 | regelleistung = 17200*(50 - data.Value) - 0.01*17200; 104 | } 105 | if (data.Value > 50+0.01) { 106 | regelleistung = 17200*(50 - data.Value) + 0.01*17200; 107 | 108 | } 109 | rlgauge.refresh(Number(regelleistung).toFixed(1)); 110 | var ts = new Date(Date.parse(data.Timestamp)); 111 | var unixtime = Math.round(ts.getTime()/1000); 112 | $("#timevalue").text(date2string(ts)); 113 | freqChart.push([{time: unixtime, y: (data.Value - 50)*1000}]) 114 | rlChart.push([{time: unixtime, y: regelleistung}]) 115 | } 116 | } else { 117 | $("#warnings").html("Ihr Browser unterstützt keine Websockets. Daher können Sie leider keine Frequenzdaten empfangen."); 118 | $("#warnings").addClass("alert alert-danger"); 119 | } 120 | }); 121 | 122 | -------------------------------------------------------------------------------- /assets/js/raphael.2.1.0.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/assets/js/raphael.2.1.0.min.js -------------------------------------------------------------------------------- /contrib/freebsd/defluxiod: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . /etc/rc.subr 4 | 5 | installdir="/home/defluxio/defluxio/software/server" 6 | configfile="/home/defluxio/defluxio/software/server/defluxio.conf" 7 | 8 | name=defluxiod 9 | rcvar=defluxiod_enable 10 | 11 | load_rc_config $name 12 | pidfile=/var/run/defluxiod.pid 13 | start_cmd=defluxiod_start 14 | stop_postcmd=defluxiod_cleanup 15 | 16 | command="${installdir}/server" 17 | command_args="-config=${configfile}" 18 | 19 | defluxiod_start() { 20 | echo "Starting defluxiod." 21 | touch ${pidfile} 22 | /usr/sbin/daemon -cf -p ${pidfile} ${command} ${command_args} 23 | } 24 | 25 | defluxiod_cleanup() { 26 | [ -f ${pidfile} ] && rm ${pidfile} 27 | } 28 | 29 | run_rc_command "$1" 30 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - release 4 | env: 5 | - "PATH=/home/travis/gopath/bin:$PATH" 6 | before_install: 7 | - go get github.com/mitchellh/gox 8 | - gox -build-toolchain 9 | - go get github.com/tcnksm/ghr 10 | after_success: 11 | - gox -output "dist/{{.OS}}_{{.Arch}}_{{.Dir}}" ./... 12 | - ghr --username gonium --token $GITHUB_TOKEN --replace --prerelease --debug pre-release dist/ 13 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/api.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . See LICENSE file for 2 | // license. 3 | 4 | package defluxio 5 | 6 | import ( 7 | "bytes" 8 | "encoding/json" 9 | "fmt" 10 | "github.com/gorilla/mux" 11 | "io/ioutil" 12 | "log" 13 | "math" 14 | "net/http" 15 | "time" 16 | ) 17 | 18 | type APIClientErrorMessage struct { 19 | Id string 20 | Message string 21 | } 22 | 23 | type StatusCode int32 24 | 25 | const ( 26 | STATUS_OK StatusCode = 200 27 | STATUS_INTERNAL_ERROR = 500 28 | STATUS_NO_DB_CONNECTION = 501 // TODO: Implement keepalive 29 | STATUS_NO_UPDATES = 502 30 | ) 31 | 32 | type ServerStatusData struct { 33 | Code StatusCode 34 | LastValueAdded time.Time 35 | Message string 36 | } 37 | 38 | var lastUpdate time.Time 39 | 40 | func ServerStatus(w http.ResponseWriter, r *http.Request) { 41 | w.Header().Set("Content-Type", "application/json") 42 | // Check when the last value was received 43 | maxTimeDeviation, _ := time.ParseDuration("30s") 44 | deviation := lastUpdate.Sub(time.Now()) 45 | if (deviation > maxTimeDeviation) || (deviation < -maxTimeDeviation) { 46 | statusmessage := ServerStatusData{ 47 | STATUS_NO_UPDATES, 48 | lastUpdate, 49 | "No updates received."} 50 | statusbytes, _ := json.Marshal(statusmessage) 51 | w.Write(statusbytes) 52 | return 53 | } 54 | // TODO: Add other checks here. 55 | 56 | // Finally: No check failed, return OK. 57 | statusmessage := ServerStatusData{ 58 | STATUS_OK, 59 | lastUpdate, 60 | "All is good."} 61 | statusbytes, _ := json.Marshal(statusmessage) 62 | w.Write(statusbytes) 63 | } 64 | 65 | /* Accepts a new reading. Format: {"Timestamp":,"Value":342.2} 66 | */ 67 | func MkSubmitReadingHandler(dbchannel chan MeterReading, serverConfig *ServerConfiguration) http.HandlerFunc { 68 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 69 | w.Header().Set("Content-Type", "application/json") 70 | // extract and check credentials. 71 | // TODO: Refactore this into a separate middleware for chaining. 72 | vars := mux.Vars(r) 73 | meterid := vars["meter"] 74 | apikey := r.Header["X-Api-Key"][0] 75 | //credentials := fmt.Sprintf("%s:%s", meterid, apikey) 76 | invalidCredentials := true 77 | for _, meter := range serverConfig.Meters { 78 | if meter.ID == meterid && meter.Key == apikey { 79 | invalidCredentials = false 80 | break 81 | } 82 | } 83 | if invalidCredentials { 84 | errormessage := APIClientErrorMessage{"invalidcredentials", "given credentials are not valid"} 85 | errorbytes, _ := json.Marshal(errormessage) 86 | http.Error(w, 87 | string(errorbytes), 88 | http.StatusUnauthorized) 89 | return 90 | } 91 | // http://stackoverflow.com/questions/23070876/reading-body-of-http-request-without-modifying-request-state 92 | body, _ := ioutil.ReadAll(r.Body) 93 | reader1 := bytes.NewBuffer(body) 94 | reader2 := bytes.NewBuffer(body) 95 | // Decode the body and check for errors. 96 | decoder := json.NewDecoder(reader1) 97 | var reading Reading 98 | format_invalid := false 99 | errormsgs := "" 100 | if err := decoder.Decode(&reading); err != nil { 101 | errormsgs = err.Error() 102 | // Standard RFC3339 decoding did not work - maybe this is an 103 | // embedded client. Is the timestamp represented as a unix 104 | // timestamp w/ milliseconds? 105 | type UnixReading struct { 106 | Timestamp float64 107 | Value float64 108 | } 109 | var unixreading UnixReading 110 | decoder = json.NewDecoder(reader2) 111 | if uerr := decoder.Decode(&unixreading); uerr != nil { 112 | format_invalid = true 113 | errormsgs = fmt.Sprintf("%s; %s", errormsgs, uerr.Error()) 114 | } else { 115 | // convert the float to a unix timestamp w/ nanosecond 116 | // resolution 117 | secs := math.Trunc(unixreading.Timestamp) 118 | nsecs := (unixreading.Timestamp - secs) * math.Pow(10, 9) 119 | reading.Timestamp = time.Unix(int64(secs), int64(nsecs)) 120 | reading.Value = unixreading.Value 121 | //log.Println("Reconstructed reading from unix timestamp: ", reading) 122 | } 123 | if format_invalid { 124 | msg := fmt.Sprintf("Cannot decode data - invalid format: %s", errormsgs) 125 | errormessage := APIClientErrorMessage{"invalidformat", msg} 126 | errorbytes, _ := json.Marshal(errormessage) 127 | http.Error(w, 128 | string(errorbytes), 129 | http.StatusBadRequest) 130 | return 131 | } 132 | } 133 | 134 | // Check whether timestamp is current 135 | maxTimeDeviation, _ := time.ParseDuration("30s") 136 | deviation := reading.Timestamp.Sub(time.Now()) 137 | if (deviation > maxTimeDeviation) || (deviation < -maxTimeDeviation) { 138 | errormessage := APIClientErrorMessage{"timedeviation", "Timestamp deviates more than 30s from server time"} 139 | errorbytes, _ := json.Marshal(errormessage) 140 | http.Error(w, 141 | string(errorbytes), 142 | http.StatusBadRequest) 143 | return 144 | } 145 | 146 | // Check if value is within plausibility range 147 | if reading.Value < 47.5 || reading.Value > 52 { 148 | errormessage := APIClientErrorMessage{"timedeviation", "Reading value is not plausible"} 149 | errorbytes, _ := json.Marshal(errormessage) 150 | http.Error(w, string(errorbytes), http.StatusBadRequest) 151 | return 152 | } 153 | 154 | // round reading.Value to 4 decimal places 155 | // reading.Value = float64(int(reading.Value*1000)) / 1000 156 | //log.Println("Accepted", meterid, ":", reading.Timestamp, "-", reading.Value) 157 | lastUpdate = reading.Timestamp 158 | 159 | if dbchannel != nil { 160 | // Push the new reading into the database 161 | meterReading := MeterReading{meterid, reading} 162 | dbchannel <- meterReading 163 | } 164 | 165 | // Update the meter cache 166 | for idx := range serverConfig.Meters { 167 | if serverConfig.Meters[idx].ID == meterid { 168 | serverConfig.Meters[idx].AppendReading(reading) 169 | break 170 | } 171 | } 172 | 173 | // Push the last reading to the clients, but only for the active 174 | // meter with the lowest rank. A meter is active if it has been 175 | // sending data within the last five seconds. Therefore: Sort the 176 | // meters by the last reading timestamp, then pick the one with the 177 | // lowest rank. If the current reading has been sent by this meter, 178 | // push the reading to the websocket clients. 179 | // Note: This is a costly operation, but right now we don't have 180 | // many meters. If the number of meters increases, change this 181 | // algorithm. 182 | if BestMeter != nil { 183 | if BestMeter.ID == meterid { 184 | // The update we have received is from the best meter 185 | //log.Println("Received update from highest ranking meter", BestMeter.ID) 186 | // wrap everything again and forward update to all connected clients. 187 | updateMessage, uerr := json.Marshal(reading) 188 | if uerr != nil { 189 | log.Printf("Cannot marshal update message for websocket consumers: ", uerr) 190 | } 191 | H.broadcast <- []byte(updateMessage) 192 | } 193 | } else { // TODO: Initialize Bestmeter 194 | log.Printf("BestMeter not initialized.") 195 | } 196 | 197 | }) 198 | } 199 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/cmd/defluxio-exporter/main.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "github.com/netzsinus/defluxio-software" 11 | "log" 12 | "os" 13 | "path/filepath" 14 | "sort" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | const ( 20 | SecsPerDay int64 = 24 * 60 * 60 21 | ) 22 | 23 | var configFile = flag.String("config", "defluxio-exporter.conf", "configuration file") 24 | var mkConfigFile = flag.Bool("genconfig", false, "generate an example configuration file") 25 | var meterID = flag.String("meter", "", "ID of the meter to query") 26 | var startTimestamp = flag.Int64("start", 0, 27 | "data export start: first unix timestamp to export") 28 | var endTimestamp = flag.Int64("end", 0, 29 | "data export end: last unix timestamp to export") 30 | var exportDirectory = flag.String("dir", ".", "path to use for export") 31 | var force = flag.Bool("force", false, "force export, overwriting existing files") 32 | var verbose = flag.Bool("verbose", false, "verbose logging") 33 | var lastday = flag.Bool("lastday", false, "do not export to the given end timestamp but up until one day before (midnight) - cron use.") 34 | var cfg *defluxio.ExporterConfiguration 35 | var dbclient *defluxio.DBClient 36 | 37 | func init() { 38 | flag.Parse() 39 | if *mkConfigFile { 40 | log.Println("Creating default configuration in file " + *configFile) 41 | cfg := defluxio.MkDefaultExporterConfiguration() 42 | err := cfg.Save(*configFile) 43 | if err != nil { 44 | log.Fatal("Failed to create default configuration:", err.Error()) 45 | } 46 | os.Exit(0) 47 | } 48 | if strings.EqualFold(*meterID, "") { 49 | log.Fatal("You must specify the meter ID (i.e. -meter=foometer)") 50 | } 51 | if *startTimestamp == 0 { 52 | log.Fatal("You must specify the start timestamp( i.e. -start=1405607436)") 53 | } 54 | if *endTimestamp == 0 { 55 | log.Fatal("You must specify the end timestamp( i.e. -end=1405607465)") 56 | } 57 | if *startTimestamp >= *endTimestamp { 58 | log.Fatal("start timestamp cannot be after end timestamp.") 59 | } 60 | // TODO: Implement new check - now writing to a directory. 61 | if !*force { 62 | if dir, err := IsDirectory(*exportDirectory); err != nil || !dir { 63 | log.Fatal("directory ", *exportDirectory, " does not exist - aborting.") 64 | } 65 | } 66 | var err error 67 | if cfg, err = defluxio.LoadExporterConfiguration(*configFile); err != nil { 68 | log.Fatal("Error loading configuration: ", err.Error()) 69 | } 70 | if dbclient, err = defluxio.NewDBClient(&cfg.InfluxDB); err != nil { 71 | log.Fatal("Cannot initialize database client:", err.Error()) 72 | } 73 | } 74 | 75 | func IsDirectory(path string) (bool, error) { 76 | if fileInfo, err := os.Stat(path); err != nil { 77 | return false, err 78 | } else { 79 | return fileInfo.IsDir(), nil 80 | } 81 | } 82 | 83 | func getCanonicalFilename(ts int64) string { 84 | date := time.Unix(ts, 0) 85 | filename := fmt.Sprintf("%04d%02d%02d.txt", date.Year(), date.Month(), date.Day()) 86 | return filepath.Join(*exportDirectory, filename) 87 | } 88 | 89 | func getStartOfDayTimestamp(ts int64) int64 { 90 | date := time.Unix(ts, 0) 91 | return time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, 92 | time.UTC).Unix() 93 | } 94 | 95 | func getEndOfDayTimestamp(ts int64) int64 { 96 | return getStartOfDayTimestamp(ts) + SecsPerDay - 1 97 | } 98 | 99 | func exportRange(start int64, end int64, filename string) (err error) { 100 | if !*force { 101 | if _, err := os.Stat(filename); err == nil { 102 | if *verbose { 103 | log.Printf("File %s already exists - skipping.", filename) 104 | } 105 | return err 106 | } 107 | } 108 | timeReadings, err := dbclient.GetFrequenciesBetween(*meterID, 109 | time.Unix(start, 0), time.Unix(end, 0)) 110 | if err != nil { 111 | err = fmt.Errorf("Failed to query database: ", err.Error()) 112 | return 113 | } 114 | sort.Sort(defluxio.ByTimestamp(timeReadings)) 115 | 116 | log.Printf("Exporting %d readings [%d, %d] into file %s", 117 | len(timeReadings), start, end, filename) 118 | 119 | if tsve, err := defluxio.NewTsvExporter(filename); err != nil { 120 | return fmt.Errorf("Cannot create exporter for file %s ", filename) 121 | } else { 122 | if err = tsve.ExportDataset(timeReadings); err != nil { 123 | return fmt.Errorf("Failed to export dataset: %s ", err.Error()) 124 | } 125 | } 126 | return 127 | } 128 | 129 | func main() { 130 | if *verbose { 131 | log.Printf("Attempting to export from meter %s\n", *meterID) 132 | } 133 | 134 | // Cron usage: In "lastday" mode, subtract a day from the end date. 135 | // The reasoning behind this is that a cronjob can use the date +%s 136 | // command to get the current timestamp and we export only up to the 137 | // day before - the current day is not yet fully passed. 138 | if *lastday { 139 | *endTimestamp -= SecsPerDay 140 | } 141 | 142 | for ts := getStartOfDayTimestamp(*startTimestamp); ts < getEndOfDayTimestamp(*endTimestamp); ts += SecsPerDay { 143 | exportRange( 144 | getStartOfDayTimestamp(ts), 145 | getEndOfDayTimestamp(ts), 146 | getCanonicalFilename(ts), 147 | ) 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/cmd/defluxio-provider/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/json" 7 | "flag" 8 | "fmt" 9 | "github.com/netzsinus/defluxio-software" 10 | "github.com/tarm/goserial" 11 | "io" 12 | "io/ioutil" 13 | "log" 14 | "math" 15 | "math/rand" 16 | "net/http" 17 | "os" 18 | "runtime" 19 | "strconv" 20 | "strings" 21 | "sync" 22 | "time" 23 | ) 24 | 25 | var simulationMode = flag.Bool("sim", false, "simulation mode (does not need measurement hardware") 26 | var configFile = flag.String("config", "defluxio-provider.conf", "configuration file") 27 | var mkConfigFile = flag.Bool("genconfig", false, "generate an example configuration file") 28 | var Cfg *defluxio.ProviderConfiguration 29 | var readingChannel = make(chan float64) 30 | var extract_wg sync.WaitGroup 31 | var pusher_wg sync.WaitGroup 32 | 33 | type ErrorMessage struct { 34 | Id string 35 | Message string 36 | } 37 | 38 | func serial_read_readings(r io.Reader) { 39 | defer extract_wg.Done() 40 | lastfrequency := 0.0 41 | scanner := bufio.NewScanner(r) 42 | // Drop everything that is currently in the serial connection 43 | // buffer. Use a startup flag to signal this. 44 | startup := true 45 | timer := time.NewTimer(time.Second * 2) 46 | go func() { 47 | <-timer.C 48 | startup = false 49 | }() 50 | for scanner.Scan() { 51 | line := scanner.Text() 52 | if err := scanner.Err(); err != nil { 53 | log.Println("Failed to scan line:" + err.Error()) 54 | continue 55 | } 56 | elements := strings.Split(line, ";") 57 | if elements[0] == "F" { 58 | frequency, err := strconv.ParseFloat(elements[1], 32) 59 | if err != nil { 60 | log.Println("Received broken frequency: " + line) 61 | continue 62 | } 63 | if frequency < 48 || frequency > 52 { 64 | log.Println("Frequency out of plausible range: " + line) 65 | continue 66 | } 67 | if startup { 68 | log.Println("Startup: Ignoring measurement", frequency) 69 | lastfrequency = frequency 70 | } else { 71 | // Now, we know that the measurement is a plausible one. Do a 72 | // last check for spikes (this is a strange phenomenon, 73 | // see https://github.com/gonium/defluxio/issues/8) 74 | if math.Abs(lastfrequency-frequency) < Cfg.Validation.SpikeThreshold { 75 | readingChannel <- frequency 76 | } else { 77 | log.Println("Rejected spike:", frequency) 78 | } 79 | lastfrequency = frequency 80 | } 81 | } else if elements[0] == "I" { 82 | log.Println("Info message: " + line) 83 | } else { 84 | log.Println("Received unknown data: " + line) 85 | } 86 | } 87 | } 88 | 89 | func simulate_readings() { 90 | defer extract_wg.Done() 91 | ticker := time.NewTicker(time.Second * 2) 92 | for { 93 | readingChannel <- 49.95 + (0.1 * rand.Float64()) 94 | <-ticker.C 95 | } 96 | } 97 | 98 | func pusher() { 99 | defer pusher_wg.Done() 100 | reqUrl := fmt.Sprintf("%s:%d/api/submit/%s", 101 | Cfg.Network.Host, 102 | Cfg.Network.Port, 103 | Cfg.Meter.ID) 104 | for frequency := range readingChannel { 105 | log.Println("Frequency: " + strconv.FormatFloat(frequency, 'f', 5, 32)) 106 | client := &http.Client{} 107 | body := defluxio.Reading{time.Now(), frequency} 108 | bodyBytes, _ := json.Marshal(body) 109 | //log.Println("Posting ", string(bodyBytes), " to endpoint ", reqUrl) 110 | req, err := http.NewRequest("POST", reqUrl, bytes.NewReader(bodyBytes)) 111 | if err != nil { 112 | log.Fatal("Failed to compose submit request: ", err.Error()) 113 | } 114 | req.Header.Set("Content-Type", "application/json") 115 | req.Header.Add("X-API-Key", Cfg.Meter.Key) 116 | resp, err := client.Do(req) 117 | if err != nil { 118 | log.Println("Error posting data: ", err.Error()) 119 | continue 120 | } 121 | 122 | response, rerr := ioutil.ReadAll(resp.Body) 123 | if rerr != nil { 124 | log.Println("Error getting post result data: ", err.Error()) 125 | resp.Body.Close() 126 | continue 127 | } 128 | if resp.StatusCode != http.StatusOK { 129 | decoder := json.NewDecoder(bytes.NewReader(response)) 130 | var errorMessage ErrorMessage 131 | err := decoder.Decode(&errorMessage) 132 | if err != nil { 133 | log.Println("Failed to decode error message: " + err.Error() + 134 | ", raw: " + string(response)) 135 | } else { 136 | log.Println(resp.StatusCode, errorMessage.Id, ":", errorMessage.Message) 137 | } 138 | } 139 | resp.Body.Close() 140 | } 141 | } 142 | 143 | func init() { 144 | flag.Parse() 145 | if *mkConfigFile { 146 | log.Println("Creating default configuration in file " + *configFile) 147 | cfg := defluxio.MkDefaultProviderConfiguration() 148 | err := cfg.Save(*configFile) 149 | if err != nil { 150 | log.Fatal("Failed to create default configuration:", err.Error()) 151 | } 152 | os.Exit(0) 153 | } 154 | var loaderror error 155 | Cfg, loaderror = defluxio.LoadProviderConfiguration(*configFile) 156 | if loaderror != nil { 157 | log.Fatal("Error loading configuration: ", loaderror.Error()) 158 | } 159 | } 160 | 161 | func main() { 162 | if !*simulationMode { 163 | c := &serial.Config{Name: Cfg.Device.Path, Baud: Cfg.Device.Baudrate} 164 | s, err := serial.OpenPort(c) 165 | if err != nil { 166 | log.Fatal(err) 167 | } 168 | // just one reader, since it is a serial connection 169 | extract_wg.Add(1) 170 | go serial_read_readings(s) 171 | } else { 172 | rand.Seed(42) 173 | extract_wg.Add(1) 174 | go simulate_readings() 175 | } 176 | for c := 0; c < runtime.NumCPU(); c++ { 177 | pusher_wg.Add(1) 178 | go pusher() 179 | } 180 | extract_wg.Wait() 181 | close(readingChannel) 182 | pusher_wg.Wait() 183 | } 184 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/cmd/defluxiod/main.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "github.com/gorilla/mux" 11 | "github.com/netzsinus/defluxio-software" 12 | "log" 13 | "net/http" 14 | "os" 15 | "text/template" 16 | "time" 17 | ) 18 | 19 | var configFile = flag.String("config", "defluxiod.conf", "configuration file") 20 | var mkConfigFile = flag.Bool("genconfig", false, "generate an example configuration file") 21 | var Cfg *defluxio.ServerConfiguration 22 | var templates *template.Template 23 | var dbclient *defluxio.DBClient 24 | var dbchannel chan defluxio.MeterReading 25 | 26 | func serveHome(w http.ResponseWriter, r *http.Request) { 27 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 28 | templates.ExecuteTemplate(w, "index", r.Host) 29 | } 30 | 31 | func serveImpressum(w http.ResponseWriter, r *http.Request) { 32 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 33 | templates.ExecuteTemplate(w, "impressum", r.Host) 34 | } 35 | 36 | func serveProjekt(w http.ResponseWriter, r *http.Request) { 37 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 38 | templates.ExecuteTemplate(w, "projekt", r.Host) 39 | } 40 | 41 | func serveDaten(w http.ResponseWriter, r *http.Request) { 42 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 43 | templates.ExecuteTemplate(w, "daten", r.Host) 44 | } 45 | 46 | func serveMeter(w http.ResponseWriter, r *http.Request) { 47 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 48 | type TemplateData struct { 49 | Meters defluxio.Meters 50 | } 51 | t := TemplateData{Meters: Cfg.Meters} 52 | err := templates.ExecuteTemplate(w, "meter", t) 53 | if err != nil { 54 | log.Println("executing meter template: ", err.Error()) 55 | } 56 | } 57 | 58 | func init() { 59 | flag.Parse() 60 | if *mkConfigFile { 61 | log.Println("Creating default configuration in file " + *configFile) 62 | cfg := defluxio.MkDefaultServerConfiguration() 63 | err := cfg.Save(*configFile) 64 | if err != nil { 65 | log.Fatal("Failed to create default configuration:", err.Error()) 66 | } 67 | os.Exit(0) 68 | } 69 | var loaderror error 70 | Cfg, loaderror = defluxio.LoadServerConfiguration(*configFile) 71 | if loaderror != nil { 72 | log.Fatal("Error loading configuration: ", loaderror.Error()) 73 | } 74 | log.Printf("Configured meters are:") 75 | for _, m := range Cfg.Meters { 76 | log.Printf("* %s (%s)", m.ID, m.Name) 77 | } 78 | log.Printf("Starting meter surveilance routine") 79 | Cfg.Meters.StartBestMeterUpdater(Cfg.MeterTimeout) 80 | funcMap := template.FuncMap{ 81 | "dosomething": func() string { return "done something" }, 82 | "doublethreedigits": func(f float64) string { 83 | return fmt.Sprintf("%2.3f", f) 84 | }, 85 | "tstodate": func(t time.Time) string { 86 | return fmt.Sprintf("%d.%d.%d", t.Day(), t.Month(), t.Year()) 87 | }, 88 | "tstotime": func(t time.Time) string { 89 | return fmt.Sprintf("%d:%d:%d", t.Hour(), t.Minute(), t.Second()) 90 | }, 91 | } 92 | templates, loaderror = template.New("").Funcs(funcMap).ParseGlob(Cfg.Assets.ViewPath + 93 | "/*") 94 | if loaderror != nil { 95 | log.Fatal("Cannot load templates: ", loaderror.Error()) 96 | } 97 | if Cfg.InfluxDB.Enabled { 98 | var err error 99 | dbclient, err = defluxio.NewDBClient(&Cfg.InfluxDB) 100 | if err != nil { 101 | log.Fatal("Cannot initialize database client:", err.Error()) 102 | } 103 | dbchannel = make(chan defluxio.MeterReading) 104 | pusher, perr := dbclient.MkDBPusher(dbchannel) 105 | if perr != nil { 106 | log.Fatal("Cannot generate database pusher: ", perr.Error()) 107 | } 108 | go pusher() 109 | } 110 | } 111 | 112 | func main() { 113 | // TODO: Refactor, H is a global right now, this is not good. 114 | go defluxio.H.Run() 115 | r := mux.NewRouter() 116 | r.HandleFunc("/", serveHome).Methods("GET") 117 | r.HandleFunc("/impressum", serveImpressum).Methods("GET") 118 | r.HandleFunc("/projekt", serveProjekt).Methods("GET") 119 | r.HandleFunc("/daten", serveDaten).Methods("GET") 120 | r.HandleFunc("/meter", serveMeter).Methods("GET") 121 | r.HandleFunc("/api/submit/{meter}", 122 | defluxio.MkSubmitReadingHandler(dbchannel, Cfg)).Methods("POST") 123 | r.HandleFunc("/api/status", defluxio.ServerStatus).Methods("GET") 124 | r.HandleFunc("/ws", defluxio.ServeWs) 125 | r.PathPrefix("/").Handler(http.FileServer(http.Dir(Cfg.Assets.AssetPath))) 126 | http.Handle("/", r) 127 | listenAddress := fmt.Sprintf("%s:%d", Cfg.Network.Host, 128 | Cfg.Network.Port) 129 | log.Println("Starting server at " + listenAddress) 130 | err := http.ListenAndServe(listenAddress, nil) 131 | if err != nil { 132 | log.Fatal("Failed to start http server: ", err) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/config.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | package defluxio 5 | 6 | import ( 7 | "encoding/json" 8 | "io/ioutil" 9 | "time" 10 | ) 11 | 12 | type InfluxDBConfig struct { 13 | Enabled bool 14 | Host string 15 | Port int 16 | Database string 17 | User string 18 | Pass string 19 | } 20 | type NetworkConfig struct { 21 | Host string 22 | Port int 23 | } 24 | type AssetConfig struct { 25 | ViewPath string 26 | AssetPath string 27 | } 28 | 29 | type ServerConfiguration struct { 30 | Meters Meters 31 | MeterTimeout time.Duration 32 | Assets AssetConfig 33 | Network NetworkConfig 34 | InfluxDB InfluxDBConfig 35 | } 36 | 37 | type DeviceConfig struct { 38 | Path string 39 | Baudrate int 40 | } 41 | type ValidationConfig struct { 42 | SpikeThreshold float64 43 | } 44 | type ProviderConfiguration struct { 45 | Meter Meter 46 | Network NetworkConfig 47 | Device DeviceConfig 48 | Validation ValidationConfig 49 | } 50 | 51 | type ExporterConfiguration struct { 52 | InfluxDB InfluxDBConfig 53 | } 54 | 55 | /** 56 | * Exporter Configuration methods 57 | */ 58 | 59 | func (pc *ExporterConfiguration) Save(configFile string) (err error) { 60 | return saveJSON(pc, configFile) 61 | } 62 | 63 | func LoadExporterConfiguration(configFile string) (cfg *ExporterConfiguration, err error) { 64 | cfg = &ExporterConfiguration{} 65 | err = loadJSON(cfg, configFile) 66 | return cfg, err 67 | } 68 | 69 | func MkDefaultExporterConfiguration() (cfg ExporterConfiguration) { 70 | cfg = ExporterConfiguration{ 71 | InfluxDB: InfluxDBConfig{ 72 | Enabled: false, 73 | Host: "127.0.0.1", 74 | Port: 8086, 75 | Database: "frequency", 76 | User: "root", 77 | Pass: "root", 78 | }, 79 | } 80 | return cfg 81 | } 82 | 83 | /** 84 | * Provider Configuration methods 85 | */ 86 | func (pc *ProviderConfiguration) Save(configFile string) (err error) { 87 | return saveJSON(pc, configFile) 88 | } 89 | 90 | func LoadProviderConfiguration(configFile string) (cfg *ProviderConfiguration, err error) { 91 | cfg = &ProviderConfiguration{} 92 | err = loadJSON(cfg, configFile) 93 | return cfg, err 94 | } 95 | 96 | func MkDefaultProviderConfiguration() (cfg ProviderConfiguration) { 97 | cfg = ProviderConfiguration{ 98 | Meter: Meter{ 99 | Rank: 0, 100 | ID: "meter1", 101 | Key: "secretkey1", 102 | Name: "Meter 1", 103 | Location: "Somewhere", 104 | }, 105 | Network: NetworkConfig{ 106 | Host: "http://127.0.0.1", 107 | Port: 8080, 108 | }, 109 | Device: DeviceConfig{ 110 | Path: "/dev/ttyAMA0", 111 | Baudrate: 115200, 112 | }, 113 | Validation: ValidationConfig{ 114 | SpikeThreshold: 150, 115 | }, 116 | } 117 | return cfg 118 | } 119 | 120 | /** 121 | * Server Configuration methods 122 | */ 123 | func (sc *ServerConfiguration) Save(configFile string) (err error) { 124 | return saveJSON(sc, configFile) 125 | } 126 | 127 | func LoadServerConfiguration(configFile string) (cfg *ServerConfiguration, err error) { 128 | cfg = new(ServerConfiguration) 129 | err = loadJSON(cfg, configFile) 130 | if err == nil { 131 | defaultReading := Reading{Value: 0.0, Timestamp: time.Now()} 132 | for idx := range cfg.Meters { 133 | cfg.Meters[idx].Cache = MakeReadingCache(cfg.Meters[idx].CacheSize) 134 | cfg.Meters[idx].AppendReading(defaultReading) 135 | } 136 | } 137 | return cfg, err 138 | } 139 | 140 | func MkDefaultServerConfiguration() (cfg ServerConfiguration) { 141 | meter1 := Meter{ 142 | Rank: 0, 143 | ID: "meter1", 144 | Key: "secretkey1", 145 | Name: "Meter 1", 146 | Location: "Somewhere", 147 | CacheSize: 10, 148 | } 149 | meter2 := Meter{ 150 | Rank: 1, 151 | ID: "meter2", 152 | Key: "secretkey2", 153 | Name: "Meter 2", 154 | Location: "Nowhere", 155 | CacheSize: 10, 156 | } 157 | 158 | cfg = ServerConfiguration{ 159 | Meters: Meters{ 160 | &meter1, 161 | &meter2, 162 | }, 163 | MeterTimeout: 10, 164 | Network: NetworkConfig{ 165 | Host: "127.0.0.1", 166 | Port: 8080, 167 | }, 168 | Assets: AssetConfig{ 169 | ViewPath: "./views", 170 | AssetPath: "./assets", 171 | }, 172 | InfluxDB: InfluxDBConfig{ 173 | Enabled: false, 174 | Host: "127.0.0.1", 175 | Port: 8086, 176 | Database: "frequency", 177 | User: "root", 178 | Pass: "root", 179 | }, 180 | } 181 | return cfg 182 | } 183 | 184 | // Helper: Saves the given configuration struct into a JSON file. 185 | func saveJSON(configuration interface{}, configFile string) (err error) { 186 | var bytes []byte 187 | bytes, err = json.MarshalIndent(configuration, "", " ") 188 | if err != nil { 189 | return err 190 | } 191 | err = ioutil.WriteFile(configFile, bytes, 0644) 192 | return err 193 | } 194 | 195 | // Helper: Loads a configuration from a JSON file. 196 | func loadJSON(configuration interface{}, configFile string) (err error) { 197 | var bytes []byte 198 | bytes, err = ioutil.ReadFile(configFile) 199 | if err != nil { 200 | return err 201 | } 202 | err = json.Unmarshal(bytes, configuration) 203 | if err != nil { 204 | return err 205 | } 206 | return nil 207 | } 208 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/config_test.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | package defluxio 5 | 6 | import ( 7 | "io/ioutil" 8 | "reflect" 9 | "syscall" 10 | "testing" 11 | ) 12 | 13 | func TestServerConfigSerialization(t *testing.T) { 14 | sc := MkDefaultServerConfiguration() 15 | f, err := ioutil.TempFile("", "testserverconfig.json") 16 | if err != nil { 17 | t.Error("Cannot create temp file") 18 | } 19 | defer syscall.Unlink(f.Name()) 20 | err = sc.Save(f.Name()) 21 | if err != nil { 22 | t.Error("Cannot save server configuration: " + err.Error()) 23 | } 24 | sc2, err := LoadServerConfiguration(f.Name()) 25 | if err != nil { 26 | t.Error("Failed to load server configuration: " + err.Error()) 27 | } 28 | // Note: This test currently fails because the reading cache is 29 | // generated dynamically after each load. 30 | //if !reflect.DeepEqual(sc, *sc2) { 31 | // t.Error("Pre-stored server configuration not equal to post-stored one") 32 | //} 33 | if !(sc.Network == sc2.Network && sc.MeterTimeout == sc2.MeterTimeout && 34 | sc.Assets == sc2.Assets && sc.InfluxDB == sc2.InfluxDB) { 35 | t.Error("Pre-stored server configuration not equal to post-stored one") 36 | } 37 | } 38 | 39 | func TestProviderConfigSerialization(t *testing.T) { 40 | sc := MkDefaultProviderConfiguration() 41 | f, err := ioutil.TempFile("", "testproviderconfig.json") 42 | if err != nil { 43 | t.Error("Cannot create temp file") 44 | } 45 | defer syscall.Unlink(f.Name()) 46 | err = sc.Save(f.Name()) 47 | if err != nil { 48 | t.Error("Cannot save server configuration: " + err.Error()) 49 | } 50 | sc2, err := LoadProviderConfiguration(f.Name()) 51 | if err != nil { 52 | t.Error("Failed to load server configuration: " + err.Error()) 53 | } 54 | if !reflect.DeepEqual(sc, *sc2) { 55 | t.Error("Pre-stored server configuration not equal to post-stored one") 56 | } 57 | } 58 | 59 | func TestExporterConfigSerialization(t *testing.T) { 60 | sc := MkDefaultExporterConfiguration() 61 | f, err := ioutil.TempFile("", "testexporterconfig.json") 62 | if err != nil { 63 | t.Error("Cannot create temp file") 64 | } 65 | defer syscall.Unlink(f.Name()) 66 | err = sc.Save(f.Name()) 67 | if err != nil { 68 | t.Error("Cannot save server configuration: " + err.Error()) 69 | } 70 | sc2, err := LoadExporterConfiguration(f.Name()) 71 | if err != nil { 72 | t.Error("Failed to load server configuration: " + err.Error()) 73 | } 74 | if !reflect.DeepEqual(sc, *sc2) { 75 | t.Error("Pre-stored server configuration not equal to post-stored one") 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/conn.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . 2 | // Code derived from the 3 | // Gorilla WebSocket Demo, which is licensed as follows: 4 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style 6 | // license that can be found in the LICENSE file. 7 | 8 | package defluxio 9 | 10 | import ( 11 | "github.com/gorilla/websocket" 12 | "log" 13 | "net/http" 14 | "time" 15 | ) 16 | 17 | const ( 18 | // Time allowed to write a message to the peer. 19 | writeWait = 10 * time.Second 20 | 21 | // Time allowed to read the next pong message from the peer. 22 | pongWait = 60 * time.Second 23 | 24 | // Send pings to peer with this period. Must be less than pongWait. 25 | pingPeriod = (pongWait * 9) / 10 26 | 27 | // Maximum message size allowed from peer. 28 | maxMessageSize = 512 29 | ) 30 | 31 | var upgrader = websocket.Upgrader{ 32 | ReadBufferSize: 1024, 33 | WriteBufferSize: 1024, 34 | CheckOrigin: func(r *http.Request) bool { 35 | return true 36 | }, 37 | } 38 | 39 | // connection is an middleman between the websocket connection and the hub. 40 | type connection struct { 41 | // The websocket connection. 42 | ws *websocket.Conn 43 | 44 | // Buffered channel of outbound messages. 45 | send chan []byte 46 | } 47 | 48 | // readPump pumps messages from the websocket connection to the hub. 49 | // incoming messages are simply ignored. 50 | func (c *connection) readPump() { 51 | defer func() { 52 | H.unregister <- c 53 | c.ws.Close() 54 | }() 55 | c.ws.SetReadLimit(maxMessageSize) 56 | c.ws.SetReadDeadline(time.Now().Add(pongWait)) 57 | c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) 58 | for { 59 | _, _, err := c.ws.ReadMessage() 60 | if err != nil { 61 | break 62 | } 63 | } 64 | } 65 | 66 | // write writes a message with the given message type and payload. 67 | func (c *connection) write(mt int, payload []byte) error { 68 | c.ws.SetWriteDeadline(time.Now().Add(writeWait)) 69 | return c.ws.WriteMessage(mt, payload) 70 | } 71 | 72 | // writePump pumps messages from the hub to the websocket connection. 73 | func (c *connection) writePump() { 74 | ticker := time.NewTicker(pingPeriod) 75 | defer func() { 76 | ticker.Stop() 77 | c.ws.Close() 78 | }() 79 | for { 80 | select { 81 | case message, ok := <-c.send: 82 | if !ok { 83 | c.write(websocket.CloseMessage, []byte{}) 84 | return 85 | } 86 | if err := c.write(websocket.TextMessage, message); err != nil { 87 | return 88 | } 89 | case <-ticker.C: 90 | if err := c.write(websocket.PingMessage, []byte{}); err != nil { 91 | return 92 | } 93 | } 94 | } 95 | } 96 | 97 | // serverWs handles webocket requests from the peer. 98 | func ServeWs(w http.ResponseWriter, r *http.Request) { 99 | if r.Method != "GET" { 100 | http.Error(w, "Method not allowed", 405) 101 | return 102 | } 103 | ws, err := upgrader.Upgrade(w, r, nil) 104 | if err != nil { 105 | log.Println("Error upgrading connection: ", err); 106 | if _, ok := err.(websocket.HandshakeError); !ok { 107 | log.Println(err) 108 | } 109 | return 110 | } 111 | c := &connection{send: make(chan []byte, 256), ws: ws} 112 | H.register <- c 113 | go c.writePump() 114 | c.readPump() 115 | } 116 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/contrib/analysis/plot_frequenz.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | import datetime as dt 5 | 6 | def load_data(filename): 7 | datafile = open(filename) 8 | datafile.readline() # skip the header 9 | data = np.loadtxt(datafile) 10 | time = [dt.datetime.fromtimestamp(ts) for ts in data[:,0]] 11 | return time, data[:,1] 12 | 13 | rhrk_time, rhrk_freq = load_data("rhrk-frequenz.txt") 14 | foo_time, foo_freq = load_data("foofrequenz.txt") 15 | plt.title("Netzfrequenz: RHRK vs. Foo") 16 | plt.xlabel("Zeit") 17 | plt.ylabel("Frequenz [Hz]") 18 | plt.plot(rhrk_time, rhrk_freq, 'r', label="RHRK") 19 | plt.plot(foo_time, foo_freq, 'b', label="Foo") 20 | plt.legend() 21 | 22 | print "Mittelwert RHRK:", np.mean(rhrk_freq) 23 | print "Mittelwert Foo:", np.mean(foo_freq) 24 | 25 | #plt.figure() 26 | #lastweek_time, lastweek_freq = load_data("lastweek-frequenz.txt") 27 | #plt.title("Netzfrequenz: Letzte Woche") 28 | #plt.xlabel("Zeit") 29 | #plt.ylabel("Frequenz [Hz]") 30 | #plt.plot(lastweek_time, lastweek_freq, 'b', label="Letzte Woche (ITWM)") 31 | #plt.legend() 32 | 33 | 34 | 35 | plt.show() 36 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/contrib/freebsd/README: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Installing InfluxDB 4 | 5 | # Building influxdb on freebsd: 6 | # see https://gist.github.com/akhenakh/10819108 7 | 8 | pkg install bison flex leveldb protobuf gmake ruby ruby-gems bzr mercurial valgrind 9 | export CC=clang 10 | export GOROOT=/home/akh/dev/gosrc 11 | export CGO_CFLAGS="-I/usr/local/include" 12 | export CGO_LDFLAGS="-L/usr/local/lib" 13 | 14 | git clone https://github.com/influxdb/influxdb.git 15 | cd influxdb 16 | git checkout 216.... // Release version! 17 | ./configure --with-flex=/usr/local/bin/flex --with-bison=/usr/local/bin/bison 18 | 19 | # edit Makefile to change the SHELL path to /usr/local/bin/bash 20 | gmake 21 | 22 | # a test will fail but the daemon binary is working 23 | 24 | # get the admin templates directory from http://s3.amazonaws.com/influxdb/influxdb-latest.386.tar.gz and move it in the same directory as the daemon binary 25 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/contrib/freebsd/influxdb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . /etc/rc.subr 4 | 5 | installdir="/home/defluxio/influxdb" 6 | configfile="/home/defluxio/etc/influxdb_config.toml" 7 | 8 | name=influxdb 9 | rcvar=influxdb_enable 10 | load_rc_config $name 11 | pidfile=/var/run/influxdb.pid 12 | start_cmd=influxdb_start 13 | stop_postcmd=influxdb_cleanup 14 | 15 | command="${installdir}/daemon" 16 | command_args="--config ${configfile}" 17 | 18 | influxdb_start() { 19 | echo "Starting influxdb." 20 | touch ${pidfile} 21 | /usr/sbin/daemon -cf -p ${pidfile} ${command} ${command_args} 22 | } 23 | 24 | influxdb_cleanup() { 25 | [ -f ${pidfile} ] && rm ${pidfile} 26 | } 27 | 28 | run_rc_command "$1" 29 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/database.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . See LICENSE file for 2 | // license. 3 | package defluxio 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "github.com/influxdata/influxdb/client/v2" 9 | "log" 10 | "time" 11 | ) 12 | 13 | // TODO: Move MeterID out of MeterReading - too much duplication 14 | // of the meter name. New type "MeterTimeseries?" 15 | type MeterReading struct { 16 | MeterID string 17 | Reading Reading 18 | } 19 | 20 | // ByTimestamp implements sort.Interface for []MeterReading 21 | // based on the timestamp field of a reading. 22 | type ByTimestamp []MeterReading 23 | 24 | func (a ByTimestamp) Len() int { return len(a) } 25 | func (a ByTimestamp) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 26 | func (a ByTimestamp) Less(i, j int) bool { 27 | return a[i].Reading.Timestamp.Unix() < a[j].Reading.Timestamp.Unix() 28 | } 29 | 30 | type DBClient struct { 31 | client client.Client 32 | serverconfig *InfluxDBConfig 33 | } 34 | 35 | func NewDBClient(serverConfig *InfluxDBConfig) (*DBClient, error) { 36 | retval := new(DBClient) 37 | var err error 38 | retval.client, err = client.NewHTTPClient(client.HTTPConfig{ 39 | Addr: fmt.Sprintf("http://%s:%d", serverConfig.Host, 40 | serverConfig.Port), 41 | Username: serverConfig.User, 42 | Password: serverConfig.Pass, 43 | }) 44 | if err != nil { 45 | return nil, fmt.Errorf("Cannot create InfluxDB client: %s", err.Error()) 46 | } 47 | 48 | // Save config for later use 49 | retval.serverconfig = serverConfig 50 | return retval, nil 51 | } 52 | 53 | func (dbc DBClient) MkDBPusher(dbchannel chan MeterReading) (func(), error) { 54 | log.Println("Getting list of databases:") 55 | response, err := dbc.client.Query( 56 | client.Query{ 57 | Command: "SHOW DATABASES", 58 | Database: dbc.serverconfig.Database, 59 | }) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | if err == nil && response.Error() != nil { 64 | return nil, fmt.Errorf("Cannot retrieve list of InfluxDB databases: %s", response.Error()) 65 | } 66 | log.Printf("Show databases response: %v", response.Results) 67 | foundDatabase := false 68 | // holy shit this is ugly 69 | for _, result := range response.Results { 70 | for _, row := range result.Series { 71 | if row.Name == "databases" { 72 | for _, values := range row.Values { 73 | for _, database := range values { 74 | log.Printf("found database: %s", database) 75 | if database == dbc.serverconfig.Database { 76 | foundDatabase = true 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | 84 | if !foundDatabase { 85 | log.Fatalf("Did not find database \"%s\" - please create it", 86 | dbc.serverconfig.Database) 87 | } 88 | return func() { 89 | for { 90 | _, ok := <-dbchannel 91 | meterreading, ok := <-dbchannel 92 | if !ok { 93 | log.Fatal("Cannot read from internal channel - aborting") 94 | } 95 | //log.Printf("Pushing reading %v", meterreading.Reading) 96 | 97 | // TODO: At the moment, each value is written individually. 98 | // A batched transfer (e.g. all five seconds) would rock. 99 | // Create a new point batch 100 | bp, _ := client.NewBatchPoints(client.BatchPointsConfig{ 101 | Database: dbc.serverconfig.Database, 102 | Precision: "ms", 103 | }) 104 | 105 | // Create a point and add to batch 106 | tags := map[string]string{"meterid": meterreading.MeterID} 107 | fields := map[string]interface{}{ 108 | "value": meterreading.Reading.Value, 109 | "timestamp": meterreading.Reading.Timestamp.Unix(), 110 | } 111 | pt, err := client.NewPoint(dbc.serverconfig.Database, tags, fields, time.Now()) 112 | if err != nil { 113 | fmt.Println("Error: ", err.Error()) 114 | } 115 | bp.AddPoint(pt) 116 | 117 | // Write the batch 118 | dbc.client.Write(bp) 119 | 120 | } 121 | }, nil 122 | } 123 | 124 | func (dbc DBClient) points2meterreadings(name string, 125 | res []client.Result) (retval []MeterReading) { 126 | for _, row := range res[0].Series { 127 | for _, v := range row.Values { 128 | json_timestamp := v[1].(json.Number) 129 | int_timestamp, _ := json_timestamp.Int64() 130 | timestamp := time.Unix(int_timestamp, 0) 131 | json_freq := v[2].(json.Number) 132 | freq, _ := json_freq.Float64() 133 | log.Printf("T: %d, F: %.3f", int_timestamp, freq) 134 | retval = append(retval, MeterReading{name, Reading{timestamp, freq}}) 135 | } 136 | } 137 | 138 | return retval 139 | } 140 | 141 | func (dbc DBClient) GetFrequenciesBetween(meterID string, 142 | start time.Time, end time.Time) (retval []MeterReading, err error) { 143 | querystr := fmt.Sprintf("select timestamp, value from %s where meterid = '%s' and timestamp > %d and timestamp < %d", dbc.serverconfig.Database, meterID, start.Unix(), end.Unix()) 144 | //log.Printf("Running query >%s<", querystr) 145 | q := client.Query{ 146 | Command: querystr, 147 | Database: dbc.serverconfig.Database, 148 | } 149 | if response, err := dbc.client.Query(q); err == nil { 150 | if response.Error() != nil { 151 | log.Printf("Failed to run query: %s", response.Error()) 152 | return nil, response.Error() 153 | } 154 | retval = dbc.points2meterreadings(meterID, response.Results) 155 | } else { 156 | log.Printf("Failed to run query: %s", err.Error()) 157 | } 158 | return retval, nil 159 | } 160 | 161 | func (dbc DBClient) GetLastFrequencies(meterID string, amount int) ([]MeterReading, error) { 162 | retval := []MeterReading{} 163 | querystr := fmt.Sprintf("select timestamp, value from %s where meterid= '%s' order by time desc limit %d", dbc.serverconfig.Database, meterID, amount) 164 | log.Printf("Running query >%s<", querystr) 165 | q := client.Query{ 166 | Command: querystr, 167 | Database: dbc.serverconfig.Database, 168 | } 169 | if response, err := dbc.client.Query(q); err == nil { 170 | if response.Error() != nil { 171 | log.Printf("Failed to run query: %s", response.Error()) 172 | return nil, response.Error() 173 | } 174 | retval = dbc.points2meterreadings(meterID, response.Results) 175 | } else { 176 | log.Printf("Failed to run query: %s", err.Error()) 177 | } 178 | return retval, nil 179 | } 180 | 181 | func (dbc DBClient) GetLastFrequency(meterID string) (MeterReading, 182 | error) { 183 | readings, error := dbc.GetLastFrequencies(meterID, 1) 184 | if error != nil { 185 | return MeterReading{}, error 186 | } else { 187 | return readings[0], error 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/export.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . See LICENSE file for 2 | // license. 3 | package defluxio 4 | 5 | import ( 6 | "bufio" 7 | "fmt" 8 | "os" 9 | "text/template" 10 | "time" 11 | ) 12 | 13 | const ( 14 | licenseTemplate = `# The https://netzsin.us grid frequency measurements are 15 | # (c) Mathias Dalheimer, . 16 | # 17 | # This database is made available under the Open Database License: 18 | # http://opendatacommons.org/licenses/odbl/1.0/. 19 | # Any rights in individual contents of the database are licensed under 20 | # the Database Contents License: 21 | # http://opendatacommons.org/licenses/dbcl/1.0/ 22 | # Please see http://opendatacommons.org/licenses/odbl/summary/ for a 23 | # human-readable explanation of the license. 24 | # 25 | # Generated on {{.GenerationDate}} 26 | #timestamp reading 27 | ` 28 | ) 29 | 30 | type LicenseData struct { 31 | GenerationDate string 32 | } 33 | 34 | type TsvExporter struct { 35 | Path string 36 | } 37 | 38 | func NewTsvExporter(path string) (*TsvExporter, error) { 39 | retval := new(TsvExporter) 40 | retval.Path = path 41 | return retval, nil 42 | } 43 | 44 | func (tsve *TsvExporter) ExportDataset(timeReadings []MeterReading) error { 45 | file, err := os.Create(tsve.Path) 46 | if err != nil { 47 | return err 48 | } 49 | defer file.Close() 50 | 51 | w := bufio.NewWriter(file) 52 | gd := time.Now().String() 53 | ld := LicenseData{GenerationDate: gd} 54 | t := 55 | template.Must(template.New("licenseTemplate").Parse(licenseTemplate)) 56 | err = t.Execute(w, ld) 57 | if err != nil { 58 | return fmt.Errorf("Failed to create license header: %s", err) 59 | } 60 | for _, element := range timeReadings { 61 | exportLine := fmt.Sprintf("%d\t%f", element.Reading.Timestamp.Unix(), 62 | element.Reading.Value) 63 | fmt.Fprintln(w, exportLine) 64 | } 65 | return w.Flush() 66 | } 67 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/firmware/Makefile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/firmware/Makefile -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/firmware/freq_capture.c: -------------------------------------------------------------------------------- 1 | #include "freq_capture.h" 2 | #include "status_led.h" 3 | #include 4 | #include 5 | 6 | #ifndef TRUE 7 | #define TRUE 0 8 | #define FALSE 1 9 | #endif 10 | 11 | volatile uint32_t end_time = 0; 12 | volatile uint32_t start_time = 0; 13 | volatile uint16_t timer1_overflows = 0; 14 | volatile uint16_t edges = 0; 15 | 16 | // timekeeping. 17 | volatile uint16_t milliseconds = 0; 18 | volatile unsigned char new_second = FALSE; 19 | 20 | // maintain the state of a measurement 21 | volatile mstate_t m_state = IDLE; 22 | 23 | void freq_capture_init(void) { 24 | /** PD6 is the input capture pin. Set data direction to input. */ 25 | DDRD &= ~(1 << PD6); 26 | /** No internal pullup - we have that covered in hardware. */ 27 | PORTD &= ~(1 << PD6); 28 | /** We use Timer1 for the input capture pin. 29 | * - No Prescaler. 30 | * - Activate input capture noise canceler. 31 | * - Trigger on rising edge. 32 | * */ 33 | TCCR1B |= ((1< 5 | 6 | typedef enum { 7 | IDLE, 8 | PENDING, 9 | RUNNING 10 | } mstate_t; 11 | 12 | void freq_capture_init(void); 13 | void freq_capture_start(void); 14 | char freq_new_second(void); 15 | mstate_t freq_get_state(void); 16 | double freq_get_result(void); 17 | 18 | #endif /* FREQ_CAPTURE_H */ 19 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/firmware/log_formatter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "log_formatter.h" 4 | #include "uart.h" 5 | 6 | void log_info(char* msg) { 7 | uart_puts_P("I;"); 8 | uart_puts(msg); 9 | uart_puts_P("\r\n"); 10 | } 11 | 12 | void log_freq(double freq) { 13 | char buffer[16]; 14 | uart_puts_P("F;"); 15 | dtostrf(freq, 7, 5, buffer); 16 | uart_puts(buffer); 17 | uart_puts_P("\r\n"); 18 | } 19 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/firmware/log_formatter.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_FORMATTER_H 2 | #define LOG_FORMATTER_H 1 3 | 4 | void log_info(char* msg); 5 | void log_freq(double freq); 6 | 7 | #endif /* LOG_FORMATTER_H */ 8 | 9 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/firmware/main.c: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | Title: example program for the Interrupt controlled UART library 3 | Author: Peter Fleury http://jump.to/fleury 4 | File: $Id: test_uart.c,v 1.4 2005/07/10 11:46:30 Peter Exp $ 5 | Software: AVR-GCC 3.3 6 | Hardware: any AVR with built-in UART, tested on AT90S8515 at 4 Mhz 7 | 8 | DESCRIPTION: 9 | This example shows how to use the UART library uart.c 10 | 11 | *************************************************************************/ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "uart.h" 20 | #include "freq_capture.h" 21 | #include "status_led.h" 22 | #include "log_formatter.h" 23 | 24 | /* 9600 baud */ 25 | #define UART_BAUD_RATE 9600 26 | 27 | 28 | 29 | void initialize(void) { 30 | 31 | //// internal Aref = Vcc, left adjust result 32 | //ADMUX = (1< 3 | 4 | void status_led_init(void) { 5 | DDRB = (1 << PB7); // output for status led 6 | } 7 | 8 | void status_led_toggle(void) { 9 | if (PORTB && (1 << PB7)) { 10 | PORTB &= ~(1 << PB7); 11 | } else { 12 | PORTB |= (1 << PB7); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/firmware/status_led.h: -------------------------------------------------------------------------------- 1 | #ifndef STATUS_LED_H 2 | #define STATUS_LED_H 1 3 | 4 | void status_led_init(void); 5 | void status_led_toggle(void); 6 | 7 | #endif /* STATUS_LED_H */ 8 | 9 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/ISR-Cycle-No-Noise-Canceler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/ISR-Cycle-No-Noise-Canceler.png -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/ISR-Cycle-No-Noise-Canceler.wfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/ISR-Cycle-No-Noise-Canceler.wfm -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/ISR-Cycle-With-Noise-Canceler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/ISR-Cycle-With-Noise-Canceler.png -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/ISR-Cycle-With-Noise-Canceler.wfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/ISR-Cycle-With-Noise-Canceler.wfm -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/OptoAmp-Before-After.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/OptoAmp-Before-After.bmp -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/OptoAmp-Before-After.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/OptoAmp-Before-After.png -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/OptoAmp-Before-After.wfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/OptoAmp-Before-After.wfm -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/SchmittTrigger-Detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/SchmittTrigger-Detail.png -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/SchmittTrigger-Detail.wfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/SchmittTrigger-Detail.wfm -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/SchmittTrigger-ISR-Activation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/SchmittTrigger-ISR-Activation.png -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/SchmittTrigger-ISR-Activation.wfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/SchmittTrigger-ISR-Activation.wfm -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/Sinus-Komparator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/Sinus-Komparator.png -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hardware/images/Sinus-Komparator.wfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netzsinus/defluxio-software/7b349376ced94f641a1e797365e8dcacc653b2b7/src/github.com/netzsinus/defluxio-software/hardware/images/Sinus-Komparator.wfm -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/hub.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . Code derived from the 2 | // Gorilla WebSocket Demo, which is licensed as follows: 3 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | package defluxio 8 | 9 | // hub maintains the set of active connections and broadcasts messages to the 10 | // connections. 11 | type hub struct { 12 | // Registered connections. 13 | connections map[*connection]bool 14 | 15 | // Inbound messages from the connections. 16 | broadcast chan []byte 17 | 18 | // Register requests from the connections. 19 | register chan *connection 20 | 21 | // Unregister requests from connections. 22 | unregister chan *connection 23 | } 24 | 25 | var H = hub{ 26 | broadcast: make(chan []byte), 27 | register: make(chan *connection), 28 | unregister: make(chan *connection), 29 | connections: make(map[*connection]bool), 30 | } 31 | 32 | func (H *hub) Run() { 33 | for { 34 | select { 35 | case c := <-H.register: 36 | H.connections[c] = true 37 | case c := <-H.unregister: 38 | delete(H.connections, c) 39 | close(c.send) 40 | case m := <-H.broadcast: 41 | for c := range H.connections { 42 | select { 43 | case c.send <- m: 44 | default: 45 | close(c.send) 46 | delete(H.connections, c) 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/meter.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package defluxio 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "sort" 11 | "time" 12 | ) 13 | 14 | type Meter struct { 15 | Rank uint16 16 | ID string 17 | Key string 18 | Name string 19 | Location string 20 | Cache *ReadingCache `json:"-"` // do not export 21 | CacheSize uint32 22 | } 23 | 24 | type Meters []*Meter 25 | 26 | var BestMeter *Meter 27 | 28 | //type MeterCollection struct { 29 | // Meters []Meter 30 | //} 31 | 32 | func isEmpty(s string) bool { 33 | return s == "" 34 | } 35 | 36 | func (m *Meter) IsValid() bool { 37 | return !isEmpty(m.ID) && !isEmpty(m.Key) && !isEmpty(m.Name) && !isEmpty(m.Location) 38 | } 39 | 40 | func (m *Meter) AppendReading(r Reading) { 41 | m.Cache.AddReading(r) 42 | } 43 | 44 | func (mc Meters) StartBestMeterUpdater(timeout time.Duration) { 45 | BestMeter = mc.GetBestMeter(timeout) 46 | ticker := time.NewTicker(time.Second * timeout / 2) 47 | go func() { 48 | for _ = range ticker.C { 49 | BestMeter = mc.GetBestMeter(timeout) 50 | } 51 | }() 52 | } 53 | 54 | func (mc Meters) GetBestMeter(timeout time.Duration) (m *Meter) { 55 | sort.Sort(ByRank{mc}) 56 | for _, m := range mc { 57 | if b, _ := m.ActiveWithinLast(time.Second * timeout); b { 58 | // do all the handling for the "best" meter 59 | return m 60 | } 61 | } 62 | return nil 63 | } 64 | 65 | func (m *Meter) ActiveWithinLast(deadline time.Duration) (bool, error) { 66 | r, err := m.Cache.LastReading() 67 | if err != nil { 68 | return false, err 69 | } else { 70 | return time.Since(r.Timestamp) < deadline, nil 71 | } 72 | } 73 | 74 | func (mc Meters) IsValid() bool { 75 | ranks := []uint16{} 76 | // Check individual meters 77 | for _, m := range mc { 78 | if !m.IsValid() { 79 | return false 80 | } 81 | // has the rank been previously seen? 82 | if intInSlice(m.Rank, ranks) { 83 | return false 84 | } else { 85 | ranks = append(ranks, m.Rank) 86 | } 87 | } 88 | return true 89 | } 90 | 91 | func (ms Meters) String() string { 92 | retval := "Available Meters:" 93 | for _, m := range ms { 94 | retval = fmt.Sprintf("%s\n* %s - %s (%d)", retval, m.ID, m.Name, m.Rank) 95 | } 96 | return retval 97 | } 98 | 99 | /************ Make meters sortable ******************/ 100 | 101 | func (m Meters) Len() int { return len(m) } 102 | func (m Meters) Swap(i, j int) { m[i], m[j] = m[j], m[i] } 103 | 104 | type ByRank struct{ Meters } 105 | type ByName struct{ Meters } 106 | type ByLastUpdate struct{ Meters } 107 | 108 | func (s ByRank) Less(i, j int) bool { 109 | return s.Meters[i].Rank < s.Meters[j].Rank 110 | } 111 | 112 | func (s ByName) Less(i, j int) bool { 113 | return s.Meters[i].Name < s.Meters[j].Name 114 | } 115 | 116 | func (s ByLastUpdate) Less(i, j int) bool { 117 | it, ierr := s.Meters[i].Cache.LastReading() 118 | jt, jerr := s.Meters[j].Cache.LastReading() 119 | if ierr != nil || jerr != nil { 120 | log.Println("Failed to acquire last reading in sorting predicate.") 121 | return false 122 | } else { 123 | return it.Timestamp.Unix() < jt.Timestamp.Unix() 124 | } 125 | } 126 | 127 | /************ Helpers below ******************/ 128 | 129 | func intInSlice(i uint16, s []uint16) bool { 130 | for _, b := range s { 131 | if b == i { 132 | return true 133 | } 134 | } 135 | return false 136 | } 137 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/meter_test.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package defluxio 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func getValidMeters() (valid_meters Meters) { 12 | valid_meters = []*Meter{ 13 | { 14 | Rank: 0, 15 | ID: "valid", 16 | Key: "valid", 17 | Name: "Valid", 18 | Location: "Here.", 19 | Cache: MakeReadingCache(10), 20 | CacheSize: 10, 21 | }, 22 | { 23 | Rank: 1, 24 | ID: "valid", 25 | Key: "valid", 26 | Name: "Valid", 27 | Location: "Here.", 28 | Cache: MakeReadingCache(10), 29 | CacheSize: 10, 30 | }, 31 | } 32 | return valid_meters 33 | } 34 | 35 | func TestMeterValid(t *testing.T) { 36 | valid := Meter{ 37 | ID: "valid", 38 | Key: "valid", 39 | Name: "Valid", 40 | Location: "Here.", 41 | } 42 | invalidID := Meter{ 43 | Key: "valid", 44 | Name: "Valid", 45 | Location: "Here.", 46 | } 47 | invalidKey := Meter{ 48 | ID: "valid", 49 | Name: "Valid", 50 | Location: "Here.", 51 | } 52 | invalidName := Meter{ 53 | ID: "valid", 54 | Key: "valid", 55 | Location: "Here.", 56 | } 57 | invalidLocation := Meter{ 58 | ID: "valid", 59 | Key: "valid", 60 | Name: "Valid", 61 | } 62 | if !valid.IsValid() { 63 | t.Error("Expected the good meter entry to be valid") 64 | } 65 | if invalidID.IsValid() || invalidKey.IsValid() || 66 | invalidName.IsValid() || invalidLocation.IsValid() { 67 | t.Error("Expected the bad meter entry to be invalid") 68 | } 69 | } 70 | 71 | func TestMeterEquality(t *testing.T) { 72 | valid_meters := getValidMeters() 73 | if valid_meters[0] != valid_meters[0] { 74 | t.Error("Same meter is assumed to be different") 75 | } 76 | if valid_meters[0] == valid_meters[1] { 77 | t.Error("Different meters are assumed to be equal") 78 | } 79 | } 80 | 81 | func TestMeterCollectionValid(t *testing.T) { 82 | valid_meters := getValidMeters() 83 | if !valid_meters.IsValid() { 84 | t.Error("Meter Collection is not valid") 85 | } 86 | duplicate_meters := append(valid_meters, valid_meters[0]) 87 | if duplicate_meters.IsValid() { 88 | t.Error("Meter Collection was accepted although it contains a duplicate") 89 | } 90 | } 91 | 92 | func TestMeterCaching(t *testing.T) { 93 | valid_meters := getValidMeters() 94 | r1 := Reading{Value: 1.0} 95 | valid_meters[0].AppendReading(r1) 96 | last_reading, _ := valid_meters[0].Cache.LastReading() 97 | if r1 != last_reading { 98 | t.Error("Meter does not provide last reading correctly.") 99 | } 100 | r2 := Reading{Value: 2.0} 101 | for i := 0; i < 100; i++ { 102 | valid_meters[0].AppendReading(r1) 103 | } 104 | valid_meters[0].AppendReading(r2) 105 | last_reading, _ = valid_meters[0].Cache.LastReading() 106 | if r2 != last_reading { 107 | t.Error("Meter does not provide last reading correctly after inserting some more readings.") 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/readings.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package defluxio 6 | 7 | import ( 8 | "container/ring" 9 | "errors" 10 | "fmt" 11 | "time" 12 | ) 13 | 14 | // Reading type. This is the type we use to represent a single 15 | // measurement. Currently, this only supports frequency measurements - 16 | // but it can be extended in the future. 17 | 18 | type Reading struct { 19 | Timestamp time.Time 20 | Value float64 21 | } 22 | 23 | func (r Reading) String() (retval string) { 24 | return fmt.Sprintf("(%s: %2.6f)", r.Timestamp, r.Value) 25 | } 26 | 27 | // The cache implements a cache of readings for a single meter. 28 | type ReadingCache struct { 29 | Cache *ring.Ring 30 | } 31 | 32 | func MakeReadingCache(size uint32) (r *ReadingCache) { 33 | r = new(ReadingCache) 34 | r.Cache = ring.New(int(size)) 35 | return r 36 | } 37 | 38 | func (c *ReadingCache) AddReading(r Reading) { 39 | if c.Cache == nil { 40 | fmt.Printf("fooo!\n") 41 | } 42 | c.Cache.Value = r 43 | c.Cache = c.Cache.Next() 44 | } 45 | 46 | func (c *ReadingCache) LastReading() (r Reading, err error) { 47 | foo := c.Cache.Prev() 48 | if foo.Value == nil { 49 | return r, errors.New("no element in cache") 50 | } else { 51 | r = foo.Value.(Reading) 52 | return r, nil 53 | } 54 | } 55 | 56 | func (c *ReadingCache) AllReadings() (r []Reading) { 57 | r = make([]Reading, 0, 10) 58 | i := 0 59 | c.Cache.Do(func(x interface{}) { 60 | if x != nil { 61 | //fmt.Printf("adding element %d: %s\n", i, x.(Reading)) 62 | r = append(r, x.(Reading)) 63 | i++ 64 | } 65 | }) 66 | //fmt.Printf("Returning %d elements\n", len(r)) 67 | return r 68 | } 69 | 70 | func (c *ReadingCache) NumElements() (retval int) { 71 | retval = 0 72 | c.Cache.Do(func(x interface{}) { 73 | if x != nil { 74 | retval += 1 75 | } 76 | }) 77 | return retval 78 | } 79 | 80 | func (c ReadingCache) String() (retval string) { 81 | retval = "[" 82 | c.Cache.Do(func(x interface{}) { 83 | if x != nil { 84 | retval = fmt.Sprintf("%s %s", retval, x) 85 | } 86 | }) 87 | retval = retval + "]" 88 | return retval 89 | } 90 | -------------------------------------------------------------------------------- /src/github.com/netzsinus/defluxio-software/readings_test.go: -------------------------------------------------------------------------------- 1 | // (C) 2014 Mathias Dalheimer . 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package defluxio 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestCacheAdding(t *testing.T) { 12 | r1 := Reading{Value: 1.0} 13 | r2 := Reading{Value: 2.0} 14 | r3 := Reading{Value: 3.0} 15 | 16 | rc := MakeReadingCache(10) 17 | rc.AddReading(r1) 18 | rc.AddReading(r2) 19 | rc.AddReading(r3) 20 | if rc.NumElements() != 3 { 21 | t.Errorf("Expected %d elements, got %d.", 3, rc.NumElements()) 22 | } 23 | } 24 | 25 | func TestCacheGetLast(t *testing.T) { 26 | rc := MakeReadingCache(10) 27 | if _, e := rc.LastReading(); e == nil { 28 | t.Errorf("Expected error when querying empty cache, but got none") 29 | } 30 | r1 := Reading{Value: 1.0} 31 | r2 := Reading{Value: 2.0} 32 | rc.AddReading(r1) 33 | if r, _ := rc.LastReading(); r != r1 { 34 | t.Errorf("Retrieving last reading: Expected %s, got %s", r1, r) 35 | } 36 | rc.AddReading(r2) 37 | if r, _ := rc.LastReading(); r != r2 { 38 | t.Errorf("Retrieving last reading: Expected %s, got %s", r2, r) 39 | } 40 | for i := 0; i < 100; i++ { 41 | rc.AddReading(r2) 42 | } 43 | if r, _ := rc.LastReading(); r != r2 { 44 | t.Errorf("Retrieving last reading: Expected %s, got %s", r2, r) 45 | } 46 | if l := rc.NumElements(); l != 10 { 47 | t.Errorf("Counting number of elements: Expected %s, got %s", 10, l) 48 | } 49 | } 50 | 51 | func TestCacheGetAll(t *testing.T) { 52 | rc := MakeReadingCache(10) 53 | if readings := rc.AllReadings(); len(readings) != 0 { 54 | t.Errorf("Expected empty cache, but got some elements: %s", readings) 55 | } 56 | r1 := Reading{Value: 1.0} 57 | for i := 0; i < 100; i++ { 58 | rc.AddReading(r1) 59 | } 60 | if readings := rc.AllReadings(); len(readings) != 10 { 61 | t.Errorf("Expected 10 elements in cache, but got %d", len(readings)) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vendor/manifest: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "dependencies": [ 4 | { 5 | "importpath": "github.com/influxdb/influxdb/pkg/escape", 6 | "repository": "https://github.com/influxdb/influxdb", 7 | "revision": "9ab5de0a34fb3e4184958984d9165259f42f70da", 8 | "branch": "master", 9 | "path": "/pkg/escape" 10 | }, 11 | { 12 | "importpath": "github.com/tarm/goserial", 13 | "repository": "https://github.com/tarm/goserial", 14 | "revision": "b3440c3c63556c3fd67e915be3b92195bc70294a", 15 | "branch": "master" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/context/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.0 5 | - 1.1 6 | - 1.2 7 | - tip 8 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/context/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Rodrigo Moraes. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/context/README.md: -------------------------------------------------------------------------------- 1 | context 2 | ======= 3 | [![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context) 4 | 5 | gorilla/context is a general purpose registry for global request variables. 6 | 7 | Read the full documentation here: http://www.gorillatoolkit.org/pkg/context 8 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/context/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package context 6 | 7 | import ( 8 | "net/http" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | var ( 14 | mutex sync.RWMutex 15 | data = make(map[*http.Request]map[interface{}]interface{}) 16 | datat = make(map[*http.Request]int64) 17 | ) 18 | 19 | // Set stores a value for a given key in a given request. 20 | func Set(r *http.Request, key, val interface{}) { 21 | mutex.Lock() 22 | if data[r] == nil { 23 | data[r] = make(map[interface{}]interface{}) 24 | datat[r] = time.Now().Unix() 25 | } 26 | data[r][key] = val 27 | mutex.Unlock() 28 | } 29 | 30 | // Get returns a value stored for a given key in a given request. 31 | func Get(r *http.Request, key interface{}) interface{} { 32 | mutex.RLock() 33 | if ctx := data[r]; ctx != nil { 34 | value := ctx[key] 35 | mutex.RUnlock() 36 | return value 37 | } 38 | mutex.RUnlock() 39 | return nil 40 | } 41 | 42 | // GetOk returns stored value and presence state like multi-value return of map access. 43 | func GetOk(r *http.Request, key interface{}) (interface{}, bool) { 44 | mutex.RLock() 45 | if _, ok := data[r]; ok { 46 | value, ok := data[r][key] 47 | mutex.RUnlock() 48 | return value, ok 49 | } 50 | mutex.RUnlock() 51 | return nil, false 52 | } 53 | 54 | // GetAll returns all stored values for the request as a map. Nil is returned for invalid requests. 55 | func GetAll(r *http.Request) map[interface{}]interface{} { 56 | mutex.RLock() 57 | if context, ok := data[r]; ok { 58 | result := make(map[interface{}]interface{}, len(context)) 59 | for k, v := range context { 60 | result[k] = v 61 | } 62 | mutex.RUnlock() 63 | return result 64 | } 65 | mutex.RUnlock() 66 | return nil 67 | } 68 | 69 | // GetAllOk returns all stored values for the request as a map and a boolean value that indicates if 70 | // the request was registered. 71 | func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { 72 | mutex.RLock() 73 | context, ok := data[r] 74 | result := make(map[interface{}]interface{}, len(context)) 75 | for k, v := range context { 76 | result[k] = v 77 | } 78 | mutex.RUnlock() 79 | return result, ok 80 | } 81 | 82 | // Delete removes a value stored for a given key in a given request. 83 | func Delete(r *http.Request, key interface{}) { 84 | mutex.Lock() 85 | if data[r] != nil { 86 | delete(data[r], key) 87 | } 88 | mutex.Unlock() 89 | } 90 | 91 | // Clear removes all values stored for a given request. 92 | // 93 | // This is usually called by a handler wrapper to clean up request 94 | // variables at the end of a request lifetime. See ClearHandler(). 95 | func Clear(r *http.Request) { 96 | mutex.Lock() 97 | clear(r) 98 | mutex.Unlock() 99 | } 100 | 101 | // clear is Clear without the lock. 102 | func clear(r *http.Request) { 103 | delete(data, r) 104 | delete(datat, r) 105 | } 106 | 107 | // Purge removes request data stored for longer than maxAge, in seconds. 108 | // It returns the amount of requests removed. 109 | // 110 | // If maxAge <= 0, all request data is removed. 111 | // 112 | // This is only used for sanity check: in case context cleaning was not 113 | // properly set some request data can be kept forever, consuming an increasing 114 | // amount of memory. In case this is detected, Purge() must be called 115 | // periodically until the problem is fixed. 116 | func Purge(maxAge int) int { 117 | mutex.Lock() 118 | count := 0 119 | if maxAge <= 0 { 120 | count = len(data) 121 | data = make(map[*http.Request]map[interface{}]interface{}) 122 | datat = make(map[*http.Request]int64) 123 | } else { 124 | min := time.Now().Unix() - int64(maxAge) 125 | for r := range data { 126 | if datat[r] < min { 127 | clear(r) 128 | count++ 129 | } 130 | } 131 | } 132 | mutex.Unlock() 133 | return count 134 | } 135 | 136 | // ClearHandler wraps an http.Handler and clears request values at the end 137 | // of a request lifetime. 138 | func ClearHandler(h http.Handler) http.Handler { 139 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 140 | defer Clear(r) 141 | h.ServeHTTP(w, r) 142 | }) 143 | } 144 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/context/context_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package context 6 | 7 | import ( 8 | "net/http" 9 | "testing" 10 | ) 11 | 12 | type keyType int 13 | 14 | const ( 15 | key1 keyType = iota 16 | key2 17 | ) 18 | 19 | func TestContext(t *testing.T) { 20 | assertEqual := func(val interface{}, exp interface{}) { 21 | if val != exp { 22 | t.Errorf("Expected %v, got %v.", exp, val) 23 | } 24 | } 25 | 26 | r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) 27 | emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil) 28 | 29 | // Get() 30 | assertEqual(Get(r, key1), nil) 31 | 32 | // Set() 33 | Set(r, key1, "1") 34 | assertEqual(Get(r, key1), "1") 35 | assertEqual(len(data[r]), 1) 36 | 37 | Set(r, key2, "2") 38 | assertEqual(Get(r, key2), "2") 39 | assertEqual(len(data[r]), 2) 40 | 41 | //GetOk 42 | value, ok := GetOk(r, key1) 43 | assertEqual(value, "1") 44 | assertEqual(ok, true) 45 | 46 | value, ok = GetOk(r, "not exists") 47 | assertEqual(value, nil) 48 | assertEqual(ok, false) 49 | 50 | Set(r, "nil value", nil) 51 | value, ok = GetOk(r, "nil value") 52 | assertEqual(value, nil) 53 | assertEqual(ok, true) 54 | 55 | // GetAll() 56 | values := GetAll(r) 57 | assertEqual(len(values), 3) 58 | 59 | // GetAll() for empty request 60 | values = GetAll(emptyR) 61 | if values != nil { 62 | t.Error("GetAll didn't return nil value for invalid request") 63 | } 64 | 65 | // GetAllOk() 66 | values, ok = GetAllOk(r) 67 | assertEqual(len(values), 3) 68 | assertEqual(ok, true) 69 | 70 | // GetAllOk() for empty request 71 | values, ok = GetAllOk(emptyR) 72 | assertEqual(value, nil) 73 | assertEqual(ok, false) 74 | 75 | // Delete() 76 | Delete(r, key1) 77 | assertEqual(Get(r, key1), nil) 78 | assertEqual(len(data[r]), 2) 79 | 80 | Delete(r, key2) 81 | assertEqual(Get(r, key2), nil) 82 | assertEqual(len(data[r]), 1) 83 | 84 | // Clear() 85 | Clear(r) 86 | assertEqual(len(data), 0) 87 | } 88 | 89 | func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) { 90 | <-wait 91 | for i := 0; i < iterations; i++ { 92 | Get(r, key) 93 | } 94 | done <- struct{}{} 95 | 96 | } 97 | 98 | func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) { 99 | <-wait 100 | for i := 0; i < iterations; i++ { 101 | Get(r, key) 102 | } 103 | done <- struct{}{} 104 | 105 | } 106 | 107 | func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) { 108 | 109 | b.StopTimer() 110 | r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) 111 | done := make(chan struct{}) 112 | b.StartTimer() 113 | 114 | for i := 0; i < b.N; i++ { 115 | wait := make(chan struct{}) 116 | 117 | for i := 0; i < numReaders; i++ { 118 | go parallelReader(r, "test", iterations, wait, done) 119 | } 120 | 121 | for i := 0; i < numWriters; i++ { 122 | go parallelWriter(r, "test", "123", iterations, wait, done) 123 | } 124 | 125 | close(wait) 126 | 127 | for i := 0; i < numReaders+numWriters; i++ { 128 | <-done 129 | } 130 | 131 | } 132 | 133 | } 134 | 135 | func BenchmarkMutexSameReadWrite1(b *testing.B) { 136 | benchmarkMutex(b, 1, 1, 32) 137 | } 138 | func BenchmarkMutexSameReadWrite2(b *testing.B) { 139 | benchmarkMutex(b, 2, 2, 32) 140 | } 141 | func BenchmarkMutexSameReadWrite4(b *testing.B) { 142 | benchmarkMutex(b, 4, 4, 32) 143 | } 144 | func BenchmarkMutex1(b *testing.B) { 145 | benchmarkMutex(b, 2, 8, 32) 146 | } 147 | func BenchmarkMutex2(b *testing.B) { 148 | benchmarkMutex(b, 16, 4, 64) 149 | } 150 | func BenchmarkMutex3(b *testing.B) { 151 | benchmarkMutex(b, 1, 2, 128) 152 | } 153 | func BenchmarkMutex4(b *testing.B) { 154 | benchmarkMutex(b, 128, 32, 256) 155 | } 156 | func BenchmarkMutex5(b *testing.B) { 157 | benchmarkMutex(b, 1024, 2048, 64) 158 | } 159 | func BenchmarkMutex6(b *testing.B) { 160 | benchmarkMutex(b, 2048, 1024, 512) 161 | } 162 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/context/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package context stores values shared during a request lifetime. 7 | 8 | For example, a router can set variables extracted from the URL and later 9 | application handlers can access those values, or it can be used to store 10 | sessions values to be saved at the end of a request. There are several 11 | others common uses. 12 | 13 | The idea was posted by Brad Fitzpatrick to the go-nuts mailing list: 14 | 15 | http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53 16 | 17 | Here's the basic usage: first define the keys that you will need. The key 18 | type is interface{} so a key can be of any type that supports equality. 19 | Here we define a key using a custom int type to avoid name collisions: 20 | 21 | package foo 22 | 23 | import ( 24 | "github.com/gorilla/context" 25 | ) 26 | 27 | type key int 28 | 29 | const MyKey key = 0 30 | 31 | Then set a variable. Variables are bound to an http.Request object, so you 32 | need a request instance to set a value: 33 | 34 | context.Set(r, MyKey, "bar") 35 | 36 | The application can later access the variable using the same key you provided: 37 | 38 | func MyHandler(w http.ResponseWriter, r *http.Request) { 39 | // val is "bar". 40 | val := context.Get(r, foo.MyKey) 41 | 42 | // returns ("bar", true) 43 | val, ok := context.GetOk(r, foo.MyKey) 44 | // ... 45 | } 46 | 47 | And that's all about the basic usage. We discuss some other ideas below. 48 | 49 | Any type can be stored in the context. To enforce a given type, make the key 50 | private and wrap Get() and Set() to accept and return values of a specific 51 | type: 52 | 53 | type key int 54 | 55 | const mykey key = 0 56 | 57 | // GetMyKey returns a value for this package from the request values. 58 | func GetMyKey(r *http.Request) SomeType { 59 | if rv := context.Get(r, mykey); rv != nil { 60 | return rv.(SomeType) 61 | } 62 | return nil 63 | } 64 | 65 | // SetMyKey sets a value for this package in the request values. 66 | func SetMyKey(r *http.Request, val SomeType) { 67 | context.Set(r, mykey, val) 68 | } 69 | 70 | Variables must be cleared at the end of a request, to remove all values 71 | that were stored. This can be done in an http.Handler, after a request was 72 | served. Just call Clear() passing the request: 73 | 74 | context.Clear(r) 75 | 76 | ...or use ClearHandler(), which conveniently wraps an http.Handler to clear 77 | variables at the end of a request lifetime. 78 | 79 | The Routers from the packages gorilla/mux and gorilla/pat call Clear() 80 | so if you are using either of them you don't need to clear the context manually. 81 | */ 82 | package context 83 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/mux/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.0 5 | - 1.1 6 | - 1.2 7 | - tip 8 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/mux/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Rodrigo Moraes. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/mux/README.md: -------------------------------------------------------------------------------- 1 | mux 2 | === 3 | [![Build Status](https://travis-ci.org/gorilla/mux.png?branch=master)](https://travis-ci.org/gorilla/mux) 4 | 5 | gorilla/mux is a powerful URL router and dispatcher. 6 | 7 | Read the full documentation here: http://www.gorillatoolkit.org/pkg/mux 8 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/mux/bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mux 6 | 7 | import ( 8 | "net/http" 9 | "testing" 10 | ) 11 | 12 | func BenchmarkMux(b *testing.B) { 13 | router := new(Router) 14 | handler := func(w http.ResponseWriter, r *http.Request) {} 15 | router.HandleFunc("/v1/{v1}", handler) 16 | 17 | request, _ := http.NewRequest("GET", "/v1/anything", nil) 18 | for i := 0; i < b.N; i++ { 19 | router.ServeHTTP(nil, request) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/mux/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package gorilla/mux implements a request router and dispatcher. 7 | 8 | The name mux stands for "HTTP request multiplexer". Like the standard 9 | http.ServeMux, mux.Router matches incoming requests against a list of 10 | registered routes and calls a handler for the route that matches the URL 11 | or other conditions. The main features are: 12 | 13 | * Requests can be matched based on URL host, path, path prefix, schemes, 14 | header and query values, HTTP methods or using custom matchers. 15 | * URL hosts and paths can have variables with an optional regular 16 | expression. 17 | * Registered URLs can be built, or "reversed", which helps maintaining 18 | references to resources. 19 | * Routes can be used as subrouters: nested routes are only tested if the 20 | parent route matches. This is useful to define groups of routes that 21 | share common conditions like a host, a path prefix or other repeated 22 | attributes. As a bonus, this optimizes request matching. 23 | * It implements the http.Handler interface so it is compatible with the 24 | standard http.ServeMux. 25 | 26 | Let's start registering a couple of URL paths and handlers: 27 | 28 | func main() { 29 | r := mux.NewRouter() 30 | r.HandleFunc("/", HomeHandler) 31 | r.HandleFunc("/products", ProductsHandler) 32 | r.HandleFunc("/articles", ArticlesHandler) 33 | http.Handle("/", r) 34 | } 35 | 36 | Here we register three routes mapping URL paths to handlers. This is 37 | equivalent to how http.HandleFunc() works: if an incoming request URL matches 38 | one of the paths, the corresponding handler is called passing 39 | (http.ResponseWriter, *http.Request) as parameters. 40 | 41 | Paths can have variables. They are defined using the format {name} or 42 | {name:pattern}. If a regular expression pattern is not defined, the matched 43 | variable will be anything until the next slash. For example: 44 | 45 | r := mux.NewRouter() 46 | r.HandleFunc("/products/{key}", ProductHandler) 47 | r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) 48 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) 49 | 50 | The names are used to create a map of route variables which can be retrieved 51 | calling mux.Vars(): 52 | 53 | vars := mux.Vars(request) 54 | category := vars["category"] 55 | 56 | And this is all you need to know about the basic usage. More advanced options 57 | are explained below. 58 | 59 | Routes can also be restricted to a domain or subdomain. Just define a host 60 | pattern to be matched. They can also have variables: 61 | 62 | r := mux.NewRouter() 63 | // Only matches if domain is "www.domain.com". 64 | r.Host("www.domain.com") 65 | // Matches a dynamic subdomain. 66 | r.Host("{subdomain:[a-z]+}.domain.com") 67 | 68 | There are several other matchers that can be added. To match path prefixes: 69 | 70 | r.PathPrefix("/products/") 71 | 72 | ...or HTTP methods: 73 | 74 | r.Methods("GET", "POST") 75 | 76 | ...or URL schemes: 77 | 78 | r.Schemes("https") 79 | 80 | ...or header values: 81 | 82 | r.Headers("X-Requested-With", "XMLHttpRequest") 83 | 84 | ...or query values: 85 | 86 | r.Queries("key", "value") 87 | 88 | ...or to use a custom matcher function: 89 | 90 | r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { 91 | return r.ProtoMajor == 0 92 | }) 93 | 94 | ...and finally, it is possible to combine several matchers in a single route: 95 | 96 | r.HandleFunc("/products", ProductsHandler). 97 | Host("www.domain.com"). 98 | Methods("GET"). 99 | Schemes("http") 100 | 101 | Setting the same matching conditions again and again can be boring, so we have 102 | a way to group several routes that share the same requirements. 103 | We call it "subrouting". 104 | 105 | For example, let's say we have several URLs that should only match when the 106 | host is "www.domain.com". Create a route for that host and get a "subrouter" 107 | from it: 108 | 109 | r := mux.NewRouter() 110 | s := r.Host("www.domain.com").Subrouter() 111 | 112 | Then register routes in the subrouter: 113 | 114 | s.HandleFunc("/products/", ProductsHandler) 115 | s.HandleFunc("/products/{key}", ProductHandler) 116 | s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) 117 | 118 | The three URL paths we registered above will only be tested if the domain is 119 | "www.domain.com", because the subrouter is tested first. This is not 120 | only convenient, but also optimizes request matching. You can create 121 | subrouters combining any attribute matchers accepted by a route. 122 | 123 | Subrouters can be used to create domain or path "namespaces": you define 124 | subrouters in a central place and then parts of the app can register its 125 | paths relatively to a given subrouter. 126 | 127 | There's one more thing about subroutes. When a subrouter has a path prefix, 128 | the inner routes use it as base for their paths: 129 | 130 | r := mux.NewRouter() 131 | s := r.PathPrefix("/products").Subrouter() 132 | // "/products/" 133 | s.HandleFunc("/", ProductsHandler) 134 | // "/products/{key}/" 135 | s.HandleFunc("/{key}/", ProductHandler) 136 | // "/products/{key}/details" 137 | s.HandleFunc("/{key}/details", ProductDetailsHandler) 138 | 139 | Now let's see how to build registered URLs. 140 | 141 | Routes can be named. All routes that define a name can have their URLs built, 142 | or "reversed". We define a name calling Name() on a route. For example: 143 | 144 | r := mux.NewRouter() 145 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). 146 | Name("article") 147 | 148 | To build a URL, get the route and call the URL() method, passing a sequence of 149 | key/value pairs for the route variables. For the previous route, we would do: 150 | 151 | url, err := r.Get("article").URL("category", "technology", "id", "42") 152 | 153 | ...and the result will be a url.URL with the following path: 154 | 155 | "/articles/technology/42" 156 | 157 | This also works for host variables: 158 | 159 | r := mux.NewRouter() 160 | r.Host("{subdomain}.domain.com"). 161 | Path("/articles/{category}/{id:[0-9]+}"). 162 | HandlerFunc(ArticleHandler). 163 | Name("article") 164 | 165 | // url.String() will be "http://news.domain.com/articles/technology/42" 166 | url, err := r.Get("article").URL("subdomain", "news", 167 | "category", "technology", 168 | "id", "42") 169 | 170 | All variables defined in the route are required, and their values must 171 | conform to the corresponding patterns. These requirements guarantee that a 172 | generated URL will always match a registered route -- the only exception is 173 | for explicitly defined "build-only" routes which never match. 174 | 175 | There's also a way to build only the URL host or path for a route: 176 | use the methods URLHost() or URLPath() instead. For the previous route, 177 | we would do: 178 | 179 | // "http://news.domain.com/" 180 | host, err := r.Get("article").URLHost("subdomain", "news") 181 | 182 | // "/articles/technology/42" 183 | path, err := r.Get("article").URLPath("category", "technology", "id", "42") 184 | 185 | And if you use subrouters, host and path defined separately can be built 186 | as well: 187 | 188 | r := mux.NewRouter() 189 | s := r.Host("{subdomain}.domain.com").Subrouter() 190 | s.Path("/articles/{category}/{id:[0-9]+}"). 191 | HandlerFunc(ArticleHandler). 192 | Name("article") 193 | 194 | // "http://news.domain.com/articles/technology/42" 195 | url, err := r.Get("article").URL("subdomain", "news", 196 | "category", "technology", 197 | "id", "42") 198 | */ 199 | package mux 200 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.1 5 | - 1.2 6 | - tip 7 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Gorilla WebSocket authors for copyright 2 | # purposes. 3 | # 4 | # Please keep the list sorted. 5 | 6 | Gary Burd 7 | Joachim Bauch 8 | 9 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/README.md: -------------------------------------------------------------------------------- 1 | # Gorilla WebSocket 2 | 3 | Gorilla WebSocket is a [Go](http://golang.org/) implementation of the 4 | [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. 5 | 6 | ### Documentation 7 | 8 | * [API Reference](http://godoc.org/github.com/gorilla/websocket) 9 | * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) 10 | * [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) 11 | 12 | ### Status 13 | 14 | The Gorilla WebSocket package provides a complete and tested implementation of 15 | the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The 16 | package API is stable. 17 | 18 | ### Installation 19 | 20 | go get github.com/gorilla/websocket 21 | 22 | ### Protocol Compliance 23 | 24 | The Gorilla WebSocket package passes the server tests in the [Autobahn Test 25 | Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn 26 | subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). 27 | 28 | ### Gorilla WebSocket compared with other packages 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
gorillago.net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Limit size of received messageYesNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
48 | 49 | Notes: 50 | 51 | 1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). 52 | 2. The application can get the type of a received data message by implementing 53 | a [Codec marshal](http://godoc.org/code.google.com/p/go.net/websocket#Codec.Marshal) 54 | function. 55 | 3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. 56 | Read returns when the input buffer is full or a frame boundary is 57 | encountered, Each call to Write sends a single frame message. The Gorilla 58 | io.Reader and io.WriteCloser operate on a single WebSocket message. 59 | 60 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "crypto/tls" 9 | "errors" 10 | "net" 11 | "net/http" 12 | "net/url" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | // ErrBadHandshake is returned when the server response to opening handshake is 18 | // invalid. 19 | var ErrBadHandshake = errors.New("websocket: bad handshake") 20 | 21 | // NewClient creates a new client connection using the given net connection. 22 | // The URL u specifies the host and request URI. Use requestHeader to specify 23 | // the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies 24 | // (Cookie). Use the response.Header to get the selected subprotocol 25 | // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). 26 | // 27 | // If the WebSocket handshake fails, ErrBadHandshake is returned along with a 28 | // non-nil *http.Response so that callers can handle redirects, authentication, 29 | // etc. 30 | func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { 31 | challengeKey, err := generateChallengeKey() 32 | if err != nil { 33 | return nil, nil, err 34 | } 35 | acceptKey := computeAcceptKey(challengeKey) 36 | 37 | c = newConn(netConn, false, readBufSize, writeBufSize) 38 | p := c.writeBuf[:0] 39 | p = append(p, "GET "...) 40 | p = append(p, u.RequestURI()...) 41 | p = append(p, " HTTP/1.1\r\nHost: "...) 42 | p = append(p, u.Host...) 43 | // "Upgrade" is capitalized for servers that do not use case insensitive 44 | // comparisons on header tokens. 45 | p = append(p, "\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: "...) 46 | p = append(p, challengeKey...) 47 | p = append(p, "\r\n"...) 48 | for k, vs := range requestHeader { 49 | for _, v := range vs { 50 | p = append(p, k...) 51 | p = append(p, ": "...) 52 | p = append(p, v...) 53 | p = append(p, "\r\n"...) 54 | } 55 | } 56 | p = append(p, "\r\n"...) 57 | 58 | if _, err := netConn.Write(p); err != nil { 59 | return nil, nil, err 60 | } 61 | 62 | resp, err := http.ReadResponse(c.br, &http.Request{Method: "GET", URL: u}) 63 | if err != nil { 64 | return nil, nil, err 65 | } 66 | if resp.StatusCode != 101 || 67 | !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || 68 | !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || 69 | resp.Header.Get("Sec-Websocket-Accept") != acceptKey { 70 | return nil, resp, ErrBadHandshake 71 | } 72 | c.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") 73 | return c, resp, nil 74 | } 75 | 76 | // A Dialer contains options for connecting to WebSocket server. 77 | type Dialer struct { 78 | // NetDial specifies the dial function for creating TCP connections. If 79 | // NetDial is nil, net.Dial is used. 80 | NetDial func(network, addr string) (net.Conn, error) 81 | 82 | // TLSClientConfig specifies the TLS configuration to use with tls.Client. 83 | // If nil, the default configuration is used. 84 | TLSClientConfig *tls.Config 85 | 86 | // HandshakeTimeout specifies the duration for the handshake to complete. 87 | HandshakeTimeout time.Duration 88 | 89 | // Input and output buffer sizes. If the buffer size is zero, then a 90 | // default value of 4096 is used. 91 | ReadBufferSize, WriteBufferSize int 92 | 93 | // Subprotocols specifies the client's requested subprotocols. 94 | Subprotocols []string 95 | } 96 | 97 | var errMalformedURL = errors.New("malformed ws or wss URL") 98 | 99 | // parseURL parses the URL. The url.Parse function is not used here because 100 | // url.Parse mangles the path. 101 | func parseURL(s string) (*url.URL, error) { 102 | // From the RFC: 103 | // 104 | // ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] 105 | // wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] 106 | // 107 | // We don't use the net/url parser here because the dialer interface does 108 | // not provide a way for applications to work around percent deocding in 109 | // the net/url parser. 110 | 111 | var u url.URL 112 | switch { 113 | case strings.HasPrefix(s, "ws://"): 114 | u.Scheme = "ws" 115 | s = s[len("ws://"):] 116 | case strings.HasPrefix(s, "wss://"): 117 | u.Scheme = "wss" 118 | s = s[len("wss://"):] 119 | default: 120 | return nil, errMalformedURL 121 | } 122 | 123 | u.Host = s 124 | u.Opaque = "/" 125 | if i := strings.Index(s, "/"); i >= 0 { 126 | u.Host = s[:i] 127 | u.Opaque = s[i:] 128 | } 129 | 130 | return &u, nil 131 | } 132 | 133 | func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { 134 | hostPort = u.Host 135 | hostNoPort = u.Host 136 | if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { 137 | hostNoPort = hostNoPort[:i] 138 | } else { 139 | if u.Scheme == "wss" { 140 | hostPort += ":443" 141 | } else { 142 | hostPort += ":80" 143 | } 144 | } 145 | return hostPort, hostNoPort 146 | } 147 | 148 | // DefaultDialer is a dialer with all fields set to the default zero values. 149 | var DefaultDialer *Dialer 150 | 151 | // Dial creates a new client connection. Use requestHeader to specify the 152 | // origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). 153 | // Use the response.Header to get the selected subprotocol 154 | // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). 155 | // 156 | // If the WebSocket handshake fails, ErrBadHandshake is returned along with a 157 | // non-nil *http.Response so that callers can handle redirects, authentication, 158 | // etc. 159 | func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { 160 | u, err := parseURL(urlStr) 161 | if err != nil { 162 | return nil, nil, err 163 | } 164 | 165 | hostPort, hostNoPort := hostPortNoPort(u) 166 | 167 | if d == nil { 168 | d = &Dialer{} 169 | } 170 | 171 | var deadline time.Time 172 | if d.HandshakeTimeout != 0 { 173 | deadline = time.Now().Add(d.HandshakeTimeout) 174 | } 175 | 176 | netDial := d.NetDial 177 | if netDial == nil { 178 | netDialer := &net.Dialer{Deadline: deadline} 179 | netDial = netDialer.Dial 180 | } 181 | 182 | netConn, err := netDial("tcp", hostPort) 183 | if err != nil { 184 | return nil, nil, err 185 | } 186 | 187 | defer func() { 188 | if netConn != nil { 189 | netConn.Close() 190 | } 191 | }() 192 | 193 | if err := netConn.SetDeadline(deadline); err != nil { 194 | return nil, nil, err 195 | } 196 | 197 | if u.Scheme == "wss" { 198 | cfg := d.TLSClientConfig 199 | if cfg == nil { 200 | cfg = &tls.Config{ServerName: hostNoPort} 201 | } else if cfg.ServerName == "" { 202 | shallowCopy := *cfg 203 | cfg = &shallowCopy 204 | cfg.ServerName = hostNoPort 205 | } 206 | tlsConn := tls.Client(netConn, cfg) 207 | netConn = tlsConn 208 | if err := tlsConn.Handshake(); err != nil { 209 | return nil, nil, err 210 | } 211 | if !cfg.InsecureSkipVerify { 212 | if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { 213 | return nil, nil, err 214 | } 215 | } 216 | } 217 | 218 | readBufferSize := d.ReadBufferSize 219 | if readBufferSize == 0 { 220 | readBufferSize = 4096 221 | } 222 | 223 | writeBufferSize := d.WriteBufferSize 224 | if writeBufferSize == 0 { 225 | writeBufferSize = 4096 226 | } 227 | 228 | if len(d.Subprotocols) > 0 { 229 | h := http.Header{} 230 | for k, v := range requestHeader { 231 | h[k] = v 232 | } 233 | h.Set("Sec-Websocket-Protocol", strings.Join(d.Subprotocols, ", ")) 234 | requestHeader = h 235 | } 236 | 237 | conn, resp, err := NewClient(netConn, u, requestHeader, readBufferSize, writeBufferSize) 238 | if err != nil { 239 | return nil, resp, err 240 | } 241 | 242 | netConn.SetDeadline(time.Time{}) 243 | netConn = nil // to avoid close in defer. 244 | return conn, resp, nil 245 | } 246 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/client_server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "crypto/tls" 9 | "crypto/x509" 10 | "io" 11 | "net" 12 | "net/http" 13 | "net/http/httptest" 14 | "net/url" 15 | "reflect" 16 | "testing" 17 | "time" 18 | ) 19 | 20 | func sendRecv(t *testing.T, ws *Conn) { 21 | const message = "Hello World!" 22 | if err := ws.SetWriteDeadline(time.Now().Add(time.Second)); err != nil { 23 | t.Fatalf("SetWriteDeadline: %v", err) 24 | } 25 | if err := ws.WriteMessage(TextMessage, []byte(message)); err != nil { 26 | t.Fatalf("WriteMessage: %v", err) 27 | } 28 | if err := ws.SetReadDeadline(time.Now().Add(time.Second)); err != nil { 29 | t.Fatalf("SetReadDeadline: %v", err) 30 | } 31 | _, p, err := ws.ReadMessage() 32 | if err != nil { 33 | t.Fatalf("ReadMessage: %v", err) 34 | } 35 | if string(p) != message { 36 | t.Fatalf("message=%s, want %s", p, message) 37 | } 38 | } 39 | 40 | func httpToWs(u string) string { 41 | return "ws" + u[len("http"):] 42 | } 43 | 44 | var handshakeUpgrader = &Upgrader{ 45 | Subprotocols: []string{"p0", "p1"}, 46 | ReadBufferSize: 1024, 47 | WriteBufferSize: 1024, 48 | } 49 | 50 | var handshakeDialer = &Dialer{ 51 | Subprotocols: []string{"p1", "p2"}, 52 | ReadBufferSize: 1024, 53 | WriteBufferSize: 1024, 54 | } 55 | 56 | type handshakeHandler struct { 57 | *testing.T 58 | } 59 | 60 | func (t handshakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 61 | if r.Method != "GET" { 62 | http.Error(w, "Method not allowed", 405) 63 | t.Logf("method = %s, want GET", r.Method) 64 | return 65 | } 66 | subprotos := Subprotocols(r) 67 | if !reflect.DeepEqual(subprotos, handshakeDialer.Subprotocols) { 68 | http.Error(w, "bad protocol", 400) 69 | t.Logf("Subprotocols = %v, want %v", subprotos, handshakeDialer.Subprotocols) 70 | return 71 | } 72 | ws, err := handshakeUpgrader.Upgrade(w, r, http.Header{"Set-Cookie": {"sessionID=1234"}}) 73 | if err != nil { 74 | t.Logf("upgrade error: %v", err) 75 | return 76 | } 77 | defer ws.Close() 78 | 79 | if ws.Subprotocol() != "p1" { 80 | t.Logf("ws.Subprotocol() = %s, want p1", ws.Subprotocol()) 81 | return 82 | } 83 | 84 | for { 85 | op, r, err := ws.NextReader() 86 | if err != nil { 87 | if err != io.EOF { 88 | t.Logf("NextReader: %v", err) 89 | } 90 | return 91 | } 92 | w, err := ws.NextWriter(op) 93 | if err != nil { 94 | t.Logf("NextWriter: %v", err) 95 | return 96 | } 97 | if _, err = io.Copy(w, r); err != nil { 98 | t.Logf("Copy: %v", err) 99 | return 100 | } 101 | if err := w.Close(); err != nil { 102 | t.Logf("Close: %v", err) 103 | return 104 | } 105 | } 106 | } 107 | 108 | func TestHandshake(t *testing.T) { 109 | s := httptest.NewServer(handshakeHandler{t}) 110 | defer s.Close() 111 | ws, resp, err := handshakeDialer.Dial(httpToWs(s.URL), http.Header{"Origin": {s.URL}}) 112 | if err != nil { 113 | t.Fatalf("Dial: %v", err) 114 | } 115 | defer ws.Close() 116 | 117 | var sessionID string 118 | for _, c := range resp.Cookies() { 119 | if c.Name == "sessionID" { 120 | sessionID = c.Value 121 | } 122 | } 123 | if sessionID != "1234" { 124 | t.Error("Set-Cookie not received from the server.") 125 | } 126 | 127 | if ws.Subprotocol() != "p1" { 128 | t.Errorf("ws.Subprotocol() = %s, want p1", ws.Subprotocol()) 129 | } 130 | sendRecv(t, ws) 131 | } 132 | 133 | type dialHandler struct { 134 | *testing.T 135 | } 136 | 137 | var dialUpgrader = &Upgrader{ 138 | ReadBufferSize: 1024, 139 | WriteBufferSize: 1024, 140 | CheckOrigin: func(r *http.Request) bool { return true }, 141 | } 142 | 143 | func (t dialHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 144 | ws, err := dialUpgrader.Upgrade(w, r, nil) 145 | if err != nil { 146 | t.Logf("upgrade error: %v", err) 147 | return 148 | } 149 | defer ws.Close() 150 | for { 151 | mt, p, err := ws.ReadMessage() 152 | if err != nil { 153 | if err != io.EOF { 154 | t.Logf("ReadMessage: %v", err) 155 | } 156 | return 157 | } 158 | if err := ws.WriteMessage(mt, p); err != nil { 159 | t.Logf("WriteMessage: %v", err) 160 | return 161 | } 162 | } 163 | } 164 | 165 | func TestDial(t *testing.T) { 166 | s := httptest.NewServer(dialHandler{t}) 167 | defer s.Close() 168 | ws, _, err := DefaultDialer.Dial(httpToWs(s.URL), nil) 169 | if err != nil { 170 | t.Fatalf("Dial() returned error %v", err) 171 | } 172 | defer ws.Close() 173 | sendRecv(t, ws) 174 | } 175 | 176 | func TestDialTLS(t *testing.T) { 177 | s := httptest.NewTLSServer(dialHandler{t}) 178 | defer s.Close() 179 | 180 | certs := x509.NewCertPool() 181 | for _, c := range s.TLS.Certificates { 182 | roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1]) 183 | if err != nil { 184 | t.Fatalf("error parsing server's root cert: %v", err) 185 | } 186 | for _, root := range roots { 187 | certs.AddCert(root) 188 | } 189 | } 190 | 191 | u, _ := url.Parse(s.URL) 192 | d := &Dialer{ 193 | NetDial: func(network, addr string) (net.Conn, error) { return net.Dial(network, u.Host) }, 194 | TLSClientConfig: &tls.Config{RootCAs: certs}, 195 | } 196 | ws, _, err := d.Dial("wss://example.com/", nil) 197 | if err != nil { 198 | t.Fatalf("Dial() returned error %v", err) 199 | } 200 | defer ws.Close() 201 | sendRecv(t, ws) 202 | } 203 | 204 | func TestDialTLSBadCert(t *testing.T) { 205 | s := httptest.NewTLSServer(dialHandler{t}) 206 | defer s.Close() 207 | _, _, err := DefaultDialer.Dial(httpToWs(s.URL), nil) 208 | if err == nil { 209 | t.Fatalf("Dial() did not return error") 210 | } 211 | } 212 | 213 | func TestDialTLSNoVerify(t *testing.T) { 214 | s := httptest.NewTLSServer(dialHandler{t}) 215 | defer s.Close() 216 | d := &Dialer{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} 217 | ws, _, err := d.Dial(httpToWs(s.URL), nil) 218 | if err != nil { 219 | t.Fatalf("Dial() returned error %v", err) 220 | } 221 | defer ws.Close() 222 | sendRecv(t, ws) 223 | } 224 | 225 | func TestDialTimeout(t *testing.T) { 226 | s := httptest.NewServer(dialHandler{t}) 227 | defer s.Close() 228 | d := &Dialer{ 229 | HandshakeTimeout: -1, 230 | } 231 | _, _, err := d.Dial(httpToWs(s.URL), nil) 232 | if err == nil { 233 | t.Fatalf("Dial() did not return error") 234 | } 235 | } 236 | 237 | func TestDialBadScheme(t *testing.T) { 238 | s := httptest.NewServer(dialHandler{t}) 239 | defer s.Close() 240 | _, _, err := DefaultDialer.Dial(s.URL, nil) 241 | if err == nil { 242 | t.Fatalf("Dial() did not return error") 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/client_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "net/url" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | var parseURLTests = []struct { 14 | s string 15 | u *url.URL 16 | }{ 17 | {"ws://example.com/", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}}, 18 | {"ws://example.com", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}}, 19 | {"ws://example.com:7777/", &url.URL{Scheme: "ws", Host: "example.com:7777", Opaque: "/"}}, 20 | {"wss://example.com/", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/"}}, 21 | {"wss://example.com/a/b", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b"}}, 22 | {"ss://example.com/a/b", nil}, 23 | } 24 | 25 | func TestParseURL(t *testing.T) { 26 | for _, tt := range parseURLTests { 27 | u, err := parseURL(tt.s) 28 | if tt.u != nil && err != nil { 29 | t.Errorf("parseURL(%q) returned error %v", tt.s, err) 30 | continue 31 | } 32 | if tt.u == nil && err == nil { 33 | t.Errorf("parseURL(%q) did not return error", tt.s) 34 | continue 35 | } 36 | if !reflect.DeepEqual(u, tt.u) { 37 | t.Errorf("parseURL(%q) returned %v, want %v", tt.s, u, tt.u) 38 | continue 39 | } 40 | } 41 | } 42 | 43 | var hostPortNoPortTests = []struct { 44 | u *url.URL 45 | hostPort, hostNoPort string 46 | }{ 47 | {&url.URL{Scheme: "ws", Host: "example.com"}, "example.com:80", "example.com"}, 48 | {&url.URL{Scheme: "wss", Host: "example.com"}, "example.com:443", "example.com"}, 49 | {&url.URL{Scheme: "ws", Host: "example.com:7777"}, "example.com:7777", "example.com"}, 50 | {&url.URL{Scheme: "wss", Host: "example.com:7777"}, "example.com:7777", "example.com"}, 51 | } 52 | 53 | func TestHostPortNoPort(t *testing.T) { 54 | for _, tt := range hostPortNoPortTests { 55 | hostPort, hostNoPort := hostPortNoPort(tt.u) 56 | if hostPort != tt.hostPort { 57 | t.Errorf("hostPortNoPort(%v) returned hostPort %q, want %q", tt.u, hostPort, tt.hostPort) 58 | } 59 | if hostNoPort != tt.hostNoPort { 60 | t.Errorf("hostPortNoPort(%v) returned hostNoPort %q, want %q", tt.u, hostNoPort, tt.hostNoPort) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/conn_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "net" 13 | "testing" 14 | "testing/iotest" 15 | "time" 16 | ) 17 | 18 | var _ net.Error = errWriteTimeout 19 | 20 | type fakeNetConn struct { 21 | io.Reader 22 | io.Writer 23 | } 24 | 25 | func (c fakeNetConn) Close() error { return nil } 26 | func (c fakeNetConn) LocalAddr() net.Addr { return nil } 27 | func (c fakeNetConn) RemoteAddr() net.Addr { return nil } 28 | func (c fakeNetConn) SetDeadline(t time.Time) error { return nil } 29 | func (c fakeNetConn) SetReadDeadline(t time.Time) error { return nil } 30 | func (c fakeNetConn) SetWriteDeadline(t time.Time) error { return nil } 31 | 32 | func TestFraming(t *testing.T) { 33 | frameSizes := []int{0, 1, 2, 124, 125, 126, 127, 128, 129, 65534, 65535, 65536, 65537} 34 | var readChunkers = []struct { 35 | name string 36 | f func(io.Reader) io.Reader 37 | }{ 38 | {"half", iotest.HalfReader}, 39 | {"one", iotest.OneByteReader}, 40 | {"asis", func(r io.Reader) io.Reader { return r }}, 41 | } 42 | 43 | writeBuf := make([]byte, 65537) 44 | for i := range writeBuf { 45 | writeBuf[i] = byte(i) 46 | } 47 | 48 | for _, isServer := range []bool{true, false} { 49 | for _, chunker := range readChunkers { 50 | 51 | var connBuf bytes.Buffer 52 | wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024) 53 | rc := newConn(fakeNetConn{Reader: chunker.f(&connBuf), Writer: nil}, !isServer, 1024, 1024) 54 | 55 | for _, n := range frameSizes { 56 | for _, iocopy := range []bool{true, false} { 57 | name := fmt.Sprintf("s:%v, r:%s, n:%d c:%v", isServer, chunker.name, n, iocopy) 58 | 59 | w, err := wc.NextWriter(TextMessage) 60 | if err != nil { 61 | t.Errorf("%s: wc.NextWriter() returned %v", name, err) 62 | continue 63 | } 64 | var nn int 65 | if iocopy { 66 | var n64 int64 67 | n64, err = io.Copy(w, bytes.NewReader(writeBuf[:n])) 68 | nn = int(n64) 69 | } else { 70 | nn, err = w.Write(writeBuf[:n]) 71 | } 72 | if err != nil || nn != n { 73 | t.Errorf("%s: w.Write(writeBuf[:n]) returned %d, %v", name, nn, err) 74 | continue 75 | } 76 | err = w.Close() 77 | if err != nil { 78 | t.Errorf("%s: w.Close() returned %v", name, err) 79 | continue 80 | } 81 | 82 | opCode, r, err := rc.NextReader() 83 | if err != nil || opCode != TextMessage { 84 | t.Errorf("%s: NextReader() returned %d, r, %v", name, opCode, err) 85 | continue 86 | } 87 | rbuf, err := ioutil.ReadAll(r) 88 | if err != nil { 89 | t.Errorf("%s: ReadFull() returned rbuf, %v", name, err) 90 | continue 91 | } 92 | 93 | if len(rbuf) != n { 94 | t.Errorf("%s: len(rbuf) is %d, want %d", name, len(rbuf), n) 95 | continue 96 | } 97 | 98 | for i, b := range rbuf { 99 | if byte(i) != b { 100 | t.Errorf("%s: bad byte at offset %d", name, i) 101 | break 102 | } 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | func TestReadLimit(t *testing.T) { 111 | 112 | const readLimit = 512 113 | message := make([]byte, readLimit+1) 114 | 115 | var b1, b2 bytes.Buffer 116 | wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, readLimit-2) 117 | rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) 118 | rc.SetReadLimit(readLimit) 119 | 120 | // Send message at the limit with interleaved pong. 121 | w, _ := wc.NextWriter(BinaryMessage) 122 | w.Write(message[:readLimit-1]) 123 | wc.WriteControl(PongMessage, []byte("this is a pong"), time.Now().Add(10*time.Second)) 124 | w.Write(message[:1]) 125 | w.Close() 126 | 127 | // Send message larger than the limit. 128 | wc.WriteMessage(BinaryMessage, message[:readLimit+1]) 129 | 130 | op, _, err := rc.NextReader() 131 | if op != BinaryMessage || err != nil { 132 | t.Fatalf("1: NextReader() returned %d, %v", op, err) 133 | } 134 | op, r, err := rc.NextReader() 135 | if op != BinaryMessage || err != nil { 136 | t.Fatalf("2: NextReader() returned %d, %v", op, err) 137 | } 138 | _, err = io.Copy(ioutil.Discard, r) 139 | if err != ErrReadLimit { 140 | t.Fatalf("io.Copy() returned %v", err) 141 | } 142 | } 143 | 144 | func TestUnderlyingConn(t *testing.T) { 145 | var b1, b2 bytes.Buffer 146 | fc := fakeNetConn{Reader: &b1, Writer: &b2} 147 | c := newConn(fc, true, 1024, 1024) 148 | ul := c.UnderlyingConn() 149 | if ul != fc { 150 | t.Fatalf("Underlying conn is not what it should be.") 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package websocket implements the WebSocket protocol defined in RFC 6455. 6 | // 7 | // Overview 8 | // 9 | // The Conn type represents a WebSocket connection. A server application uses 10 | // the Upgrade function from an Upgrader object with a HTTP request handler 11 | // to get a pointer to a Conn: 12 | // 13 | // var upgrader = websocket.Upgrader{ 14 | // ReadBufferSize: 1024, 15 | // WriteBufferSize: 1024, 16 | // } 17 | // 18 | // func handler(w http.ResponseWriter, r *http.Request) { 19 | // conn, err := upgrader.Upgrade(w, r, nil) 20 | // if err != nil { 21 | // log.Println(err) 22 | // return 23 | // } 24 | // ... Use conn to send and receive messages. 25 | // } 26 | // 27 | // Call the connection WriteMessage and ReadMessages methods to send and 28 | // receive messages as a slice of bytes. This snippet of code shows how to echo 29 | // messages using these methods: 30 | // 31 | // for { 32 | // messageType, p, err := conn.ReadMessage() 33 | // if err != nil { 34 | // return 35 | // } 36 | // if err = conn.WriteMessage(messageType, p); err != nil { 37 | // return err 38 | // } 39 | // } 40 | // 41 | // In above snippet of code, p is a []byte and messageType is an int with value 42 | // websocket.BinaryMessage or websocket.TextMessage. 43 | // 44 | // An application can also send and receive messages using the io.WriteCloser 45 | // and io.Reader interfaces. To send a message, call the connection NextWriter 46 | // method to get an io.WriteCloser, write the message to the writer and close 47 | // the writer when done. To receive a message, call the connection NextReader 48 | // method to get an io.Reader and read until io.EOF is returned. This snippet 49 | // snippet shows how to echo messages using the NextWriter and NextReader 50 | // methods: 51 | // 52 | // for { 53 | // messageType, r, err := conn.NextReader() 54 | // if err != nil { 55 | // return 56 | // } 57 | // w, err := conn.NextWriter(messageType) 58 | // if err != nil { 59 | // return err 60 | // } 61 | // if _, err := io.Copy(w, r); err != nil { 62 | // return err 63 | // } 64 | // if err := w.Close(); err != nil { 65 | // return err 66 | // } 67 | // } 68 | // 69 | // Data Messages 70 | // 71 | // The WebSocket protocol distinguishes between text and binary data messages. 72 | // Text messages are interpreted as UTF-8 encoded text. The interpretation of 73 | // binary messages is left to the application. 74 | // 75 | // This package uses the TextMessage and BinaryMessage integer constants to 76 | // identify the two data message types. The ReadMessage and NextReader methods 77 | // return the type of the received message. The messageType argument to the 78 | // WriteMessage and NextWriter methods specifies the type of a sent message. 79 | // 80 | // It is the application's responsibility to ensure that text messages are 81 | // valid UTF-8 encoded text. 82 | // 83 | // Control Messages 84 | // 85 | // The WebSocket protocol defines three types of control messages: close, ping 86 | // and pong. Call the connection WriteControl, WriteMessage or NextWriter 87 | // methods to send a control message to the peer. 88 | // 89 | // Connections handle received ping and pong messages by invoking a callback 90 | // function set with SetPingHandler and SetPongHandler methods. These callback 91 | // functions can be invoked from the ReadMessage method, the NextReader method 92 | // or from a call to the data message reader returned from NextReader. 93 | // 94 | // Connections handle received close messages by returning an error from the 95 | // ReadMessage method, the NextReader method or from a call to the data message 96 | // reader returned from NextReader. 97 | // 98 | // Concurrency 99 | // 100 | // A Conn supports a single concurrent caller to the write methods (NextWriter, 101 | // SetWriteDeadline, WriteMessage) and a single concurrent caller to the read 102 | // methods (NextReader, SetReadDeadline, ReadMessage). The Close and 103 | // WriteControl methods can be called concurrently with all other methods. 104 | // 105 | package websocket 106 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/examples/autobahn/README.md: -------------------------------------------------------------------------------- 1 | # Test Server 2 | 3 | This package contains a server for the [Autobahn WebSockets Test Suite](http://autobahn.ws/testsuite). 4 | 5 | To test the server, run 6 | 7 | go run server.go 8 | 9 | and start the client test driver 10 | 11 | wstest -m fuzzingclient -s fuzzingclient.json 12 | 13 | When the client completes, it writes a report to reports/servers/index.html. 14 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/examples/autobahn/fuzzingclient.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "options": {"failByDrop": false}, 4 | "outdir": "./reports/clients", 5 | "servers": [ 6 | {"agent": "ReadAllWriteMessage", "url": "ws://localhost:9000/m", "options": {"version": 18}}, 7 | {"agent": "ReadAllWrite", "url": "ws://localhost:9000/r", "options": {"version": 18}}, 8 | {"agent": "CopyFull", "url": "ws://localhost:9000/f", "options": {"version": 18}}, 9 | {"agent": "CopyWriterOnly", "url": "ws://localhost:9000/c", "options": {"version": 18}} 10 | ], 11 | "cases": ["*"], 12 | "exclude-cases": [], 13 | "exclude-agent-cases": {} 14 | } 15 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/examples/chat/README.md: -------------------------------------------------------------------------------- 1 | # Chat Example 2 | 3 | This application shows how to use use the 4 | [websocket](https://github.com/gorilla/websocket) package and 5 | [jQuery](http://jquery.com) to implement a simple web chat application. 6 | 7 | ## Running the example 8 | 9 | The example requires a working Go development environment. The [Getting 10 | Started](http://golang.org/doc/install) page describes how to install the 11 | development environment. 12 | 13 | Once you have Go up and running, you can download, build and run the example 14 | using the following commands. 15 | 16 | $ go get github.com/gorilla/websocket 17 | $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/chat` 18 | $ go run *.go 19 | 20 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/examples/chat/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "github.com/gorilla/websocket" 9 | "log" 10 | "net/http" 11 | "time" 12 | ) 13 | 14 | const ( 15 | // Time allowed to write a message to the peer. 16 | writeWait = 10 * time.Second 17 | 18 | // Time allowed to read the next pong message from the peer. 19 | pongWait = 60 * time.Second 20 | 21 | // Send pings to peer with this period. Must be less than pongWait. 22 | pingPeriod = (pongWait * 9) / 10 23 | 24 | // Maximum message size allowed from peer. 25 | maxMessageSize = 512 26 | ) 27 | 28 | var upgrader = websocket.Upgrader{ 29 | ReadBufferSize: 1024, 30 | WriteBufferSize: 1024, 31 | } 32 | 33 | // connection is an middleman between the websocket connection and the hub. 34 | type connection struct { 35 | // The websocket connection. 36 | ws *websocket.Conn 37 | 38 | // Buffered channel of outbound messages. 39 | send chan []byte 40 | } 41 | 42 | // readPump pumps messages from the websocket connection to the hub. 43 | func (c *connection) readPump() { 44 | defer func() { 45 | h.unregister <- c 46 | c.ws.Close() 47 | }() 48 | c.ws.SetReadLimit(maxMessageSize) 49 | c.ws.SetReadDeadline(time.Now().Add(pongWait)) 50 | c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) 51 | for { 52 | _, message, err := c.ws.ReadMessage() 53 | if err != nil { 54 | break 55 | } 56 | h.broadcast <- message 57 | } 58 | } 59 | 60 | // write writes a message with the given message type and payload. 61 | func (c *connection) write(mt int, payload []byte) error { 62 | c.ws.SetWriteDeadline(time.Now().Add(writeWait)) 63 | return c.ws.WriteMessage(mt, payload) 64 | } 65 | 66 | // writePump pumps messages from the hub to the websocket connection. 67 | func (c *connection) writePump() { 68 | ticker := time.NewTicker(pingPeriod) 69 | defer func() { 70 | ticker.Stop() 71 | c.ws.Close() 72 | }() 73 | for { 74 | select { 75 | case message, ok := <-c.send: 76 | if !ok { 77 | c.write(websocket.CloseMessage, []byte{}) 78 | return 79 | } 80 | if err := c.write(websocket.TextMessage, message); err != nil { 81 | return 82 | } 83 | case <-ticker.C: 84 | if err := c.write(websocket.PingMessage, []byte{}); err != nil { 85 | return 86 | } 87 | } 88 | } 89 | } 90 | 91 | // serverWs handles webocket requests from the peer. 92 | func serveWs(w http.ResponseWriter, r *http.Request) { 93 | if r.Method != "GET" { 94 | http.Error(w, "Method not allowed", 405) 95 | return 96 | } 97 | ws, err := upgrader.Upgrade(w, r, nil) 98 | if err != nil { 99 | if _, ok := err.(websocket.HandshakeError); !ok { 100 | log.Println(err) 101 | } 102 | return 103 | } 104 | c := &connection{send: make(chan []byte, 256), ws: ws} 105 | h.register <- c 106 | go c.writePump() 107 | c.readPump() 108 | } 109 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/examples/chat/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chat Example 5 | 6 | 47 | 84 | 85 | 86 |
87 |
88 | 89 | 90 |
91 | 92 | 93 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/examples/chat/hub.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | // hub maintains the set of active connections and broadcasts messages to the 8 | // connections. 9 | type hub struct { 10 | // Registered connections. 11 | connections map[*connection]bool 12 | 13 | // Inbound messages from the connections. 14 | broadcast chan []byte 15 | 16 | // Register requests from the connections. 17 | register chan *connection 18 | 19 | // Unregister requests from connections. 20 | unregister chan *connection 21 | } 22 | 23 | var h = hub{ 24 | broadcast: make(chan []byte), 25 | register: make(chan *connection), 26 | unregister: make(chan *connection), 27 | connections: make(map[*connection]bool), 28 | } 29 | 30 | func (h *hub) run() { 31 | for { 32 | select { 33 | case c := <-h.register: 34 | h.connections[c] = true 35 | case c := <-h.unregister: 36 | delete(h.connections, c) 37 | close(c.send) 38 | case m := <-h.broadcast: 39 | for c := range h.connections { 40 | select { 41 | case c.send <- m: 42 | default: 43 | close(c.send) 44 | delete(h.connections, c) 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/examples/chat/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "log" 10 | "net/http" 11 | "text/template" 12 | ) 13 | 14 | var addr = flag.String("addr", ":8080", "http service address") 15 | var homeTempl = template.Must(template.ParseFiles("home.html")) 16 | 17 | func serveHome(w http.ResponseWriter, r *http.Request) { 18 | if r.URL.Path != "/" { 19 | http.Error(w, "Not found", 404) 20 | return 21 | } 22 | if r.Method != "GET" { 23 | http.Error(w, "Method nod allowed", 405) 24 | return 25 | } 26 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 27 | homeTempl.Execute(w, r.Host) 28 | } 29 | 30 | func main() { 31 | flag.Parse() 32 | go h.run() 33 | http.HandleFunc("/", serveHome) 34 | http.HandleFunc("/ws", serveWs) 35 | err := http.ListenAndServe(*addr, nil) 36 | if err != nil { 37 | log.Fatal("ListenAndServe: ", err) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/examples/filewatch/README.md: -------------------------------------------------------------------------------- 1 | # File Watch example. 2 | 3 | This example sends a file to the browser client for display whenever the file is modified. 4 | 5 | $ go get github.com/gorilla/websocket 6 | $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/filewatch` 7 | $ go run main.go 8 | # Open http://localhost:8080/ . 9 | # Modify the file to see it update in the browser. 10 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/examples/filewatch/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "io/ioutil" 10 | "log" 11 | "net/http" 12 | "os" 13 | "strconv" 14 | "text/template" 15 | "time" 16 | 17 | "github.com/gorilla/websocket" 18 | ) 19 | 20 | const ( 21 | // Time allowed to write the file to the client. 22 | writeWait = 10 * time.Second 23 | 24 | // Time allowed to read the next pong message from the client. 25 | pongWait = 60 * time.Second 26 | 27 | // Send pings to client with this period. Must be less than pongWait. 28 | pingPeriod = (pongWait * 9) / 10 29 | 30 | // Poll file for changes with this period. 31 | filePeriod = 10 * time.Second 32 | ) 33 | 34 | var ( 35 | addr = flag.String("addr", ":8080", "http service address") 36 | homeTempl = template.Must(template.New("").Parse(homeHTML)) 37 | filename string 38 | upgrader = websocket.Upgrader{ 39 | ReadBufferSize: 1024, 40 | WriteBufferSize: 1024, 41 | } 42 | ) 43 | 44 | func readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) { 45 | fi, err := os.Stat(filename) 46 | if err != nil { 47 | return nil, lastMod, err 48 | } 49 | if !fi.ModTime().After(lastMod) { 50 | return nil, lastMod, nil 51 | } 52 | p, err := ioutil.ReadFile(filename) 53 | if err != nil { 54 | return nil, fi.ModTime(), err 55 | } 56 | return p, fi.ModTime(), nil 57 | } 58 | 59 | func reader(ws *websocket.Conn) { 60 | defer ws.Close() 61 | ws.SetReadLimit(512) 62 | ws.SetReadDeadline(time.Now().Add(pongWait)) 63 | ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) 64 | for { 65 | _, _, err := ws.ReadMessage() 66 | if err != nil { 67 | break 68 | } 69 | } 70 | } 71 | 72 | func writer(ws *websocket.Conn, lastMod time.Time) { 73 | lastError := "" 74 | pingTicker := time.NewTicker(pingPeriod) 75 | fileTicker := time.NewTicker(filePeriod) 76 | defer func() { 77 | pingTicker.Stop() 78 | fileTicker.Stop() 79 | ws.Close() 80 | }() 81 | for { 82 | select { 83 | case <-fileTicker.C: 84 | var p []byte 85 | var err error 86 | 87 | p, lastMod, err = readFileIfModified(lastMod) 88 | 89 | if err != nil { 90 | if s := err.Error(); s != lastError { 91 | lastError = s 92 | p = []byte(lastError) 93 | } 94 | } else { 95 | lastError = "" 96 | } 97 | 98 | if p != nil { 99 | ws.SetWriteDeadline(time.Now().Add(writeWait)) 100 | if err := ws.WriteMessage(websocket.TextMessage, p); err != nil { 101 | return 102 | } 103 | } 104 | case <-pingTicker.C: 105 | ws.SetWriteDeadline(time.Now().Add(writeWait)) 106 | if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { 107 | return 108 | } 109 | } 110 | } 111 | } 112 | 113 | func serveWs(w http.ResponseWriter, r *http.Request) { 114 | ws, err := upgrader.Upgrade(w, r, nil) 115 | if err != nil { 116 | if _, ok := err.(websocket.HandshakeError); !ok { 117 | log.Println(err) 118 | } 119 | return 120 | } 121 | 122 | var lastMod time.Time 123 | if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err != nil { 124 | lastMod = time.Unix(0, n) 125 | } 126 | 127 | go writer(ws, lastMod) 128 | reader(ws) 129 | } 130 | 131 | func serveHome(w http.ResponseWriter, r *http.Request) { 132 | if r.URL.Path != "/" { 133 | http.Error(w, "Not found", 404) 134 | return 135 | } 136 | if r.Method != "GET" { 137 | http.Error(w, "Method not allowed", 405) 138 | return 139 | } 140 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 141 | p, lastMod, err := readFileIfModified(time.Time{}) 142 | if err != nil { 143 | p = []byte(err.Error()) 144 | lastMod = time.Unix(0, 0) 145 | } 146 | var v = struct { 147 | Host string 148 | Data string 149 | LastMod string 150 | }{ 151 | r.Host, 152 | string(p), 153 | strconv.FormatInt(lastMod.UnixNano(), 16), 154 | } 155 | homeTempl.Execute(w, &v) 156 | } 157 | 158 | func main() { 159 | flag.Parse() 160 | if flag.NArg() != 1 { 161 | log.Fatal("filename not specified") 162 | } 163 | filename = flag.Args()[0] 164 | http.HandleFunc("/", serveHome) 165 | http.HandleFunc("/ws", serveWs) 166 | if err := http.ListenAndServe(*addr, nil); err != nil { 167 | log.Fatal(err) 168 | } 169 | } 170 | 171 | const homeHTML = ` 172 | 173 | 174 | WebSocket Example 175 | 176 | 177 |
{{.Data}}
178 | 191 | 192 | 193 | ` 194 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "encoding/json" 9 | ) 10 | 11 | // WriteJSON is deprecated, use c.WriteJSON instead. 12 | func WriteJSON(c *Conn, v interface{}) error { 13 | return c.WriteJSON(v) 14 | } 15 | 16 | // WriteJSON writes the JSON encoding of v to the connection. 17 | // 18 | // See the documentation for encoding/json Marshal for details about the 19 | // conversion of Go values to JSON. 20 | func (c *Conn) WriteJSON(v interface{}) error { 21 | w, err := c.NextWriter(TextMessage) 22 | if err != nil { 23 | return err 24 | } 25 | err1 := json.NewEncoder(w).Encode(v) 26 | err2 := w.Close() 27 | if err1 != nil { 28 | return err1 29 | } 30 | return err2 31 | } 32 | 33 | // ReadJSON is deprecated, use c.ReadJSON instead. 34 | func ReadJSON(c *Conn, v interface{}) error { 35 | return c.ReadJSON(v) 36 | } 37 | 38 | // ReadJSON reads the next JSON-encoded message from the connection and stores 39 | // it in the value pointed to by v. 40 | // 41 | // See the documentation for the encoding/json Marshal function for details 42 | // about the conversion of JSON to a Go value. 43 | func (c *Conn) ReadJSON(v interface{}) error { 44 | _, r, err := c.NextReader() 45 | if err != nil { 46 | return err 47 | } 48 | return json.NewDecoder(r).Decode(v) 49 | } 50 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/json_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bytes" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func TestJSON(t *testing.T) { 14 | var buf bytes.Buffer 15 | c := fakeNetConn{&buf, &buf} 16 | wc := newConn(c, true, 1024, 1024) 17 | rc := newConn(c, false, 1024, 1024) 18 | 19 | var actual, expect struct { 20 | A int 21 | B string 22 | } 23 | expect.A = 1 24 | expect.B = "hello" 25 | 26 | if err := wc.WriteJSON(&expect); err != nil { 27 | t.Fatal("write", err) 28 | } 29 | 30 | if err := rc.ReadJSON(&actual); err != nil { 31 | t.Fatal("read", err) 32 | } 33 | 34 | if !reflect.DeepEqual(&actual, &expect) { 35 | t.Fatal("equal", actual, expect) 36 | } 37 | } 38 | 39 | func TestDeprecatedJSON(t *testing.T) { 40 | var buf bytes.Buffer 41 | c := fakeNetConn{&buf, &buf} 42 | wc := newConn(c, true, 1024, 1024) 43 | rc := newConn(c, false, 1024, 1024) 44 | 45 | var actual, expect struct { 46 | A int 47 | B string 48 | } 49 | expect.A = 1 50 | expect.B = "hello" 51 | 52 | if err := WriteJSON(wc, &expect); err != nil { 53 | t.Fatal("write", err) 54 | } 55 | 56 | if err := ReadJSON(rc, &actual); err != nil { 57 | t.Fatal("read", err) 58 | } 59 | 60 | if !reflect.DeepEqual(&actual, &expect) { 61 | t.Fatal("equal", actual, expect) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "net/http" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | var subprotocolTests = []struct { 14 | h string 15 | protocols []string 16 | }{ 17 | {"", nil}, 18 | {"foo", []string{"foo"}}, 19 | {"foo,bar", []string{"foo", "bar"}}, 20 | {"foo, bar", []string{"foo", "bar"}}, 21 | {" foo, bar", []string{"foo", "bar"}}, 22 | {" foo, bar ", []string{"foo", "bar"}}, 23 | } 24 | 25 | func TestSubprotocols(t *testing.T) { 26 | for _, st := range subprotocolTests { 27 | r := http.Request{Header: http.Header{"Sec-Websocket-Protocol": {st.h}}} 28 | protocols := Subprotocols(&r) 29 | if !reflect.DeepEqual(st.protocols, protocols) { 30 | t.Errorf("SubProtocols(%q) returned %#v, want %#v", st.h, protocols, st.protocols) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vendor/src/github.com/gorilla/websocket/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "crypto/rand" 9 | "crypto/sha1" 10 | "encoding/base64" 11 | "io" 12 | "net/http" 13 | "strings" 14 | ) 15 | 16 | // tokenListContainsValue returns true if the 1#token header with the given 17 | // name contains token. 18 | func tokenListContainsValue(header http.Header, name string, value string) bool { 19 | for _, v := range header[name] { 20 | for _, s := range strings.Split(v, ",") { 21 | if strings.EqualFold(value, strings.TrimSpace(s)) { 22 | return true 23 | } 24 | } 25 | } 26 | return false 27 | } 28 | 29 | var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") 30 | 31 | func computeAcceptKey(challengeKey string) string { 32 | h := sha1.New() 33 | h.Write([]byte(challengeKey)) 34 | h.Write(keyGUID) 35 | return base64.StdEncoding.EncodeToString(h.Sum(nil)) 36 | } 37 | 38 | func generateChallengeKey() (string, error) { 39 | p := make([]byte, 16) 40 | if _, err := io.ReadFull(rand.Reader, p); err != nil { 41 | return "", err 42 | } 43 | return base64.StdEncoding.EncodeToString(p), nil 44 | } 45 | -------------------------------------------------------------------------------- /vendor/src/github.com/influxdata/influxdb/client/v2/example_test.go: -------------------------------------------------------------------------------- 1 | package client_test 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "time" 8 | 9 | "github.com/influxdb/influxdb/client/v2" 10 | ) 11 | 12 | // Create a new client 13 | func ExampleClient() { 14 | // NOTE: this assumes you've setup a user and have setup shell env variables, 15 | // namely INFLUX_USER/INFLUX_PWD. If not just omit Username/Password below. 16 | _, err := client.NewHTTPClient(client.HTTPConfig{ 17 | Addr: "http://localhost:8086", 18 | Username: os.Getenv("INFLUX_USER"), 19 | Password: os.Getenv("INFLUX_PWD"), 20 | }) 21 | if err != nil { 22 | fmt.Println("Error creating InfluxDB Client: ", err.Error()) 23 | } 24 | } 25 | 26 | // Write a point using the UDP client 27 | func ExampleClient_uDP() { 28 | // Make client 29 | config := client.UDPConfig{Addr: "localhost:8089"} 30 | c, err := client.NewUDPClient(config) 31 | if err != nil { 32 | fmt.Println("Error: ", err.Error()) 33 | } 34 | defer c.Close() 35 | 36 | // Create a new point batch 37 | bp, _ := client.NewBatchPoints(client.BatchPointsConfig{ 38 | Precision: "s", 39 | }) 40 | 41 | // Create a point and add to batch 42 | tags := map[string]string{"cpu": "cpu-total"} 43 | fields := map[string]interface{}{ 44 | "idle": 10.1, 45 | "system": 53.3, 46 | "user": 46.6, 47 | } 48 | pt, err := client.NewPoint("cpu_usage", tags, fields, time.Now()) 49 | if err != nil { 50 | fmt.Println("Error: ", err.Error()) 51 | } 52 | bp.AddPoint(pt) 53 | 54 | // Write the batch 55 | c.Write(bp) 56 | } 57 | 58 | // Write a point using the HTTP client 59 | func ExampleClient_write() { 60 | // Make client 61 | c, err := client.NewHTTPClient(client.HTTPConfig{ 62 | Addr: "http://localhost:8086", 63 | }) 64 | if err != nil { 65 | fmt.Println("Error creating InfluxDB Client: ", err.Error()) 66 | } 67 | defer c.Close() 68 | 69 | // Create a new point batch 70 | bp, _ := client.NewBatchPoints(client.BatchPointsConfig{ 71 | Database: "BumbleBeeTuna", 72 | Precision: "s", 73 | }) 74 | 75 | // Create a point and add to batch 76 | tags := map[string]string{"cpu": "cpu-total"} 77 | fields := map[string]interface{}{ 78 | "idle": 10.1, 79 | "system": 53.3, 80 | "user": 46.6, 81 | } 82 | pt, err := client.NewPoint("cpu_usage", tags, fields, time.Now()) 83 | if err != nil { 84 | fmt.Println("Error: ", err.Error()) 85 | } 86 | bp.AddPoint(pt) 87 | 88 | // Write the batch 89 | c.Write(bp) 90 | } 91 | 92 | // Create a batch and add a point 93 | func ExampleBatchPoints() { 94 | // Create a new point batch 95 | bp, _ := client.NewBatchPoints(client.BatchPointsConfig{ 96 | Database: "BumbleBeeTuna", 97 | Precision: "s", 98 | }) 99 | 100 | // Create a point and add to batch 101 | tags := map[string]string{"cpu": "cpu-total"} 102 | fields := map[string]interface{}{ 103 | "idle": 10.1, 104 | "system": 53.3, 105 | "user": 46.6, 106 | } 107 | pt, err := client.NewPoint("cpu_usage", tags, fields, time.Now()) 108 | if err != nil { 109 | fmt.Println("Error: ", err.Error()) 110 | } 111 | bp.AddPoint(pt) 112 | } 113 | 114 | // Using the BatchPoints setter functions 115 | func ExampleBatchPoints_setters() { 116 | // Create a new point batch 117 | bp, _ := client.NewBatchPoints(client.BatchPointsConfig{}) 118 | bp.SetDatabase("BumbleBeeTuna") 119 | bp.SetPrecision("ms") 120 | 121 | // Create a point and add to batch 122 | tags := map[string]string{"cpu": "cpu-total"} 123 | fields := map[string]interface{}{ 124 | "idle": 10.1, 125 | "system": 53.3, 126 | "user": 46.6, 127 | } 128 | pt, err := client.NewPoint("cpu_usage", tags, fields, time.Now()) 129 | if err != nil { 130 | fmt.Println("Error: ", err.Error()) 131 | } 132 | bp.AddPoint(pt) 133 | } 134 | 135 | // Create a new point with a timestamp 136 | func ExamplePoint() { 137 | tags := map[string]string{"cpu": "cpu-total"} 138 | fields := map[string]interface{}{ 139 | "idle": 10.1, 140 | "system": 53.3, 141 | "user": 46.6, 142 | } 143 | pt, err := client.NewPoint("cpu_usage", tags, fields, time.Now()) 144 | if err == nil { 145 | fmt.Println("We created a point: ", pt.String()) 146 | } 147 | } 148 | 149 | // Create a new point without a timestamp 150 | func ExamplePoint_withoutTime() { 151 | tags := map[string]string{"cpu": "cpu-total"} 152 | fields := map[string]interface{}{ 153 | "idle": 10.1, 154 | "system": 53.3, 155 | "user": 46.6, 156 | } 157 | pt, err := client.NewPoint("cpu_usage", tags, fields) 158 | if err == nil { 159 | fmt.Println("We created a point w/o time: ", pt.String()) 160 | } 161 | } 162 | 163 | // Write 1000 points 164 | func ExampleClient_write1000() { 165 | sampleSize := 1000 166 | 167 | // Make client 168 | c, err := client.NewHTTPClient(client.HTTPConfig{ 169 | Addr: "http://localhost:8086", 170 | }) 171 | if err != nil { 172 | fmt.Println("Error creating InfluxDB Client: ", err.Error()) 173 | } 174 | defer c.Close() 175 | 176 | rand.Seed(42) 177 | 178 | bp, _ := client.NewBatchPoints(client.BatchPointsConfig{ 179 | Database: "systemstats", 180 | Precision: "us", 181 | }) 182 | 183 | for i := 0; i < sampleSize; i++ { 184 | regions := []string{"us-west1", "us-west2", "us-west3", "us-east1"} 185 | tags := map[string]string{ 186 | "cpu": "cpu-total", 187 | "host": fmt.Sprintf("host%d", rand.Intn(1000)), 188 | "region": regions[rand.Intn(len(regions))], 189 | } 190 | 191 | idle := rand.Float64() * 100.0 192 | fields := map[string]interface{}{ 193 | "idle": idle, 194 | "busy": 100.0 - idle, 195 | } 196 | 197 | pt, err := client.NewPoint( 198 | "cpu_usage", 199 | tags, 200 | fields, 201 | time.Now(), 202 | ) 203 | if err != nil { 204 | println("Error:", err.Error()) 205 | continue 206 | } 207 | bp.AddPoint(pt) 208 | } 209 | 210 | err = c.Write(bp) 211 | if err != nil { 212 | fmt.Println("Error: ", err.Error()) 213 | } 214 | } 215 | 216 | // Make a Query 217 | func ExampleClient_query() { 218 | // Make client 219 | c, err := client.NewHTTPClient(client.HTTPConfig{ 220 | Addr: "http://localhost:8086", 221 | }) 222 | if err != nil { 223 | fmt.Println("Error creating InfluxDB Client: ", err.Error()) 224 | } 225 | defer c.Close() 226 | 227 | q := client.NewQuery("SELECT count(value) FROM shapes", "square_holes", "ns") 228 | if response, err := c.Query(q); err == nil && response.Error() == nil { 229 | fmt.Println(response.Results) 230 | } 231 | } 232 | 233 | // Create a Database with a query 234 | func ExampleClient_createDatabase() { 235 | // Make client 236 | c, err := client.NewHTTPClient(client.HTTPConfig{ 237 | Addr: "http://localhost:8086", 238 | }) 239 | if err != nil { 240 | fmt.Println("Error creating InfluxDB Client: ", err.Error()) 241 | } 242 | defer c.Close() 243 | 244 | q := client.NewQuery("CREATE DATABASE telegraf", "", "") 245 | if response, err := c.Query(q); err == nil && response.Error() == nil { 246 | fmt.Println(response.Results) 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /vendor/src/github.com/influxdb/influxdb/models/rows.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "hash/fnv" 5 | "sort" 6 | ) 7 | 8 | // Row represents a single row returned from the execution of a statement. 9 | type Row struct { 10 | Name string `json:"name,omitempty"` 11 | Tags map[string]string `json:"tags,omitempty"` 12 | Columns []string `json:"columns,omitempty"` 13 | Values [][]interface{} `json:"values,omitempty"` 14 | Err error `json:"err,omitempty"` 15 | } 16 | 17 | // SameSeries returns true if r contains values for the same series as o. 18 | func (r *Row) SameSeries(o *Row) bool { 19 | return r.tagsHash() == o.tagsHash() && r.Name == o.Name 20 | } 21 | 22 | // tagsHash returns a hash of tag key/value pairs. 23 | func (r *Row) tagsHash() uint64 { 24 | h := fnv.New64a() 25 | keys := r.tagsKeys() 26 | for _, k := range keys { 27 | h.Write([]byte(k)) 28 | h.Write([]byte(r.Tags[k])) 29 | } 30 | return h.Sum64() 31 | } 32 | 33 | // tagKeys returns a sorted list of tag keys. 34 | func (r *Row) tagsKeys() []string { 35 | a := make([]string, 0, len(r.Tags)) 36 | for k := range r.Tags { 37 | a = append(a, k) 38 | } 39 | sort.Strings(a) 40 | return a 41 | } 42 | 43 | // Rows represents a collection of rows. Rows implements sort.Interface. 44 | type Rows []*Row 45 | 46 | func (p Rows) Len() int { return len(p) } 47 | 48 | func (p Rows) Less(i, j int) bool { 49 | // Sort by name first. 50 | if p[i].Name != p[j].Name { 51 | return p[i].Name < p[j].Name 52 | } 53 | 54 | // Sort by tag set hash. Tags don't have a meaningful sort order so we 55 | // just compute a hash and sort by that instead. This allows the tests 56 | // to receive rows in a predictable order every time. 57 | return p[i].tagsHash() < p[j].tagsHash() 58 | } 59 | 60 | func (p Rows) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 61 | -------------------------------------------------------------------------------- /vendor/src/github.com/influxdb/influxdb/models/time.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Helper time methods since parsing time can easily overflow and we only support a 4 | // specific time range. 5 | 6 | import ( 7 | "fmt" 8 | "math" 9 | "time" 10 | ) 11 | 12 | var ( 13 | // Maximum time that can be represented via int64 nanoseconds since the epoch. 14 | MaxNanoTime = time.Unix(0, math.MaxInt64).UTC() 15 | // Minumum time that can be represented via int64 nanoseconds since the epoch. 16 | MinNanoTime = time.Unix(0, math.MinInt64).UTC() 17 | 18 | // The time is out of the representable range using int64 nanoseconds since the epoch. 19 | ErrTimeOutOfRange = fmt.Errorf("time outside range %s - %s", MinNanoTime, MaxNanoTime) 20 | ) 21 | 22 | // Safely calculate the time given. Will return error if the time is outside the 23 | // supported range. 24 | func SafeCalcTime(timestamp int64, precision string) (time.Time, error) { 25 | mult := GetPrecisionMultiplier(precision) 26 | if t, ok := safeSignedMult(timestamp, mult); ok { 27 | return time.Unix(0, t).UTC(), nil 28 | } else { 29 | return time.Time{}, ErrTimeOutOfRange 30 | } 31 | 32 | } 33 | 34 | // Check that a time is within the safe range. 35 | func CheckTime(t time.Time) error { 36 | if t.Before(MinNanoTime) || t.After(MaxNanoTime) { 37 | return ErrTimeOutOfRange 38 | } 39 | return nil 40 | } 41 | 42 | // Perform the multiplication and check to make sure it didn't overflow. 43 | func safeSignedMult(a, b int64) (int64, bool) { 44 | if a == 0 || b == 0 || a == 1 || b == 1 { 45 | return a * b, true 46 | } 47 | if a == math.MinInt64 || b == math.MaxInt64 { 48 | return 0, false 49 | } 50 | c := a * b 51 | return c, c/b == a 52 | } 53 | -------------------------------------------------------------------------------- /vendor/src/github.com/influxdb/influxdb/pkg/escape/bytes.go: -------------------------------------------------------------------------------- 1 | package escape 2 | 3 | import "bytes" 4 | 5 | func Bytes(in []byte) []byte { 6 | for b, esc := range Codes { 7 | in = bytes.Replace(in, []byte{b}, esc, -1) 8 | } 9 | return in 10 | } 11 | 12 | func Unescape(in []byte) []byte { 13 | i := 0 14 | inLen := len(in) 15 | var out []byte 16 | 17 | for { 18 | if i >= inLen { 19 | break 20 | } 21 | if in[i] == '\\' && i+1 < inLen { 22 | switch in[i+1] { 23 | case ',': 24 | out = append(out, ',') 25 | i += 2 26 | continue 27 | case '"': 28 | out = append(out, '"') 29 | i += 2 30 | continue 31 | case ' ': 32 | out = append(out, ' ') 33 | i += 2 34 | continue 35 | case '=': 36 | out = append(out, '=') 37 | i += 2 38 | continue 39 | } 40 | } 41 | out = append(out, in[i]) 42 | i += 1 43 | } 44 | return out 45 | } 46 | -------------------------------------------------------------------------------- /vendor/src/github.com/influxdb/influxdb/pkg/escape/bytes_test.go: -------------------------------------------------------------------------------- 1 | package escape 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestUnescape(t *testing.T) { 9 | tests := []struct { 10 | in []byte 11 | out []byte 12 | }{ 13 | { 14 | []byte(nil), 15 | []byte(nil), 16 | }, 17 | 18 | { 19 | []byte(""), 20 | []byte(nil), 21 | }, 22 | 23 | { 24 | []byte("\\,\\\"\\ \\="), 25 | []byte(",\" ="), 26 | }, 27 | 28 | { 29 | []byte("\\\\"), 30 | []byte("\\\\"), 31 | }, 32 | 33 | { 34 | []byte("plain and simple"), 35 | []byte("plain and simple"), 36 | }, 37 | } 38 | 39 | for ii, tt := range tests { 40 | got := Unescape(tt.in) 41 | if !reflect.DeepEqual(got, tt.out) { 42 | t.Errorf("[%d] Unescape(%#v) = %#v, expected %#v", ii, string(tt.in), string(got), string(tt.out)) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /vendor/src/github.com/influxdb/influxdb/pkg/escape/strings.go: -------------------------------------------------------------------------------- 1 | package escape 2 | 3 | import "strings" 4 | 5 | var ( 6 | Codes = map[byte][]byte{ 7 | ',': []byte(`\,`), 8 | '"': []byte(`\"`), 9 | ' ': []byte(`\ `), 10 | '=': []byte(`\=`), 11 | } 12 | 13 | codesStr = map[string]string{} 14 | ) 15 | 16 | func init() { 17 | for k, v := range Codes { 18 | codesStr[string(k)] = string(v) 19 | } 20 | } 21 | 22 | func UnescapeString(in string) string { 23 | for b, esc := range codesStr { 24 | in = strings.Replace(in, esc, b, -1) 25 | } 26 | return in 27 | } 28 | 29 | func String(in string) string { 30 | for b, esc := range codesStr { 31 | in = strings.Replace(in, b, esc, -1) 32 | } 33 | return in 34 | } 35 | -------------------------------------------------------------------------------- /vendor/src/github.com/tarm/goserial/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/src/github.com/tarm/goserial/README.md: -------------------------------------------------------------------------------- 1 | THIS PACKAGE IS DEPRECATED BUT RETAINED FOR COMPATIBILITY. 2 | 3 | Please use [github.com/tarm/serial](https://github.com/tarm/serial) for future projects. 4 | 5 | It is 99.9% compatible (it returns a concrete *Port instead of an 6 | io.ReadWriteCloser interface) while allowing room for future 7 | expansion. Please update your projects when you have the chance. 8 | -------------------------------------------------------------------------------- /vendor/src/github.com/tarm/goserial/basic_test.go: -------------------------------------------------------------------------------- 1 | package serial 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestConnection(t *testing.T) { 9 | c0 := &Config{Name: "/dev/ttyUSB0", Baud: 115200} 10 | c1 := &Config{Name: "/dev/ttyUSB1", Baud: 115200} 11 | 12 | s1, err := OpenPort(c0) 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | 17 | s2, err := OpenPort(c1) 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | 22 | ch := make(chan int, 1) 23 | go func() { 24 | buf := make([]byte, 128) 25 | var readCount int 26 | for { 27 | n, err := s2.Read(buf) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | readCount++ 32 | t.Logf("Read %v %v bytes: % 02x %s", readCount, n, buf[:n], buf[:n]) 33 | select { 34 | case <-ch: 35 | ch <- readCount 36 | close(ch) 37 | default: 38 | } 39 | } 40 | }() 41 | 42 | if _, err = s1.Write([]byte("hello")); err != nil { 43 | t.Fatal(err) 44 | } 45 | if _, err = s1.Write([]byte(" ")); err != nil { 46 | t.Fatal(err) 47 | } 48 | time.Sleep(time.Second) 49 | if _, err = s1.Write([]byte("world")); err != nil { 50 | t.Fatal(err) 51 | } 52 | time.Sleep(time.Second / 10) 53 | 54 | ch <- 0 55 | s1.Write([]byte(" ")) // We could be blocked in the read without this 56 | c := <-ch 57 | exp := 5 58 | if c >= exp { 59 | t.Fatalf("Expected less than %v read, got %v", exp, c) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /vendor/src/github.com/tarm/goserial/serial.go: -------------------------------------------------------------------------------- 1 | /* 2 | Goserial is a simple go package to allow you to read and write from 3 | the serial port as a stream of bytes. 4 | 5 | It aims to have the same API on all platforms, including windows. As 6 | an added bonus, the windows package does not use cgo, so you can cross 7 | compile for windows from another platform. Unfortunately goinstall 8 | does not currently let you cross compile so you will have to do it 9 | manually: 10 | 11 | GOOS=windows make clean install 12 | 13 | Currently there is very little in the way of configurability. You can 14 | set the baud rate. Then you can Read(), Write(), or Close() the 15 | connection. Read() will block until at least one byte is returned. 16 | Write is the same. There is currently no exposed way to set the 17 | timeouts, though patches are welcome. 18 | 19 | Currently all ports are opened with 8 data bits, 1 stop bit, no 20 | parity, no hardware flow control, and no software flow control. This 21 | works fine for many real devices and many faux serial devices 22 | including usb-to-serial converters and bluetooth serial ports. 23 | 24 | You may Read() and Write() simulantiously on the same connection (from 25 | different goroutines). 26 | 27 | Example usage: 28 | 29 | package main 30 | 31 | import ( 32 | "github.com/tarm/goserial" 33 | "log" 34 | ) 35 | 36 | func main() { 37 | c := &serial.Config{Name: "COM5", Baud: 115200} 38 | s, err := serial.OpenPort(c) 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | n, err := s.Write([]byte("test")) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | buf := make([]byte, 128) 49 | n, err = s.Read(buf) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | log.Print("%q", buf[:n]) 54 | } 55 | */ 56 | package serial 57 | 58 | import ( 59 | "io" 60 | "time" 61 | ) 62 | 63 | // Config contains the information needed to open a serial port. 64 | // 65 | // Currently few options are implemented, but more may be added in the 66 | // future (patches welcome), so it is recommended that you create a 67 | // new config addressing the fields by name rather than by order. 68 | // 69 | // For example: 70 | // 71 | // c0 := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: time.Millisecond * 500} 72 | // or 73 | // c1 := new(serial.Config) 74 | // c1.Name = "/dev/tty.usbserial" 75 | // c1.Baud = 115200 76 | // c1.ReadTimeout = time.Millisecond * 500 77 | // 78 | type Config struct { 79 | Name string 80 | Baud int 81 | ReadTimeout time.Duration // Total timeout 82 | 83 | // Size int // 0 get translated to 8 84 | // Parity SomeNewTypeToGetCorrectDefaultOf_None 85 | // StopBits SomeNewTypeToGetCorrectDefaultOf_1 86 | 87 | // RTSFlowControl bool 88 | // DTRFlowControl bool 89 | // XONFlowControl bool 90 | 91 | // CRLFTranslate bool 92 | } 93 | 94 | // OpenPort opens a serial port with the specified configuration 95 | func OpenPort(c *Config) (io.ReadWriteCloser, error) { 96 | return openPort(c.Name, c.Baud, c.ReadTimeout) 97 | } 98 | 99 | // Converts the timeout values for Linux / POSIX systems 100 | func posixTimeoutValues(readTimeout time.Duration) (vmin uint8, vtime uint8) { 101 | const MAXUINT8 = 1<<8 - 1 // 255 102 | // set blocking / non-blocking read 103 | var minBytesToRead uint8 = 1 104 | var readTimeoutInDeci int64 105 | if readTimeout > 0 { 106 | // EOF on zero read 107 | minBytesToRead = 0 108 | // convert timeout to deciseconds as expected by VTIME 109 | readTimeoutInDeci = (readTimeout.Nanoseconds() / 1e6 / 100) 110 | // capping the timeout 111 | if readTimeoutInDeci < 1 { 112 | // min possible timeout 1 Deciseconds (0.1s) 113 | readTimeoutInDeci = 1 114 | } else if readTimeoutInDeci > MAXUINT8 { 115 | // max possible timeout is 255 deciseconds (25.5s) 116 | readTimeoutInDeci = MAXUINT8 117 | } 118 | } 119 | return minBytesToRead, uint8(readTimeoutInDeci) 120 | } 121 | 122 | // func Flush() 123 | 124 | // func SendBreak() 125 | 126 | // func RegisterBreakHandler(func()) 127 | -------------------------------------------------------------------------------- /vendor/src/github.com/tarm/goserial/serial_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux,!cgo 2 | 3 | package serial 4 | 5 | import ( 6 | "io" 7 | "os" 8 | "syscall" 9 | "time" 10 | "unsafe" 11 | ) 12 | 13 | func openPort(name string, baud int, readTimeout time.Duration) (rwc io.ReadWriteCloser, err error) { 14 | 15 | var bauds = map[int]uint32{ 16 | 50: syscall.B50, 17 | 75: syscall.B75, 18 | 110: syscall.B110, 19 | 134: syscall.B134, 20 | 150: syscall.B150, 21 | 200: syscall.B200, 22 | 300: syscall.B300, 23 | 600: syscall.B600, 24 | 1200: syscall.B1200, 25 | 1800: syscall.B1800, 26 | 2400: syscall.B2400, 27 | 4800: syscall.B4800, 28 | 9600: syscall.B9600, 29 | 19200: syscall.B19200, 30 | 38400: syscall.B38400, 31 | 57600: syscall.B57600, 32 | 115200: syscall.B115200, 33 | 230400: syscall.B230400, 34 | 460800: syscall.B460800, 35 | 500000: syscall.B500000, 36 | 576000: syscall.B576000, 37 | 921600: syscall.B921600, 38 | 1000000: syscall.B1000000, 39 | 1152000: syscall.B1152000, 40 | 1500000: syscall.B1500000, 41 | 2000000: syscall.B2000000, 42 | 2500000: syscall.B2500000, 43 | 3000000: syscall.B3000000, 44 | 3500000: syscall.B3500000, 45 | 4000000: syscall.B4000000, 46 | } 47 | 48 | rate := bauds[baud] 49 | 50 | if rate == 0 { 51 | return 52 | } 53 | 54 | f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | defer func() { 60 | if err != nil && f != nil { 61 | f.Close() 62 | } 63 | }() 64 | 65 | fd := f.Fd() 66 | vmin, vtime := posixTimeoutValues(readTimeout) 67 | t := syscall.Termios{ 68 | Iflag: syscall.IGNPAR, 69 | Cflag: syscall.CS8 | syscall.CREAD | syscall.CLOCAL | rate, 70 | Cc: [32]uint8{syscall.VMIN: vmin, syscall.VTIME: vtime}, 71 | Ispeed: rate, 72 | Ospeed: rate, 73 | } 74 | 75 | if _, _, errno := syscall.Syscall6( 76 | syscall.SYS_IOCTL, 77 | uintptr(fd), 78 | uintptr(syscall.TCSETS), 79 | uintptr(unsafe.Pointer(&t)), 80 | 0, 81 | 0, 82 | 0, 83 | ); errno != 0 { 84 | return nil, errno 85 | } 86 | 87 | if err = syscall.SetNonblock(int(fd), false); err != nil { 88 | return 89 | } 90 | 91 | return f, nil 92 | } 93 | -------------------------------------------------------------------------------- /vendor/src/github.com/tarm/goserial/serial_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows,cgo 2 | 3 | package serial 4 | 5 | // #include 6 | // #include 7 | import "C" 8 | 9 | // TODO: Maybe change to using syscall package + ioctl instead of cgo 10 | 11 | import ( 12 | "errors" 13 | "fmt" 14 | "io" 15 | "os" 16 | "syscall" 17 | "time" 18 | //"unsafe" 19 | ) 20 | 21 | func openPort(name string, baud int, readTimeout time.Duration) (rwc io.ReadWriteCloser, err error) { 22 | f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666) 23 | if err != nil { 24 | return 25 | } 26 | 27 | fd := C.int(f.Fd()) 28 | if C.isatty(fd) != 1 { 29 | f.Close() 30 | return nil, errors.New("File is not a tty") 31 | } 32 | 33 | var st C.struct_termios 34 | _, err = C.tcgetattr(fd, &st) 35 | if err != nil { 36 | f.Close() 37 | return nil, err 38 | } 39 | var speed C.speed_t 40 | switch baud { 41 | case 115200: 42 | speed = C.B115200 43 | case 57600: 44 | speed = C.B57600 45 | case 38400: 46 | speed = C.B38400 47 | case 19200: 48 | speed = C.B19200 49 | case 9600: 50 | speed = C.B9600 51 | case 4800: 52 | speed = C.B4800 53 | case 2400: 54 | speed = C.B2400 55 | default: 56 | f.Close() 57 | return nil, fmt.Errorf("Unknown baud rate %v", baud) 58 | } 59 | 60 | _, err = C.cfsetispeed(&st, speed) 61 | if err != nil { 62 | f.Close() 63 | return nil, err 64 | } 65 | _, err = C.cfsetospeed(&st, speed) 66 | if err != nil { 67 | f.Close() 68 | return nil, err 69 | } 70 | 71 | // Turn off break interrupts, CR->NL, Parity checks, strip, and IXON 72 | st.c_iflag &= ^C.tcflag_t(C.BRKINT | C.ICRNL | C.INPCK | C.ISTRIP | C.IXOFF | C.IXON | C.PARMRK) 73 | 74 | // Select local mode, turn off parity, set to 8 bits 75 | st.c_cflag &= ^C.tcflag_t(C.CSIZE | C.PARENB) 76 | st.c_cflag |= (C.CLOCAL | C.CREAD | C.CS8) 77 | 78 | // Select raw mode 79 | st.c_lflag &= ^C.tcflag_t(C.ICANON | C.ECHO | C.ECHOE | C.ISIG) 80 | st.c_oflag &= ^C.tcflag_t(C.OPOST) 81 | 82 | // set blocking / non-blocking read 83 | /* 84 | * http://man7.org/linux/man-pages/man3/termios.3.html 85 | * - Supports blocking read and read with timeout operations 86 | */ 87 | vmin, vtime := posixTimeoutValues(readTimeout) 88 | st.c_cc[C.VMIN] = C.cc_t(vmin) 89 | st.c_cc[C.VTIME] = C.cc_t(vtime) 90 | 91 | _, err = C.tcsetattr(fd, C.TCSANOW, &st) 92 | if err != nil { 93 | f.Close() 94 | return nil, err 95 | } 96 | 97 | //fmt.Println("Tweaking", name) 98 | r1, _, e := syscall.Syscall(syscall.SYS_FCNTL, 99 | uintptr(f.Fd()), 100 | uintptr(syscall.F_SETFL), 101 | uintptr(0)) 102 | if e != 0 || r1 != 0 { 103 | s := fmt.Sprint("Clearing NONBLOCK syscall error:", e, r1) 104 | f.Close() 105 | return nil, errors.New(s) 106 | } 107 | 108 | /* 109 | r1, _, e = syscall.Syscall(syscall.SYS_IOCTL, 110 | uintptr(f.Fd()), 111 | uintptr(0x80045402), // IOSSIOSPEED 112 | uintptr(unsafe.Pointer(&baud))); 113 | if e != 0 || r1 != 0 { 114 | s := fmt.Sprint("Baudrate syscall error:", e, r1) 115 | f.Close() 116 | return nil, os.NewError(s) 117 | } 118 | */ 119 | 120 | return f, nil 121 | } 122 | -------------------------------------------------------------------------------- /vendor/src/github.com/tarm/goserial/serial_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package serial 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | "os" 9 | "sync" 10 | "syscall" 11 | "time" 12 | "unsafe" 13 | ) 14 | 15 | type serialPort struct { 16 | f *os.File 17 | fd syscall.Handle 18 | rl sync.Mutex 19 | wl sync.Mutex 20 | ro *syscall.Overlapped 21 | wo *syscall.Overlapped 22 | } 23 | 24 | type structDCB struct { 25 | DCBlength, BaudRate uint32 26 | flags [4]byte 27 | wReserved, XonLim, XoffLim uint16 28 | ByteSize, Parity, StopBits byte 29 | XonChar, XoffChar, ErrorChar, EofChar, EvtChar byte 30 | wReserved1 uint16 31 | } 32 | 33 | type structTimeouts struct { 34 | ReadIntervalTimeout uint32 35 | ReadTotalTimeoutMultiplier uint32 36 | ReadTotalTimeoutConstant uint32 37 | WriteTotalTimeoutMultiplier uint32 38 | WriteTotalTimeoutConstant uint32 39 | } 40 | 41 | func openPort(name string, baud int, readTimeout time.Duration) (rwc io.ReadWriteCloser, err error) { 42 | if len(name) > 0 && name[0] != '\\' { 43 | name = "\\\\.\\" + name 44 | } 45 | 46 | h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(name), 47 | syscall.GENERIC_READ|syscall.GENERIC_WRITE, 48 | 0, 49 | nil, 50 | syscall.OPEN_EXISTING, 51 | syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED, 52 | 0) 53 | if err != nil { 54 | return nil, err 55 | } 56 | f := os.NewFile(uintptr(h), name) 57 | defer func() { 58 | if err != nil { 59 | f.Close() 60 | } 61 | }() 62 | 63 | if err = setCommState(h, baud); err != nil { 64 | return 65 | } 66 | if err = setupComm(h, 64, 64); err != nil { 67 | return 68 | } 69 | if err = setCommTimeouts(h, readTimeout); err != nil { 70 | return 71 | } 72 | if err = setCommMask(h); err != nil { 73 | return 74 | } 75 | 76 | ro, err := newOverlapped() 77 | if err != nil { 78 | return 79 | } 80 | wo, err := newOverlapped() 81 | if err != nil { 82 | return 83 | } 84 | port := new(serialPort) 85 | port.f = f 86 | port.fd = h 87 | port.ro = ro 88 | port.wo = wo 89 | 90 | return port, nil 91 | } 92 | 93 | func (p *serialPort) Close() error { 94 | return p.f.Close() 95 | } 96 | 97 | func (p *serialPort) Write(buf []byte) (int, error) { 98 | p.wl.Lock() 99 | defer p.wl.Unlock() 100 | 101 | if err := resetEvent(p.wo.HEvent); err != nil { 102 | return 0, err 103 | } 104 | var n uint32 105 | err := syscall.WriteFile(p.fd, buf, &n, p.wo) 106 | if err != nil && err != syscall.ERROR_IO_PENDING { 107 | return int(n), err 108 | } 109 | return getOverlappedResult(p.fd, p.wo) 110 | } 111 | 112 | func (p *serialPort) Read(buf []byte) (int, error) { 113 | if p == nil || p.f == nil { 114 | return 0, fmt.Errorf("Invalid port on read %v %v", p, p.f) 115 | } 116 | 117 | p.rl.Lock() 118 | defer p.rl.Unlock() 119 | 120 | if err := resetEvent(p.ro.HEvent); err != nil { 121 | return 0, err 122 | } 123 | var done uint32 124 | err := syscall.ReadFile(p.fd, buf, &done, p.ro) 125 | if err != nil && err != syscall.ERROR_IO_PENDING { 126 | return int(done), err 127 | } 128 | return getOverlappedResult(p.fd, p.ro) 129 | } 130 | 131 | var ( 132 | nSetCommState, 133 | nSetCommTimeouts, 134 | nSetCommMask, 135 | nSetupComm, 136 | nGetOverlappedResult, 137 | nCreateEvent, 138 | nResetEvent uintptr 139 | ) 140 | 141 | func init() { 142 | k32, err := syscall.LoadLibrary("kernel32.dll") 143 | if err != nil { 144 | panic("LoadLibrary " + err.Error()) 145 | } 146 | defer syscall.FreeLibrary(k32) 147 | 148 | nSetCommState = getProcAddr(k32, "SetCommState") 149 | nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts") 150 | nSetCommMask = getProcAddr(k32, "SetCommMask") 151 | nSetupComm = getProcAddr(k32, "SetupComm") 152 | nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult") 153 | nCreateEvent = getProcAddr(k32, "CreateEventW") 154 | nResetEvent = getProcAddr(k32, "ResetEvent") 155 | } 156 | 157 | func getProcAddr(lib syscall.Handle, name string) uintptr { 158 | addr, err := syscall.GetProcAddress(lib, name) 159 | if err != nil { 160 | panic(name + " " + err.Error()) 161 | } 162 | return addr 163 | } 164 | 165 | func setCommState(h syscall.Handle, baud int) error { 166 | var params structDCB 167 | params.DCBlength = uint32(unsafe.Sizeof(params)) 168 | 169 | params.flags[0] = 0x01 // fBinary 170 | params.flags[0] |= 0x10 // Assert DSR 171 | 172 | params.BaudRate = uint32(baud) 173 | params.ByteSize = 8 174 | 175 | r, _, err := syscall.Syscall(nSetCommState, 2, uintptr(h), uintptr(unsafe.Pointer(¶ms)), 0) 176 | if r == 0 { 177 | return err 178 | } 179 | return nil 180 | } 181 | 182 | func setCommTimeouts(h syscall.Handle, readTimeout time.Duration) error { 183 | var timeouts structTimeouts 184 | const MAXDWORD = 1<<32 - 1 185 | 186 | if readTimeout > 0 { 187 | // non-blocking read 188 | timeoutMs := readTimeout.Nanoseconds() / 1e6 189 | if timeoutMs < 1 { 190 | timeoutMs = 1 191 | } else if timeoutMs > MAXDWORD { 192 | timeoutMs = MAXDWORD 193 | } 194 | timeouts.ReadIntervalTimeout = 0 195 | timeouts.ReadTotalTimeoutMultiplier = 0 196 | timeouts.ReadTotalTimeoutConstant = uint32(timeoutMs) 197 | } else { 198 | // blocking read 199 | timeouts.ReadIntervalTimeout = MAXDWORD 200 | timeouts.ReadTotalTimeoutMultiplier = MAXDWORD 201 | timeouts.ReadTotalTimeoutConstant = MAXDWORD - 1 202 | } 203 | 204 | /* From http://msdn.microsoft.com/en-us/library/aa363190(v=VS.85).aspx 205 | 206 | For blocking I/O see below: 207 | 208 | Remarks: 209 | 210 | If an application sets ReadIntervalTimeout and 211 | ReadTotalTimeoutMultiplier to MAXDWORD and sets 212 | ReadTotalTimeoutConstant to a value greater than zero and 213 | less than MAXDWORD, one of the following occurs when the 214 | ReadFile function is called: 215 | 216 | If there are any bytes in the input buffer, ReadFile returns 217 | immediately with the bytes in the buffer. 218 | 219 | If there are no bytes in the input buffer, ReadFile waits 220 | until a byte arrives and then returns immediately. 221 | 222 | If no bytes arrive within the time specified by 223 | ReadTotalTimeoutConstant, ReadFile times out. 224 | */ 225 | 226 | r, _, err := syscall.Syscall(nSetCommTimeouts, 2, uintptr(h), uintptr(unsafe.Pointer(&timeouts)), 0) 227 | if r == 0 { 228 | return err 229 | } 230 | return nil 231 | } 232 | 233 | func setupComm(h syscall.Handle, in, out int) error { 234 | r, _, err := syscall.Syscall(nSetupComm, 3, uintptr(h), uintptr(in), uintptr(out)) 235 | if r == 0 { 236 | return err 237 | } 238 | return nil 239 | } 240 | 241 | func setCommMask(h syscall.Handle) error { 242 | const EV_RXCHAR = 0x0001 243 | r, _, err := syscall.Syscall(nSetCommMask, 2, uintptr(h), EV_RXCHAR, 0) 244 | if r == 0 { 245 | return err 246 | } 247 | return nil 248 | } 249 | 250 | func resetEvent(h syscall.Handle) error { 251 | r, _, err := syscall.Syscall(nResetEvent, 1, uintptr(h), 0, 0) 252 | if r == 0 { 253 | return err 254 | } 255 | return nil 256 | } 257 | 258 | func newOverlapped() (*syscall.Overlapped, error) { 259 | var overlapped syscall.Overlapped 260 | r, _, err := syscall.Syscall6(nCreateEvent, 4, 0, 1, 0, 0, 0, 0) 261 | if r == 0 { 262 | return nil, err 263 | } 264 | overlapped.HEvent = syscall.Handle(r) 265 | return &overlapped, nil 266 | } 267 | 268 | func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int, error) { 269 | var n int 270 | r, _, err := syscall.Syscall6(nGetOverlappedResult, 4, 271 | uintptr(h), 272 | uintptr(unsafe.Pointer(overlapped)), 273 | uintptr(unsafe.Pointer(&n)), 1, 0, 0) 274 | if r == 0 { 275 | return n, err 276 | } 277 | 278 | return n, nil 279 | } 280 | -------------------------------------------------------------------------------- /views/_footer.html: -------------------------------------------------------------------------------- 1 | {{define "footer"}} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{end}} 12 | -------------------------------------------------------------------------------- /views/_header.html: -------------------------------------------------------------------------------- 1 | {{define "header"}} 2 | 3 | 4 | 5 | Netzsinus 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 19 | 45 | {{end}} 46 | -------------------------------------------------------------------------------- /views/daten.html: -------------------------------------------------------------------------------- 1 | {{define "daten"}} 2 | {{template "header"}} 3 |
4 | 5 |
6 |
7 |

8 | 9 | Archiv der Frequenzmessdaten 10 |

11 |
12 |
13 |
14 |
15 |
16 |

Unter http://data.netzsin.us 18 | findest Du das Archiv der Frequenzmessdaten. Derzeit wird nur 19 | ein Sensor exportiert, in Zukunft sollen die Datensätze aller 20 | Sensoren exportiert werden.

21 | 22 |

Die Messdaten unterliegen der ODC 24 | Open Database Lizenz. Pro Messtag wird eine Datei mit 25 | folgendem Format generiert:

26 |

27 | # The https://netzsin.us grid frequency measurements are 
28 | # (c) Mathias Dalheimer, .
29 | #
30 | # This database is made available under the Open Database License:
31 | #   http://opendatacommons.org/licenses/odbl/1.0/.
32 | # Any rights in individual contents of the database are licensed under
33 | # the Database Contents License:
34 | #   http://opendatacommons.org/licenses/dbcl/1.0/
35 | # Please see http://opendatacommons.org/licenses/odbl/summary/ for a
36 | # human-readable explanation of the license.
37 | #
38 | # Generated on 2015-04-23 12:52:55.549436393 +0000 UTC
39 | #timestamp	reading
40 | 1405468800	50.005638
41 | 1405468802	50.003479
42 | 1405468804	50.006439
43 | 1405468806	50.005501
44 | 1405468808	50.005428
45 | 1405468810	50.001591
46 | 1405468812	49.996689
47 | 1405468814	49.993832
48 | 1405468816	49.987782
49 | 1405468818	49.984360
50 | 1405468820	49.982029
51 | 				

52 | 53 |

In jeder Zeile steht zunächst ein Unix-Timestamp (UTC), die 54 | gemessene Frequenz steht dahinter. Die Daten können einfach mit 55 | wget heruntergeladen werden:

56 |

57 |

$ wget -r http://data.netzsin.us
58 |

59 |

Die bislang entstandenen Analysescripte sind 60 | auf der Netsin.us Github-Seite 61 | verfügbar. Wenn Du eine coole Analyse entwickelt hast, schicke 62 | bitte eine Mail an Mathias (md@gonium.net).

63 |
64 |
65 |
66 | {{template "footer"}} 67 | {{end}} 68 | -------------------------------------------------------------------------------- /views/impressum.html: -------------------------------------------------------------------------------- 1 | {{define "impressum"}} 2 | {{template "header"}} 3 |
4 | 5 |
6 |
7 |

8 | 9 | Impressum 10 |

11 |
12 |
13 |
14 |
15 |
16 |

Angaben gemäß § 5 TMG

Mathias Dalheimer
17 | Pirmasenser Strasse 7
18 | 67655 Kaiserslautern
19 |

Vertreten durch:
20 | Mathias Dalheimer
21 |

Kontakt:
22 | Telefon: 0631/31600-4484
23 | Fax: 0631/31600-5484
24 | E-Mail: md@gonium.net
25 | GPG key: B6501625
26 | Fingerprint: E7BA B68E A2D3 9B6A ECEF 4DE4 823E 0DBE B650 1625
27 |

28 |

Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV:
29 | Mathias Dalheimer
30 | Pirmasenser Strasse 7
31 | 67655 Kaiserslautern

Haftungsausschluss:

Haftung für Inhalte

32 | Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen. Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.

Haftung für Links

33 | Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.

Urheberrecht

34 | Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet. Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.

Datenschutz

35 | Die Nutzung unserer Webseite ist in der Regel ohne Angabe personenbezogener Daten möglich. Soweit auf unseren Seiten personenbezogene Daten (beispielsweise Name, Anschrift oder eMail-Adressen) erhoben werden, erfolgt dies, soweit möglich, stets auf freiwilliger Basis. Diese Daten werden ohne Ihre ausdrückliche Zustimmung nicht an Dritte weitergegeben.
36 | Wir weisen darauf hin, dass die Datenübertragung im Internet (z.B. bei der Kommunikation per E-Mail) Sicherheitslücken aufweisen kann. Ein lückenloser Schutz der Daten vor dem Zugriff durch Dritte ist nicht möglich.
37 | Der Nutzung von im Rahmen der Impressumspflicht veröffentlichten Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich angeforderter Werbung und Informationsmaterialien wird hiermit ausdrücklich widersprochen. Die Betreiber der Seiten behalten sich ausdrücklich rechtliche Schritte im Falle der unverlangten Zusendung von Werbeinformationen, etwa durch Spam-Mails, vor.
38 |


39 | Impressum generiert mit dem Impressum Generator von Franziska Hasselbach, Anwältin für Arbeitsrecht in Köln 40 |
41 |
42 |
43 |
44 | {{template "footer"}} 45 | {{end}} 46 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | {{define "index"}} 2 | {{template "header"}} 3 |
4 | 5 |
6 |
7 |

8 | 9 | Momentane Frequenzabweichung des Stromnetzes 10 |

11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |

39 | Die Messung der Netzfrequenz liefert Einblicke in das Gleichgewicht 40 | unseres Stromnetz: Normalerweise beträgt die Netzfrequenz 50 41 | Hertz. Sinkt sie ab, wird nicht genügend Leistung von 42 | Kraftwerken in das Netz eingespeist. Liegt sie darüber, wird 43 | zuviel Leistung eingespeist. Kleinere Frequenzabweichungen 44 | sind normal. 45 | Wenn die Netzfrequenz jedoch um mehr 46 | als 10 mHz von der Nennfrequenz 50 Hz abweicht wird die 47 | Primärregelung des Stromnetzes aktiv und gleicht 48 | die Abweichung aus.

49 |

Das Tacho zeigt immer den aktuellen Wert 50 | an, während ein Graph der Abweichung der letzen drei Minuten 51 | (in mHz) dargestellt wird. Die Schwankungen der Netzfrequenz sind 52 | üblicherweise nicht bedenklich — unser Stromnetz ist recht 53 | stabil.

54 |

Darunter befindet sich die gleiche Darstellung für die 55 | vermutlich eingesetzte Regelleistung. Die Regelleistung wird durch die 56 | europäischen Übertragungsnetzbetreiber zur Verfügung gestellt 57 | und dient dazu, kurzfristige Schwankungen der Frequenz 58 | auszugleichen. Laut 60 | ENTSO-E Handbook beträgt die durchschnittlich verfügbare 61 | Regelleistung 19.500 MW/Hz. Sobald die Netzfrequenz mehr als 62 | 10mHz von den angestrebten 50Hz abweicht, wird durch die 63 | beteiligten Kraftwerke die Regelleistung zur Verfügung gestellt. 64 | Die hier dargestellten Werte stellen eine Schätzung dar. 65 |

Das Forum ist der beste 66 | Anlaufpunkt für alle Fragen rund um das Projekt.

67 |
68 |
69 |
70 |
71 | 72 |
73 | 80 | {{template "footer"}} 81 | {{end}} 82 | -------------------------------------------------------------------------------- /views/meters.html: -------------------------------------------------------------------------------- 1 | {{define "meter"}} 2 | {{template "header"}} 3 |
4 | 5 |
6 |
7 |

8 | 9 | Messgeräte des Projekts 10 |

11 |
12 |
13 |
14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {{range .Meters}} 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | {{end}} 34 |
RangNameStandortFrequenzEmpfangen
{{.Rank}}{{.Name}}{{.Location}}{{.Cache.LastReading.Value | doublethreedigits }}{{.Cache.LastReading.Timestamp | tstotime }} — 31 | {{.Cache.LastReading.Timestamp | tstodate }}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | {{template "footer"}} 44 | {{end}} 45 | -------------------------------------------------------------------------------- /views/projekt.html: -------------------------------------------------------------------------------- 1 | {{define "projekt"}} 2 | {{template "header"}} 3 |
4 | 5 |
6 |
7 |

8 | 9 | Projekt 10 |

11 |
12 |
13 |
14 |
15 |
16 |

Das Projekt Netzsin.us ist aus dem Wunsch entstanden, die 17 | Funktionsweise des Stromnetzes besser zu verstehen. Die 18 | Hintergründe des Projekts wurden in den folgenden Vorträgen 19 | näher vorgestellt:

20 | 21 | 40 | 41 |

Netzsin.us verwendet ausschließlich Open Source-Komponenten, 42 | um eine zeitlich hochaufgelöste Netzfrequenzmessung zu 43 | realisieren. Die Daten sind für nichtkommerzielle Zwecke frei 44 | verwendbar. Alle Hard- und Softwarekomponenten sind auf der 45 | auf der Netsin.us Github-Seite 46 | dokumentiert.

47 |
48 |
49 |
50 |
51 |
52 |
53 | Responsive image 55 | Responsive image 57 |
58 |
59 |
60 |
61 | 62 | 63 |
64 |
65 |

66 | 67 | Mitmachen 68 |

69 |
70 |
71 |
72 |
73 |
74 | 75 |

Wenn Du mithelfen möchtest: Sehr gerne! Bitte gehe ins Forum und stelle Deine Ideen vor. 77 | Die folgenden 78 | Bereiche können von Deiner Hilfe profitieren:

79 |
    80 |
  • Nutze die Daten! Entwickele eigene Analysen auf der Basis 81 | der Meßdaten. Schau Dir die Hinweise zu den 82 | Datensätzen an.
  • 83 |
  • Im Moment suchen wir noch weitere Messgerätebetreiber, die 84 | im Europäischen Ausland Meßgeräte betreiben und die Daten an 85 | unseren Server schicken. Weitere Messgeräte in Deutschland 86 | schaden aber nicht!
  • 87 |
  • Das Messgerät funktioniert gut, kostet derzeit aber 88 | relativ viel. Eine Neuentwicklung des Meßgeräts wäre sehr 89 | toll!
  • 90 |
  • Wir freuen uns natürlich über Pull-Requests auf 91 | Github!
  • 92 |
93 | 94 |
95 |
96 |
97 | {{template "footer"}} 98 | {{end}} 99 | --------------------------------------------------------------------------------