Add javascript client

Add server cli options
This commit is contained in:
Jan Klopper 2016-05-17 17:00:12 +02:00
parent e2c5aee5cc
commit d81f12480a
2 changed files with 215 additions and 13 deletions

186
client.js Normal file
View file

@ -0,0 +1,186 @@
// Install nodejs nodejs-legacy
// Install npm
// with npm install jspack
// with npm install sleep
// imports
var dgram = require('dgram');
var struct = require('jspack')['jspack'];
var sleep = require('sleep');
// static vars
var PROTOCOL_PREAMBLE = 'pixelvloed';
var DISCOVER_PORT = 5006;
var MAX_PROTOCOL_VERSION = 1;
var MAX_PIXELS = 140;
var UDP_PORT = 5005;
PixelVloedClient = {
/*Sets up a client
Arguments:
firstserver: (bool) False, select the first server immediately
debug: (bool) False
ip: (str) None
port: (int) None
width: (int) 640
height: (int) 480
Listens for servers if no ip is given
*/
discoverysock: null,
sock: null,
sleep: 100,
debug: true,
width: 640,
height: 480,
effect: null,
alpha: false,
init: function(firstserver, debug,
ip, port,
width, height,
effect, alpha){
if(debug){ this.debug = debug; }
if(effect){ this.effect = effect; }
if(alpha){ this.alpha = alpha; }
if (ip){
this.ipaddress = ip;
this.port = (port?port:UDP_PORT);
this.width = width;
this.height = height;
this.start();
} else {
this.DiscoverServers(firstserver)
}
},
start: function(){
this.sock = dgram.createSocket('udp4');
if (this.debug){
console.log('displaying on '+this.ipaddress+':'+this.port+', '+this.width+'*'+this.height+'px');
}
this.effect();
},
Sleep: function(duration){
// Sleeps the designated amount of time
sleep.usleep((duration?duration:this.sleep) * 1000);
},
SendPacket: function(message, delay){
/*Sends the message to the udp server
Arguments:
message: (str, 140)
sleep: (bool) True, should the client sleep for a while?
*/
this.sock.send(message, 0, message.length,
this.port, this.ipaddress,
this.effect.bind(this));
if (delay){
sleep.usleep(this.sleep);
}
},
DiscoverServers: function (){
//Discover servers that send out the pixelvloed preample
this.discoverysock = dgram.createSocket("udp4");
this.discoverysock.on("message", this.handleDiscoveryPacket.bind(this));
this.discoverysock.on('error', function (err) {
console.error(err);
process.exit(0);
});
this.discoverysock.bind(DISCOVER_PORT);
},
handleDiscoveryPacket: function (data, rinfo) {
data = String(data);
console.log("server got: " + typeof String(data) + " from " + rinfo.address + ":" + rinfo.port);
if (data.startsWith(PROTOCOL_PREAMBLE)){
var dataset = data.split(' ');
if (dataset[0].split(':')[1] <= MAX_PROTOCOL_VERSION){
this.ipaddress = dataset[1].split(':')[0];
this.port = parseInt(dataset[1].split(':')[1], 10);
this.width = parseInt(dataset[2].split('*')[0], 10);
this.height = parseInt(dataset[2].split('*')[1], 10);
this.discoverysock.close();
this.start();
}
}
}
};
function NewMessage(alpha){
// Creates a new message with the correct max size, rgb mode and version
var message = new Buffer((MAX_PIXELS*(alpha?8:7))+2);
message.fill(0);
//MaxSizeList(+2);
message[0] = SetRGBAMode(alpha);
message[1] = SetVersionBit(1);
return message;
}
function RGBPixel(message, offset, x, y, r, g, b, a){
// Generates the packed data for a pixel
message.writeUInt16LE(x, offset);
message.writeUInt16LE(y, offset+2);
message.writeUInt8(r, offset+4);
message.writeUInt8(g, offset+5);
message.writeUInt8(b, offset+6);
if (a){
message.writeUInt8(a, offset+7);
}
}
function SetRGBAMode(mode){
// Generate the rgb/rgba bit
return struct.Pack("<B", [mode]);
}
function SetVersionBit(protocol){
// Generate the Version bit
return struct.Pack("<B", [protocol]);
}
function RandomFill(width, height){
// Generates a random number of pixels with a random color
var msg = NewMessage();
for(var i=0; i<getRandomIntInclusive(10, MAX_PIXELS); i++){
RGBPixel(msg, (i*(this.alpha?8:7))+2,
getRandomIntInclusive(0, width),
getRandomIntInclusive(0, height),
getRandomIntInclusive(0, 255),
getRandomIntInclusive(0, 255),
getRandomIntInclusive(0, 255),
(this.alpha?getRandomIntInclusive(0, 255):null))
}
return msg;
}
// Create a new client instance
var client = PixelVloedClient;
// bind the effect
client.effect = function(err, bytes){
if (err) throw err;
var msg = RandomFill(client.width, client.height);
client.SendPacket(msg);
}
// Init the clients autodetection
client.init();
// helpers
function getRandomIntInclusive(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// polyfills
if (!String.prototype.startsWith) {
String.prototype.startsWith = function(searchString, position){
position = position || 0;
return this.substr(position, searchString.length) === searchString;
};
}

View file

@ -26,24 +26,26 @@ PROTOCOL_VERSION = 1
MAX_PROTOCOL_VERSION = 1 MAX_PROTOCOL_VERSION = 1
PROTOCOL_PREAMBLE = "pixelvloed" PROTOCOL_PREAMBLE = "pixelvloed"
MAX_PIXELS = 140 MAX_PIXELS = 140
DEFAULT_WIDTH = 786
DEFAULT_HEIGHT = 1366
class Canvas(object): class Canvas(object):
"""PixelVloed display class""" """PixelVloed display class"""
def __init__(self, queue, width=1366, height=768, debug=False): def __init__(self, queue, options):
"""Init the pixelVloed server""" """Init the pixelVloed server"""
self.debug = debug self.debug = options.debug if options.debug else False
self.pixeloffset = 2 self.pixeloffset = 2
self.fps = 30 self.fps = 30
self.screen = None self.screen = None
self.udp_ip = UDP_IP self.udp_ip = UDP_IP
self.udp_port = UDP_PORT self.udp_port = UDP_PORT
self.width = width self.width = options.width if options.width else DEFAULT_WIDTH
self.height = height self.height = options.height if options.height else DEFAULT_HEIGHT
self.canvas() self.canvas()
self.set_title() self.set_title()
self.queue = queue self.queue = queue
self.limit = MAX_PIXELS self.limit = options.maxpixels if options.maxpixels else MAX_PIXELS
self.pixels = None self.pixels = None
self.broadcastsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.broadcastsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.broadcastsocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) self.broadcastsocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
@ -87,8 +89,7 @@ class Canvas(object):
def CanvasUpdate(self): def CanvasUpdate(self):
"""Updates the screen according to self.fps""" """Updates the screen according to self.fps"""
lasttime = time.time() lasttime = lastbroadcast = time.time()
lastbroadcast = time.time()
changed = False changed = False
while True: while True:
changed = self.Draw() or changed changed = self.Draw() or changed
@ -167,8 +168,9 @@ class PixelVloedServer(DatagramServer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Set up some vars for this instance""" """Set up some vars for this instance"""
self.queue = Queue() self.queue = Queue()
pixelcanvas = Canvas(self.queue) pixelcanvas = Canvas(self.queue, kwargs['options'])
__request_processing_greenlet = spawn(pixelcanvas.CanvasUpdate) __request_processing_greenlet = spawn(pixelcanvas.CanvasUpdate)
del (kwargs['options'])
DatagramServer.__init__(self, *args, **kwargs) DatagramServer.__init__(self, *args, **kwargs)
def handle(self, data, _address): def handle(self, data, _address):
@ -208,7 +210,7 @@ class PixelVloedClient(object):
servers[0]) servers[0])
else: else:
self.ipaddress = ip self.ipaddress = ip
self.port = port if port else UDP_IP self.port = port if port else UDP_PORT
self.width = width self.width = width
self.height = height self.height = height
if self.debug: if self.debug:
@ -275,7 +277,7 @@ def NewMessage():
def RGBPixel(x, y, r, g, b, a=None): # pylint: disable=C0103 def RGBPixel(x, y, r, g, b, a=None): # pylint: disable=C0103
"""Generates the packed data for a pixel""" """Generates the packed data for a pixel"""
if a: if a is not None:
return struct.pack("<2H4B", x, y, r, g, b, a) return struct.pack("<2H4B", x, y, r, g, b, a)
return struct.pack("<2H3B", x, y, r, g, b) return struct.pack("<2H3B", x, y, r, g, b)
@ -304,9 +306,23 @@ class MaxSizeList(list):
raise IndexError('max size reached') raise IndexError('max size reached')
super(MaxSizeList, self).append(item) super(MaxSizeList, self).append(item)
def RunServer(): def RunServer(options):
"""Runs a pixelvloed server""" """Runs a pixelvloed server"""
PixelVloedServer('%s:%d' %(UDP_IP, UDP_PORT)).serve_forever() PixelVloedServer('%s:%d' %(options.ip, options.port),
options=options).serve_forever()
if __name__ == '__main__': if __name__ == '__main__':
RunServer() import optparse
parser = optparse.OptionParser()
parser.add_option('-v', action="store_true", dest="debug", default=False)
parser.add_option('-i', action="store", dest="ip", default=UDP_IP)
parser.add_option('-p', action="store", dest="port", default=UDP_PORT,
type="int")
parser.add_option('-x', action="store", dest="width", default=DEFAULT_WIDTH,
type="int")
parser.add_option('-y', action="store", dest="height", default=DEFAULT_HEIGHT,
type="int")
parser.add_option('-m', action="store", dest="maxpixels", default=MAX_PIXELS,
type="int")
options, remainder = parser.parse_args()
RunServer(options)