diff --git a/artnet.py b/artnet.py index 5150d9fe..e453aa0b 100644 --- a/artnet.py +++ b/artnet.py @@ -1,20 +1,22 @@ import bpy -from socket import (socket, AF_INET, SOCK_DGRAM, SHUT_RDWR) -from struct import unpack +from socket import socket, AF_INET, SOCK_DGRAM, SHUT_RDWR, SOL_SOCKET, SO_BROADCAST, SO_REUSEADDR +import struct import threading from dmx.data import DMX_Data - +from dmx.network import DMX_Network # ArtnetPacket class taken from here: # https://gist.github.com/alarrosa14/07bd1ee88a19204cbf22 # Thank you, @alarrosa! ARTNET_PORT = 6454 -class ArtnetPacket: - ARTNET_HEADER = b'Art-Net\x00' +class ArtnetPacket: + ARTNET_HEADER = b"Art-Net\x00" + opcode_ArtDMX = 0x5000 + opcode_ArtPoll = 0x2000 def __init__(self): self.op_code = None @@ -26,38 +28,36 @@ def __init__(self): self.data = None def __str__(self): - return ("ArtNet package:\n - op_code: {0}\n - version: {1}\n - " - "sequence: {2}\n - physical: {3}\n - universe: {4}\n - " - "length: {5}\n - data : {6}").format( - self.op_code, self.ver, self.sequence, self.physical, - self.universe, self.length, [c for c in self.data]) + return ("ArtNet package:\n - op_code: {0}\n - version: {1}\n - " "sequence: {2}\n - physical: {3}\n - universe: {4}\n - " "length: {5}\n - data : {6}").format( + self.op_code, self.ver, self.sequence, self.physical, self.universe, self.length, [c for c in self.data] + ) def build(udp_data): - - if unpack('!8s', udp_data[:8])[0] != ArtnetPacket.ARTNET_HEADER: + if struct.unpack("!8s", udp_data[:8])[0] != ArtnetPacket.ARTNET_HEADER: print("Received a non Art-Net packet") return None packet = ArtnetPacket() - (packet.op_code, packet.ver, packet.sequence, packet.physical, - packet.universe, packet.length) = unpack('!HHBBHH', udp_data[8:18]) - (packet.universe,) = unpack('", 60000)) self._socket.settimeout(30) self._stopped = False @@ -70,78 +70,130 @@ def stop(self): def run(self): while not self._stopped: - try: - data, _ = self._socket.recvfrom(1024) - packet = ArtnetPacket.build(data) - self._dmx.artnet_status = 'online' - - if (packet.universe >= len(self._dmx.universes)): - continue - if (not self._dmx.universes[packet.universe]): - continue - if (self._dmx.universes[packet.universe].input != 'ARTNET'): - continue - - DMX_Data.set_universe(packet.universe, bytearray(packet.data), "ARTNET") - - #print(packet) - except Exception as e: - print(e) - #self._socket.close() - print('Closing socket...') - self._dmx.artnet_status = 'socket_close' + data = self._socket.recv(1024) + print("data", data) + if struct.unpack("!8s", data[:8])[0] != ArtnetPacket.ARTNET_HEADER: + continue + opcode = struct.unpack("", 6454)) + + def handleArtNet(self, data): + packet = ArtnetPacket.build(data) + self._dmx.artnet_status = "online" + if packet.universe >= len(self._dmx.universes): + return + if not self._dmx.universes[packet.universe]: + return + if self._dmx.universes[packet.universe].input != "ARTNET": + return + DMX_Data.set_universe(packet.universe, bytearray(packet.data), "ARTNET") + + def build_ArtPollReply(self): + """Builds an ArtPollReply message.""" + content = [] + # Name, 7byte + 0x00 + content.append(b"Art-Net\x00") + # OpCode ArtPollReply -> 0x2100, Low Byte first + content.append(struct.pack("H', 14)) # <- not in ArtPollReply + # IP + if len(DMX_Network.cards(None, None)): + ip = DMX_Network.cards(None, None)[-1][0] + else: + ip = "0.0.0.0" + ip = [int(i) for i in ip.split(".")] + content += [i.to_bytes() for i in ip] + # Port + content.append(struct.pack(" Nope -> 0 + # Status1 + content.append(struct.pack("H", 0)) + # Manufacture ESTA Code + content.append(struct.pack("H", 8)) #0000 + content.append(struct.pack(">L", 0)) #0000 + content.append(struct.pack("46s", b"")) #0000 + + # stitch together + return b"".join(content) + @staticmethod def run_render(): bpy.context.scene.dmx.render() - return (1.0/60.0) - + return 1.0 / 60.0 + @staticmethod def enable(): - if (DMX_ArtNet._thread): - return print('ArtNet client already started.') - + if DMX_ArtNet._thread: + return print("ArtNet client already started.") + dmx = bpy.context.scene.dmx - - print('Starting ArtNet client...') - print('\t%s:%s' % (dmx.artnet_ipaddr, ARTNET_PORT)) - dmx.artnet_status = 'socket_open' + + print("Starting ArtNet client...") + print("\t%s:%s" % (dmx.artnet_ipaddr, ARTNET_PORT)) + dmx.artnet_status = "socket_open" DMX_ArtNet._thread = DMX_ArtNet(dmx.artnet_ipaddr) DMX_ArtNet._thread.start() - + bpy.app.timers.register(DMX_ArtNet.run_render) - dmx.artnet_status = 'listen' - print('ArtNet client started.') - + dmx.artnet_status = "listen" + print("ArtNet client started.") + @staticmethod def disable(): - if (bpy.app.timers.is_registered(DMX_ArtNet.run_render)): + if bpy.app.timers.is_registered(DMX_ArtNet.run_render): bpy.app.timers.unregister(DMX_ArtNet.run_render) - + dmx = bpy.context.scene.dmx - if (DMX_ArtNet._thread): - print('Stopping ArtNet client...', end='', flush=True) - dmx.artnet_status = 'stop' + if DMX_ArtNet._thread: + print("Stopping ArtNet client...", end="", flush=True) + dmx.artnet_status = "stop" DMX_ArtNet._thread.stop() DMX_ArtNet._thread.join() DMX_ArtNet._thread = None - dmx.artnet_status = 'offline' - print('DONE') - elif(dmx): - dmx.artnet_status = 'offline' - + dmx.artnet_status = "offline" + print("DONE") + elif dmx: + dmx.artnet_status = "offline" + @staticmethod def status(): return [ - ('offline', 'Offline', ''), - ('socket_open', 'Opening socket...', ''), - ('listen', 'Waiting for data...', ''), - ('online', 'Online', ''), - ('stop', 'Stopping thread...', ''), - ('socket_close', 'Closing socket...', ''), + ("offline", "Offline", ""), + ("socket_open", "Opening socket...", ""), + ("listen", "Waiting for data...", ""), + ("online", "Online", ""), + ("stop", "Stopping thread...", ""), + ("socket_close", "Closing socket...", ""), ]