#! /usr/bin/python # -*- coding: utf-8 -*- """ Valid-License-Identifier: GPL-2.0-only SPDX-URL: https://spdx.org/licenses/GPL-2.0-only.html (c) 2012 micha@librelight.de """ print("suche ArtNet Nodes ") import time import socket, struct import sys import _thread as thread import copy import random import traceback sys.stdout.write("\x1b]2;Nodescan\x07") print(socket.AF_INET) try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(('', 6454)) sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) except socket.error as e: print("Socket 6454 ", "ERR: {0} ".format(e.args)) #sys.exit() try: sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock2.bind(('', 6455)) sock2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) except socket.error as e: print("Socket2 6455 ", "ERR: {0} ".format(e.args)) #sys.exit() print(socket.AF_INET) sock_cmd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def bind_cmd_node(): global sock_cmd try: sock_cmd.bind(('', 7601)) #7601 #sock_cmd.bind(('', 49737)) sock_cmd.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) except socket_cmd.error as e: print("Socket 6454 ", "ERR: {0} ".format(e.args)) sys.exit() if __name__ == "__main__": bind_cmd_node() def ArtNet_poll(ip,port=6454): print("POLL",[ip,port],end="") #traceback.print_exception() #try: if 1: sock.sendto(b'Art-Net\x00\x00 \x00\x0e\x06\x00',(ip,port)) # ArtPol / ping print(" OK ;",end="") #send_node_cmd(ip=(2,255,255,255),cmd="CMD GT ") #except Exception as e: # print("Exception ArtNet-POLL",e,) print() def ArtPollReplyDelay(): time.sleep(1) ArtPollReply() def ArtPollReply(): print("ArtPollReply()") port = 6454 content = [] header = [] # Name, 7byte + 0x00 content.append("Art-Net\x00") # OpCode ArtPollReply -> 0x2100, Low Byte first content.append(struct.pack('<H', 0x2100)) # Protocol Version 14, High Byte first content.append(struct.pack('>H', 14)) # IP #ip = [int(i) for i in self.own_ip.split('.')] ip = [2, 0, 0, 10] content += [chr(i) for i in ip] # Port content.append(struct.pack('<H', 0x1936)) # Firmware Version content.append(struct.pack('>H', 200)) # Net and subnet of this node net = 0 subnet = 0 content.append(chr(net)) content.append(chr(subnet)) # OEM Code (E:Cue 1x DMX Out) content.append(struct.pack('>H', 0x0360)) # UBEA Version -> Nope -> 0 content.append(chr(0)) # Status1 content.append(struct.pack('>H', 0b11010000)) # Manufacture ESTA Code content.append('LL') # Short Name content.append('LOOP-OpPollReplay\x00') # Long Name content.append('LOOP-OpPollReplay_ArtNet_Node' + '_' * 34 + '\x00') content.append('\x00'*100) # stitch together content = ''.join(content) #print(self.lang['send_ArtPollReply']) #self.s.sendto(content, ("<broadcast>", self.__port)) print("send" ,[content]) sock.sendto(content, ("<broadcast>", port)) sock.sendto(content, ("2.0.0.255", port)) sock.sendto(content, ("10.10.10.255", port)) sock.sendto(content, ("192.168.2.255", port)) #sock.sendto(content, ("2.0.0.255", port)) #sock.sendto def testBounce(ip,port): print(("TESTBOUNCE", (ip, port))) try: sock.sendto("TESTBOUNCE " +ip, (ip, port)) except socket.error as e: print("Socket", "ERR: {0} ".format(e.args)) print(("TESTBOUNCE", (ip, port))) def reciveBounce(timeout=10): start = time.time() while 1: data = sock.recv(500) print("bounce",data) #data, addr = sock.recvfrom(500) if data: #print(addr) print("rBounte:",data) print() if time.time() > start+timeout: print("timeout stopping reciveBounce ") break def poll(): port = 6454 #ip = "255.255.255.255" #ArtNet_poll(ip) #ip = "<broadcast>" #ArtNet_poll(ip) ip = "192.168.0.255" ArtNet_poll(ip) ip = "192.168.0.99" ArtNet_poll(ip) ip = "2.255.255.255" ArtNet_poll(ip) ip = "2.0.0.255" ArtNet_poll(ip) ip = "2.255.255.255" ArtNet_poll(ip) #ip = "2.0.0.255" #ArtNet_poll(ip) #ip = "2.255.255.255" #ArtNet_poll(ip) print("") class ArtNetNodes(): def __init__(self): print("CONSTRUCKT:",self) self.__nodes = [] self.__nodes_mac = [] self.__lock = thread.allocate_lock() self.__tick = 0 #self.__lock.acquire() #self.__lock.release() def clear(self): self.__lock.acquire() self.__nodes = [] self.__lock.release() def add(self,add_node): #print("ArtNetNodes.add()",add_node) #for i in add_node: # print(i,[add_node[i]]) #print() #print("add",add_node) try: self.__lock.acquire() update_node = 0 if "MSG" in add_node and "BOOT" in add_node["MSG"].upper(): BOOT = time.time() print(" BOOOOOOOOT") else: BOOT = 0 #self.__nodes_mac = [] for node in self.__nodes: info = node["MAC"],node["IP"].ljust(16," "),[node["SwIn"],node["SwOut"],node["PortTypes"]] node_match = 1 keys = ["MAC","SwOut","SwIn","PortTypes"] for i in keys: if node[i] != add_node[i]: node_match = 0 break if node_match: # NODE MAC update_node = 0 for i in add_node: UPDATECOUNTER = node["UPDATECOUNTER"] #print("update i:",i,add_node[i]) if i not in node: node[i] = "" if node[i] != add_node[i]: node_match = 0 update_node += 1 node[i] = add_node[i] UPDATECOUNTER +=1 self.__tick += 1 #break if update_node: node["UPDATECOUNTER"] = UPDATECOUNTER node["UPDATESTAMP"] = time.time() node["REFRESHSTAMP"] = time.time() if BOOT: node["BOOT"] = BOOT print("UPDATE NODE".ljust(16," "),info) else: #print("NODE NOT CHANGE".ljust(16," "),info) node["REFRESHSTAMP"] = time.time() update_node = 1 update_node = 0 print("x-node:",update_node,add_node) if not update_node: # ADD NEW NODE node = add_node if node: print("add_node",node) node["BOOT"] = BOOT info = node["MAC"],node["IP"].ljust(16," "),[node["SwIn"],node["SwOut"],node["PortTypes"]] node["UPDATECOUNTER"] = 1 node["REFRESHSTAMP"] = time.time() node["UPDATESTAMP"] = time.time() print("ADD NEW NODE".ljust(16," "),node["UPDATECOUNTER"],info) self.__tick += 1 self.__nodes += [node] finally: #print("release lock") self.__lock.release() def tick(self): self.__lock.acquire() x = self.__tick self.__lock.release() return x return random.randint(0,1000) def get(self): self.__lock.acquire() out = [] #out = {} if self.__nodes: out = copy.deepcopy(self.__nodes) #for node in self.__nodes: # out[node["MAC"]] = node self.__lock.release() return out def recive(self): print("-- NODE READ LOOP START ---") print() while 1: data, addr = sock.recvfrom(300) new_node = ArtNet_decode_pollreplay( data ) #print("rvc loop",addr) if new_node: #print("rcv 333",new_node) self.add(new_node) time.sleep(0.001) print("-- NODE READ LOOP END ---") print() def loop(self): thread.start_new_thread(self.recive, () ) time.sleep(5) #poll() Reciver = ArtNetNodes def ArtNet_decode_pollreplay(data): debug = 0 node = {} if len(data) >= 10: #min opcode opcode = data[8:9+1] #print([opcode]) #if opcode != struct.pack("<H",0x5000): #OpPollReplay if opcode == struct.pack("<H",0x2100): #OpPollReplay if len(data) >= 207: #Mal #if debug:print("-----------------------------------------") print("===================================================================-") print("decode",data[:13]) if debug:print([opcode] ,"OpPollReplay") _ip = [] #print("data[10]",data[10]) _ip.append( data[10] ) _ip.append( data[11] ) _ip.append( data[12] ) _ip.append( data[13] ) node["IP"] = str(_ip) if debug:print([_ip]) _port = struct.unpack("<H",data[14:15+1] ) #Versinfo = struct.unpack("<H",data[16:17+1] ) Versinfo = data[16:17+1] node["port"] = _port if debug:print("_port :", [_port ]) node["version"] = Versinfo if debug:print("Version:",[Versinfo]) NetSwitch = data[18] node["NetSwitch"] = NetSwitch if debug:print("NetSwitch:",[NetSwitch]) SubSwitch = data[19] node["SubSwitch"] = SubSwitch if debug:print("SubSwitch:",[SubSwitch]) #oem = struct.unpack("<H",data[19:20+1] ) oem = data[20:21+1] node["oem"] = oem if debug:print("oem",[oem]) ubea = data[22] node["ubea"] = ubea if debug:print("ubea ver.",[ubea]) stat = data[23] node["status"] = stat if debug:print("Status1 ",[stat]) esta = data[24:25+1] node["esta"] = esta if debug:print("esta Manuf",[esta]) sname = data[26:26+17] #if debug:print(len(sname) #17+1) sname = sname.strip(b"\x00") node["sname"] = sname lname = data[44:44+43] #if debug:print(len(lname) #43+1) lname = lname.strip(b"\x00") node["lname"] = lname NodeReport = data[108:108+20] NodeReport = NodeReport.strip(b"\x00") #if debug:print("Node",node_nr,addr) if debug:print("43r:",[sname,lname,NodeReport]) NumPort = data[173] node["NumPort"] = NumPort if debug:print("NumPort",[NumPort]) PortTypes = data[174:174+4] node["PortTypes"] = PortTypes if debug:print("PortTypes",[PortTypes]) GoodInput = data[178:178+4] node["GoodInput"] = GoodInput if debug:print("GoodInput",[GoodInput]) GoodOutput = data[182:182+4] node["GoodOutput"] = GoodOutput if debug:print("GoodOutput",[GoodOutput]) SwIn = data[186:186+4] node["SwIn"] = SwIn if debug:print("SwIn",[SwIn]) SwOut = data[190:190+4] node["SwOut"] = SwOut if debug:print("SwOut",[SwOut]) msg = data[108:108+40] node["MSG"] = msg.replace(b"\x00",b"")#.decode(errors="ignore") if debug:print("MSG",[msg]) MAC = data[201:201+6] _MAC = [] for x in MAC: #x = hex(ord(x))[2:] x = hex(x)[2:] x = x.rjust(2,"0") _MAC.append(x) #hex(ord("\xf9"))[2:] if debug:print("MAC",[":".join(_MAC)]) node["MAC"] = ":".join(_MAC) #node_nr += 1 #if debug:print([addr,data]) #print() for k,v in node.items(): if type(node[k]) is bytes: node[k] = v.decode(errors="ignore") else: print(opcode, len(data)) return node def ArtAddress(ip="192.168.0.99" ,ShortName="ShortName", LongName="LongName",Port="",Universes=0,raw=0): node_nr = 1 #send port port = 7600 port = 6454 print( ip) data = [] # [struct.pack('<B', 0)]*150 header = [] # Name, 7byte + 0x00 header.append(b"Art-Net\x00") # OpCode ArtDMX -> 0x6000, Low Byte first header.append(struct.pack('<H', 0x6000)) # Protocol Version 14, High Byte first header.append(struct.pack('>H', 14)) data = header[:] # NetSwitch data.append(struct.pack('<B',128)) # no change 0x7f data.append(struct.pack('<B', 0)) # filler #Short Name sname = ShortName[:17] sname = sname.ljust(18,"\x00") data.append( sname ) lname = LongName[:63] lname = lname.ljust(64,"\x00") #lname = lname[:-2]+"X\x00" data.append( lname ) print( "len sname:lname",len(sname),len(lname)) #SwIn 4; Port-Adress # univers 0-f == \x80 - \x8f i = 4 i=int(Universes)+1 #random.randint(0,99) data.append(struct.pack('<B', 127+i)) data.append(struct.pack('<B', 127+i)) data.append(struct.pack('<B', 127+i)) data.append(struct.pack('<B', 127+i)) #SwOut 4; Port-Adress data.append(struct.pack('<B', 127+i)) data.append(struct.pack('<B', 127+i)) data.append(struct.pack('<B', 127+i)) data.append(struct.pack('<B', 127+i)) #SubSwitch comination with Swin[] SwOut[] data.append(struct.pack('<B', 0)) # SubSwitch Write 128 data.append(struct.pack('<B', 255)) data.append(struct.pack('<B', 0)) data.append(struct.pack('<B', 0)) #data.append("\xf4") #print( ["ArtAdress SEND:",data,(ip,port)] ) data2 = b"" for d in data: #print(d,type(d)) if type(d) is str: data2+=bytes(d,"utf-8") elif type(d) is bytes: data2+=d else: data2+=bytes(str(d),"ascii") print(data2) if raw: return data2,(ip,port) sock.sendto(data2 ,(ip,port)) def set_ip4(cur_ip=(2,0,0,91),new_ip=(2,0,0,201),new_netmask=(255,0,0,0)): #send ip port = 7600 #print(ip) data = [] #New ip #_ip = [192, 168, 2, 91] _ip = [ 2, 0, 0, 181] # CLASS C NET _ip = [ 2, 0, 0, 101] # CLASS C NET #_ip = [192, 168, 0, 91] _ip = new_ip print("NEW NODE _ip:", _ip) data.append(struct.pack('<B', _ip[0])) data.append(struct.pack('<B', _ip[1])) data.append(struct.pack('<B', _ip[2])) data.append(struct.pack('<B', _ip[3])) #_ip = [255, 255, 255, 255] # cange all nodes in Network to the same _ip ! DANGER ! #_ip = [002, 000, 000, 255] # cange all nodes in subnet to the same _ip ! DANGER ! _ip = [ 2, 0, 0, 199] # CLASS A NET _ip = [192, 168, 0, 91] #_ip = [ 2, 0, 0, 191] # CLASS C NET _ip = cur_ip print("OLD NODE _ip:", _ip) #OLD _ip , Target Node to change data.append(struct.pack('<B', _ip[0])) data.append(struct.pack('<B', _ip[1])) data.append(struct.pack('<B', _ip[2])) data.append(struct.pack('<B', _ip[3])) ip = ".".join(str(x) for x in _ip) #print("send to ip:", ip) # NETMASK MASK = [] netmask = [255, 255, 255 , 0] #fast CLASS C funktioniert #netmask = [255, 0, 0 , 0] #CLASS C funkioniert nicht netmask = new_netmask print("NEW NODE net:",netmask) MASK.append(struct.pack('<B', netmask[0])) MASK.append(struct.pack('<B', netmask[1])) MASK.append(struct.pack('<B', netmask[2])) MASK.append(struct.pack('<B', netmask[3])) data += MASK data += [struct.pack('<B', 255)]*11 print("------------------------------") data = b'CMD IP '+ b"".join(data) print("SENDING TO ",(ip,port)) print([data]) #, cur_ip=(2,0,0,91)) #sock.sendto(data ,(ip,port)) sock.sendto(data ,(ip,port)) def send_cmd(ip=(2,0,0,91),cmd=""): node_nr = 1 port = 7600 print(ip) data = [] _ip = [ 2, 0, 0, 91] # CLASS C NET print("NEW NODE _ip:", _ip) data.append(struct.pack('<B', _ip[0])) data.append(struct.pack('<B', _ip[1])) data.append(struct.pack('<B', _ip[2])) data.append(struct.pack('<B', _ip[3])) #_ip = [255, 255, 255, 255] # cange all nodes in Network to the same _ip ! DANGER ! #_ip = [002, 000, 000, 255] # cange all nodes in subnet to the same _ip ! DANGER ! _ip = [ 2, 0, 0, 199] # CLASS A NET _ip = [ 2, 0, 0, 91] # CLASS A NET #_ip = [192, 168, 0, 91] _ip = [ 2, 0, 0, 255] # CLASS C NET #_ip = [ 2, 255, 255, 255] # CLASS C NET print("OLD NODE _ip:", _ip) #OLD _ip , Target Node to change data.append(struct.pack('<B', _ip[0])) data.append(struct.pack('<B', _ip[1])) data.append(struct.pack('<B', _ip[2])) data.append(struct.pack('<B', _ip[3])) ip = ".".join(str(x) for x in ip) print("send to ip:", ip) # NETMASK MASK = [] netmask = [255, 255, 255 , 0] #fast CLASS C funktioniert netmask = [255, 0, 0 , 0] #CLASS C funkioniert nicht print("NEW NODE net:",netmask) MASK.append(struct.pack('<B', netmask[0])) MASK.append(struct.pack('<B', netmask[1])) MASK.append(struct.pack('<B', netmask[2])) MASK.append(struct.pack('<B', netmask[3])) data += MASK data += [struct.pack('<B', 255)]*11 print("------------------------------") data = 'CMD '+cmd+' '+ "".join(data) print("SENDING TO ",(ip,port)) print([data] ) #sock.sendto(data ,(ip,port)) sock.sendto(data ,(ip,port)) def pack_ip(_ip): data = [b"\x00",b"\x00", b"\x00", b"\x00"] if _ip: data[0] = struct.pack('<B', int(_ip[0])) data[1] = struct.pack('<B', int(_ip[1])) data[2] = struct.pack('<B', int(_ip[2])) data[3] = struct.pack('<B', int(_ip[3])) return data def send_node_cmd(ip="",ip2="",cmd=""): print() port = 7600 data = [] print("send_node_cmd",ip,ip2,cmd,port) data = pack_ip(ip[:]) print("ip",ip,ip2) if len(ip2) == 4: ip = ip2 if len(ip) == 4: ip = ".".join(map(str,ip)) print("send to ip:", ip) data2="" if not cmd: data2 = 'CMD GT' data2 = 'CMD ST' data2 = 'DMX OUT STORE' data2 = 'CMD DMX=IN ' data2 = 'CMD DMX=OUT ' data2 = 'CMD DMX=PIN ' if type(cmd) == bytes: data2 = cmd else: data2 = bytes(str(cmd),"ascii",errors="ignore") print([data2],type(data2) ) data2 = data2.ljust(20,b" ") + b"".join(data) print("SENDING COMMAND TO ",[data2],(ip,port)) sock.sendto(data2 ,(ip,port)) node_cmd_buf_list = [] def node_cmd_recive(): global node_cmd_buf_list #sock.sendto('\x00\x00\x00\x00\x00',(ip,port)) # ArtPol / ping while 1: data, addr = sock_cmd.recvfrom(5000) #print(len(data)) #print([addr,data]) if len(data) == 207: print() else: print("NODE CMD RESPONSE:", [addr,data]) node_cmd_buf_list = [addr,data] #print([data]) pass time.sleep(0.05) #send_node_cmd(ip="",cmd="CMD DMX=IN") #send_node_cmd(ip="",cmd="CMD DMX=OUT") #send_node_cmd(ip=(2,0,0,91),cmd="CMD DMX=PIN") #send_node_cmd(ip=(2,0,0,91),cmd="DMX OUT STORE") #send_node_cmd(ip=(2,0,0,255),cmd="DMX OUT STORE") #send_node_cmd(ip=(2,0,0,201),cmd="DMX OUT STORE") #send_node_cmd(ip=(2,0,0,255),ip2=(2,255,255,255),cmd="DMX OUT STORE") #send_node_cmd(ip=(2,0,0,201),ip2=(2,255,255,255),cmd="DMX OUT STORE") #send_node_cmd(ip=(2,0,0,255),ip2=(2,0,0,201),cmd="DMX OUT STORE") #send_node_cmd(ip=(2,0,0,201),ip2=(2,0,0,201),cmd="DMX OUT STORE") #send_node_cmd(ip=(2,0,0,201),ip2=(255,255,255,255),cmd="DMX OUT STORE") #send_node_cmd(ip=(255,255,255,255),ip2=(255,255,255,255),cmd="DMX OUT STORE") #exit() # if __name__ == "__main__": thread.start_new_thread(node_cmd_recive, () ) #send_node_cmd(ip=(2,0,0,91),cmd="DMX OUT STORE") send_node_cmd(ip=(2,255,255,255),cmd="CMD GT ") rx = ArtNetNodes() rx.loop() z = 0 while 1: nodes = rx.get() #print(len(nodes)) if z % 10 == 0: print() pass print("node count",len(nodes),rx.tick(),2 ) #for i in nodes: #print(i) z += 1 time.sleep(0.2) print() print("time out") raw_input("ENDE")