nodescan2.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. # -*- coding: utf-8 -*-
  2. """
  3. Valid-License-Identifier: GPL-2.0-only
  4. SPDX-URL: https://spdx.org/licenses/GPL-2.0-only.html
  5. (c) 2012 micha@librelight.de
  6. """
  7. import time
  8. import socket, struct
  9. import sys
  10. import os
  11. import _thread as thread
  12. import copy
  13. import random
  14. import traceback
  15. sys.stdout.write("\x1b]2;Nodescan\x07")
  16. def UDP_Socket(bind=False,ip='',port=6454):
  17. sock = False
  18. try:
  19. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  20. sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
  21. if bind:
  22. sock.bind((ip, port))
  23. except socket.error as e:
  24. print("Socket 6454 ", "ERR: {0} ".format(e.args))
  25. sock = False
  26. return sock
  27. def ArtPoll(sock=None,ip="2.255.255.255",port=6454):
  28. print("ArtPoll",ip,port)
  29. if not sock:
  30. sock=UDP_Socket()
  31. #port=6454
  32. #ip="2.255.255.255"
  33. PKG=b'Art-Net\x00\x00 \x00\x0e\x06\x00'
  34. print("SEND:",[PKG])
  35. sock.sendto(PKG,(ip,port)) # ArtPol / ping
  36. sock.close()
  37. def convert_mac(MAC):
  38. #MAC = data[201:201+6]
  39. _MAC = []
  40. for x in MAC:
  41. #x = hex(ord(x))[2:]
  42. x = hex(x)[2:]
  43. x = x.rjust(2,"0")
  44. _MAC.append(x)
  45. _MAC = ":".join(_MAC)
  46. return _MAC
  47. def convert_bin(d):
  48. return bin(d)[2:].rjust(8,"0")
  49. def convert_ip(d):
  50. IP="[0,0,0,0]"
  51. _ip = []
  52. try:
  53. _ip.append( d[0] )
  54. _ip.append( d[1] )
  55. _ip.append( d[2] )
  56. _ip.append( d[3] )
  57. IP = str(_ip)
  58. except:pass
  59. return IP
  60. def convert_to_hex(x,d):
  61. out = b""
  62. try:
  63. a = struct.unpack(x, d)[0]
  64. #out = hex(a)
  65. out = "{0:#0{1}x}".format(a,6)
  66. except:
  67. pass
  68. return out
  69. def ArtNet_decode_pollreplay(data):
  70. debug = 0
  71. node = {}
  72. if len(data) < 10: #min opcode
  73. return node
  74. opcode=convert_to_hex("<h",data[8:10])
  75. if opcode != '0x2100': #OpPollReplay
  76. return node
  77. if len(data) < 207: #Mal
  78. return node
  79. #print(data[174:174+4])
  80. #print("===================================================================-")
  81. #print("decode",data[:13])
  82. # UDP PACKAGE VALUE:INDEX:RAGE
  83. CONF = {}
  84. CONF["IP"] = [10,14+1]
  85. CONF["port"] = [14,15+1]
  86. CONF["version"] = [16,17+1]
  87. CONF["NetSwitch"] = [18]
  88. CONF["SubSwitch"] = [19]
  89. CONF["oem"] = [20,21+1]
  90. CONF["ubea"] = [22]
  91. CONF["status"] = [23]
  92. CONF["esta"] = [24,25+1]
  93. CONF["sname"] = [26,26+17]
  94. CONF["lname"] = [44,44+43]
  95. CONF["NodeReport"] = [108,108+20]
  96. CONF["NumPort"] = [173]
  97. CONF["PortTypes"] = [174,174+4]
  98. CONF["GoodInput"] = [178,178+4]
  99. CONF["GoodOutput"] = [182,182+4]
  100. CONF["SwIn"] = [186,186+4]
  101. CONF["SwOut"] = [190,190+4]
  102. #CONF["MSG"] = [108,108+40]
  103. CONF["MAC"] = [201,201+6]
  104. cleanup = ["sname","lname","MSG","NodeReport"]
  105. for k,v in CONF.items():
  106. val = b'undefined'
  107. if len(v) == 2:
  108. val = data[v[0]:v[1]]
  109. if k in cleanup:
  110. val = val.strip(b"\x00")
  111. val = val.decode(errors="ignore")
  112. if len(v) == 1:
  113. val = data[v[0]]
  114. node[k] = val
  115. # ================================
  116. node["MAC"] = convert_mac(node["MAC"])
  117. node["IP"] = convert_ip(node["IP"])
  118. node["status"] = convert_bin(node["status"])
  119. node["opcode"] = opcode
  120. unpack = {"port":b"<H"}
  121. for k,v in node.items():
  122. if k in unpack:
  123. up = unpack[k]
  124. #print(k,v,up)
  125. node[k] = struct.unpack(up,v)[0]
  126. to_hex = {"version":'>H',"oem":'>H',"esta":"<H"}
  127. for k,u in to_hex.items():
  128. if k in node:
  129. v=node[k]
  130. node[k] = convert_to_hex(u,v)
  131. #for k,v in node.items():
  132. # if type(node[k]) is bytes:
  133. # node[k] = v.decode(errors="ignore")
  134. return node
  135. def test():
  136. UDP_ArtPollReplay = b'Art-Net\x00\x00!\x02\x00\x00T6\x19\x03P\x00\x00\x11\x10\x00\x00RUAADN-01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00AVR-ArtNet DMX NODE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Node is ready\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x00\x00\x00\x08\x00\x00\x00\x82\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\xb3\xd5\xfa\xff\xfa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  137. NODE_OLD = {'IP': '[2, 0, 0, 84]', 'port': (6454,), 'version': '\x03P', 'NetSwitch': 0, 'SubSwitch': 0, 'oem': '\x11\x10', 'ubea': 0, 'status': 0, 'esta': 'RU', 'sname': 'AADN-01', 'lname': 'AVR-ArtNet DMX NODE', 'NumPort': 1, 'PortTypes': '\x00\x00\x00', 'GoodInput': '\x08\x00\x00\x00', 'GoodOutput': '\x00\x00\x00', 'SwIn': '\x07\x00\x00\x00', 'SwOut': '\x00\x00\x00\x00', 'MSG': 'Node is ready', 'MAC': '70:b3:d5:fa:ff:fa'}
  138. NODE = {'IP': '[2, 0, 0, 84]', 'port': 6454, 'version': '0x0350', 'NetSwitch': 0, 'SubSwitch': 0, 'oem': '0x1110', 'ubea': 0, 'status': '00000000', 'esta': '0x5552', 'sname': 'AADN-01', 'lname': 'AVR-ArtNet DMX NODE', 'NodeReport': 'Node is ready', 'NumPort': 1, 'PortTypes': b'\x80\x00\x00\x00', 'GoodInput': b'\x08\x00\x00\x00', 'GoodOutput': b'\x82\x00\x00\x00', 'SwIn': b'\x07\x00\x00\x00', 'SwOut': b'\x00\x00\x00\x00', 'MAC': '70:b3:d5:fa:ff:fa', 'opcode': '0x2100'}
  139. node = ArtNet_decode_pollreplay(UDP_ArtPollReplay)
  140. k_miss =[]
  141. if NODE != node:
  142. for k,v in node.items():
  143. if k in NODE:
  144. print(v==NODE[k],"- diff -",k,v, NODE[k])
  145. else:
  146. k_miss.append(k)
  147. print("MISSING KEY:",k_miss)
  148. print(node)
  149. print(NODE)
  150. assert NODE == node
  151. def ArtPollReply(sock=None):
  152. print("ArtPollReply()")
  153. if not sock:
  154. sock=UDP_Socket()
  155. port = 6454
  156. content = []
  157. header = []
  158. # Name, 7byte + 0x00
  159. content.append(b"Art-Net\x00")
  160. # OpCode ArtPollReply -> 0x2100, Low Byte first
  161. content.append(struct.pack('<H', 0x2100))
  162. # Protocol Version 14, High Byte first
  163. content.append( b"\xff\xff\xff\xff")
  164. # Port
  165. content.append(struct.pack('<H', 0x1936))
  166. # Firmware Version
  167. content.append(struct.pack('>H', 200))
  168. # Net and subnet of this node
  169. net = 0
  170. subnet = 0
  171. content.append(chr(net))
  172. content.append(chr(subnet))
  173. # OEM Code (E:Cue 1x DMX Out)
  174. content.append(struct.pack('>H', 0x0360))
  175. # UBEA Version -> Nope -> 0
  176. content.append(chr(0))
  177. # Status1
  178. content.append(struct.pack('>H', 0b11010000))
  179. # Manufacture ESTA Code
  180. content.append(chr(0))
  181. hostname=os.popen("hostname").read().strip()
  182. # Short Name, len == 30
  183. content.append(hostname.ljust(18,"\x00")[:30])
  184. # Long Name, len == 64
  185. content.append(("LibreLight "+hostname).ljust(64,"\x00")[:64] )
  186. content2=[]
  187. for c in content:
  188. if type(c) is not bytes:
  189. c= bytes(c,"ascii")
  190. content2.append(c)
  191. content = b''.join(content2)
  192. fill = 240-len(content)-1
  193. print()
  194. if fill > 0:
  195. content = content + b"\x00"*fill
  196. def inject(i,v,content):
  197. content = content[:i]+ v + content[i+1:]
  198. return content
  199. def create_ip(IP,content):
  200. #CONF["IP"] = [10,14+1]
  201. x=[]
  202. j=10
  203. for i in IP.split("."):
  204. i = 33
  205. _ip = struct.pack("B",int(i))
  206. # content = content[:10+j]+ _ip + content[10+j:]
  207. content = inject(j,_ip,content)
  208. j+=1
  209. x.append([_ip,i])
  210. print(IP,x)
  211. #CONF["MAC"] = [201,201+6]
  212. content = inject(201,b"\xfa",content)
  213. content = inject(201+1,b"\xfa",content)
  214. content = inject(201+2,b"\xfa",content)
  215. content = inject(201+3,b"\xfa",content)
  216. content = inject(201+4,b"\xfa",content)
  217. content = inject(201+5,b"\xfa",content)
  218. #CONF["oem"] = [20,21+1]
  219. content = inject(20,b"\x11",content)
  220. content = inject(20+1,b"\x10",content)
  221. content = inject(100,b"\x11",content)
  222. print()
  223. IP="2.0.0.255"
  224. create_ip(IP,content)
  225. sock.sendto(content, (IP, port))
  226. IP="10.10.10.255"
  227. content = [0]*(282-42) # new PKG
  228. content[0:0] = b"Art-Net\x00"
  229. content[8:9+1] = b"\x00\x21" # Protocol 0x2100
  230. content[10:13+1] = b"\xaa\xaa\xaa\xaa" # IP
  231. content[20:21+1] = b"\x11\x10" # oem
  232. content[26] = ord("A") # SHORT NAME
  233. content[26+18] = ord("B") #0x42 # LONG NAME
  234. content[108:108] = b"HalloX" #0x42 # MSG 26+18+64
  235. print("send" ,[content])
  236. j=0
  237. content2 = content[:]
  238. for i in IP.split("."):
  239. content2[10+j] = int(i)
  240. j+=1
  241. content2 = bytes(content2)
  242. #create_ip(IP,content)
  243. sock.sendto(content2, (IP, port))
  244. print("send" ,[content])
  245. IP="192.168.2.255"
  246. j=0
  247. content2 = content[:]
  248. for i in IP.split("."):
  249. content2[10+j] = int(i)
  250. j+=1
  251. #create_ip(IP,content)
  252. content2 = bytes(content2)
  253. sock.sendto(content2, (IP, port))
  254. IP="2.0.0.255"
  255. j=0
  256. content2 = content[:]
  257. for i in IP.split("."):
  258. content2[10+j] = int(i)
  259. j+=1
  260. #create_ip(IP,content)
  261. content2 = bytes(content2)
  262. sock.sendto(content2, (IP, port))
  263. print("send" ,[content2])
  264. #print(dir(sock),sock.getsockname())
  265. def main():
  266. print("start main()")
  267. sock = UDP_Socket(bind=True,ip='',port=6454)
  268. print("-- loop --")
  269. while sock:
  270. data, addr = sock.recvfrom(300)
  271. opcode=""
  272. if len(data) >= 10:
  273. opcode=convert_to_hex("<h",data[8:10])
  274. print("-",addr,len(data),opcode)
  275. print()
  276. nodes = ArtNet_decode_pollreplay(data)
  277. for k,v in nodes.items():
  278. print("-",[k,v])
  279. print("end main()")
  280. if __name__ == "__main__":
  281. test()
  282. if "--ArtPoll" in sys.argv:
  283. ArtPoll()
  284. if "--ArtPollReplay" in sys.argv:
  285. ArtPollReply()
  286. else:
  287. main()