ArtNetProcessor.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. #! /usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. #from __future__ import absolute_import, division, print_function
  4. #from builtins import str, open, range, dict
  5. #from builtins import *
  6. """
  7. This file is part of librelight.
  8. librelight is free software: you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation, either version 2 of the License, or
  11. (at your option) any later version.
  12. librelight is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16. You should have received a copy of the GNU General Public License
  17. along with librelight. If not, see <http://www.gnu.org/licenses/>.
  18. (c) 2012 micha.rathfelder@gmail.com
  19. """
  20. import sys
  21. sys.stdout.write("\x1b]2;DMX-SHEET 5\x07") # terminal title
  22. import string
  23. import time
  24. import os
  25. import socket, struct
  26. import fcntl #socket control
  27. import errno
  28. import json
  29. import curses
  30. from collections import OrderedDict
  31. class CursesDummy():
  32. def __init__(self):
  33. pass
  34. def test(self):
  35. pass
  36. def init(self):
  37. pass
  38. def addstr(self,x,y,txt):
  39. pass
  40. def draw_lines(self,lines):
  41. pass
  42. def inp(self):
  43. return ""
  44. pass
  45. def read(self):
  46. pass
  47. def clear(self):
  48. pass
  49. def exit(self):
  50. pass
  51. class Curses():
  52. def __init__(self):
  53. self.myscreen = curses.initscr()
  54. print( dir(self.myscreen))
  55. print( self.myscreen.getmaxyx() )
  56. self._inp=""
  57. def dir(self):
  58. return dir(self.myscreen)
  59. def test(self):
  60. self.init()
  61. #self.loop()
  62. self.draw_lines(["a","b","c"])
  63. try:
  64. time.sleep(10)
  65. finally:
  66. self.exit()
  67. def init(self):
  68. curses.savetty()
  69. curses.noecho()
  70. curses.cbreak()
  71. curses.noqiflush() #?
  72. curses.noraw() #?
  73. self.clear()
  74. curses.beep()
  75. frame = 10
  76. i = 0
  77. def addstr(self,x,y,txt):
  78. self.myscreen.addstr(x, y, txt ) #zeile,spalte,text
  79. def draw_lines(self,lines):
  80. self.clear()
  81. try:
  82. x,y= self.myscreen.getmaxyx()
  83. for i,l in enumerate(lines):
  84. #print(i,l)
  85. if i >= x-2:
  86. break
  87. self.myscreen.addstr(i+1, 1, l ) #zeile,spalte,text
  88. if i >= self.myscreen.getmaxyx()[0]-2:
  89. self.myscreen.addstr(i+1, 1, "..." ) #zeile,spalte,text
  90. self.myscreen.refresh()
  91. self.myscreen.resize(x-1,y-1) # to prevent slowdown..
  92. self.myscreen.resize(x,y)
  93. except KeyboardInterrupt as e:
  94. self.exit()
  95. print("KeyboardInterrupt")
  96. raise e
  97. #except Exception as e:
  98. # self.exit()
  99. # raise e
  100. def inp(self):
  101. x= self._inp
  102. self._inp=""
  103. return x
  104. def read(self):
  105. self.myscreen.nodelay(1)
  106. try:
  107. self._inp=self.myscreen.getkey()
  108. if not self._inp:
  109. self._inp = self.myscreen.getch()
  110. self.myscreen.addstr(0, 1, str(self._inp) ) #zeile,spalte,text
  111. self.myscreen.refresh()
  112. return self._inp
  113. except:
  114. pass#self._inp=""
  115. def clear(self):
  116. self.myscreen.clear()
  117. self.myscreen.border(0)
  118. curses.nocbreak();
  119. self.myscreen.keypad(0);
  120. #self.read()
  121. curses.echo()
  122. curses.resetty()
  123. #self.myscreen.addstr(10, 2, x ) #zeile,spalte,text
  124. def exit(self):
  125. self.clear()
  126. curses.endwin()
  127. print("ENDE")
  128. class xUniversum():
  129. def __init__(self,univers_nr=0):
  130. self.__hosts = []
  131. self.__universes_dmx = {}
  132. self.__universes_fps = {}
  133. self.__universes_frames = {}
  134. self.__universes_flag = {}
  135. self.__universes_x_frames = {}
  136. self.__universes_x_time = {}
  137. self.__universes_count = {}
  138. self.__universes_timer = {}
  139. self.__universes_matrix = ["."]*512
  140. self.__universes_info = {}
  141. self.__univers_nr = univers_nr
  142. self.__frame = 0
  143. def _add(self,host):
  144. if host not in self.__hosts:
  145. self.__hosts.append(host) #re-order hosts list for LTP
  146. #print( "ADDING HOST:",host,"UNIV:",self.__univers_nr)
  147. self.__universes_dmx[host] = [0]*512
  148. self.__universes_frames[host] = 0
  149. self.__universes_x_frames[host] = 0
  150. self.__universes_fps[host] = [""]*20
  151. self.__universes_flag[host] = [0]*20
  152. self.__universes_x_time[host] = time.time()
  153. self.__universes_timer[host] = [0]*512
  154. self.__universes_info[host] = {}
  155. def _next_frame(self,host):
  156. self.__frame += 1
  157. self.__universes_frames[host] += 1
  158. self.__universes_x_frames[host] += 1
  159. if self.__universes_x_time[host]+10 < time.time():
  160. sec = time.time()-self.__universes_x_time[host]
  161. fps = self.__universes_x_frames[host] /sec
  162. #fps = round(fps,1)
  163. fps = int(fps)
  164. self.__universes_fps[host].pop(0)
  165. self.__universes_fps[host].append(fps)
  166. self.__universes_x_time[host] = time.time()
  167. self.__universes_x_frames[host] = 0
  168. def update(self,host,dmxframe):
  169. if type(dmxframe) != list:
  170. #print( "update ERROR dmxframe is not a list", host )
  171. return
  172. self._add(host)
  173. update_matrix = [0]*512
  174. dmx=[0]*512
  175. update_flag = 0
  176. dmxframe_old = self.__universes_dmx[host]
  177. self._next_frame(host)
  178. if len(dmxframe) <= 512: #len(dmxframe_old):
  179. for i,v in enumerate(dmxframe):
  180. if dmxframe[i] != dmxframe_old[i]:
  181. update_flag += 1
  182. self.__universes_matrix[i] = self.__hosts.index(host)
  183. dmx[i] = v
  184. self.__universes_flag[host].pop(0)
  185. self.__universes_flag[host].append( update_flag )
  186. tmp = {}
  187. tmp["flag"] =update_flag
  188. tmp["flagx"] = self.__universes_flag[host]
  189. tmp["fpsx"] = int(self.__universes_x_frames[host] / (time.time()-self.__universes_x_time[host]))
  190. tmp["frame"] = self.__frame
  191. #tmp["hosts"] = self.__hosts
  192. tmp["uni"] = self.__univers_nr
  193. tmp["fps"] = self.__universes_fps[host]
  194. self.__universes_info[host] = tmp
  195. if update_flag:
  196. #print( "UPDATE HOST:",host, update_flag,"UNIV:",self.__univers_nr)
  197. self.__universes_dmx[host] = dmx # dmxframe
  198. self.__universes_timer[host] = update_matrix
  199. def get(self,host=""):
  200. if host and host in self.__hosts:
  201. return self.__universes_dmx[host]
  202. dmx = [":"]*512
  203. for i,v in enumerate(self.__universes_matrix):
  204. if type(v) is int:
  205. host = self.__hosts[v]
  206. v = self.__universes_dmx[host][i]
  207. dmx[i] = v
  208. return dmx
  209. def get_mtx(self,host=""):
  210. return self.__universes_matrix
  211. def info(self):
  212. return self.__universes_info
  213. def hosts(self):
  214. x = self.__universes_dmx.keys()
  215. x=list(x)
  216. x.sort()
  217. return x
  218. class Hosts():
  219. def __init__(self):
  220. self.__hosts = [] # LTP order
  221. self.__universes = OrderedDict() # {} # 192.168.0.1 = [0]*512
  222. #self.update(host="localhost",univ=0,dmxframe=[6]*512)
  223. dmxframe = [0]*512
  224. dmxframe[15] = 6
  225. #self.update(host="333.333.333.333",univ=8,dmxframe=dmxframe)
  226. def get_mtx(self,host="", univ=""):
  227. return self.__universes[str(univ)].get_mtx(host)
  228. def get(self,host="", univ=""):
  229. if str(univ) in self.__universes:
  230. return self.__universes[str(univ)].get(host)
  231. else:
  232. return [-8]*512
  233. def hosts(self):
  234. hosts = []
  235. for univ in self.__universes:
  236. x=self.__universes[univ].hosts()
  237. for y in x:
  238. #x=univ.hosts()
  239. if y not in hosts:
  240. hosts.append(y)
  241. hosts.sort()
  242. return hosts
  243. def univs(self):
  244. x=self.__universes.keys()
  245. x=list(x)
  246. #x.sort()
  247. return x
  248. def update(self,host,univ, dmxframe):
  249. #print( "update", host )
  250. if str(univ) not in self.__universes:
  251. self.__universes[str(univ)] = xUniversum(str(univ))
  252. self.__universes[str(univ)].update(host,dmxframe)
  253. def info(self,univ=0):
  254. out = {}
  255. #print self.__universes.keys()
  256. for univ in self.__universes.keys():
  257. #print("INFO:",univ)
  258. x=self.__universes[univ]
  259. out[univ] = x.info()
  260. return out
  261. def toPrintable(nonprintable):
  262. out = ""
  263. for i in str(nonprintable):
  264. printable = string.ascii_letters + string.digits +"/()=?{[]}\;:,.-_ "
  265. if str(i) in printable :
  266. out += str(i)
  267. return out
  268. class Xsocket():
  269. def __init__(self):
  270. self.__poll = 0
  271. self.__data = []
  272. self.__addr = "NONE"
  273. pass
  274. def poll(self):
  275. if not self.__poll:
  276. try:
  277. self.__data, self.__addr = sock.recvfrom(6454)
  278. self.__poll = 1
  279. return 1
  280. except socket.timeout as e:
  281. err = e.args[0]
  282. if err == 'timed out':
  283. sleep(1)
  284. print('recv timed out, retry later')
  285. else:
  286. print(e)
  287. except socket.error as e:
  288. pass
  289. def recive(self):
  290. if self.__poll:
  291. self.__poll = 0
  292. return (self.__data,self.__addr)
  293. def unpack_art_dmx(data):
  294. dmx = []
  295. for i in range(len(data[18:]) ):
  296. x=data[18+i]
  297. #print("x",x)
  298. #print( "data",b'!B', data[18+i])
  299. #x=struct.unpack( b'!B',data[18+i])
  300. #print( "data",b'!B', data[18+i],x)
  301. #x=x[0]
  302. dmx += [x]
  303. return dmx
  304. class Pager(): #scroll thru list
  305. def __init__(self):
  306. self.data = []
  307. self.index = 0
  308. self.wrap = 0
  309. self.maxindex = 0
  310. def append(self,val):
  311. self.data.append(val)
  312. self.check()
  313. def set(self,nr):
  314. self.index = nr
  315. self.check()
  316. def get(self):
  317. self.check()
  318. if self.data:
  319. return self.data[self.index]
  320. def next(self):
  321. self.index += 1
  322. self.check(flag=1)
  323. def prev(self):
  324. self.index -= 1
  325. self.check(flag=1)
  326. def check(self,flag=0):
  327. if flag:
  328. if self.maxindex and self.maxindex <= len(self.data):
  329. max = self.maxindex
  330. else:
  331. max = len(self.data)
  332. else:
  333. max = len(self.data)
  334. if self.wrap:
  335. if self.index >= max:
  336. self.index = 0
  337. elif self.index < 0:
  338. self.index = max-1
  339. else:
  340. if self.index >= max:
  341. self.index = max-1
  342. elif self.index < 0:
  343. self.index = 0
  344. if __name__ == "__main__":
  345. frames = [0]*10000
  346. print("frame",frames)
  347. ohost = Hosts()
  348. try:
  349. print("connecting to ArtNet Port 6454")
  350. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  351. sock.bind(('', 6454))
  352. fcntl.fcntl(sock, fcntl.F_SETFL, os.O_NONBLOCK)
  353. except socket.error as e:
  354. print("Socket 6454 ", "ERR: {0} ".format(e.args))
  355. #raw_input()
  356. #sys.exit()
  357. univers = None
  358. if len(sys.argv) >= 2+1 and sys.argv[1] == "-u":
  359. univers = sys.argv[2]
  360. inp = "q"
  361. univ2= "8"
  362. univers = 0# inp.read(univers)
  363. debug = 0# inp.debug()
  364. packets = 0
  365. #self.__myscreen.getch()
  366. dmx = [0] * 512
  367. fps = 0
  368. fpsi = 0
  369. fpst =int(time.time())
  370. head= "XXX"
  371. dmx_ch_buffer = []
  372. screen=Curses()
  373. #screen=CursesDummy()
  374. #screen.init()
  375. screen.exit()
  376. if 0: #testunivers
  377. while 1:
  378. screen.draw("head",list(range(1,512+1)))
  379. time.sleep(1)
  380. screen.draw("head",[0]*512)
  381. time.sleep(1)
  382. frame = 0
  383. xsocket = Xsocket()
  384. univ_dmx = [ ["x"]*512 ]*16
  385. univ_heads = [ ["none"]*2 ]*16
  386. ttime = time.time()
  387. counter = 0
  388. cmd=[]
  389. mode=""
  390. sel_host=Pager()
  391. sel_host.wrap=1
  392. sel_univ=Pager()
  393. sel_univ.wrap=1
  394. sel_mode=Pager()
  395. sel_mode.wrap=1
  396. sel_mode.data = ["dmx","ltp","mtx","main"] # mtx = matrix
  397. sel_mode.maxindex = len( sel_mode.data )-1
  398. head_uni=""
  399. dmx = []
  400. headlines = ""
  401. try:
  402. while 1:
  403. dmx = univ_dmx[univers]
  404. headlines = univ_heads[univers]
  405. dmx = []
  406. text = ""
  407. if xsocket.poll():
  408. data, addr = xsocket.recive()
  409. head = [data[:18]]
  410. dmx = data[18:]
  411. try:
  412. head = struct.unpack("!8sHBBBBHBB" , head[0] )
  413. except:
  414. continue
  415. head_uni = head[6]/255 # /512 # * 512
  416. head_uni = int(head_uni)
  417. #print("head_uni", head_uni)
  418. if head_uni < len(frames):# and len(data) == 530:
  419. frames[head_uni] += 1
  420. host = addr[0]
  421. dmx = unpack_art_dmx(data)
  422. ohost.update(host,head_uni,dmx)
  423. #screen.exit()
  424. if 0:# len(dmx):
  425. print( host)
  426. print( head_uni, data[:30] )#dmx[:10] )
  427. print( head_uni, dmx[:10] )
  428. #continue
  429. # input command buffer
  430. screen.read()
  431. inp2=screen.inp()
  432. if "q" == inp2:
  433. inp2=""
  434. screen.exit()
  435. sys.exit()
  436. elif "?" == inp2:
  437. mode = "?"
  438. elif "," == inp2:
  439. sel_mode.next()
  440. inp2=""
  441. elif ";" == inp2:
  442. sel_mode.prev()
  443. inp2=""
  444. elif "." == inp2:
  445. sel_univ.next()
  446. inp2=""
  447. elif ":" == inp2:
  448. sel_univ.prev()
  449. inp2=""
  450. elif "-" == inp2:
  451. sel_host.next()
  452. inp2=""
  453. elif "_" == inp2:
  454. sel_host.prev()
  455. inp2=""
  456. elif "#" == inp2:
  457. if "main" in sel_mode.data:
  458. x = sel_mode.data.index( "main")
  459. sel_mode.index = x
  460. sel_mode.check()
  461. inp2=""
  462. if inp2 == "\n":
  463. cmd2 = "".join( cmd).split()
  464. cmd=[]
  465. if len(cmd2) < 2:
  466. pass
  467. elif "C^" in cmd2:
  468. screen.exit()
  469. sys.exit()
  470. elif "univ" in cmd2 or "u" == cmd2[0]:
  471. x=""
  472. if cmd2[1] in sel_univ.data:
  473. x = sel_univ.data.index( cmd2[1])
  474. sel_univ.index = x
  475. sel_univ.check()
  476. elif "mode" in cmd2 or "m" == cmd2[0]:
  477. if cmd2[1] in sel_mode.data:
  478. x = sel_mode.data.index( cmd2[1])
  479. sel_mode.index = x
  480. sel_mode.check()
  481. elif "host" in cmd2 or "h" == cmd2[0]:
  482. try:
  483. x=int(cmd2[1])
  484. sel_host.set(x)
  485. except:
  486. pass
  487. else:
  488. cmd.append(inp2)
  489. sel_univ.data = ohost.univs()
  490. sel_univ.check()
  491. sel_host.data = ohost.hosts()
  492. sel_host.check()
  493. host = sel_host.get()
  494. univ2 = sel_univ.get()
  495. mode = sel_mode.get()
  496. if time.time()-0.12 > ttime:
  497. lines = [ ]
  498. #print("cmd:",cmd)
  499. lines.append(" CMD:" + "".join(cmd) )
  500. if mode=="help" or mode=="?":
  501. lines.append("HILFE[h]: " )
  502. lines.append("MODE [m]: inp, in2 in1 " )
  503. lines.append("UNIV [u]: 0-16 " )
  504. lines.append(" " )
  505. lines.append("HILFE " )
  506. elif mode=="dmx" or mode == "DMX":
  507. ttime = time.time()
  508. dmx=ohost.get(host,univ=univ2)#univ=head_uni)
  509. info=ohost.info()
  510. #lines.append("frame "+str(info.keys()) )
  511. if univ2 in info:
  512. if host in info[univ2] :
  513. lines.append("frame "+str(info[univ2][host]["frame"]))
  514. x=""
  515. for i,v in enumerate(dmx):
  516. if v == 0:
  517. v = "+"
  518. x += str(v).rjust(4," ")
  519. if (i+1) % 20 == 0:# and i:
  520. lines.append(x)
  521. x=""
  522. if x:
  523. lines.append(x)
  524. lines.append(" ")
  525. lines.append(str(ttime))
  526. #screen.draw_lines(lines)
  527. elif mode=="mtx":
  528. ttime = time.time()
  529. dmx=ohost.get_mtx(host,univ=univ2)#univ=head_uni)
  530. info=ohost.info()
  531. #lines.append("frame "+str(info.keys()) )
  532. if univ2 in info:
  533. if host in info[univ2] :
  534. lines.append("frame "+str(info[univ2][host]["frame"]))
  535. x=""
  536. for i,v in enumerate(dmx):
  537. x += str(v).rjust(4," ")
  538. if (i+1) % 20 == 0:# and i:
  539. lines.append(x)
  540. x=""
  541. if x:
  542. lines.append(x)
  543. lines.append(" ")
  544. lines.append(str(ttime))
  545. #screen.draw_lines(lines)
  546. elif mode=="ltp" or mode=="LTP":
  547. ttime = time.time()
  548. dmx=ohost.get(univ=univ2)#head_uni)
  549. #univ2=""
  550. host=""
  551. info=ohost.info()
  552. lines.append("frame "+str(info.keys()) )
  553. x=""
  554. for i,v in enumerate(dmx):
  555. x += str(v).rjust(4," ")
  556. if (i+1) % 20 == 0:
  557. lines.append(x)
  558. x=""
  559. if x:
  560. lines.append(x)
  561. lines.append(" ")
  562. lines.append(str(ttime))
  563. #screen.draw_lines(lines)
  564. else:
  565. ttime = time.time()
  566. x=ohost.get(univ=head_uni)
  567. #lines = []
  568. host=""
  569. univ2=""
  570. info=ohost.info()
  571. jinfo = ""
  572. for i in info:
  573. xl = json.dumps(i) + "======= "
  574. lines.append( xl )
  575. for j in info[i]:
  576. lines.append( " " + json.dumps([j,""]) )
  577. if j not in sel_host.data:
  578. pass#sel_host.append(j)
  579. for k in info[i][j]:
  580. #lines.append( " " + json.dumps( info[i][j]) )
  581. lines.append( " "+str(k).ljust(5," ")+": " + json.dumps( info[i][j][k]) )
  582. lines.append(" ")
  583. lines.append(str(ttime))
  584. #screen.draw_lines(lines)
  585. tmp = ""
  586. tmp += " mode:"+(str(mode).ljust(10," "))
  587. tmp += " univ:"+str(sel_univ.index)+":"+(str(univ2).ljust(10," "))
  588. tmp += " host:"+str(sel_host.index)+":"+(str(host).ljust(10," "))
  589. lines.insert(0,tmp)
  590. tmp = ""
  591. tmp += " univ:"+ (str(sel_univ.data))#.ljust(20," "))
  592. tmp += " list:"+ (str(sel_host.data))#.ljust(20," "))
  593. lines.insert(0,tmp)
  594. screen.draw_lines(lines)
  595. time.sleep(.001)
  596. finally:
  597. screen.exit()
  598. #print(dir(screen))
  599. #print("###")
  600. #print(screen.dir())
  601. #print("###")
  602. #print(dir(curses))
  603. print( "finally",sel_host.index,sel_host.data)