55from uuid import uuid4
66
77import pyartnet .impl_sacn .universe
8- from pyartnet .base import BaseNode
8+ from pyartnet .base import BaseNode , SequenceCounter
99from pyartnet .errors import InvalidCidError , InvalidUniverseAddressError
1010
1111# -----------------------------------------------------------------------------
@@ -35,7 +35,7 @@ def __init__(self, ip: str, port: int, *,
3535
3636 # sACN E1.31 specific fields
3737 cid : Optional [bytes ] = None , source_name : Optional [str ] = None
38- ):
38+ ) -> None :
3939 super ().__init__ (ip = ip , port = port ,
4040 max_fps = max_fps ,
4141 refresh_every = refresh_every , start_refresh_task = start_refresh_task ,
@@ -63,28 +63,28 @@ def __init__(self, ip: str, port: int, *,
6363 packet .extend (b'\x00 \x10 ' ) # | 2 | Preamble Size
6464 packet .extend (b'\x00 \x00 ' ) # | 2 | Post-amble Size
6565 packet .extend (ACN_PACKET_IDENTIFIER ) # | 12 | Packet Identifier
66- packet .extend ([ 0x72 , 0x57 ] ) # | 2 | Flags, Length
66+ packet .extend (( 0x72 , 0x57 ) ) # | 2 | Flags, Length
6767 packet .extend (VECTOR_ROOT_E131_DATA ) # | 4 | Vector
6868 packet .extend (cid ) # | 16 | CID, a unique identifier
6969
7070 self ._packet_base : bytearray = packet
7171
7272 self ._synchronization_address : int = 0
73- self ._sync_sequence_number : int = 0
73+ self ._sync_sequence_number : Final = SequenceCounter ()
7474
7575
7676 def _send_universe (self , id : int , byte_size : int , values : bytearray ,
77- universe : 'pyartnet.impl_sacn.universe.SacnUniverse' ):
77+ universe : 'pyartnet.impl_sacn.universe.SacnUniverse' ) -> None :
7878 packet = bytearray ()
7979
8080 # DMX Start Code is not included in the byte size from the universe
8181 prop_count = byte_size + 1
8282
8383 # Framing layer Part 1
84- packet .extend ((( 87 + prop_count ) | 0x7000 ).to_bytes (2 , 'big' )) # Flags and Length
85- packet .extend (VECTOR_E131_DATA_PACKET ) # | 4 | Vector
86- packet .extend (self ._source_name_byte ) # | 64 |Source Name
87- packet .append (100 ) # | 1 |Priority
84+ packet .extend ((( 87 + prop_count ) | 0x7000 ).to_bytes (2 , 'big' )) # | 2 | Flags and Length
85+ packet .extend (VECTOR_E131_DATA_PACKET ) # | 4 | Vector
86+ packet .extend (self ._source_name_byte ) # | 64 | Source Name
87+ packet .append (100 ) # | 1 | Priority
8888 packet .extend (int (self ._synchronization_address ).to_bytes (2 , 'big' )) # | 2 | Synchronization universe
8989
9090 # Framing layer Part 2
@@ -104,9 +104,10 @@ def _send_universe(self, id: int, byte_size: int, values: bytearray,
104104 packet .append (0x00 ) # | 1 | Property Values - DMX Start Code
105105 packet .extend (values ) # | 0-512 | Property Values - DMX Data
106106
107- # Update length for base packet
107+ # Update length and package type for base packet
108108 base_packet = self ._packet_base
109- base_packet [16 :18 ] = ((109 + prop_count ) | 0x7000 ).to_bytes (2 , 'big' ) # root layer
109+ base_packet [16 :18 ] = ((109 + prop_count ) | 0x7000 ).to_bytes (2 , 'big' ) # | 2 | Flags, Length
110+ base_packet [18 :22 ] = VECTOR_ROOT_E131_DATA # | 4 | Vector
110111
111112 self ._send_data (packet )
112113
@@ -115,43 +116,46 @@ def _send_universe(self, id: int, byte_size: int, values: bytearray,
115116 log .debug (f"Sending sACN frame to { self ._ip } :{ self ._port } : { (base_packet + packet ).hex ()} " )
116117
117118 def _create_universe (self , nr : int ) -> 'pyartnet.impl_sacn.SacnUniverse' :
118- # 6.2.7 E1.31 Data Packet: Universe
119- if not 1 <= nr < 63_999 :
119+ return pyartnet .impl_sacn .SacnUniverse (self , self ._validate_universe_nr (nr ))
120+
121+ def _validate_universe_nr (self , nr : int ) -> int :
122+ if not isinstance (nr , int ):
123+ raise TypeError ()
124+ # See spec 6.2.7 E1.31 Data Packet: Universe
125+ if not 1 <= nr <= 63_999 :
120126 raise InvalidUniverseAddressError ()
121- return pyartnet .impl_sacn .SacnUniverse (self , nr )
127+ return int (nr )
128+
122129
123- def set_synchronous_mode (self , enabled : bool , synchronization_address : int = 0 ):
130+ def set_synchronous_mode (self , enabled : bool , synchronization_address : int = 0 ):
124131 if enabled :
125- assert (synchronization_address != 0 )
126- self ._synchronization_address = synchronization_address
132+ self ._synchronization_address = self ._validate_universe_nr (synchronization_address )
127133 else :
128- assert (synchronization_address == 0 )
134+ if synchronization_address != 0 :
135+ msg = 'synchronization_address must be 0 when disabling synchronous mode!'
136+ raise ValueError (msg )
129137 self ._synchronization_address = 0
130138
131- def _send_synchronization (self ):
132- if ( self ._synchronization_address == 0 ) :
139+ def _send_synchronization (self ) -> None :
140+ if not self ._synchronization_address :
133141 return
134142
135143 packet = bytearray ()
136144
137- try :
138- base_packet = self ._packet_base
139- base_packet [16 :18 ] = ((33 ) | 0x7000 ).to_bytes (2 , 'big' ) # root layer
140- base_packet [18 :22 ] = VECTOR_ROOT_E131_EXTENDED
141- # Framing layer
142- packet .extend (((11 ) | 0x7000 ).to_bytes (2 , 'big' )) # | 2 | Flags and Length
143- packet .extend (VECTOR_E131_EXTENDED_SYNCHRONIZATION ) # | 4 | Vector
144- self ._sync_sequence_number += 1
145- if (self ._sync_sequence_number >= 255 ):
146- self ._sync_sequence_number = 0
147- packet .append (self ._sync_sequence_number ) # | 1 | Sequence Number
148- packet .extend (self ._synchronization_address .to_bytes (2 , 'big' )) # | 2 | Synchronization universe
149- packet .extend ([0 , 0 ]) # | 2 | Reserved
150-
151- self ._send_data (packet )
152-
153- if log .isEnabledFor (LVL_DEBUG ):
154- # log complete packet
155- log .debug (f"Sending sACN Syncronization Packet to { self ._ip } :{ self ._port } : { (base_packet + packet ).hex ()} " )
156- finally :
157- self ._packet_base [18 :22 ] = VECTOR_ROOT_E131_DATA
145+ # Framing layer
146+ packet .extend ((11 | 0x7000 ).to_bytes (2 , 'big' )) # | 2 | Flags and Length
147+ packet .extend (VECTOR_E131_EXTENDED_SYNCHRONIZATION ) # | 4 | Vector
148+ packet .append (self ._sync_sequence_number .value ) # | 1 | Sequence Number
149+ packet .extend (self ._synchronization_address .to_bytes (2 , 'big' )) # | 2 | Synchronization universe
150+ packet .extend ([0 , 0 ]) # | 2 | Reserved
151+
152+ # Update length and package type for base packet
153+ base_packet = self ._packet_base
154+ base_packet [16 :18 ] = (33 | 0x7000 ).to_bytes (2 , 'big' ) # | 2 | Flags, Length
155+ base_packet [18 :22 ] = VECTOR_ROOT_E131_EXTENDED # | 4 | Vector
156+
157+ self ._send_data (packet )
158+
159+ if log .isEnabledFor (LVL_DEBUG ):
160+ # log complete packet
161+ log .debug (f"Sending sACN Synchronization Packet to { self ._ip } :{ self ._port } : { (base_packet + packet ).hex ()} " )
0 commit comments