@@ -108,7 +108,9 @@ def __init__(self, portaddress: int):
108108 self .last_data = bytearray (DMX_UNIVERSE_SIZE )
109109 self ._last_seq = 1
110110 self ._last_publish : float = 0.0
111+ self ._last_tod_request : float = 0.0
111112 self .publisherseq : dict [Tuple [DGAddr , int ], int ] = {}
113+ self .tod_uuids : set [bytes ] = set ()
112114
113115 def split (self ) -> Tuple [int , int , int ]:
114116 # name net:sub_net:universe
@@ -159,6 +161,8 @@ def __init__(self, client: "ArtNetClient"):
159161 0x2000 : self .on_art_poll ,
160162 0x2100 : self .on_art_poll_reply ,
161163 0x5000 : self .on_art_dmx ,
164+ 0x8000 : self .on_art_tod_request ,
165+ 0x8100 : self .on_art_tod_data ,
162166 }
163167 client .protocol = self
164168 self .node_report_counter = 0
@@ -176,7 +180,7 @@ def datagram_received(self, data: bytes, addr: DGAddr) -> None:
176180 if h :
177181 h (addr , data [10 :])
178182 else :
179- logger .debug (
183+ logger .info (
180184 f"Received unsupported Art-Net: op { hex (opcode )} from { addr } : { data [10 :]!r} "
181185 )
182186 else :
@@ -335,6 +339,44 @@ def on_art_dmx(self, addr: DGAddr, data: bytes) -> None:
335339 # Only two sources are allowed to contribute to the values in the universe
336340 u .last_data [0 :chlen ] = data [8 : 8 + chlen ]
337341
342+ def on_art_tod_request (self , addr : DGAddr , data : bytes ) -> None :
343+ (ver ,) = struct .unpack ("<H" , data [0 :2 ])
344+ net = data [11 ]
345+ cmd = data [12 ]
346+ address_count = data [13 ]
347+ # net+address => universe address
348+ for universe in [int (x ) + net << 8 for x in data [14 : 14 + address_count ]]:
349+ logger .debug (
350+ f"Received Art-Net TOD request: ver { ver } cmd { cmd } universe { universe } from { addr } "
351+ )
352+ # TODO: if we are an output, answer this with our cached tod table by sending
353+ # art_tod_data packets
354+
355+ def on_art_tod_data (self , addr : DGAddr , data : bytes ) -> None :
356+ ver , rdm_ver , port = struct .unpack ("<HBB" , data [0 :4 ])
357+ # 6 bytes spare
358+ bind_index , net , response , address , tot_uid , block_count , uid_count = (
359+ struct .unpack ("<BBBBHBB" , data [10 :18 ])
360+ )
361+ portaddress = net << 8 + address
362+ TOD_FULL = 0
363+
364+ if response != TOD_FULL :
365+ logger .info (
366+ "Got unexpected art_tod_data with response {response} from {addr}"
367+ )
368+ return
369+
370+ u = self .client ._get_create_universe (portaddress )
371+
372+ # I think tot_uid and block_count are *paging* related if more tod UIDs than can fit in a packet
373+ for i in range (uid_count ):
374+ uid = data [18 + i * 6 : 18 + (i + 1 ) * 6 ]
375+ u .tod_uuids .add (uid )
376+ logger .info (
377+ f"Received Art-Net TOD entry: universe { portaddress } uid={ uid .hex ()} from { addr } "
378+ )
379+
338380 async def art_poll_task (self ) -> None :
339381 while True :
340382 await asyncio .sleep (0.1 )
@@ -343,6 +385,8 @@ async def art_poll_task(self) -> None:
343385 for u in self .client ._publishing :
344386 if t > u ._last_publish + 1.0 :
345387 self ._send_art_dmx (u )
388+ if t > u ._last_tod_request + 5.0 :
389+ self ._send_art_tod_request (u )
346390
347391 if t > self ._last_poll + 2.0 :
348392 self ._send_art_poll ()
@@ -468,6 +512,37 @@ def _send_art_dmx_subscriber(
468512 if self .transport :
469513 self .transport .sendto (message , addr = (node .ip , node .udpport ))
470514
515+ def _send_art_tod_request (self , universe : ArtNetUniverse ):
516+ logger .debug (f"sending art tod request for { universe } " )
517+ universe ._last_tod_request = time .time ()
518+
519+ subuni = universe .portaddress & 0xFF
520+ net = universe .portaddress >> 8
521+ message = ARTNET_PREFIX + struct .pack (
522+ "<HBBBBBBBBBBBBBBB" ,
523+ 0x8000 ,
524+ 0 ,
525+ 14 ,
526+ 0 ,
527+ 0 ,
528+ 0 ,
529+ 0 ,
530+ 0 ,
531+ 0 ,
532+ 0 ,
533+ 0 ,
534+ 0 ,
535+ net ,
536+ 0 , # command TodFull
537+ 1 ,
538+ subuni ,
539+ )
540+
541+ if self .transport :
542+ # send direct to node?
543+ # self.transport.sendto(message, addr=(node.ip, node.udpport))
544+ self .transport .sendto (message , addr = (self .client .broadcast_ip , ARTNET_PORT ))
545+
471546 def error_received (self , exc : Exception ) -> None :
472547 logger .warn ("Error received:" , exc )
473548
@@ -586,6 +661,7 @@ def set_port_config(
586661 universe : UniverseKey ,
587662 is_input : bool = False ,
588663 is_output : bool = False ,
664+ rdm : bool = False ,
589665 ) -> ArtNetUniverse :
590666 port_addr = self ._parse_universe (universe )
591667
0 commit comments