# coding=UTF-8 """ LJ OSC and Websockets laser commands v0.7.0 LICENCE : CC by Sam Neurohack, Loloster from /team/laser Commands reference. Use commands from websocket (webUI) or OSC, do not set values in redis directly except for /pl. DAChecks() UpdateAllwww() /forwardui "htmlid args" /scale/X/lasernumber value (0-200) /scale/Y/lasernumber value (0-200) /client or note on < 8 : change client displayed for Current Laser 23 < /noteon < 32 : PL number displayed on webUI simulator /grid/lasernumber value (0 or 1) : switch given laser with grid display on or off /black/lasernumber value (0 or 1) : set given laser to black on or off /emergency value (0 or 1) : set all lasers to black on or off /ip/lasernumber value : change given laser IP i.e '192.168.1.1' /kpps/lasernumber value Live change of kpps is not implemented in newdac.py. Change will effect next startup. /angle/lasernumber value : angle correction for given laser by value (0-360) /intens/lasernumber value : increase/decrease intensity for given laser by value /resampler/lasernumber lsteps : change resampling strategy (glitch art) for given laser lsteps is a string like "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]" /mouse/lasernumber value (0 or 1) /swap/X/lasernumber value (0 or 1) /swap/Y/lasernumber value (0 or 1) /loffset/X/lasernumber value : change X offset of given laser to value (-32000/32000) /loffset/Y/lasernumber value : change Y offset of given laser to value (-32000/32000) /order value : instruct tracer what to do. 0 : display user pointlist with current client key. See below for client key. 1 : pull in redis a new correction matrix (EDH) 2 : display black 3 : display grid 4 : resampler 5 : pull in redis a new client key 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 9 : shutdown /planet will be forwarded to planetarium client. /nozoid will be forwarded to nozoid client. /scene/scenenumber/start 0 or 1 /regen : regen webui index html page. /plugins/start pluginame /plugins/stop pluginame /pl/clientnumber/lasernumber value : value is the pointlist to draw as string type. For string format see code in clients directory. Example : client 0 send 2 point lists one for laser 0 and one for laser 1 by sending in redis : /pl/0/0 and /pl/0/1 The "client key" when client 0 is selected to be displayed by lasers is "/pl/0/". Each tracer pull its pointlist by using the current client key "/pl/0/" and add its laser number at startup : /pl0/0 ant /pl/0/1 "Client" is a concept. Imagine in a demoparty there is 4 lasers. John and Paul want to draw on all lasers. Let's give John client 0, he will send points to /pl/0/0, /pl/0/1, /pl/0/2 and /pl/0/3. Paul is client 1, so he will use /pl/1/0, /pl/1/1, /pl/1/2 and /pl/1/3. Both can send their pointlists to redis server. When John get the lasers switch to client 0, when it's Paul turn switch to client 1. But say Bob and Lisa needs only 2 lasers each. Give them client 2. Bob could use /pl/2/0 and /pl/2/1 and Lisa could use /pl/2/2 and /pl/2/3. """ import types, time, socket, math from libs3 import gstt import redis from libs3 import OSC3 from libs3 import settings, plugins, homographyp,log r = redis.StrictRedis(host=gstt.RediServerIP , port=6379, db=0) #r = redis.StrictRedis(host=gstt.RediServerIP , port=6379, db=0, password='-+F816Y+-') GenericCommands = ["start", "align", "ljclient", "scene", "addest", "deldest", "dest", "clientnumber", "vcvrack", "fft", "mitraille", "faceosc", "midigen", "viewgen", "audiogen", "noteon", "cc", "ljpong", "ljwars", "mouse", "emergency", "simu", "status", "run", "nozoid", "planet", "live", "words", "ai", "bank0", "pose", "lj", "cycl", "glyph", "pong", "maxw", "custom1", "square", "regen", "trckr", "aurora", "line1", "ForwardUI", "settings", "debug", "pl", "plugins"] def UserOn(laser): print("User for laser ", laser) plugins.sendWSall("/status User on laser " + str(laser)) r.set('/order/'+str(laser), 0) def NewEDH(laser): print("New EDH requested for laser ", laser) plugins.sendWSall("/status New EDH on laser " + str(laser)) settings.Write() print("Settings saving swapX ", gstt.swapX[laser]) print("Settings saving swapY ", gstt.swapY[laser]) homographyp.newEDH(laser) def BlackOn(laser): print("Black for laser ", laser) plugins.sendWSall("/status Black on laser " + str(laser)) sendOSCUI('/black/'+str(laser),1) r.set('/order/'+str(laser), 2) def GridOn(laser): print("Grid for laser ", laser) plugins.sendWSall("/status Grid on laser " + str(laser)) sendOSCUI('/grid/'+str(laser),1) r.set('/order/'+str(laser), 3) def Resampler(laser,args): # lsteps is a string like : "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]" print("Resampler change for laser", laser, "[("+str(args[0])+","+str(args[1])+"),("+str(args[2])+","+str(args[3])+"),("+str(args[4])+","+str(args[5])+"),("+str(args[6])+","+str(args[7])+")]") #r.set('/resampler/' + str(laser), lsteps) r.set('/resampler/' + str(laser), "[("+str(args[0])+","+str(args[1])+"),("+str(args[2])+","+str(args[3])+"),("+str(args[4])+","+str(args[5])+"),("+str(args[6])+","+str(args[7])+")]") r.set('/order/'+str(laser), 4) def LasClientChange(clientnumber): if r.get("/pl/"+str(clientnumber)+"/0") != None: print("Switching to laser client", clientnumber) gstt.SceneNumber = clientnumber plugins.sendWSall("/status Client " + str(gstt.SceneNumber) + " laser " + str(gstt.Laser)) r.set('/clientkey', "/pl/"+str(clientnumber)+"/") print("clientkey set to", "/pl/"+str(clientnumber)+"/") for laserid in range(0,gstt.LaserNumber): r.set('/order/'+str(laserid), 5) else: print("ERROR : Maximum number of scenes is set to ", gstt.MaxScenes) def SceneChange(newscene): print("Switching to scene", newscene) gstt.SceneNumber = int(newscene) plugins.sendWSall("/status Scene " + newscene) r.set('/clientkey', "/pl/"+ newscene +"/") print("clientkey set to", "/pl/" + newscene + "/") for laserid in range(0,gstt.LaserNumber): r.set('/order/'+str(laserid), 5) plugins.sendWSall("/scene/" + str(laserid) + "/start 0") plugins.sendWSall("/scene/" + newscene + "/start 1") # Change current laser and send "/scim lasernumber to each plugin" def NoteOn(note): print() print("NoteOn", note) # Change laser client if note < 8: LasClientChange(note) # Change PL displayed on webui if note > 23 and note < 32: if note - 24 > gstt.LaserNumber -1: print("Only",gstt.LaserNumber,"lasers asked, you dum ass !") plugins.sendWSall("/redstatus No Laser"+str(note-24)) plugins.sendWSall("/laser "+str(gstt.LaserNumber-1)) plugins.SendAll("/laser "+str(gstt.LaserNumber-1)) else: gstt.Laser = note -24 print("Current Laser switched to", gstt.Laser) plugins.sendWSall("/status Laser " + str(gstt.Laser)) plugins.SendAll("/scim "+str(gstt.Laser)) print("sending cropper data..") plugins.sendWSall("/loffset/X/" + str(gstt.Laser) +" " +str(gstt.centerX[gstt.Laser])) plugins.sendWSall("/loffset/Y/" + str(gstt.Laser) +" " +str(gstt.centerY[gstt.Laser])) plugins.sendWSall("/scale/X/" + str(gstt.Laser) +" " + str(gstt.zoomX[gstt.Laser])) plugins.sendWSall("/scale/Y/" + str(gstt.Laser) +" " +str(gstt.zoomY[gstt.Laser])) def Scim(path, tags, args, source): laser = int(args[0]) print("OSC /scim", laser) # Change PL displayed on webui if laser > 23 and laser < 32: if laser - 24 > gstt.LaserNumber -1: print("Only",gstt.LaserNumber,"lasers asked, you dum ass !") plugins.sendWSall("/redstatus No Laser"+str(note-24)) plugins.sendWSall("/laser "+str(gstt.LaserNumber-1)) else: gstt.Laser = laser -24 plugins.sendWSall("/status Laser " + str(gstt.Laser)) print("Current Laser switched to", gstt.Laser) def Line1(path, tags, args, source): line1 = args[0] print("OSC /line1", line1) plugins.sendWSall("/line1 " +"Fx "+line1) # forward def ForwardUI(path, tags, args, source): line = args[0] print("OSC /forwardui to WebUI :", line) print('from path', path, 'args', args) plugins.sendWSall(line) sendOSCUI('/status',line) def CC(number, value): print("CC", note, value) def Mouse(x1,y1,x2,y2): print("Mouse", x1,y1,x2,y2) def handler(oscpath, args): #print("OSC handler in commands.py got /"+ str(oscpath)+ " with args :",args) if gstt.debug > 0: print("OSC handler in commands.py got /"+ str(oscpath)+ " with args :",args) # 2 incoming cases : generic or specific for a given lasernumber : # # Generic : Commands without a laser number # if oscpath[1] in GenericCommands: if gstt.debug > 0: print("GenericCommand :", oscpath[1], "with args", args) if oscpath[1] == "ljclient": #LasClientChange(int(args[0])) SceneChange(args[0]) if oscpath[1] == "pl": print("new pl for", "/"+oscpath[1]+"/"+oscpath[2]+"/"+oscpath[3], ":", args[0]) # was r.set(oscpath, args[0]) r.set("/"+oscpath[1]+"/"+oscpath[2]+"/"+oscpath[3], args[0]) if oscpath[0] == "pl" : r.set(oscpath, args[0]) #/scene/scenenumber/start 0 or 1 if oscpath[1] == "scene": print(oscpath[1], oscpath[2], args[0]) if args[0] == '1' and r.get("/pl/" + oscpath[2] + "/0") != None: SceneChange(oscpath[2]) else: print("ERROR : Maximum number of scenes is set to ", gstt.MaxScenes) elif oscpath[1] == "noteon": NoteOn(int(args[0])) elif oscpath[1] == "CC": CC(int(args[0]), int(args[1])) elif oscpath[1] == "pong": #print "LJ commands got pong from", args if gstt.debug >0: print(("/" + args[0] + "/start 1")) print(("/status got pong from "+ args[0] +".")) plugins.sendWSall("/" + args[0] + "/start 1") #plugins.sendWSall("/status got pong from "+ args[0] +".") elif oscpath[1] == "vcvrack": pass ''' #print "LJ commands got /vcvrack from", args if oscpath[2] == "1" : r.set('/vcvrack/1', args[0]) #print('/vcvrack/1', args[0]) if oscpath[2] == "2" : r.set('/vcvrack/2', args[0]) #print('/vcvrack/2', args[0]) ''' elif oscpath[1] == "mouse": Mouse(int(args[0]),int(args[1]),int(args[2]),int(args[3])) # /emergency value (0 or 1) elif oscpath[1] == "emergency": if args[0] == "1": for laser in range(gstt.lasernumber): print("Black requested for laser ", laser) BlackOn(laser) print("EMERGENCY MODE") plugins.sendWSall("/status EMERGENCY MODE") else: for laser in range(gstt.lasernumber): print("Back to normal for laser ", laser) UserOn(laser) # Plugins commands : elif oscpath[1] == "plugins": # /plugins/start pluginame if oscpath[2] == "start": print() print("Starting plugin :",args[0]) print() plugins.Start(args[0]) # /plugins/stop pluginame if oscpath[2] == "stop": print() print("Stopping plugin :",args[0]) print() plugins.Kill(args[0]) # /plugins/restart pluginame if oscpath[2] == "restart": print() print("Restarting plugin :",args[0]) print() plugins.Restart(args[0]) # Settings commands : elif oscpath[1] == "settings": if oscpath[2] == "lasers": print() print("new laser number",args[0]) print() gstt.LaserNumber = int(args[0]) settings.Write() if oscpath[2] == "regen": print() print("Regen www pages with",gstt.wwwIP,"...") UpdateAllwww() if oscpath[2] == "IP": print() print("new server IP for www regen",args[0]) gstt.wwwIP = args[0] settings.Write() if oscpath[2] == "debug": print() print("Debug level",args[0]) print() gstt.debug = int(args[0]) plugins.SendAll("/debug "+str(gstt.debug)) settings.Write() if oscpath[2] == "rescan": 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() print("Restarting", args[0], "...") if args[0] == "lj": LJautokill() import os os.execv(sys.executable, ['python3'] + sys.argv) else: plugins.Restart(args[0]) print() ''' regen index.html (python build.py) elif oscpath[1] == "regen": print("new ui IP", args[0]) #subprocess.Popen(["python3", plugins.ljpath + "/webui/build.py"]) ''' # # Commands with a laser number # else: pathlength = len(oscpath) if gstt.debug > 0: print("Non Generic Command :", oscpath[1], "with args", args) #print "oscpath", oscpath #print "pathlength", pathlength #print "args", args if pathlength == 3: laser = int(oscpath[2]) else: laser = int(oscpath[3]) #print "args[0] :",args[0]," ", type(args[0]) # /black/lasernumber value (0 or 1) if oscpath[1] == "black": if args[0] == "1": print("Black requested for laser ", laser) BlackOn(laser) else: print("User mode for laser ", laser) UserOn(laser) # /grid/lasernumber value (0 or 1) if oscpath[1] == "grid": if args[0] == "1": print("Grid requested for laser ", laser) GridOn(laser) else: print("No grid for laser ", laser) UserOn(laser) # /ip/lasernumber value if oscpath[1] == "ip": print("New IP for laser ", laser) gstt.lasersIPS[laser]= args[0] settings.Write() # /kpps/lasernumber value # Live change of kpps is not implemented in newdac.py. Change will effect next startup. if oscpath[1] == "kpps": print("New kpps for laser ", laser, " next startup", int(args[0])) gstt.kpps[laser]= int(args[0]) settings.Write() sendOSCUI('/kpps/' + str(laser), args[0]) r.set('/kpps/' + str(laser), str(args[0])) r.set('/order/'+str(laser), 7) # /angle/lasernumber value if oscpath[1] == "angle": print("New Angle modification for laser ", oscpath[2], ":", float(args[0])) gstt.finANGLE[laser] = math.radians(float(args[0])) sendOSCUI('/angle/' + str(laser), gstt.finANGLE[laser]) NewEDH(laser) print("New angle", gstt.finANGLE[laser]) # /intens/lasernumber value if oscpath[1] == "intens": print("LJ2 : New intensity requested for laser ", laser, ":", int(args[0])) plugins.sendWSall("/status Intensity " + str(args[0])) sendOSCUI('/intens/' + str(laser), args[0]) r.set('/intensity/' + str(laser), str(args[0])) r.set('/order/'+str(laser), 6) # /resampler/lasernumber lsteps # lsteps is a string like "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]" if oscpath[1] == "resampler": #print"resampler with args", args Resampler(laser,args) # /mouse/lasernumber value (0 or 1) if oscpath[1] == "mouse": if args[0] == "1": print("Mouse requested for laser ", oscpath[2]) gstt.Laser = oscpath[2] else: print("No mouse for laser ", oscpath[2]) # /swap/X/lasernumber value (0 or 1) if oscpath[1] == "swap" and oscpath[2] == "X": print("swapX was", gstt.swapX[laser]) if args[0] == "0": print("swap X -1 for laser ", laser) gstt.swapX[laser]= -1 NewEDH(laser) else: print("swap X 1 for laser ", laser) gstt.swapX[laser]= 1 NewEDH(laser) # /swap/Y/lasernumber value (0 or 1) if oscpath[1] == "swap" and oscpath[2] == "Y": print("swapY was", gstt.swapX[laser]) if args[0] == "0": print("swap Y -1 for laser ", laser) gstt.swapY[laser]= -1 NewEDH(laser) else: print("swap Y 1 for laser ", laser) gstt.swapY[laser]= 1 NewEDH(laser) # /loffset/X/lasernumber value if oscpath[1] == "loffset" and oscpath[2] == "X": if -32000 < float(args[0]) < 32000: print("offset/X laser", laser, "modified to", args[0]) gstt.centerX[laser] = float(args[0]) NewEDH(laser) # /loffset/Y/lasernumber value if oscpath[1] == "loffset" and oscpath[2] == "Y": if -32000 < float(args[0]) < 32000: print("offset/Y laser", laser, "modified to", args[0]) gstt.centerY[laser] = float(args[0]) NewEDH(laser) # /scale/X/lasernumber value if oscpath[1] == "scale" and oscpath[2] == "X": gstt.zoomX[laser] = float(args[0]) print("scale/X laser", laser , "modified to", gstt.zoomX[laser]) NewEDH(laser) # /scale/Y/lasernumber value if oscpath[1] == "scale" and oscpath[2] == "Y": gstt.zoomY[laser] = float(args[0]) print("scale/Y laser", laser, "modified to", gstt.zoomY[laser]) NewEDH(laser) # OSC UI feedback def sendOSCUI(oscaddress,oscargs=''): if gstt.TouchOSCUI == True: oscmsg = OSC3.OSCMessage() oscmsg.setAddress(oscaddress) oscmsg.append(oscargs) oscui = OSC3.OSCClient() oscui.connect((gstt.TouchOSCIP, 8001)) #print("main sending OSC UI message :", oscmsg, "to", gstt.TouchOSCIP, ":8001") if gstt.debug >0: print("main sending OSC UI message :", oscmsg, "to", gstt.TouchOSCIP, ":8001") try: oscui.sendto(oscmsg, (gstt.TouchOSCIP, 8001)) oscmsg.clearData() except: print ('Connection to OSC UI refused : died ?') pass def UpdateOSCUI(laserid): sendOSCUI("/ip/" + str(laserid), str(gstt.lasersIPS[laserid])) sendOSCUI("/kpps/" + str(laserid), str(gstt.kpps[laserid])) sendOSCUI("/intens/" + str(laserid), str(gstt.intens[laserid])) sendOSCUI("/red/" + str(laserid), str(gstt.red[laserid])) sendOSCUI("/green/" + str(laserid), str(gstt.green[laserid])) sendOSCUI("/blue/" + str(laserid), str(gstt.blue[laserid])) sendOSCUI("/loffset/X/" + str(laserid), str(gstt.centerX[laserid])) sendOSCUI("/loffset/Y/" + str(laserid), str(gstt.centerY[laserid])) sendOSCUI("/scale/X/" + str(laserid), str(gstt.zoomX[laserid])) sendOSCUI("/scale/Y/" + str(laserid), str(gstt.zoomY[laserid])) sendOSCUI("/angle/" + str(laserid), str(gstt.finANGLE[laserid])) sendOSCUI("/type/" + str(laserid), str(gstt.lasertype[laserid])) if gstt.swapX[laserid] == 1: sendOSCUI("/swap/X/" + str(laserid), 1) else: sendOSCUI("/swap/X/" + str(laserid), 0) if gstt.swapY[laserid] == 1: sendOSCUI("/swap/Y/" + str(laserid), 1) else: sendOSCUI("/swap/Y/" + str(laserid), 0) # # Different useful codes for some commands # def Updatepage(file_name): print("updating", file_name) f=open(file_name,"r+") a=f.readlines() #print a for line in a: # python3 IPline = ("var LJ = " in line) if IPline == True: p=a.index(line) #so now we have the position of the line which to be modified a[p]=" var LJ = 'ws://"+gstt.wwwIP+":9001/'\n" #print(p, line, a[p]) f.seek(0) f.truncate() #ersing all data from the file f.close() #so now we have an empty file and we will write the modified content now in the file o=open(file_name,"w") for i in a: o.write(i) o.close() #now the modification is done in the file # Not used anymare see configure.py def UpdateAllwww(): pass print("Updating www files to use", gstt.wwwIP) Updatepage(gstt.ljpath+"/www/LJ.js") Updatepage(gstt.ljpath+"/www/trckr/trckrcam1.html") Updatepage(gstt.ljpath+"/www/simu.html") Updatepage(gstt.ljpath+"/www/settings.html") Updatepage(gstt.ljpath+"/www/auralls.html") Updatepage(gstt.ljpath+"/www/index.html") def isOpen(ip): dacksock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) dacksock.settimeout(1) istate = False try: dacksock.connect((ip, 7765)) #s.shutdown(2) istate = True dacksock.shutdown(socket.SHUT_RDWR) except: time.sleep(1) finally: dacksock.close() return istate ''' def isconnected(IP): ipup = False for i in range(retry): if isOpen(IP, 7765): ipup = True break else: time.sleep(delay) return ipup ''' def LJautokill(): log.warn("LJ stopping...") print(gstt.LaserNumber,"Tracers launched") for laser in range(gstt.LaserNumber): if gstt.debug > 0: print('Shutdown order for tracer '+str(laser), 9) r.set('/order/'+str(laser), 9) lasernumber= gstt.LaserNumber -1 log.warn("Ending tracer0...") worker0.join() if lasernumber >0: log.warn("Ending tracer1...") worker1.join() if lasernumber >1: log.warn("Ending tracer2...") worker2.join() if lasernumber >2: log.warn("Ending tracer3...") worker3.join() log.warn("Laser feedbacks resetting...") for laserid in range(0,lasernumber+1): r.set('/lack/'+str(laserid),64) r.set('/lstt/'+str(laserid),64) r.set('/cap/'+str(laserid),0) log.infog("LJ stopped.") # autodetect DACs in LJ.conf. def DAChecks(): gstt.dacs = [-1, -1, -1, -1] gstt.dacnumber = 0 print("Searching DACs...") for dac in range(gstt.maxdacs): if isOpen(gstt.lasersIPS[dac]): print("DAC", dac, "at", gstt.lasersIPS[dac], ": UP") gstt.dacs[gstt.dacnumber] = dac gstt.dacnumber +=1 else: print("DAC", dac, "at", gstt.lasersIPS[dac], ": DOWN") ''' # At least one. if gstt.dacnumber == 0: gstt.dacs = [0, -1, -1, -1] gstt.dacnumber = 1 gstt.LaserNumber = gstt.dacnumber ''' ''' For reference values of EDH modifier if assign to keyboard keys (was alignp) gstt.centerY[gstt.Laser] -= 20 gstt.centerY[gstt.Laser] += 20 gstt.zoomX[gstt.Laser]-= 0.1 gstt.zoomX[gstt.Laser] += 0.1 gstt.zoomY[gstt.Laser] -= 0.1 gstt.zoomY[gstt.Laser] += 0.1 gstt.sizeX[gstt.Laser] -= 50 gstt.sizeX[gstt.Laser] += 50 gstt.sizeY[gstt.Laser] -= 50 gstt.sizeY[gstt.Laser] += 50 gstt.finANGLE[gstt.Laser] -= 0.001 gstt.finANGLE[gstt.Laser] += 0.001 Code for bit analysis 2 bits / laser to encode order. # Grid PL is Laser bit 0 = 1 and bit 1 = 1 #order = r.get('/order') #neworder = order | (1<