#! /usr/bin/python3 # -*- coding: utf-8 -*- """ This file is part of LibreLight. LibreLight is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2 of the License. LibreLight is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with LibreLight. If not, see . (c) 2012 micha@uxsrv.de """ import sys rnd_id = "" rnd_id += " Beta 22.02 " import subprocess rnd_id += subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).decode('ascii').strip() if "__file__" in dir(): sys.stdout.write("\x1b]2;"+str(__file__)+" "+rnd_id+"\x07") # terminal title else: sys.stdout.write("\x1b]2;"+str("__file__")+" "+rnd_if+"\x07") # terminal title __run_main = 0 if __name__ == "__main__": __run_main = 1 else: import __main__ print(dir()) if "unittest" not in dir(__main__): __run_main = 1 import time import socket import struct import sys import random import math from collections import OrderedDict import lib.chat as chat import lib.ArtNetNode as ANN import _thread as thread #thread.start_new_thread import lib.motion as motion #idmx = [0]*512 # incremental dmx dmx = [0]*512 # absolute dmx data gcolor = 1 def cprint(*text,color="blue",space=" ",end="\n"): #return 0 #disable print dbg if not gcolor: print(text) return 0 if color == "green": txt = '\033[92m' elif color == "red": txt = '\033[0;31m\033[1m' elif color == "yellow": txt = '\033[93m\033[1m' elif color == "cyan": txt = '\033[96m' else: txt = '\033[94m' for t in text: txt += str(t ) +" " #HEADER = '\033[95m' #OKBLUE = '\033[94m' #OKCYAN = '\033[96m' #OKGREEN = '\033[92m' #WARNING = '\033[93m' #FAIL = '\033[91m' #ENDC = '\033[0m' #BOLD = '\033[1m' #UNDERLINE = '\033[4m' txt += '\033[0m' print(txt,end=end) #return txt def artnet_loop(): #artnet = ANN.ArtNetNode(to="127.0.0.1",port=6555,univ=12) #artnet = ANN.ArtNetNode(to="127.0.0.1",port=6555,univ=0) artnet = ANN.ArtNetNode(to="10.10.10.255",univ=0) #artnet = ANN.ArtNetNode(to="2.0.0.255",univ=0) #artnet = ANN.ArtNetNode(to="10.10.10.255",univ=1) #dmx[205] = 255 #205 BLUE artnet.dmx= dmx #[0]*512 artnet.send() while 1: #artnet._test_frame() artnet.next() time.sleep(0.01) class Main(): def __init__(self): #artnet = ANN.ArtNetNode(to="127.0.0.1",port=6555,univ=12) #artnet = ANN.ArtNetNode(to="127.0.0.1",port=6555,univ=0) #artnet = ANN.ArtNetNode(to="2.0.0.255",univ=0) #artnet = ANN.ArtNetNode(to="10.10.10.255",univ=1) self.artnet = {} #self.artnet["0"] = ANN.ArtNetNode(to="10.10.10.255",univ=0) #self.artnet["0"].dmx[512-1] = 10 #self.artnet["1"] = ANN.ArtNetNode(to="10.10.10.255",univ=1) #self.artnet["1"].dmx[512-1] = 11 self.fx = {} # key is dmx address def loop(self): #dmx[205] = 255 #205 BLUE #self.artnet.send() xx = [0]*512 #artnet = self.artnet["0"] #artnet.dmx = xx# [:] #dmx #[0]*512 old_univ = -1 while 1: t = clock.time() ii = 0 for ii,dmxch in enumerate(Bdmx): i = ii%512 univ = ii//512 if str(univ) not in self.artnet: print("add uiv",univ) self.artnet[str(univ)] = ANN.ArtNetNode(to="10.10.10.255",univ=univ) self.artnet[str(univ)].dmx[512-1] = 100+univ if univ != old_univ: old_univ = univ #print("UNIV",ii/512) try: artnet.next() except:pass artnet = self.artnet[str(univ)] artnet.dmx = xx v = dmxch.next(t) if i == 0: #print(dmxch) if int(xx[i]*100) != int( v*100): #print("----v",x[i],v,t) pass #print("i:{:0.2f} xx:{:0.2f} v:{:0.2f} {:0.2f}----v {}".format(i,xx[i],v,t+100,dmxch)) #print("i:{:0.2f} xx:{:0.2f} v:{:0.2f} {:0.2f}----v {}".format(i,xx[i],v,t+100,dmxch)) xx[i] = int(v) try: artnet.next() except:pass time.sleep(0.01) main = Main() if __run_main: #thread.start_new_thread(artnet_loop,()) thread.start_new_thread(main.loop,()) class CLOCK(): def __init__(self): self.__time = 0 self.__start = time.time() # only for debugging self.__tick = 0.01 # incremental timer drift's on highe cpu load ? def time(self): return self.__time def get_drift(self): run_time = time.time() - self.__start tick_time = self.__time # * self.__tick print( "runtime:{:0.2f} tick_timer:{:0.2f} drift:{:0.2f}".format(run_time,tick_time,run_time-tick_time)) def loop(self): while 1: self.__time +=self.__tick #if int(self.__time*100)/10. % 10 == 0:# self.__time % 2 == 0: # print( self.get_drift()) #print(self.__time) #for i in range(10): time.sleep(self.__tick) class CLOCK_REAL(): def __init__(self): self.__time = 0 self.__start = time.time() # only for debugging self.__tick = 0.01 # incremental timer drift's on highe cpu load ? def time(self): self.__time = time.time() return self.__time def get_drift(self): run_time = time.time() - self.__start tick_time = self.__time # * self.__tick print( "runtime:{:0.2f} tick_timer:{:0.2f} drift:{:0.2f}".format(run_time,tick_time,run_time-tick_time)) def loop(self): pass #clock = CLOCK() clock = CLOCK_REAL() if __run_main: thread.start_new_thread(clock.loop,()) class Fade(): def __init__(self,start,target,ftime,clock,delay=0): #print("init Fade ",start,target,ftime,clock) if delay < 0: delay = 0.0001 if ftime <= 0: ftime = 0.0001 clock += delay self.__delay = delay self.__clock = clock self.__clock_curr = clock self.__ftime = ftime self.__start = start self.__last = start self.__target = target self.run = 1 #print("INIT", str(self) ) def __str__(self): return self.__repr__() def __repr__(self): return "".format( self.__last, self.__start,self.__target,self.__ftime,self.__clock_curr,self.run,self.__delay ) def next(self,clock=None): if self.__ftime <= 0 and self.__delay <= 0: self.__last = self.__target self.run = 0 if type(clock) is float or type(clock) is int:#not None: self.__clock_curr = clock if self.__target > self.__start: if self.__last >= self.__target: self.run = 0 return self.__target else: if self.__last <= self.__target: self.run = 0 return self.__target current = (self.__clock - self.__clock_curr) / self.__ftime length = self.__start - self.__target self.__last = self.__start+ length*current #if self.__last < 0: # self.__last = 0 #if self.__last > 255: # self.__last = 255 self.run = 1 return self.__last def ctl(self,cmd="",value=None): # if x-fade cmd="%" value=50 # start,stop,fwd,bwd,revers pass class MASTER_FX(): def __init__(self): #cprint(self,"MASTER_FX INIT !",color="green") self.__data = [] self.__ok = [] self.i=0 self.old_offsets = [] self.offsets = [] self.count = -1 self.init = 10 def add(self,fx): if fx not in self.__data: #cprint(self,"ADD TO MASTER !",color="green") self.__data.append(fx) info = fx._get_info() #cprint(self,"ADD" ,info,color="green") offset = 0 if "offset" in info: offset = info["offset"] self.old_offsets.append(offset) self.offsets.append(offset) if "xtype" in info: if info["xtype"] == "rnd": self._shuffle() #self.init += 1 def _shuffle(self): #cprint(self,"REORDER RANDOM !",color="green") #self.init = 0 #cprint(self.old_offsets) random.shuffle(self.old_offsets) #cprint(self.old_offsets) def _init(self): self._shuffle() #self.offsets = [] for i,v in enumerate(self.old_offsets): offset = self.old_offsets[i] self.offsets[i] = offset self.init = 0 def next(self,child): i = self.__data.index(child) offset = self.old_offsets[i] self.offsets[i] = offset return offset #for i,v in enumerate(self.old_offsets): # offset = self.old_offsets[i] # self.offsets[i] = offset def get(self,child,count): offset = 0 if child not in self.__data: return offset if self.init: self._init() idx = self.__data.index(child) if (self.count != count and idx == 0 ) or self.init == 0: self.init = 1 self._shuffle() #print( count) self.count=count idx = self.__data.index(child) offset = self.offsets[idx] return offset class FX(): def __init__(self,xtype="sinus",size=10,speed=10,invert=0,width=100,start=0,offset=0,base="",clock=0,master=None): self.__xtype=xtype self.__size = size self.__start = start if width > 200: width = 200 if width <= 0: width = 1 self.__width = width self.__invert = invert self.__base = base self.__speed = speed self.__offset = offset self.__clock = clock self.__clock_curr = clock self.out = 0 self.old_v = -1 self.run = 1 self.count = -1 self.__angel = self.__clock_curr*360%360 if master is None: cprint(master, "MASTER_FX ERR",master,color="red") self.__master = MASTER_FX() self.__master.add(self) else: #cprint( "MASTER_FX OK",master,color="red") self.__master = master self.__master.add(self) if self.__xtype == "rnd": self.__offset = self.__master.get(self,-2) self.__offset = self.__master.next(self)#,count) self.next() #print("init FX",self) def _get_info(self): print(self.__offset) return {"offset":self.__offset,"xtype":self.__xtype} #return self.next(),self.__xtype, self.__size,self.__speed,self.__angel, self.__base,self.__clock_curr,self.run def __str__(self): return self.__repr__() def __repr__(self): return "".format( self.next(),self.__xtype, self.__size,self.__speed,self.__angel, self.__base,self.__clock_curr,self.run ) def next(self,clock=None): if type(clock) is float or type(clock) is int:#not None: self.__clock_curr = clock t = self.__clock_curr * self.__speed / 60 t += self.__offset / 100 #255 #1024 #255 t += self.__start / 1024 #255 tw = t%1 count = t//1 t = t * (100/self.__width) if tw > self.__width/100: t = 1 self.__angel = t%1*360 t = t%1 rad = math.radians(self.__angel) v=0 out = 0 base = 0 size = self.__size if self.__base == "+": # add base = size/2 elif self.__base == "-": # sub base = size/2*-1 if self.__xtype == "sinus": v = math.sin( rad ) v/=2 elif self.__xtype == "cosinus": v = math.cos( rad ) if self.__base == "+": # add size *= -1 v/=2 elif self.__xtype == "rnd": #base = 0 if self.__angel > 90 and self.__angel <=270: v=1 else: v=0 #if count != self.count and v: # % 2 == 0:#!= self.count: # #self.__offset = random.randint(0,1024)# /1024 # self.__master._shuffle() if count != self.count and v == 0: # and v: # % 2 == 0:#!= self.count: self.__master.next(self)#,count) #self.__master.next(self)#,count) self.__offset = self.__master.get(self,count) base = 0 if self.__base == "-": # sub if self.__invert: v = 1-v #base = -size size *=-1 v *=-1 elif self.__base == "+": # sub if self.__invert: v = v-1 else: v = (t%1-0.5) elif self.__xtype == "on": #base = 0 if self.__angel > 90 and self.__angel <=270: v=1 else: v=0 base = 0 if self.__base == "-": # sub if self.__invert: v = 1-v #base = -size size *=-1 v *=-1 elif self.__base == "+": # sub if self.__invert: v = v-1 else: v = (t%1-0.5) elif self.__xtype == "ramp" or self.__xtype == "ramp": v = (t%1) base = 0 if self.__base == "-": # sub if self.__invert: v = 1-v #base = -size size *=-1 v *=-1 elif self.__base == "+": # sub if self.__invert: v = v-1 else: v = (t%1-0.5) elif self.__xtype == "ramp2" or self.__xtype == "bump2": v = (t%1) v = 1-v if v == 1: v=0 base = 0 if self.__base == "-": # sub if self.__invert: v = 1-v #base = -size size *=-1 v *=-1 elif self.__base == "+": # sub if self.__invert: v = v-1 else: v = (t%1-0.5) elif self.__xtype == "fade": x = t * 2 if x > 1: x = 2-x x -= 0.5 v = x*2 #base /= 2 #base *=2 if self.__base == "+": # add pass#base /= 2 else: v *= -1 v/=2 if self.__invert: v *=-1 out = v *size +base self.out = out self.count = count return out class DMXCH(object): def __init__(self): self._base_value = 0 self._fade = None self._fx = None self._fx_value = 0 self._flash = None self._flash_fx = None self._flash_fx_value = 0 self._last_val = None def fade(self,target,ftime=0,clock=0,delay=0): if target != self._base_value: try: target = float(target) self._fade = Fade(self._base_value,target,ftime=ftime,clock=clock,delay=delay) #self._fade.next() #self._fade.next() except Exception as e: print( "Except:fade",e,target,ftime,clock) def fx(self,xtype="sinus",size=40,speed=40,invert=0,width=100,start=0,offset=0,base="", clock=0,master=None): print([self,xtype,size,speed,start,offset,base, clock]) if str(xtype).lower() == "off": fx_value = self._fx_value if fx_value != 0: cprint("???????______ FX OFF AS FADE",fx_value,0,255) self._fx = Fade(fx_value,0,ftime=0.5,clock=clock)#,delay=delay) else: #self._fx = Fade(self._fx_value,target=0,ftime=2,clock=clock) self._fx = None self._fx_value = 0 else: self._fx = FX(xtype=xtype,size=size,speed=speed,invert=invert,width=width,start=start,offset=offset,base=base,clock=clock,master=master) def flash(self,target,ftime=0,clock=0,delay=0): if str(target).lower() == "off": self._flash = None else:#elif target != self._base_value: try: target = float(target) self._flash = Fade(self._last_val,target,ftime=ftime,clock=clock,delay=delay) except Exception as e: print( "Except:flash",target,ftime,clock,__name__,e,) def flash_fx(self,xtype="sinus",size=40,speed=40,invert=0,width=100,start=0,offset=0,base="",clock=0,master=None): #if self._flash_fx is not None : # cprint("flash_fx",xtype) if str(xtype).lower() == "off": fx_value = self._fx_value #if fx_value != 0: # cprint("???????______ FX OFF AS FADE",fx_value,0,255) # self._flash_fx = Fade(fx_value,0,ftime=0.5,clock=clock)#,delay=delay) # self._flash_fx = None #else: # self._flash_fx = None # self._flash_fx_value = 0 self._flash_fx = None self._flash_fx_value = 0 else: self._flash_fx = FX(xtype=xtype,size=size,speed=speed,invert=invert,width=width,start=start,offset=offset,base=base,clock=clock,master=master) def fx_ctl(self,cmd=""):#start,stop,off pass def __str__(self): return self.__repr__() def __repr__(self): return "< DMXCH {:0.2f} > {} {}".format( self._last_val,self._fx,self._fade) def fade_ctl(self,cmd=""):#start,stop,backw,fwd,bounce pass def next(self,clock=0): value = self._base_value if self._last_val is None: self._last_val = value fx_value = self._fx_value if self._flash is not None: value = self._flash.next(clock) #flicker bug ?! value = self._flash.next(clock) fx_value = 0 elif self._fade is not None:#is Fade:# is Fade: self._base_value = self._fade.next(clock) #flicker bug ?! self._base_value = self._fade.next(clock) value = self._base_value if self._flash_fx is not None:# is FX: fx_value = self._flash_fx.next(clock) elif self._fx is not None and self._flash is None:# is FX: self._fx_value = self._fx.next(clock) fx_value = self._fx_value self._last_val = value+fx_value return self._last_val Bdmx = [] for i in range(512*3): Bdmx.append( DMXCH() ) #print(type(dmx[i])) def split_cmd(data): if "cmd" in data: cmd = data["cmd"] #print("cmd",cmd) if "," in cmd: cmds = cmd.split(",") else: cmds = [cmd] return cmds import time import json import zlib def JCB(data): #json client input t_start = time.time() #jdatas = data["cmd"].split("\x00") jdatas = [data["cmd"]] c = clock.time() c = float(c) print("JCB",round(c,2)) ftime = 0 delay = 0 for j in jdatas: master_fx = MASTER_FX() if not j: continue try: cprint("JCB::")#,j) jdata = j #jdatas[j] jtxt = jdata #jtxt = zlib.decompress(jtxt) #jtxt.decode()) jtxt = str(jtxt,"UTF-8") cmds = json.loads(jtxt) for x in cmds: #cprint(int(clock.time()*1000)/1000,end=" ",color="yellow")#time.time()) #cprint("json", x,type(x),color="yellow")#,cmds[x]) if "DMX" in x: DMX = int(x["DMX"]) else:continue if DMX > 0: DMX -=1 else:continue if "VALUE" in x:# and x["VALUE"] is not None: v = x["VALUE"] else:continue if "FX" in x:# and x["VALUE"] is not None: fx = x["FX"] else:fx="" if "FX2" in x:# and x["VALUE"] is not None: fx2 = x["FX2"] else:fx2={} if "FADE" in x: ftime = x["FADE"] else:ftime=0 if "DELAY" in x: delay = x["DELAY"] else:delay=0 if len(Bdmx) < DMX: continue if v is not None: if "FLASH" in x: #print("FLASH") Bdmx[DMX].flash(target=v,ftime=ftime, clock=c,delay=delay) else: #print("FADE") Bdmx[DMX].fade(target=v,ftime=ftime, clock=c,delay=delay) if type(fx2) is dict and fx2: #cprint("FX2",DMX,fx2,color="green") xtype="fade" size = 10 speed = 10 start = 0 offset= 0 width=100 invert=0 base = "-" if "TYPE" in fx2: xtype = fx2["TYPE"] if "SIZE" in fx2: size = fx2["SIZE"] if "SPEED" in fx2: speed = fx2["SPEED"] if "OFFSET" in fx2: offset = fx2["OFFSET"] if "BASE" in fx2: base = fx2["BASE"] if "INVERT" in fx2: invert = fx2["INVERT"] if "WIDTH" in fx2: width = fx2["WIDTH"] if "off" == x["VALUE"]: #fix fx flash off xtype= "off" if "alloff" == xtype.lower(): for i in Bdmx: if i is not None: i.flash_fx(xtype="off",clock=c) i.fx(xtype="off",clock=c) if "FLASH" in x: Bdmx[DMX].flash_fx(xtype=xtype,size=size,speed=speed,invert=invert,width=width,start=start,offset=offset,base=base,clock=c,master=master_fx) else: Bdmx[DMX].fx(xtype=xtype,size=size,speed=speed,invert=invert,width=width,start=start,offset=offset,base=base,clock=c,master=master_fx) elif type(fx) is str and fx: # old fx like sinus:200:12:244 ccm = str(DMX+1)+":"+fx print("fx",ccm) if "FLASH" in x: CB({"cmd":"fxf"+ccm}) else: CB({"cmd":"fx"+ccm}) cprint("{:0.04} sec.".format(time.time()-t_start),color="yellow") cprint("{:0.04} t.".format(time.time()),color="yellow") except Exception as e: cprint("EXCEPTION JCB",e,color="red") cprint("----",jdata,color="red") cprint("Error on line {}".format(sys.exc_info()[-1].tb_lineno),color="red") cprint() cprint("{:0.04} sec.".format(time.time()-t_start),color="yellow") cprint("{:0.04} t.".format(time.time()),color="yellow") def CB(data): # raw/text client input #print("CB",data) cmds = split_cmd(data) c = clock.time() c = float(c) ftime = 0 delay = 0 for xcmd in cmds: if xcmd: cprint("CB",xcmd,end=" ") pass else: continue if xcmd.startswith("fxf"): xxcmd=xcmd[3:].split(":") #print("fxf:",xxcmd) if "alloff" == xxcmd[1].lower(): for i in Bdmx: if i is not None: i.flash_fx(xtype="off",clock=c) l = xxcmd try: xtype="" size=40 speed=100 start=0 offset=0 base="" k=int(l[0])-1 xtype=l[1] if len(l) >= 3: try:size=int(l[2]) except:pass if len(l) >= 4: try:speed=int(l[3]) except:pass if len(l) >= 5: try:start=int(l[4]) except:pass if len(l) >= 6: try:offset=int(l[5]) except:pass if len(l) >= 7: try:base=l[6] except:pass if len(Bdmx) > k: #Bdmx[k].fade(target=v,ftime=t, clock=c) Bdmx[k].flash_fx(xtype=xtype,size=size,speed=speed,start=start,offset=offset,base=base,clock=c) except Exception as e: print("EXCEPTION IN FX",e) print("Error on line {}".format(sys.exc_info()[-1].tb_lineno)) elif xcmd.startswith("fx"): xxcmd=xcmd[2:].split(":") print("DMX:",xxcmd) if len(xxcmd) < 2: print("xxcmd err",xxcmd,xcmd) continue if "alloff" == xxcmd[1].lower(): for i in Bdmx: i.fx(xtype="off",clock=c) l = xxcmd try: xtype="" size=40 speed=100 start=0 offset=0 base="" k=int(l[0])-1 xtype=l[1] if len(l) >= 3: try:size=int(l[2]) except:pass if len(l) >= 4: try:speed=int(l[3]) except:pass if len(l) >= 5: try:start=int(l[4]) except:pass if len(l) >= 6: try:offset=int(l[5]) except:pass if len(l) >= 7: try:base=l[6] except:pass if len(Bdmx) > k: #Bdmx[k].fade(target=v,ftime=t, clock=c) Bdmx[k].fx(xtype=xtype,size=size,speed=speed,start=start,offset=offset,base=base,clock=c) except Exception as e: print("EXCEPTION IN FX",xcmd,e) print("Error on line {}".format(sys.exc_info()[-1].tb_lineno)) if __run_main: #jchat = chat.CMD(CB,port=50001) # server listener #thread.start_new_thread(jchat.poll,()) chat.cmd(JCB) # server listener #chat.cmd(JCB,port=50001) # server listener #input("END")