documented the frame format
created nodejs display server created html5 canvas visualizer of framesmaster
parent
16beb2f684
commit
050d0a0c1c
47
HACKING
47
HACKING
|
@ -13,3 +13,50 @@ Currently used versions of dependencies:
|
||||||
-- Documentation: http://www.tldp.org/HOWTO/NCURSES-Programming-HOWTO/
|
-- 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
and manages the multiple connections to web clients visualizing the virtual building
|
||||||
|
|
||||||
howto install node js: http://howtonode.org/how-to-install-nodejs
|
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
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
?>
|
|
@ -11,16 +11,43 @@
|
||||||
var message = document.getElementById('message').value;
|
var message = document.getElementById('message').value;
|
||||||
ws.send(message);
|
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() {
|
function connection() {
|
||||||
ws = new WebSocket('ws://localhost:1234', 'echo-protocol');
|
ws = new WebSocket('ws://localhost:1234', 'echo-protocol');
|
||||||
|
|
||||||
ws.addEventListener("message", function(e) {
|
ws.addEventListener("message", function(e) {
|
||||||
// The data is simply the message that we're sending back
|
// The data is the message from stdin of the server
|
||||||
var msg = e.data;
|
var frame = e.data;
|
||||||
|
// we got a frame here
|
||||||
// Append the message
|
// the header is skipped (headersize)
|
||||||
document.getElementById('chatlog').innerHTML += '<br>' + msg;
|
// 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';
|
document.getElementById('connect_button').style.display = 'none';
|
||||||
|
@ -30,6 +57,101 @@
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body onload=connection()>
|
<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>Received from server:</div>
|
||||||
<div id='chatlog'></div>
|
<div id='chatlog'></div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# udp
|
||||||
|
|
||||||
|
The correct name for Node.js's `dgram` module.
|
||||||
|
|
||||||
|
Documentation: http://api.nodejs.org/dgram.html
|
|
@ -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"
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('dgram');
|
|
@ -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 count = 0;
|
||||||
var clients = {};
|
var clients = {};
|
||||||
|
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue