2727from .animation_info import AnimationInfo
2828from .end_animation import EndAnimation
2929from .global_vars import *
30+ from .message import Message
3031from .section import Section
3132from .strip_info import StripInfo
3233
@@ -39,21 +40,40 @@ def __init__(self, ip_address: str, port_num: int):
3940 self .port : int = port_num
4041 self .connection : 'socket.socket' = socket .socket ()
4142 self .connected : bool = False
43+ self .started : bool = False
4244 self .recv_thread : Optional ['Thread' ] = None
45+
4346 self .running_animations : Dict [str , 'AnimationData' ] = {}
4447 self .sections : Dict [str , 'Section' ] = {}
45- self .stripInfo : Optional ['StripInfo' ] = None
4648 self .supported_animations : List ['AnimationInfo' ] = []
49+ self .strip_info : Optional ['StripInfo' ] = None
50+
51+ self .on_connect_callback : Optional [Callable [[str , int ], Any ]] = None
52+ self .on_disconnect_callback : Optional [Callable [[str , int ], Any ]] = None
53+ self .on_unable_to_connect_callback : Optional [Callable [[str , int ], Any ]] = None
4754
48- self .receiveCallback : Optional [Callable [[bytes ], Any ]] = None
49- self .newAnimationDataCallback : Optional [Callable [['AnimationData' ], Any ]] = None
50- self .newAnimationInfoCallback : Optional [Callable [['AnimationInfo' ], Any ]] = None
51- self .newEndAnimationCallback : Optional [Callable [['EndAnimation' ], Any ]] = None
52- self .newSectionCallback : Optional [Callable [['Section' ], Any ]] = None
53- self .newStripInfoCallback : Optional [Callable [['StripInfo' ], Any ]] = None
55+ self .on_receive_callback : Optional [Callable [[bytes ], Any ]] = None
56+ self .on_new_animation_data_callback : Optional [Callable [['AnimationData' ], Any ]] = None
57+ self .on_new_animation_info_callback : Optional [Callable [['AnimationInfo' ], Any ]] = None
58+ self .on_new_end_animation_callback : Optional [Callable [['EndAnimation' ], Any ]] = None
59+ self .on_new_message_callback : Optional [Callable [['Message' ], Any ]] = None
60+ self .on_new_section_callback : Optional [Callable [['Section' ], Any ]] = None
61+ self .on_new_strip_info_callback : Optional [Callable [['StripInfo' ], Any ]] = None
62+
63+ self .partial_data : bytes = b''
5464
5565 def start (self ) -> 'AnimationSender' :
5666 """Connect to the server"""
67+ if self .started :
68+ return self
69+
70+ self .running_animations .clear ()
71+ self .sections .clear ()
72+ self .supported_animations .clear ()
73+ self .strip_info = None
74+
75+ self .started = True
76+
5777 # Attempt to connect to the server
5878 self .connection = socket .create_connection ((self .address , self .port ), timeout = 2.0 )
5979
@@ -74,7 +94,7 @@ def end(self) -> 'AnimationSender':
7494 # Connection has been closed, so set connected = False
7595 self .connected = False
7696
77- self .stripInfo = None
97+ self .strip_info = None
7898
7999 # If the separate thread for receiving animations was started, join it with the main thread.
80100 # The loop should stop because the connection is closed and connected is False,
@@ -100,15 +120,26 @@ def parse_data(self):
100120 try :
101121 all_input : bytes = self .connection .recv (4096 )
102122
123+ # Handle any partial data from previous communications
124+ complete_input : bytes = self .partial_data + all_input
125+ self .partial_data = b''
126+
103127 # Split up data (multiple may have come in the same message -
104128 # they are split up with triple semicolons)
105- # TODO: Support partial data
106- for split_input in all_input .split (DELIMITER ):
129+ input_list = complete_input .split (DELIMITER )
130+
131+ # If last input is partial, save for later when the rest comes
132+ if not complete_input .endswith (DELIMITER ):
133+ self .partial_data = input_list [- 1 ]
134+ input_list = input_list [:- 1 ]
135+
136+ # Process inputs
137+ for split_input in input_list :
107138 if len (split_input ) == 0 :
108139 continue
109140
110- if self .receiveCallback :
111- self .receiveCallback (split_input )
141+ if self .on_receive_callback :
142+ self .on_receive_callback (split_input )
112143
113144 if split_input .startswith (ANIMATION_DATA_PREFIX ):
114145 # Create the AnimationData instance
@@ -118,8 +149,8 @@ def parse_data(self):
118149 self .running_animations [data .id ] = data
119150
120151 # Call callback
121- if self .newAnimationDataCallback :
122- self .newAnimationDataCallback (data )
152+ if self .on_new_animation_data_callback :
153+ self .on_new_animation_data_callback (data )
123154
124155 elif split_input .startswith (ANIMATION_INFO_PREFIX ):
125156 # Create the AnimationInfo instance
@@ -129,8 +160,11 @@ def parse_data(self):
129160 self .supported_animations .append (info )
130161
131162 # Call callback
132- if self .newAnimationInfoCallback :
133- self .newAnimationInfoCallback (info )
163+ if self .on_new_animation_info_callback :
164+ self .on_new_animation_info_callback (info )
165+
166+ elif split_input .startswith (COMMAND_PREFIX ):
167+ logging .warning ("Receiving Command is not supported by client" )
134168
135169 elif split_input .startswith (END_ANIMATION_PREFIX ):
136170 # Create the EndAnimation instance
@@ -140,32 +174,40 @@ def parse_data(self):
140174 del self .running_animations [data .id ]
141175
142176 # Call callback
143- if self .newEndAnimationCallback :
144- self .newEndAnimationCallback (data )
177+ if self .on_new_end_animation_callback :
178+ self .on_new_end_animation_callback (data )
179+
180+ elif split_input .startswith (MESSAGE_PREFIX ):
181+ # Create the Message instance
182+ msg = Message .from_json (split_input )
183+
184+ # Call callback
185+ if self .on_new_message_callback :
186+ self .on_new_message_callback (msg )
145187
146188 elif split_input .startswith (SECTION_PREFIX ):
147- # Create the EndAnimation instance
189+ # Create the Section instance
148190 sect = Section .from_json (split_input )
149191
150192 # Add new section to the sections dict
151193 self .sections [sect .name ] = sect
152194
153195 # Call callback
154- if self .newSectionCallback :
155- self .newSectionCallback (sect )
196+ if self .on_new_section_callback :
197+ self .on_new_section_callback (sect )
156198
157199 elif split_input .startswith (STRIP_INFO_PREFIX ):
158200 # Create the StripInfo instance
159201 info = StripInfo .from_json (split_input )
160202
161- self .stripInfo = info
203+ self .strip_info = info
162204
163205 # Call callback
164- if self .newStripInfoCallback :
165- self .newStripInfoCallback (info )
206+ if self .on_new_strip_info_callback :
207+ self .on_new_strip_info_callback (info )
166208
167209 else :
168- logging .warning ('Unrecognized data type: {} ({}) ' .format (split_input [:4 ], split_input ))
210+ logging .warning ('Unrecognized data type: {}' .format (split_input [:4 ]))
169211
170212 except socket .timeout :
171213 pass
0 commit comments