Add javascript client
Add server cli options
This commit is contained in:
parent
e2c5aee5cc
commit
d81f12480a
2 changed files with 215 additions and 13 deletions
186
client.js
Normal file
186
client.js
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
42
vloed.py
42
vloed.py
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue