console.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. #! /usr/bin/python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. This file is part of LibreLight.
  5. LibreLight is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, version 2 of the License.
  8. LibreLight is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with LibreLight. If not, see <http://www.gnu.org/licenses/>.
  14. (c) 2012 micha@uxsrv.de
  15. """
  16. import sys
  17. rnd_id = ""
  18. rnd_id += " Beta 22.02 "
  19. import subprocess
  20. rnd_id += subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).decode('ascii').strip()
  21. if "__file__" in dir():
  22. sys.stdout.write("\x1b]2;"+str(__file__)+" "+rnd_id+"\x07") # terminal title
  23. else:
  24. sys.stdout.write("\x1b]2;"+str("__file__")+" "+rnd_if+"\x07") # terminal title
  25. import time
  26. import socket
  27. import struct
  28. import sys
  29. import random
  30. import math
  31. from collections import OrderedDict
  32. import lib.chat as chat
  33. import lib.ArtNetNode as ANN
  34. import _thread as thread
  35. #thread.start_new_thread
  36. import lib.motion as motion
  37. #idmx = [0]*512 # incremental dmx
  38. dmx = [0]*512 # absolute dmx data
  39. gcolor = 1
  40. def cprint(*text,color="blue",space=" ",end="\n"):
  41. #return 0 #disable print dbg
  42. if not gcolor:
  43. print(text)
  44. return 0
  45. if color == "green":
  46. txt = '\033[92m'
  47. elif color == "red":
  48. txt = '\033[0;31m\033[1m'
  49. elif color == "yellow":
  50. txt = '\033[93m\033[1m'
  51. elif color == "cyan":
  52. txt = '\033[96m'
  53. else:
  54. txt = '\033[94m'
  55. for t in text:
  56. txt += str(t ) +" "
  57. #HEADER = '\033[95m'
  58. #OKBLUE = '\033[94m'
  59. #OKCYAN = '\033[96m'
  60. #OKGREEN = '\033[92m'
  61. #WARNING = '\033[93m'
  62. #FAIL = '\033[91m'
  63. #ENDC = '\033[0m'
  64. #BOLD = '\033[1m'
  65. #UNDERLINE = '\033[4m'
  66. txt += '\033[0m'
  67. print(txt,end=end)
  68. #return txt
  69. def artnet_loop():
  70. #artnet = ANN.ArtNetNode(to="127.0.0.1",port=6555,univ=12)
  71. #artnet = ANN.ArtNetNode(to="127.0.0.1",port=6555,univ=0)
  72. artnet = ANN.ArtNetNode(to="10.10.10.255",univ=0)
  73. #artnet = ANN.ArtNetNode(to="2.0.0.255",univ=0)
  74. #artnet = ANN.ArtNetNode(to="10.10.10.255",univ=1)
  75. #dmx[205] = 255 #205 BLUE
  76. artnet.dmx= dmx #[0]*512
  77. artnet.send()
  78. while 1:
  79. #artnet._test_frame()
  80. artnet.next()
  81. time.sleep(0.01)
  82. class Main():
  83. def __init__(self):
  84. #artnet = ANN.ArtNetNode(to="127.0.0.1",port=6555,univ=12)
  85. #artnet = ANN.ArtNetNode(to="127.0.0.1",port=6555,univ=0)
  86. #artnet = ANN.ArtNetNode(to="2.0.0.255",univ=0)
  87. #artnet = ANN.ArtNetNode(to="10.10.10.255",univ=1)
  88. self.artnet = {}
  89. #self.artnet["0"] = ANN.ArtNetNode(to="10.10.10.255",univ=0)
  90. #self.artnet["0"].dmx[512-1] = 10
  91. #self.artnet["1"] = ANN.ArtNetNode(to="10.10.10.255",univ=1)
  92. #self.artnet["1"].dmx[512-1] = 11
  93. self.fx = {} # key is dmx address
  94. def loop(self):
  95. #dmx[205] = 255 #205 BLUE
  96. #self.artnet.send()
  97. xx = [0]*512
  98. #artnet = self.artnet["0"]
  99. #artnet.dmx = xx# [:] #dmx #[0]*512
  100. old_univ = -1
  101. while 1:
  102. t = clock.time()
  103. ii = 0
  104. for ii,dmxch in enumerate(Bdmx):
  105. i = ii%512
  106. univ = ii//512
  107. if str(univ) not in self.artnet:
  108. print("add uiv",univ)
  109. self.artnet[str(univ)] = ANN.ArtNetNode(to="10.10.10.255",univ=univ)
  110. self.artnet[str(univ)].dmx[512-1] = 100+univ
  111. if univ != old_univ:
  112. old_univ = univ
  113. #print("UNIV",ii/512)
  114. try:
  115. artnet.next()
  116. except:pass
  117. artnet = self.artnet[str(univ)]
  118. artnet.dmx = xx
  119. v = dmxch.next(t)
  120. if i == 0:
  121. if int(xx[i]*100) != int( v*100):
  122. #print("----v",x[i],v,t)
  123. pass
  124. #print("i:{:0.2f} xx:{:0.2f} v:{:0.2f} {:0.2f}----v {}".format(i,xx[i],v,t+100,dmxch))
  125. #print("i:{:0.2f} xx:{:0.2f} v:{:0.2f} {:0.2f}----v {}".format(i,xx[i],v,t+100,dmxch))
  126. xx[i] = int(v)
  127. try:
  128. artnet.next()
  129. except:pass
  130. time.sleep(0.01)
  131. main = Main()
  132. #thread.start_new_thread(artnet_loop,())
  133. thread.start_new_thread(main.loop,())
  134. class CLOCK():
  135. def __init__(self):
  136. self.__time = 0
  137. self.__start = time.time() # only for debugging
  138. self.__tick = 0.01 # incremental timer drift's on highe cpu load ?
  139. def time(self):
  140. return self.__time
  141. def get_drift(self):
  142. run_time = time.time() - self.__start
  143. tick_time = self.__time # * self.__tick
  144. print( "runtime:{:0.2f} tick_timer:{:0.2f} drift:{:0.2f}".format(run_time,tick_time,run_time-tick_time))
  145. def loop(self):
  146. while 1:
  147. self.__time +=self.__tick
  148. #if int(self.__time*100)/10. % 10 == 0:# self.__time % 2 == 0:
  149. # print( self.get_drift())
  150. #print(self.__time)
  151. #for i in range(10):
  152. time.sleep(self.__tick)
  153. clock = CLOCK()
  154. thread.start_new_thread(clock.loop,())
  155. class Fade():
  156. def __init__(self,start,target,ftime,clock,delay=0):
  157. #print("init Fade ",start,target,ftime,clock)
  158. if delay < 0:
  159. delay = 0.0001
  160. if ftime <= 0:
  161. ftime = 0.0001
  162. clock += delay
  163. self.__delay = delay
  164. self.__clock = clock
  165. self.__clock_curr = clock
  166. self.__ftime = ftime
  167. self.__start = start
  168. self.__last = start
  169. self.__target = target
  170. self.run = 1
  171. #print("INIT", str(self) )
  172. def __str__(self):
  173. return self.__repr__()
  174. def __repr__(self):
  175. return "<Fade Next:{:0.2f} Start:{:0.2f} Target:{:0.2f} T{:0.2f} Clock:{:0.2f} run:{} delay:{:0.2f}>".format(
  176. self.__last, self.__start,self.__target,self.__ftime,self.__clock_curr,self.run,self.__delay )
  177. def next(self,clock=None):
  178. if self.__ftime <= 0 and self.__delay <= 0:
  179. self.__last = self.__target
  180. self.run = 0
  181. if type(clock) is float or type(clock) is int:#not None:
  182. self.__clock_curr = clock
  183. if self.__target > self.__start:
  184. if self.__last >= self.__target:
  185. self.run = 0
  186. return self.__target
  187. else:
  188. if self.__last <= self.__target:
  189. self.run = 0
  190. return self.__target
  191. current = (self.__clock - self.__clock_curr) / self.__ftime
  192. length = self.__start - self.__target
  193. self.__last = self.__start+ length*current
  194. #if self.__last < 0:
  195. # self.__last = 0
  196. #if self.__last > 255:
  197. # self.__last = 255
  198. self.run = 1
  199. return self.__last
  200. def ctl(self,cmd="",value=None): # if x-fade cmd="%" value=50
  201. # start,stop,fwd,bwd,revers
  202. pass
  203. class FX():
  204. def __init__(self,xtype="sinus",size=10,speed=10,start=0,offset=0,base="",clock=0):
  205. self.__xtype=xtype
  206. self.__size = size
  207. self.__start = start
  208. self.__base = base
  209. self.__speed = speed
  210. self.__offset = offset
  211. self.__clock = clock
  212. self.__clock_curr = clock
  213. self.run = 1
  214. self.__angel = self.__clock_curr*360%360
  215. def __str__(self):
  216. return self.__repr__()
  217. def __repr__(self):
  218. return "<FX Next:{:0.2f} xtype:{} Size:{:0.2f} Speed:{:0.2f} ang:{:0.2f} base:{} Clock:{:0.2f} run:{}>".format(
  219. self.next(),self.__xtype, self.__size,self.__speed,self.__angel, self.__base,self.__clock_curr,self.run )
  220. def next(self,clock=None):
  221. if type(clock) is float or type(clock) is int:#not None:
  222. self.__clock_curr = clock
  223. t = self.__clock_curr * self.__speed / 255
  224. t += self.__offset / 1024 #255
  225. t += self.__start / 1024 #255
  226. self.__angel = t%1*360 #self.__clock_curr%1 #*360%360
  227. t = t%1
  228. rad = math.radians(self.__angel)
  229. base = 0
  230. if self.__base == "+": # add
  231. base = self.__size
  232. elif self.__base == "-": # sub
  233. base = self.__size*-1
  234. # todo start angle 20°
  235. # todo width angle 90°
  236. #print("{:0.2f} {:0.2f} {:0.2f} {:0.2f}".format(self.__angel ,self.__clock_curr,self.__angel ,math.sin(rad) ) )
  237. if self.__xtype == "sinus":
  238. return math.sin( rad ) * self.__size/2 + base/2
  239. elif self.__xtype == "cosinus":
  240. return math.cos( rad ) * self.__size/2 + base/2
  241. elif self.__xtype == "on2":
  242. out = self.__size/2
  243. if self.__angel > 90 and self.__angel <=270:
  244. out *=-1
  245. out += base/2
  246. print("ON {:0.2f} {:0.2f} {:0.2f} {:0.2f}".format(out,t,0,self.__angel, base))
  247. return out
  248. elif self.__xtype == "on":
  249. out = self.__size/2
  250. if self.__angel > 90 and self.__angel <=270:
  251. pass
  252. else:
  253. out *=-1
  254. out += base/2
  255. return out
  256. elif self.__xtype == "bump":
  257. out = 0
  258. if self.__base == "-": # sub
  259. out = (t%1-1) * self.__size
  260. elif self.__base == "+": # sub
  261. out = (t%1) * self.__size
  262. else:
  263. out = (t%1-0.5) * self.__size
  264. #print("bump",out)
  265. return out
  266. elif self.__xtype == "bump2":
  267. out = 0
  268. if self.__base == "+": # sub
  269. out = (t%1-1) * (self.__size *-1)
  270. elif self.__base == "-": # sub
  271. out = (t%1) * (self.__size *-1)
  272. else:
  273. out = (t%1-0.5) * (self.__size *-1)
  274. #print("bump",out)
  275. return out
  276. elif self.__xtype == "fade":
  277. x = t * 2
  278. if x > 1:
  279. x = 2-x
  280. x -= 0.5
  281. out = x * self.__size + base/2
  282. #print("FADE {:0.2f} {:0.2f} {:0.2f} {:0.2f}".format(out,t,x,self.__angel, base))
  283. return out
  284. else:
  285. return 0
  286. class DMXCH(object):
  287. def __init__(self):
  288. self._base_value = 0
  289. self._fade = None
  290. self._fx = None
  291. self._fx_value = 0
  292. self._flush = None
  293. self._flush_fx = None
  294. self._flush_fx_value = 0
  295. self._last_val = None
  296. def fade(self,target,ftime=0,clock=0,delay=0):
  297. if target != self._base_value:
  298. try:
  299. target = float(target)
  300. self._fade = Fade(self._base_value,target,ftime=ftime,clock=clock,delay=delay)
  301. #self._fade.next()
  302. #self._fade.next()
  303. except Exception as e:
  304. print( "Except:fade",e,target,ftime,clock)
  305. def fx(self,xtype="sinus",size=40,speed=40,start=0,offset=0,base="", clock=0):
  306. print([self,xtype,size,speed,start,offset,base, clock])
  307. if str(xtype).lower() == "off":
  308. #self._fx = Fade(self._fx_value,target=0,ftime=2,clock=clock)
  309. self._fx = None
  310. self._fx_value = 0
  311. else:
  312. self._fx = FX(xtype=xtype,size=size,speed=speed,start=start,offset=offset,base=base,clock=clock)
  313. def flush(self,target,ftime=0,clock=0,delay=0):
  314. if str(target).lower() == "off":
  315. self._flush = None
  316. else:#elif target != self._base_value:
  317. try:
  318. target = float(target)
  319. self._flush = Fade(self._last_val,target,ftime=ftime,clock=clock,delay=delay)
  320. except Exception as e:
  321. print( "Except:flush",target,ftime,clock,__name__,e,)
  322. def flush_fx(self,xtype="sinus",size=40,speed=40,start=0,offset=0,base="",clock=0):
  323. if str(xtype).lower() == "off":
  324. #self._fx = Fade(self._fx_value,target=0,ftime=2,clock=clock)
  325. self._flush_fx = None
  326. self._flush_fx_value = 0
  327. else:
  328. self._flush_fx = FX(xtype=xtype,size=size,speed=speed,start=start,offset=offset,base=base,clock=clock)
  329. def fx_ctl(self,cmd=""):#start,stop,off
  330. pass
  331. def __str__(self):
  332. return self.__repr__()
  333. def __repr__(self):
  334. return "< DMXCH {:0.2f} > {} {}".format( self._last_val,self._fx,self._fade)
  335. def fade_ctl(self,cmd=""):#start,stop,backw,fwd,bounce
  336. pass
  337. def next(self,clock=0):
  338. value = self._base_value
  339. if self._last_val is None:
  340. self._last_val = value
  341. fx_value = self._fx_value
  342. if self._flush is not None:
  343. value = self._flush.next(clock)
  344. #flicker bug ?!
  345. value = self._flush.next(clock)
  346. fx_value = 0
  347. elif self._fade is not None:#is Fade:# is Fade:
  348. self._base_value = self._fade.next(clock)
  349. #flicker bug ?!
  350. self._base_value = self._fade.next(clock)
  351. value = self._base_value
  352. if self._flush_fx is not None:# is FX:
  353. fx_value = self._flush_fx.next(clock)
  354. elif self._fx is not None and self._flush is None:# is FX:
  355. self._fx_value = self._fx.next(clock)
  356. fx_value = self._fx_value
  357. self._last_val = value+fx_value
  358. return self._last_val
  359. Bdmx = []
  360. for i in range(512*3):
  361. Bdmx.append( DMXCH() )
  362. #print(type(dmx[i]))
  363. def split_cmd(data):
  364. if "cmd" in data:
  365. cmd = data["cmd"]
  366. #print("cmd",cmd)
  367. if "," in cmd:
  368. cmds = cmd.split(",")
  369. else:
  370. cmds = [cmd]
  371. return cmds
  372. import time
  373. import json
  374. def JCB(data):
  375. #jdatas = data["cmd"].split("\x00")
  376. jdatas = [data["cmd"]]
  377. #print("JCB")
  378. c = clock.time()
  379. c = float(c)
  380. ftime = 0
  381. delay = 0
  382. for j in jdatas:
  383. if not j:
  384. continue
  385. try:
  386. jdata = j #jdatas[j]
  387. #print(j)
  388. cmds = json.loads(jdata)
  389. for x in cmds:
  390. cprint(int(clock.time()*1000)/1000,end=" ",color="yellow")#time.time())
  391. cprint("json", x,type(x),color="yellow")#,cmds[x])
  392. if "DMX" in x:
  393. DMX = int(x["DMX"])
  394. else:continue
  395. if DMX > 0:
  396. DMX -=1
  397. else:continue
  398. if "VALUE" in x:# and x["VALUE"] is not None:
  399. v = x["VALUE"]
  400. else:continue
  401. if "FX" in x:# and x["VALUE"] is not None:
  402. fx = x["FX"]
  403. else:fx=""
  404. if "FX2" in x:# and x["VALUE"] is not None:
  405. fx2 = x["FX2"]
  406. else:fx2={}
  407. if "FADE" in x:
  408. ftime = x["FADE"]
  409. else:ftime=0
  410. if "DELAY" in x:
  411. delay = x["DELAY"]
  412. else:delay=0
  413. if len(Bdmx) < DMX:
  414. continue
  415. if v is not None:
  416. if "FLASH" in x:
  417. #print("FLASH")
  418. Bdmx[DMX].flush(target=v,ftime=ftime, clock=c,delay=delay)
  419. else:
  420. #print("FADE")
  421. Bdmx[DMX].fade(target=v,ftime=ftime, clock=c,delay=delay)
  422. if type(fx2) is dict and fx2:
  423. cprint("FX2",DMX,fx2,color="green")
  424. xtype="fade"
  425. size = 10
  426. speed = 10
  427. start = 0
  428. offset= 0
  429. base = "-"
  430. if "TYPE" in fx2:
  431. xtype = fx2["TYPE"]
  432. if "SIZE" in fx2:
  433. size = fx2["SIZE"]
  434. if "SPEED" in fx2:
  435. speed = fx2["SPEED"]
  436. if "OFFSET" in fx2:
  437. offset = fx2["OFFSET"]
  438. if "BASE" in fx2:
  439. base = fx2["BASE"]
  440. if "alloff" == xtype.lower():
  441. for i in Bdmx:
  442. if i is not None:
  443. i.flush_fx(xtype="off",clock=c)
  444. i.fx(xtype="off",clock=c)
  445. if "FLASH" in x:
  446. Bdmx[DMX].flush_fx(xtype=xtype,size=size,speed=speed,start=start,offset=offset,base=base,clock=c)
  447. else:
  448. Bdmx[DMX].fx(xtype=xtype,size=size,speed=speed,start=start,offset=offset,base=base,clock=c)
  449. elif type(fx) is str and fx: # old fx like sinus:200:12:244
  450. ccm = str(DMX+1)+":"+fx
  451. print("fx",ccm)
  452. if "FLASH" in x:
  453. CB({"cmd":"fxf"+ccm})
  454. else:
  455. CB({"cmd":"fx"+ccm})
  456. return
  457. except Exception as e:
  458. cprint("EXCEPTION JCB",e,color="red")
  459. cprint("----",jdata,color="red")
  460. cprint("Error on line {}".format(sys.exc_info()[-1].tb_lineno),color="red")
  461. def CB(data):
  462. #print("CB",data)
  463. cmds = split_cmd(data)
  464. c = clock.time()
  465. c = float(c)
  466. ftime = 0
  467. delay = 0
  468. for xcmd in cmds:
  469. if xcmd:
  470. cprint("CB",xcmd,end=" ")
  471. pass
  472. else:
  473. continue
  474. if xcmd.startswith("fxf"):
  475. xxcmd=xcmd[3:].split(":")
  476. #print("fxf:",xxcmd)
  477. if "alloff" == xxcmd[1].lower():
  478. for i in Bdmx:
  479. if i is not None:
  480. i.flush_fx(xtype="off",clock=c)
  481. l = xxcmd
  482. try:
  483. xtype=""
  484. size=40
  485. speed=100
  486. start=0
  487. offset=0
  488. base=""
  489. k=int(l[0])-1
  490. xtype=l[1]
  491. if len(l) >= 3:
  492. try:size=int(l[2])
  493. except:pass
  494. if len(l) >= 4:
  495. try:speed=int(l[3])
  496. except:pass
  497. if len(l) >= 5:
  498. try:start=int(l[4])
  499. except:pass
  500. if len(l) >= 6:
  501. try:offset=int(l[5])
  502. except:pass
  503. if len(l) >= 7:
  504. try:base=l[6]
  505. except:pass
  506. if len(Bdmx) > k:
  507. #Bdmx[k].fade(target=v,ftime=t, clock=c)
  508. Bdmx[k].flush_fx(xtype=xtype,size=size,speed=speed,start=start,offset=offset,base=base,clock=c)
  509. except Exception as e:
  510. print("EXCEPTION IN FX",e)
  511. print("Error on line {}".format(sys.exc_info()[-1].tb_lineno))
  512. elif xcmd.startswith("fx"):
  513. xxcmd=xcmd[2:].split(":")
  514. print("DMX:",xxcmd)
  515. if len(xxcmd) < 2:
  516. print("xxcmd err",xxcmd,xcmd)
  517. continue
  518. if "alloff" == xxcmd[1].lower():
  519. for i in Bdmx:
  520. i.fx(xtype="off",clock=c)
  521. l = xxcmd
  522. try:
  523. xtype=""
  524. size=40
  525. speed=100
  526. start=0
  527. offset=0
  528. base=""
  529. k=int(l[0])-1
  530. xtype=l[1]
  531. if len(l) >= 3:
  532. try:size=int(l[2])
  533. except:pass
  534. if len(l) >= 4:
  535. try:speed=int(l[3])
  536. except:pass
  537. if len(l) >= 5:
  538. try:start=int(l[4])
  539. except:pass
  540. if len(l) >= 6:
  541. try:offset=int(l[5])
  542. except:pass
  543. if len(l) >= 7:
  544. try:base=l[6]
  545. except:pass
  546. if len(Bdmx) > k:
  547. #Bdmx[k].fade(target=v,ftime=t, clock=c)
  548. Bdmx[k].fx(xtype=xtype,size=size,speed=speed,start=start,offset=offset,base=base,clock=c)
  549. except Exception as e:
  550. print("EXCEPTION IN FX",xcmd,e)
  551. print("Error on line {}".format(sys.exc_info()[-1].tb_lineno))
  552. #jchat = chat.CMD(CB,port=50001) # server listener
  553. #thread.start_new_thread(jchat.poll,())
  554. chat.cmd(JCB) # server listener
  555. #chat.cmd(JCB,port=50001) # server listener
  556. #input("END")