From 050d0a0c1c03e24759195dff57aa0861e7ac1963 Mon Sep 17 00:00:00 2001 From: Gunstick Date: Mon, 16 Sep 2013 23:11:42 +0200 Subject: [PATCH] documented the frame format created nodejs display server created html5 canvas visualizer of frames --- HACKING | 47 ++++++ displayclienthtml/README | 10 ++ displayclienthtml/feed_stream.sh | 45 ++++++ displayclienthtml/stream.php | 11 ++ displayclienthtml/visionneuse.html | 134 +++++++++++++++++- .../websocket_demo/node_modules/udp/README.md | 5 + .../node_modules/udp/package.json | 27 ++++ .../websocket_demo/node_modules/udp/udp.js | 1 + displayclienthtml/ws_stdin.js | 12 ++ displayclienthtml/ws_udp.js | 92 ++++++++++++ 10 files changed, 378 insertions(+), 6 deletions(-) create mode 100755 displayclienthtml/feed_stream.sh create mode 100644 displayclienthtml/stream.php create mode 100644 displayclienthtml/websocket_demo/node_modules/udp/README.md create mode 100644 displayclienthtml/websocket_demo/node_modules/udp/package.json create mode 100644 displayclienthtml/websocket_demo/node_modules/udp/udp.js create mode 100644 displayclienthtml/ws_udp.js diff --git a/HACKING b/HACKING index 30c50ed..c75d6c7 100644 --- a/HACKING +++ b/HACKING @@ -13,3 +13,50 @@ Currently used versions of dependencies: -- Documentation: http://www.tldp.org/HOWTO/NCURSES-Programming-HOWTO/ +Frame format (frame=all infos to display the windows and segments) +The frames are separated in UDP packets, so each packet has one frame + +Original format (this will be changed, see below): +This is set in frameserver/defines.h +* byte "abcdefghij" (10 chars) +* byte z (indicates z level. 1=the most backwards) +* byte '\n' +* byte windows (width*height*channels) (so this starts 12 bytes from beginning) +* byte segments (displays*segments*channels) + + + +Proposed new format, to be able to treat a stream of frames +* byte "s2l\n" +* byte windows per row (i.e. max X) in ASCII. in our case 12 => '<' +* byte rows (i.e. max Y) in ASCII. in our case 8 => '8' +* byte displays per row (usually same as windows per row) '<' +* byte disprows (how many rows of 7 segment displays). In our case always '1' +* byte segments per display (can be 7,8,14 ... depending on the hardware) +* byte reserved (future use) currently always set to 0x20 ' ' +* byte z (indicates z level. 1=the most backwards) this is in binary... +* byte '\n' +* byte windows (channels*width+1)*height (so this starts 12 bytes from beginning) +* byte segments displays*(channels*segments+1)*disprows + +channels??? Is always 4, means you send RGBA values. If the display is just +greyscale the output from frameserver to display should be quantized +accordingly. Best done in the display code. E.g. the building is B/W, then +render RGB to HUE in the display client. The html simulator could output +it as is, or do the conversion via a toggle button on the webpage. + ++1??? Well looks like in the frameserver code and in the clients the +"lines" of each frame are separated by a nice \n character. +And the 7 segment displays are each separated by a \n +Oh so why not? Let's have it display nicely on screen if dumped in +a network trace. + +so size max of a UDP packet is + (full rgba) : 10+2+(4*12+1)*8+12*(4*8+1)*1=800 bytes + +Note that the current code needs change of HASH define to +"s2l\n<8<18 " +as that value is always checked to validate a correct frame +and the code does not use the infos to size buffers (else: buffer overrun hack) + + diff --git a/displayclienthtml/README b/displayclienthtml/README index 7529483..84374eb 100644 --- a/displayclienthtml/README +++ b/displayclienthtml/README @@ -4,3 +4,13 @@ the final setup will be a central server using node.js which gets it's data from and manages the multiple connections to web clients visualizing the virtual building howto install node js: http://howtonode.org/how-to-install-nodejs + +*run the latest demo +** run the webserver/udp listener: +nodejs ws_udp.js + +** run the random data feeder +./feed_stream.sh + +** run the visualizer +./firefox visionneuse.html diff --git a/displayclienthtml/feed_stream.sh b/displayclienthtml/feed_stream.sh new file mode 100755 index 0000000..cbef864 --- /dev/null +++ b/displayclienthtml/feed_stream.sh @@ -0,0 +1,45 @@ +#!/bin/bash +while true +do +frame="$( + echo "s2l" + printf "<8<18 " + printf "1" + echo "" + a=$(date +%S) + a=$((0+a)) + j=0 + while [ $j -lt 8 ] + do + i=0 + while [ $i -lt 12 ] + do + n=$((j*8+i)) + if [ $n -eq $a ] + then + printf "zzz " + else + printf "000 " + fi + i=$((i+1)) + done + j=$((j+1)) + echo "" # next row + done + d=0 + while [ $d -lt 12 ] + do + s=0 + while [ $s -lt 8 ] + do + printf "1${s}1 " + s=$((s+1)) + done + d=$((d+1)) + echo "" # next display + done +)" + echo "$frame" + echo "$frame" | nc -w 1 -u localhost 4422 + sleep 1 +done diff --git a/displayclienthtml/stream.php b/displayclienthtml/stream.php new file mode 100644 index 0000000..8151348 --- /dev/null +++ b/displayclienthtml/stream.php @@ -0,0 +1,11 @@ + + + diff --git a/displayclienthtml/visionneuse.html b/displayclienthtml/visionneuse.html index 3185938..f16330d 100644 --- a/displayclienthtml/visionneuse.html +++ b/displayclienthtml/visionneuse.html @@ -11,16 +11,43 @@ var message = document.getElementById('message').value; ws.send(message); } - +// this displays a standard frame +// header: 10 bytes. must be "s2l\n<8<18 " +// z buffer: 1 byte +// CR \n: 1 byte +headersize=12; function connection() { ws = new WebSocket('ws://localhost:1234', 'echo-protocol'); ws.addEventListener("message", function(e) { - // The data is simply the message that we're sending back - var msg = e.data; - - // Append the message - document.getElementById('chatlog').innerHTML += '
' + msg; + // The data is the message from stdin of the server + var frame = e.data; + // we got a frame here + // the header is skipped (headersize) + // we should test here if it's correct header + // first the windows 8*(12*4+1) (8 rows of 12 windows with 4 valus plus a \n per row) + for(y=0;y<8;y++) + for(x=0;x<12;x++) { + r=frame[headersize+y*(x*4+1)] // 4 because RGBA (we ignore A) + g=frame[headersize+y*(x*4+1)] + b=frame[headersize+y*(x*4+1)] +//console.log("put on d="+d+" s="+s+" val="+r+","+g+","+b+"="+b.charCodeAt(0)) + lightWindow(x,y,r.charCodeAt(0),g.charCodeAt(0),b.charCodeAt(0)); + } + // now the 7 segments display + // it contains 12*(8*4+1) bytes for the 7 segments + // bytes012: color of segment0, display0 + // bytes456: color of segment1, display0 + // bytes89a: color of segment2, display0 + headersize+=8*(12*4+1) + for(d=0;d<12;d++) + for(s=0;s<8;s++) { + r=frame[headersize+d*(8*4+1)+s*4+0] // 4 because RGBA (we ignore A) + g=frame[headersize+d*(8*4+1)+s*4+1] + b=frame[headersize+d*(8*4+1)+s*4+2] +//console.log("put on d="+d+" s="+s+" val="+r+","+g+","+b+"="+b.charCodeAt(0)) + lightSegment(d,s,r.charCodeAt(0),g.charCodeAt(0),b.charCodeAt(0)); + } }); document.getElementById('connect_button').style.display = 'none'; @@ -30,6 +57,101 @@ + +
Received from server:
diff --git a/displayclienthtml/websocket_demo/node_modules/udp/README.md b/displayclienthtml/websocket_demo/node_modules/udp/README.md new file mode 100644 index 0000000..e16d0f6 --- /dev/null +++ b/displayclienthtml/websocket_demo/node_modules/udp/README.md @@ -0,0 +1,5 @@ +# udp + +The correct name for Node.js's `dgram` module. + +Documentation: http://api.nodejs.org/dgram.html diff --git a/displayclienthtml/websocket_demo/node_modules/udp/package.json b/displayclienthtml/websocket_demo/node_modules/udp/package.json new file mode 100644 index 0000000..249174d --- /dev/null +++ b/displayclienthtml/websocket_demo/node_modules/udp/package.json @@ -0,0 +1,27 @@ +{ + "name": "udp", + "version": "1.0.0", + "description": "The correct name for Node.js's `dgram` module.", + "main": "udp.js", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/udp" + }, + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "BSD-2-Clause", + "readme": "# udp\n\nThe correct name for Node.js's `dgram` module.\n\nDocumentation: http://api.nodejs.org/dgram.html\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/isaacs/udp/issues" + }, + "_id": "udp@1.0.0", + "dist": { + "shasum": "728875c1a2855ccdd9af488749826b4502523182" + }, + "_from": "udp@", + "_resolved": "https://registry.npmjs.org/udp/-/udp-1.0.0.tgz" +} diff --git a/displayclienthtml/websocket_demo/node_modules/udp/udp.js b/displayclienthtml/websocket_demo/node_modules/udp/udp.js new file mode 100644 index 0000000..ef1b057 --- /dev/null +++ b/displayclienthtml/websocket_demo/node_modules/udp/udp.js @@ -0,0 +1 @@ +module.exports = require('dgram'); diff --git a/displayclienthtml/ws_stdin.js b/displayclienthtml/ws_stdin.js index b323c66..91a150a 100644 --- a/displayclienthtml/ws_stdin.js +++ b/displayclienthtml/ws_stdin.js @@ -1,4 +1,16 @@ +// listener +// it wants some binary data +// who want to convert it from listening on stdin to listening on a TCP socket? +// +// data format +// basically it's the frame format of the frame server +// byte "s2l\n<8<18 " +// byte z (can be ignored) +// byte \n +// byte windows 12*4 RGBA values '\n' repeated 8 times for the 8 rows +// byte segments 8*4 RGBA values '\n' repeated 12 times for the top row + var count = 0; var clients = {}; diff --git a/displayclienthtml/ws_udp.js b/displayclienthtml/ws_udp.js new file mode 100644 index 0000000..66ad269 --- /dev/null +++ b/displayclienthtml/ws_udp.js @@ -0,0 +1,92 @@ + +// listener UDP version +// it wants some binary data +// +// data format +// basically it's the frame format of the frame server +// byte "s2l\n<8<18 " +// byte z (can be ignored) +// byte \n +// byte windows 12*4 RGBA values '\n' repeated 8 times for the 8 rows +// byte segments 8*4 RGBA values '\n' repeated 12 times for the top row + +var count = 0; +var clients = {}; + +var http = require('http'); +var dgram=require('dgram'); +var server = http.createServer(function(request, response) {}); + +server.listen(1234, function() { + console.log((new Date()) + ' Webserver is listening on port 1234'); +}); + +var WebSocketServer = require('websocket').server; +wsServer = new WebSocketServer({ + httpServer: server +}); + +var server = dgram.createSocket('udp4'); +server.bind(4422, '127.0.0.1'); + +console.log((new Date()) + ' UDP display is listening on port 4422'); + + +wsServer.on('request', function(r) { + + var connection = r.accept('echo-protocol', r.origin); + + // Specific id for this client & increment count + var id = count++; + + // Store the connection method so we can loop through & contact all clients + clients[id] = connection + + console.log((new Date()) + ' Connection accepted [' + id + ']'); + // clients[id].sendUTF("Welcome to the server. You are connected. This message has been pushed to you."); + + // Create event listener + connection.on('message', function(message) { + + // The string message that was sent to us + var msgString = message.utf8Data; + + // Loop through all clients + for(var i in clients){ + // Send a message to the client with the message + clients[i].sendUTF(msgString); + } + }); + + // Create event listener for close + connection.on('close', function(reasonCode, description) { + delete clients[id]; + console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.'); + }); +}); + +var sendTime = function () { + var now, i = 0; + + // get time now + now = new Date(); + + // send time to all clients + for(i in clients) { + // Send a message to the client with the message + clients[i].sendUTF(now); + } + + // repeat in 5 seconds + setTimeout(sendTime, 5000); +}; + +server.on("message", function (msg, rinfo) { + console.log("server got: " + msg + " from " + + rinfo.address + ":" + rinfo.port); + for(i in clients) { + // Send a message to the client with the message + clients[i].sendUTF(msg); + } +}); +// every 5 seconds send the date/time