diff --git a/LJ.conf b/LJ.conf index 5b2ebba..73181d1 100644 --- a/LJ.conf +++ b/LJ.conf @@ -1,8 +1,8 @@ [General] -lasernumber = 4 +lasernumber = 1 debug = 0 ljayserverip = 0.0.0.0 -wwwip = 192.168.2.44 +wwwip = 127.0.0.1 nozoscip = 127.0.0.1 bhoroscip = 127.0.0.1 autostart = artnet @@ -12,7 +12,7 @@ wsport = 9001 [laser0] color = -1 type = DS1000 -ip = 192.168.2.3 +ip = 127.0.0.1 kpps = 25000 centerx = 0 centery = 0 @@ -21,7 +21,7 @@ zoomy = 50.0 sizex = 32000 sizey = 32000 finangle = 0.0 -swapx = -1 +swapx = 1 swapy = -1 lsteps = [ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)] warpdest = [[-1500., 1500.], @@ -36,8 +36,8 @@ ip = 192.168.2.5 kpps = 25000 centerx = 0 centery = 0 -zoomx = 80.0 -zoomy = 80.0 +zoomx = 50.0 +zoomy = 50.0 sizex = 32000 sizey = 32000 finangle = 0.0 @@ -45,9 +45,9 @@ swapx = -1 swapy = -1 lsteps = [ (1.0, 2),(0.25, 1), (0.75, 1), (1.0, 5)] warpdest = [[-1500., 1500.], - [ 1500., 1500.], - [ 1500.,-1500.], - [-1500.,-1500.]] + [ 1500., 1500.], + [ 1500.,-1500.], + [-1500.,-1500.]] [laser2] color = -1 @@ -65,14 +65,14 @@ swapx = -1 swapy = -1 lsteps = [(1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)] warpdest = [[-1500., 1500.], - [ 1500., 1500.], - [ 1500.,-1500.], - [-1500.,-1500.]] + [ 1500., 1500.], + [ 1500.,-1500.], + [-1500.,-1500.]] [laser3] color = -1 type = LUKE400 -ip = 192.168.2.4 +ip = 192.168.1.5 kpps = 25000 centerx = 0 centery = 0 @@ -81,34 +81,19 @@ zoomy = 50.0 sizex = 32000 sizey = 32000 finangle = 0.0 -swapx = 1 -swapy = 1 +swapx = -1 +swapy = -1 lsteps = [(1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)] warpdest = [[-1500., 1500.], - [ 1500., 1500.], - [ 1500.,-1500.], - [-1500.,-1500.]] + [ 1500., 1500.], + [ 1500.,-1500.], + [-1500.,-1500.]] [plugins] plugins = { "aurora": {"OSC": 8090, "command": "python3 plugins/aurora/aurora.py", "display": True}, - "nozoid": {"OSC": 8003, "command": "python3 plugins/audio/nozoids3.py", "display": True}, - "glyph": {"OSC": 8004, "command": "python3 plugins/laserglyph.py", "display": True}, - "planet": {"OSC": 8005, "command": "python3 plugins/planetarium/main.py", "display": True}, - "words": {"OSC": 8006, "command": "python3 plugins/livewords3.py", "display": True}, - "cycl": {"OSC": 8007, "command": "python3 plugins/textcycl.py", "display": True}, - "simu": {"OSC": 8008, "command": "python plugins/pysimu.py", "display": False}, "artnet": {"OSC": 8009, "command": "python3 libs3/artnet.py", "display": False}, - "trckr": {"OSC": 8017, "command": "python3 plugins/trckr.py", "display": False}, - "maxw": {"OSC": 8012, "command": "python3 plugins/maxwell.py", "display": True}, "square": {"OSC": 8013, "command": "python3 plugins/square.py", "display": True}, - "custom1": {"OSC": 8014, "command": "python3 plugins/custom1.py", "display": True}, - "mitraille": {"OSC": 8015, "command": "python3 plugins/audio/mitraille.py", "display": True}, - "livecode": {"OSC": 8016, "command": "python3 plugins/livecoding.py", "display": True}, - "ljpong": {"OSC": 8020, "command": "python plugins/games/ljpong/main.py", "display": True}, - "ljwars": {"OSC": 8021, "command": "python plugins/games/ljsw/main.py", "display": True}, - "audiogen": {"OSC": 8030, "command": "python3 plugins/audio/audiogen.py", "display": False}, - "midigen": {"OSC": 8031, "command": "python3 plugins/audio/midigen.py", "display": False}, - "viewgen": {"OSC": 8032, "command": "python3 plugins/audio/viewgen.py", "display": True} + "custom1": {"OSC": 8014, "command": "python3 plugins/custom1.py", "display": True} } diff --git a/LJ_template.conf b/LJ_template.conf deleted file mode 100644 index 002fe4c..0000000 --- a/LJ_template.conf +++ /dev/null @@ -1,114 +0,0 @@ -[General] -lasernumber = 1 -debug = 0 -ljayserverip = 0.0.0.0 -wwwip = 192.168.2.43 -nozoscip = 127.0.0.1 -bhoroscip = 127.0.0.1 -autostart = artnet -wstype = ws -wsport = 9001 - -[laser0] -color = -1 -type = DS1000 -ip = 192.168.2.3 -kpps = 25000 -centerx = 0 -centery = 0 -zoomx = 50.0 -zoomy = 50.0 -sizex = 32000 -sizey = 32000 -finangle = 0.0 -swapx = 1 -swapy = -1 -lsteps = [ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)] -warpdest = [[-1500., 1500.], - [ 1500., 1500.], - [ 1500.,-1500.], - [-1500.,-1500.]] - -[laser1] -color = -1 -type = LOCAL -ip = 192.168.2.5 -kpps = 25000 -centerx = 0 -centery = 0 -zoomx = 50.0 -zoomy = 50.0 -sizex = 32000 -sizey = 32000 -finangle = 0.0 -swapx = -1 -swapy = -1 -lsteps = [ (1.0, 2),(0.25, 1), (0.75, 1), (1.0, 5)] -warpdest = [[-1500., 1500.], - [ 1500., 1500.], - [ 1500.,-1500.], - [-1500.,-1500.]] - -[laser2] -color = -1 -type = LUKE400 -ip = 192.168.2.6 -kpps = 25000 -centerx = 0 -centery = 0 -zoomx = 50.0 -zoomy = 50.0 -sizex = 32000 -sizey = 32000 -finangle = 0.0 -swapx = -1 -swapy = -1 -lsteps = [(1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)] -warpdest = [[-1500., 1500.], - [ 1500., 1500.], - [ 1500.,-1500.], - [-1500.,-1500.]] - -[laser3] -color = -1 -type = LUKE400 -ip = 192.168.1.5 -kpps = 25000 -centerx = 0 -centery = 0 -zoomx = 50.0 -zoomy = 50.0 -sizex = 32000 -sizey = 32000 -finangle = 0.0 -swapx = -1 -swapy = -1 -lsteps = [(1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)] -warpdest = [[-1500., 1500.], - [ 1500., 1500.], - [ 1500.,-1500.], - [-1500.,-1500.]] - -[plugins] -plugins = { - "aurora": {"OSC": 8090, "command": "python3 plugins/aurora/aurora.py", "display": True}, - "nozoid": {"OSC": 8003, "command": "python3 plugins/audio/nozoids3.py", "display": True}, - "glyph": {"OSC": 8004, "command": "python3 plugins/laserglyph.py", "display": True}, - "planet": {"OSC": 8005, "command": "python3 plugins/planetarium/main.py", "display": True}, - "words": {"OSC": 8006, "command": "python3 plugins/livewords3.py", "display": True}, - "cycl": {"OSC": 8007, "command": "python3 plugins/textcycl.py", "display": True}, - "simu": {"OSC": 8008, "command": "python plugins/pysimu.py", "display": False}, - "artnet": {"OSC": 8009, "command": "python3 libs3/artnet.py", "display": False}, - "trckr": {"OSC": 8017, "command": "python3 plugins/trckr.py", "display": False}, - "maxw": {"OSC": 8012, "command": "python3 plugins/maxwell.py", "display": True}, - "square": {"OSC": 8013, "command": "python3 plugins/square.py", "display": True}, - "custom1": {"OSC": 8014, "command": "python3 plugins/custom1.py", "display": True}, - "mitraille": {"OSC": 8015, "command": "python3 plugins/audio/mitraille.py", "display": True}, - "livecode": {"OSC": 8016, "command": "python3 plugins/livecoding.py", "display": True}, - "ljpong": {"OSC": 8020, "command": "python plugins/games/ljpong/main.py", "display": True}, - "ljwars": {"OSC": 8021, "command": "python plugins/games/ljsw/main.py", "display": True}, - "audiogen": {"OSC": 8030, "command": "python3 plugins/audio/audiogen.py", "display": False}, - "midigen": {"OSC": 8031, "command": "python3 plugins/audio/midigen.py", "display": False}, - "viewgen": {"OSC": 8032, "command": "python3 plugins/audio/viewgen.py", "display": True} - } - diff --git a/Pd/.DS_Store b/Pd/.DS_Store new file mode 100644 index 0000000..31a0d67 Binary files /dev/null and b/Pd/.DS_Store differ diff --git a/Pd/LJsender.pd b/Pd/LJsender.pd new file mode 100644 index 0000000..82c3bd3 --- /dev/null +++ b/Pd/LJsender.pd @@ -0,0 +1,23 @@ +#N canvas 468 143 709 527 10; +#X msg 60 237 disconnect; +#X floatatom 27 294 0 0 0 0 - - -; +#X text 22 315 Outlet is nonzero if connection is open \, zero otherwise. +; +#X msg 37 60 send mytext trololo; +#X obj 28 267 netsend 1; +#X msg 51 125 send /pl/0/0 (150 2300 65280) (170 170 65280) (230 170 +65280) (210 230 65280) (150 230 65280); +#X text 258 29 LJ Sender; +#X msg 46 84 send /pl/0/0 150 2300 65280 170 170 65280 230 170 65280 +210 230 65280 150 230 65280; +#X msg 26 39 connect 127.0.0.1 8083; +#X msg 58 165 send /pl/0/0 [(150.0:230.0:65280) (170.0:170.0:65280) +(230.0:170.0:65280) (210.0:230.0:65280) (150.0:230.0:65280)]; +#X text 444 174 <- le mieux; +#X connect 0 0 4 0; +#X connect 3 0 4 0; +#X connect 4 0 1 0; +#X connect 5 0 4 0; +#X connect 7 0 4 0; +#X connect 8 0 4 0; +#X connect 9 0 4 0; diff --git a/Pd/ljpd.py b/Pd/ljpd.py new file mode 100644 index 0000000..67c97d1 --- /dev/null +++ b/Pd/ljpd.py @@ -0,0 +1,106 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + +''' + +LJPD + +Udp server to redis +v0.1b + +''' +import traceback, time +import argparse +import socket +import _thread +import redis + + +print() +print ("LJPD") +print ("Arguments parsing if needed...") +argsparser = argparse.ArgumentParser(description="dumpUDP v0.1b help mode") +argsparser.add_argument("-i","--IP",help="IP to bind to (0.0.0.0 by default)", type=str) +argsparser.add_argument("-p","--port",help="UDP port to bind to (9000 by default)", type=str) +argsparser.add_argument("-l","--lj",help="LJ IP address (127.0.0.1 by default)", type=str) + + +args = argsparser.parse_args() + +# LJ server IP name +if args.IP: + ljIP = lj.IP +else: + ljIP = "127.0.0.1" + +# Server +if args.IP: + serverIP = args.IP +else: + serverIP = "0.0.0.0" + +# ORCA destination device +if args.port: + UDPORT = int(args.port) +else: + UDPORT = 8083 + +print("Connecting to Redis...") + +r = redis.StrictRedis(host= ljIP, port=6379, db=0) + +def GetTime(): + return time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime()) + + +def udp_thread(): + + while True: + + payload, client_address = sock.recvfrom(1024) + udpath = payload.decode('utf_8') + print(GetTime(),"From", str(client_address),"got", udpath ) + #r.set('/pl/0/0', "/pl/"+str(clientnumber)+"/") + #print(udpath[0:1], " ",udpath[1:2], " ",udpath[2:3], " ",udpath[3:4], " " ) + + + # Reply to client + bytesToSend = str.encode("ACK :"+str(payload)) + serverAddressPort = (client_address, UDPORT) + bufferSize = 1024 + #sock.sendto(bytesToSend, serverAddressPort) + sock.sendto(bytesToSend, client_address) + + + time.sleep(0.005) + + + +def Start(serverIP, UDPORT): + global sock + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + server = ( serverIP,UDPORT) + sock.bind(server) + _thread.start_new_thread(udp_thread, ()) + + + +# Launch server in another thread. +print("Launching UDP Server", serverIP,':', UDPORT) +Start(serverIP, UDPORT) + + +# Do something else +try: + + while True: + time.sleep(0.005) + +except Exception: + traceback.print_exc() + +finally: + print("") + print("ljpd stopped.") \ No newline at end of file diff --git a/README.md b/README.md index e43eebb..3618c65 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -LJ v0.8.2 +LJ v0.8.2 'fireandforget' By Sam Neurohack, Loloster, Cocoa @@ -7,17 +7,26 @@ LICENCE : CC BY ![LJ](https://www.teamlaser.fr/lj/images/lj2.png) -A software laser framework with GUI, for up to 4 lasers live actions with ethedreams DACs. Think creative like Laser "battles", planetarium, sharing available lasers in demoparties for competition, ... - +LJ is like a video projector where you fire images and forget. Lasers are dangerous : you can really fire real world objects. + +A software laser framework with webGUI, for up to 4 lasers live actions with ethedreams DACs. Think creative like Laser "battles", planetarium, sharing available lasers in demoparties for competition,... LJ has 5 main components : -- "Plugins" are points generators (to one or more lasers). Lot examples comes with LJ : planetarium, 3D anaglyph animations,... See plugins directory. +- "Plugins" are "frames" generators to one or more lasers. Frames goes to different possible frames inputs of the "manager". With laser one frame = one polyline, like in LOGO. Lot examples comes with LJ : planetarium, 3D anaglyph animations,... See plugins directory. - A "tracer" per etherdream/laser that take its given point list, correct geometry, recompute in laser controller coordinates, send it to its controller and report its status to the "manager". -- A "manager" that talk to all tracers (which client number point lists to draw, new geometry correction,...), handle IOs (webui functions, OSC commands,...) and plugins. +- A "manager" that talk to all tracers (which point lists to draw, new geometry correction,...), handle IOs (webui functions, OSC commands,...) and plugins. - A web GUI in html, css, and vanilla js. This UI can be used in a tablet, computer, whatever. LJ does not come with an html server but absolutely can . This GUI has a (currently slow) simulator, but one can also use an etherdream/laser emulator (see nannou simulator below) to work without physical lasers !! - A network available database (redis). "Plugins" can send directly their pointlists to redis. Each "tracer" is instructed to get one of the avalaible pointlist in redis. +"Frames" (actually polylines) inputs are : + +- OSC (port 8002) +- Websocket (port 9001) +- Cli pipe (see clitools) +- Redis keys + + Important : for best performance LJ is meant to run in a dedicated computer especially with multiple lasers and highly multitasked load : if you watch video, use live webcam face recognition, webui simulator,... and run LJ on the same computer, well you need a bunch of cores. If you don't, spread the load : you can use webui on a tablet, the livecam on a phone, run pointlists generators on another computer,... # @@ -384,6 +393,8 @@ python3 talk3.py -i etherdreamIP - Switch to simu page. If you don't see anything : check redis server or your points in redis doesn't respect pointlist formatting (see command reference). +- Check Leds : for each IRL lasers the two sets of must be green at startup and laser should display their number or the pointlist you want. + - If talk3 works but you don't see your points : click on the Grid icon in Align page. This will override your pointlist and display squares. If Grid works : recomputed points by tracers are "bad" with given values in LJ.conf. "Bad points" ? @@ -444,8 +455,6 @@ lsteps is a string like "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]" /scene/scenenumber/start 0 or 1 : tell all tracers to use given scene -/regen : regen webui index html page. - /scim : change webui simulated laser. diff --git a/clitools/exports/toUDP.py b/clitools/exports/toUDP.py new file mode 100644 index 0000000..64787db --- /dev/null +++ b/clitools/exports/toUDP.py @@ -0,0 +1,84 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + + +''' + +toUDP +v0.1.0 + +A basic exporter + +LICENCE : CC + +by cocoa + + +''' +from __future__ import print_function +import sys +import os +import argparse +import time +import socket +import ast + +argsparser = argparse.ArgumentParser(description="toUDP v0.1b help mode") +argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output") +argsparser.add_argument("-i","--ip",help="IP to bind to (0.0.0.0 by default)",default="0.0.0.0",type=str) +argsparser.add_argument("-p","--port",help="UDP port to bind to (9000 by default)",default="9003",type=str) +args = argsparser.parse_args() + +verbose = args.verbose +ip = args.ip +port = int(args.port) +verbose = args.verbose + + +name = "exports::toUDP" + +def debug(*args, **kwargs): + if( verbose == False ): + return + print(*args, file=sys.stderr, **kwargs) + +def ClientStart(ip, port): + global sockclient + + sockclient = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) + +def ClientSend(msgFromClient): + + bytesToSend = str.encode(str(msgFromClient)) + serverAddressPort = (ip, port) + bufferSize = 1024 + + # Send to server using created UDP socket + sockclient.sendto(bytesToSend, serverAddressPort) + + ''' + # If reply : + msgFromServer = sockclient.recvfrom(bufferSize) + + msg = "Message from Server {}".format(msgFromServer[0]) + print(msg) + ''' + +try: + + ClientStart(ip, port) + while True: + + line = sys.stdin.readline() + if line == "": + time.sleep(0.01) + line = line.rstrip('\n') + #pointsList = ast.literal_eval(line) + debug(name,": "+line) + ClientSend(line) + + +except EOFError: + debug("break")# no more information + diff --git a/clitools/filters/kaleidoscope.py b/clitools/filters/kaleidoscope.py index 0495f23..5ca4396 100755 --- a/clitools/filters/kaleidoscope.py +++ b/clitools/filters/kaleidoscope.py @@ -163,6 +163,7 @@ try: # Do the filter result = kaleidoscope( pointsList ) print( result, flush=True ) + looptime = time.time() - start # debug(name+" looptime:"+str(looptime)) if( looptime < optimal_looptime ): diff --git a/clitools/generators/fromOSC.py b/clitools/generators/fromOSC.py new file mode 100644 index 0000000..d8005c5 --- /dev/null +++ b/clitools/generators/fromOSC.py @@ -0,0 +1,126 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + +''' +Forward /pl pointlist to cli + +input OSC in END points format : (x,y,color) +output CLI in CLI points format : [x,y,color] + +/pl "[(150.0, 230.0, 255), (170.0, 170.0, 255), (230.0, 170.0, 255), (210.0, 230.0, 255), (150.0, 230.0, 255)]" + +v0.1.0 + +LICENCE : CC + +by Cocoa, Sam Neurohack + +''' +from __future__ import print_function +from OSC3 import OSCServer, OSCClient, OSCMessage +import sys +from time import sleep +import argparse +import ast + +argsparser = argparse.ArgumentParser(description="fromOSC generator") +argsparser.add_argument("-i","--ip",help="IP to bind to (0.0.0.0 by default)",default="0.0.0.0",type=str) +argsparser.add_argument("-p","--port",help="OSC port to bind to (9002 by default)",default=9002,type=str) +argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output") +args = argsparser.parse_args() + +verbose = args.verbose +ip = args.ip +port = int(args.port) + +def debug(*args, **kwargs): + if( verbose == False ): + return + print(*args, file=sys.stderr, **kwargs) + +oscserver = OSCServer( (ip, port) ) +oscserver.timeout = 0 +run = True + +# this method of reporting timeouts only works by convention +# that before calling handle_request() field .timed_out is +# set to False +def handle_timeout(self): + self.timed_out = True + +# funny python's way to add a method to an instance of a class +import types +oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver) + +# RAW OSC Frame available ? +def OSC_frame(): + # clear timed_out flag + oscserver.timed_out = False + # handle all pending requests then return + while not oscserver.timed_out: + oscserver.handle_request() + + +# default handler +def OSChandler(oscpath, tags, args, source): + + oscaddress = ''.join(oscpath.split("/")) + debug("fromOSC Default OSC Handler got oscpath", oscpath, "from" + str(source[0]), ":", args) + #print("OSC address", path) + #print("find.. /bhoreal ?", path.find('/bhoreal')) + + if oscpath == "/pl" and len(args)==1: + + debug("correct OSC type :'/pl") + + if validate(args[0]) == True: + + debug("new pl : ", args[0]) + line = args[0].replace("(",'[') + line = line.replace(")",']') + line = "[{}]".format(line) + print(line, flush=True); + + else: + debug("Bad pointlist -> msg trapped.") + + else: + debug("BAD OSC Message : " + oscpath +" " +args[0]) + + +oscserver.addMsgHandler( "default", OSChandler ) + + +def validate(pointlist): + + state = True + + if len(pointlist)<9: + debug("Not enough characters :", pointlist) + state = False + + if pointlist.find("(") == -1: + debug("Bad format : use () not [] for points", pointlist) + state = False + + try: + pl = bytes(pointlist, 'ascii') + check = ast.literal_eval(pl.decode('ascii')) + + except: + debug("BAD POINTLIST :", pointlist) + state = False + + return state + + +# simulate a "game engine" +while run: + # do the game stuff: + sleep(0.01) + # call user script + OSC_frame() + +oscserver.close() + diff --git a/clitools/generators/fromUDP.py b/clitools/generators/fromUDP.py new file mode 100644 index 0000000..5f6e5e9 --- /dev/null +++ b/clitools/generators/fromUDP.py @@ -0,0 +1,84 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + +''' + +fromUDP + +Udp server to cli +v0.1b + +''' +from __future__ import print_function +import traceback, time +import argparse +import socket +import _thread +import sys + +name="generator::fromUDP" + + +def debug(*args, **kwargs): + if( verbose == False ): + return + print(*args, file=sys.stderr, **kwargs) + + +argsparser = argparse.ArgumentParser(description="fromUDP v0.1b help mode") +argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output") +argsparser.add_argument("-i","--ip",help="IP to bind to (0.0.0.0 by default)",default="0.0.0.0",type=str) +argsparser.add_argument("-p","--port",help="UDP port to bind to (9000 by default)",default=9000,type=str) +args = argsparser.parse_args() + +verbose = args.verbose +ip = args.ip +port = int(args.port) +verbose = args.verbose + + + +def udp_thread(): + + while True: + + payload, client_address = sock.recvfrom(1024) + udpath = payload.decode('utf_8') + debug(udpath[0:]) + print(udpath[0:], flush=True); + + ''' + # Reply to client + bytesToSend = str.encode("ACK :"+str(payload)) + serverAddressPort = (client_address, port) + bufferSize = 1024 + #sock.sendto(bytesToSend, serverAddressPort) + sock.sendto(bytesToSend, client_address) + ''' + +def StartUDP(serverIP, UDPORT): + global sock + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + server = ( serverIP,UDPORT) + sock.bind(server) + _thread.start_new_thread(udp_thread, ()) + + +StartUDP(ip, port) + + +# Do something else +try: + + while True: + time.sleep(0.005) + +except Exception: + traceback.print_exc() + + + + + diff --git a/ethertools/receivebroadcast.py b/ethertools/receivebroadcast.py new file mode 100644 index 0000000..ec73293 --- /dev/null +++ b/ethertools/receivebroadcast.py @@ -0,0 +1,22 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + +# or tcpdump -i eth1 port 54545 -XX + +import socket + + +def find_LJ(): + """Listen for broadcast packets.""" + + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.bind(("0.0.0.0", 54545)) + + while True: + data, addr = s.recvfrom(1024) + print(" %s " % (data, )) + print("Packet from %s: " % (addr, )) + + +find_LJ() \ No newline at end of file diff --git a/libs3/OSCom.py b/libs3/OSCom.py index 4bf607b..a297f4b 100644 --- a/libs3/OSCom.py +++ b/libs3/OSCom.py @@ -70,12 +70,9 @@ def OSCframe(): oscserver.handle_request() - - # OSC server Thread : handler, dacs reports and simulator points sender to UI. def osc_thread(): - #print("osc Thread launched") try: while True: @@ -92,8 +89,6 @@ def osc_thread(): print("\n") - - # Properly close the system. Todo def Stop(): oscserver.close() diff --git a/libs3/commands.py b/libs3/commands.py index abbfd98..e80363b 100644 --- a/libs3/commands.py +++ b/libs3/commands.py @@ -398,6 +398,16 @@ def handler(oscpath, args): print() DAChecks() print("Done.") + + + if oscpath[2] == "reset": + import shutil + print() + shutil.copyfile(gstt.ljpath+'/templates/LJ_template.conf', gstt.ljpath+'/LJ.conf') + print("templates/LJ_template.conf copied to LJ.conf.") + print("** RESTART LJ **") + #LJautokill() + if oscpath[2] == "restart": print() @@ -597,7 +607,7 @@ def UpdateAllwww(): Updatepage(gstt.ljpath+"/www/LJ.js") Updatepage(gstt.ljpath+"/www/trckr/trckrcam1.html") Updatepage(gstt.ljpath+"/www/simu.html") - Updatepage(gstt.ljpath+"/www/align.html") + Updatepage(gstt.ljpath+"/www/settings.html") Updatepage(gstt.ljpath+"/www/auralls.html") Updatepage(gstt.ljpath+"/www/index.html") diff --git a/libs3/lj23layers.py b/libs3/lj23layers.py index f766121..a6488f3 100644 --- a/libs3/lj23layers.py +++ b/libs3/lj23layers.py @@ -1012,8 +1012,4 @@ def TextRGB(message, zpos, c, layer, xpos, ypos, resize, rotx, roty, rotz): Text(message, zpos, int('0x%02x%02x%02x' % (red,green,blue),0), layer, xpos, ypos, resize, rotx, roty, rotz) - - - - \ No newline at end of file diff --git a/libs3/plugins.py b/libs3/plugins.py index bfa4eb1..392bd1c 100644 --- a/libs3/plugins.py +++ b/libs3/plugins.py @@ -15,7 +15,7 @@ from libs3 import gstt import os import subprocess import sys - +from socket import * def Init(wserver): global WSserver @@ -177,6 +177,14 @@ def OSCsend(name, oscaddress, oscargs =''): #PluginStart(name) return False +def sendbroadcast(): + + if gstt.debug > 0: + print("Sending broadcast") + cs = socket(AF_INET, SOCK_DGRAM) + cs.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) + cs.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) + cs.sendto("LJ 0.8".encode(), ("255.255.255.255", 54545)) # for each plugin will automatically add /pluginame before oscpath to send like /aurora/scim 1, if oscpath = "/scim 1" diff --git a/libs3/tracer3.py b/libs3/tracer3.py index 02ffa1e..85400c2 100644 --- a/libs3/tracer3.py +++ b/libs3/tracer3.py @@ -23,44 +23,44 @@ This tracer include an enhanced version (support for several lasers) of the ethe - Drawing things : -/pl/Scene/lasernumber [(x,y,color),(x1,y1,color),...] The live list of drawn pygame points. Tracer continously ask redis for key /clientkey+lasernumber +/pl/Scene/lasernumber [(x,y,color),(x1,y1,color),...] The live list of drawn pygame points. Tracer continously ask redis for key /clientkey+lasernumber /resampler/lasernumber [(1.0,8), (0.25,3),(0.75,3),(1.0,10)] : a string for resampling rules. the first tuple (1.0,8) is for short line < 4000 in etherdream space (0.25,3),(0.75,3),(1.0,10) for long line > 4000 i.e (0.25,3) means go at 25% position on the line, send 3 times this position to etherdream -/clientkey "/pl/SceneNumber/" What Scene to retrieve from redis +/clientkey "/pl/SceneNumber/" What Scene to retrieve from redis /EDH/lasernumber - Tracer control : -/order 0-8 Set redis key with new value then issue the order number +/order 0-8 Set redis key with new value then issue the order number 0 : Draw Normal point list - 1 : Get the new EDH = reread redis key /EDH/lasernumber + 1 : Get the new EDH = reread redis key /EDH/lasernumber 2 : Draw BLACK point list 3 : Draw GRID point list 4 : Resampler Change (longs and shorts lsteps) - 5 : Client Key Change = reread redis key /clientkey - 6 : Max Intensity Change = reread redis key /intensity - 7 : kpps change = reread redis key /kpps - 8 : color balance change = reread redis keys /red /green /blue + 5 : Client Key Change = reread redis key /clientkey + 6 : Max Intensity Change = reread redis key /intensity + 7 : kpps change = reread redis key /kpps + 8 : color balance change = reread redis keys /red /green /blue - Managing Etherdream DACs : Discrete drawing values -/kpps 0- DAC output speed to laser, then order 7. Depends of actual angle -/intensity 0-255 Laser output power, then order 6 (for alignement,...) -/red 0-100 % of full red, then order 8 -/green 0-100 % of full green, then order 8 -/blue 0-100 % of full blue, then order 8 +/kpps 0- DAC output speed to laser, then order 7. Depends of actual angle +/intensity 0-255 Laser output power, then order 6 (for alignement,...) +/red 0-100 % of full red, then order 8 +/green 0-100 % of full green, then order 8 +/blue 0-100 % of full blue, then order 8 DAC status report -/lstt/lasernumber etherdream last_status.playback_state (0: idle 1: prepare 2: playing) -/cap/lasernumber number of empty points sent to fill etherdream buffer (up to 1799) -/lack/lasernumber "a": ACK "F": Full "I": invalid. 64 or 35 for no connection. +/lstt/lasernumber etherdream last_status.playback_state (0: idle 1: prepare 2: playing) +/cap/lasernumber number of empty points sent to fill etherdream buffer (up to 1799) +/lack/lasernumber "a": ACK "F": Full "I": invalid. 64 or 35 for no connection. Geometric corrections @@ -223,7 +223,7 @@ class DAC(object): while True: - #pdb.set_trace() + #pdb.set_trace() for indexpoint,currentpoint in enumerate(self.pl): #print indexpoint, currentpoint xyc = [currentpoint[0],currentpoint[1],currentpoint[2]] @@ -335,7 +335,7 @@ class DAC(object): # ipconn state is -1 at startup (see gstt) and modified here r.set('/lack/'+str(self.mylaser), self.connstatus) - gstt.lstt_ipconn[self.mylaser] = self.connstatus + gstt.lstt_ipconn[self.mylaser] = self.connstatus self.buf = b'' # Upper case PL is the Point List number @@ -349,7 +349,7 @@ class DAC(object): if r.get('/EDH/'+str(self.mylaser)) == None: #print("Laser",self.mylaser,"NO EDH !! Computing one...") homographyp.newEDH(self.mylaser) - else: + else: gstt.EDH[self.mylaser] = np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser)).decode('ascii'))) #print("Laser",self.mylaser,"found its EDH in redis") @@ -447,9 +447,17 @@ class DAC(object): if order == 0: # USER point list - self.pl = ast.literal_eval(r.get(self.clientkey+str(self.mylaser)).decode('ascii')) + + #self.pl = ast.literal_eval(r.get(self.clientkey+str(self.mylaser)).decode('ascii')) #print("Tracer : laser", self.mylaser, " order 0 : pl : ",len(self.pl)) + try: + self.pl = ast.literal_eval(r.get(self.clientkey+str(self.mylaser)).decode('ascii')) + + except SyntaxError: + print("BAD POINTLIST on Tracer : laser", self.mylaser, " order 0 : pl :",self.pl) + self.pl = grid_points + else: # Get the new EDH @@ -524,13 +532,13 @@ class DAC(object): time.sleep(0.001) cap += 150 -# print("Writing %d points" % (cap, )) + #print("Writing %d points" % (cap, )) #t0 = time.time() #if self.mylaser == 2: - # print(points) + # print(points) self.write(points) #t1 = time.time() -# print("Took %f" % (t1 - t0, ) + #print("Took %f" % (t1 - t0, ) if not started: print("Tracer", self.mylaser, "starting with", gstt.kpps[self.mylaser],"kpps") @@ -571,7 +579,7 @@ def find_dac(): order = r.get('/order') neworder = order & ~(1<< self.mylaser*2) neworder = neworder & ~(1<< 1+ self.mylaser*2) - r.set('/order', str(neworder)) + r.set('/order', str(neworder)) else: # Laser bit 0 = 1 diff --git a/main.py b/main.py index acaefe9..08dbd47 100755 --- a/main.py +++ b/main.py @@ -36,35 +36,32 @@ print("") import redis import os ljpath = r'%s' % os.getcwd().replace('\\','/') - - import sys #sys.path.append('libs3/') - from libs3 import gstt, settings -gstt.ljpath= ljpath +gstt.ljpath = ljpath log.info("Reading " + gstt.ConfigName + " setup file...") settings.Read() # Arguments may alter .conf file so import settings first then cli from libs3 import cli - settings.Write() from multiprocessing import Process, set_start_method import random, ast - from libs3 import plugins + + +#from libs3 import lasytracer as tracer from libs3 import tracer3 as tracer + + from libs3 import homographyp, commands, font1 - #import subprocess - import os #import midi - from libs3 import OSC3 from websocket_server import WebsocketServer #import socket @@ -264,14 +261,16 @@ def osc_thread(): #print("Sending simu frame from",'/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser)) #print(r.get('/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser))) - sendWSall("/simul" +" "+ str(r.get('/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser)).decode('ascii'))) + sendWSall("/simul" +" "+ str(r.get('/po/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser)).decode('ascii'))) + if random.randint(0,100)>95: + plugins.sendbroadcast() except Exception as e: import sys, traceback - print('\n---------------------') - print('Exception: %s' % e) - print('- - - - - - - - - - -') + print('\n--------------------------') + print('OSC Thread Exception: %s' % e) + print('- - - - - - - - - - - - - - ') traceback.print_tb(sys.exc_info()[2]) print("\n") diff --git a/plugins/aurora/aurora.py b/plugins/aurora/aurora.py index 303baa0..5bdbe60 100644 --- a/plugins/aurora/aurora.py +++ b/plugins/aurora/aurora.py @@ -181,7 +181,7 @@ log.infog("Aurora v0.1b") OSCinPort = 8090 ljscene = 0 -StartFXs = ["anim.Maxwell","anim.Starfield","anim.Starfield", "anim.Word"] +StartFXs = ["anim.Starfield","anim.Starfield","anim.Starfield", "anim.Word"] # Useful variables init. white = lj.rgb2int(255,255,255) @@ -1125,9 +1125,9 @@ def AllFX(): dots = eval(LAY['FX']+"(LAY)") if LAY['FX'] != "Zero" or lent(dots) != 0: #print(dots, LAY['color']) - for cc in range(15): - ccmidi[cc] = int(lj.fromKey("/midi/cc/1/"+str(cc))) - #print(ccmidi) + #for cc in range(15): + # ccmidi[cc] = int(lj.fromKey("/midi/cc/1/"+str(cc))) + # #print(ccmidi) lj.rPolyLineOneColor(dots, c = LAY['color'], layer = l, closed = LAY['closed'], xpos = LAY['Xcoord'] + LAY['stepvals'][LAY['step']] - (LAY['lineSize']/2), ypos = Layer[l]['Ycoord'], resize = LAY['scale'] * audioR, rotx = LAY['Xrotdirec'], roty = LAY['Yrotdirec'], rotz = LAY['Zrotdirec']) #lj.rPolyLineOneColor(dots, c = LAY['color'], layer = l, closed = LAY['closed'], xpos = LAY['Xcoord'] - (ccmidi[0]-64)*4 - (LAY['lineSize']/2), ypos = Layer[l]['Ycoord'] + (ccmidi[1]-64)*4, resize = LAY['scale'] * audioR * (ccmidi[2])/50, rotx = LAY['Xrotdirec']+(ccmidi[3]*3), roty = LAY['Yrotdirec']+(ccmidi[3]*3), rotz = LAY['Zrotdirec']+(ccmidi[3]*3)) else: diff --git a/www/align.html b/www/align.html deleted file mode 100644 index 6a6d427..0000000 --- a/www/align.html +++ /dev/null @@ -1,937 +0,0 @@ - - - - Align Rack - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
- -
- - - - - -
-
-
-

- /TL Align -   -

- - -
-
-
- Align Rack -
-
- /team/laser -
-
-
- Stt - - - - -
-
- Ack - - - - -
- -
-
- - -
-
- -
-

Scene

-
- - -
- -
- -
-
- -
- -
- -
-
- -
- -
- - -
-
- -
- -
- - -
-
-
-
-
-
-
- - - - - - -
- -
- -
- - -
- -
-
- -
-
- - - -
- -
- - - - - - - - -
- - -
- - -
- - - -
- -
-
-
- -
-
kPPS
-
Buffer
-
-
-
- - -
-
-
-
Angle
-
Offset X
-
Offset Y
-
-
-
-
-
-
- - -
-
-
-
Intens
-
Scale X
-
Scale X
-
-
-
-
-
-
- - -
-
-
-
Red
-
Green
-
Blue
-
-
- - - -
- -
-
- -
-
-
-
- - - - - - - - - - - -
-
- - -
- - - - -
- - -
-
-
-
-
kPPS
-
Buffer
-
-
-
- - -
-
-
-
Angle
-
Offset X
-
Offset Y
-
-
-
-
-
-
- - -
-
-
-
Intens
-
Scale X
-
Scale X
-
-
-
-
-
-
- - -
-
-
-
Red
-
Green
-
Blue
-
-
- - - -
- -
-
- -
-
-
-
- - - - - - - - - - - -
-
- - -
- - -
- - -
-
-
-
-
kPPS
-
Buffer
-
-
-
- - -
-
-
-
Angle
-
Offset X
-
Offset Y
-
-
-
-
-
-
- - -
-
-
-
Intens
-
Scale X
-
Scale X
-
-
-
-
-
-
- - -
-
-
-
Red
-
Green
-
Blue
-
-
- - - -
- -
-
- -
-
-
-
- - - - - - - - - - - -
-
- - -
- - - -
- - -
-
-
-
-
kPPS
-
Buffer
-
-
-
- - -
-
-
-
Angle
-
Offset X
-
Offset Y
-
-
-
-
-
-
- - -
-
-
-
Intens
-
Scale X
-
Scale X
-
-
-
-
-
-
- - -
-
-
-
Red
-
Green
-
Blue
-
-
- - -
- -
- -
- - - - - - - - - - - - - - - - diff --git a/www/auralls.html b/www/auralls.html index 866ed23..daf2a20 100644 --- a/www/auralls.html +++ b/www/auralls.html @@ -59,10 +59,10 @@
- +
@@ -93,8 +93,8 @@
-
diff --git a/www/index.html b/www/index.html index ddde89a..e843eae 100644 --- a/www/index.html +++ b/www/index.html @@ -101,15 +101,15 @@ - +

- Align + Settings  

@@ -117,7 +117,7 @@
- Align + Settings
  diff --git a/www/simu.html b/www/simu.html index e3b1320..68885af 100644 --- a/www/simu.html +++ b/www/simu.html @@ -56,10 +56,10 @@
- +
@@ -90,8 +90,8 @@
-
diff --git a/www/trckr/trckr.html b/www/trckr/trckr.html deleted file mode 100644 index 1cc933c..0000000 --- a/www/trckr/trckr.html +++ /dev/null @@ -1,680 +0,0 @@ - - - - LASERCam 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- -
- - - - -
-
-
-

- /TL RGY 1 -   -

- - -
-
-
- LASERcam 1 : Allow to use your webcam + start -
-
- /team/laser -
-
-
-
-
-
-
-
-
-
-
- - - -
-
-
-

- Colors - -

-
- -
-
-
-
-
-
-
-
-
- -
-
- - - -
- - - -
- - - -
-
-
-

To try it out: -

    -
  1. Allow the page to use your webcamera
  2. -
  3. Make sure that your face is clearly visible in the video, and click start
  4. -
  5. See the model fitted to your face
  6. -
      -

      -
-
-
-
-
-
- - - - - - - - - - - - - - - - diff --git a/www/trckr/trckrcam1.html b/www/trckr/trckrcam1.html index f94f0b5..4e2056e 100644 --- a/www/trckr/trckrcam1.html +++ b/www/trckr/trckrcam1.html @@ -93,10 +93,10 @@
- +
@@ -127,8 +127,8 @@
-