documented the frame format

created nodejs display server
created html5 canvas visualizer of frames
master
Gunstick 2013-09-16 23:11:42 +02:00
parent 16beb2f684
commit 050d0a0c1c
10 changed files with 378 additions and 6 deletions

47
HACKING
View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,11 @@
<?
while (true) {
?>
<script type="text/javascript">
$('news').innerHTML = '<?= date(DATE_RFC822) ?>';
</script>
<?
flush(); // Ensure the Javascript tag is written out immediately
sleep(10);
}
?>

View File

@ -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 += '<br>' + 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 @@
</script>
</head>
<body onload=connection()>
<canvas id="myCanvas" width="1437" height="1051"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.src = 'building-isometric_night.jpg';
// windowTab[0][0]={x:488,y:265};
// windowTab[1][0]={x:528,y:265};
function lightWindowBW(x,y,r,g,b) {
luminance = 0.299*r + 0.587*g + 0.114*Bb
context.beginPath();
context.rect(488+40.3*x, 265+87.4*y, 23, 34);
context.fillStyle = 'rgb('+luminance+','+luminance+','+luminance+')';
context.fill();
}
function lightWindow(x,y,r,g,b) {
context.beginPath();
context.rect(488+40.3*x, 265+87.4*y, 23, 34);
context.fillStyle = 'rgb('+r+','+g+','+b+')';
context.fill();
}
function lightSegment(display,segment,r,g,b) {
context.beginPath();
segx=488+40.3*display;
segy=176
switch(segment) {
case 0: //segment 0
context.moveTo(segx,segy);
context.lineTo(segx+23,segy);
break;
case 1:
context.moveTo(segx+23,segy);
context.lineTo(segx+23,segy+17);
break;
case 2:
context.moveTo(segx+23,segy+18);
context.lineTo(segx+23,segy+34);
break;
case 3:
context.moveTo(segx,segy+34);
context.lineTo(segx+23,segy+34);
break;
case 4:
context.moveTo(segx,segy+18);
context.lineTo(segx,segy+34);
break;
case 5:
context.moveTo(segx,segy);
context.lineTo(segx,segy+17);
break;
case 6:
context.moveTo(segx,segy+17);
context.lineTo(segx+23,segy+17);
break;
case 7:
context.moveTo(segx+23,segy+38);
context.lineTo(segx+23,segy+42);
break;
}
context.lineWidth=5;
context.strokeStyle = 'rgb('+r+','+g+','+b+')';
context.stroke();
}
var ascii2segments = new Array (
0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02,
0x80, 0x0f, 0x80, 0x80, 0x04, 0x40, 0x80, 0x80,
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
0x7F, 0x6F, 0x80, 0x80, 0x80, 0x48, 0x80, 0x27,
0x80, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71, 0x3d,
0x76, 0x30, 0x1E, 0x76, 0x38, 0x15, 0x37, 0x3f,
0x73, 0x67, 0x31, 0x6d, 0x78, 0x3e, 0x1C, 0x2A,
0x76, 0x6e, 0x5b, 0x39, 0x80, 0x0F, 0x80, 0x08,
0x80, 0x5f, 0x7c, 0x58, 0x5e, 0x7b, 0x71, 0x6F,
0x74, 0x30, 0x0E, 0x76, 0x06, 0x15, 0x54, 0x5c,
0x73, 0x67, 0x50, 0x6d, 0x78, 0x1c, 0x1c, 0x2A,
0x76, 0x6e, 0x5b, 0x39, 0x80, 0x0F, 0x80, 0x08
);
imageObj.onload = function() {
context.drawImage(imageObj, 0, 0);
for(x=0;x<12;x++)
for(y=-1;y<8;y++)
lightWindow(x,y,0,0,0);
};
// delete segment s on display d (0=left, 11=right)
// lightSegment(d,s,0,0,0);
// color r g b of segment s on display d
// lightSegment(d,s,r,g,b);
// windows light
// lightWindow(i,j,0) // switch off window i (0=left) in row j (0=top)
// lightWindow(i,j,w) // switch on new window with brightness w
</script>
<div>Received from server:</div>
<div id='chatlog'></div>
</body>

View File

@ -0,0 +1,5 @@
# udp
The correct name for Node.js's `dgram` module.
Documentation: http://api.nodejs.org/dgram.html

View File

@ -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"
}

View File

@ -0,0 +1 @@
module.exports = require('dgram');

View File

@ -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 = {};

View File

@ -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