@@ -47,16 +47,10 @@ def set_property(self, **kwargs):
4747 setattr (self , key , kwargs [key ])
4848
4949class SignalNode (TreeNode ):
50- def __init__ (self , feature_name , func_dict , * , node_type = 'one-step' , device_related = False , need_response = False ):
50+ def __init__ (self , feature_name , func_dict , * , node_type = 'one-step' , device_related = False , need_response = False , ** kwargs ):
5151 super ().__init__ (feature_name , func_dict , node_type = node_type , device_related = device_related , need_response = need_response )
52- self .has_response_func = func_dict .get ('main-response' ) != None
5352 self .wait_response = False
5453
55- # if node type is multi-step, the node should have one response function
56- if self .node_type == 'multi-step' and self .has_response_func == False and self .device_related == False :
57- self .node_funcs ['main-response' ] = dummy_True
58- self .has_response_func = True
59-
6054 def run (self , * args , wait_response = False ):
6155 # initialize the node when first time entering it
6256 if not self .is_initialized :
@@ -66,15 +60,15 @@ def run(self, *args, wait_response=False):
6660 if not wait_response :
6761 # print(self.node_name, 'running function:', self.node_funcs['main'])
6862 result = self .node_funcs ['main' ](* args )
69- if self .has_response_func :
63+ if self .need_response :
7064 self .wait_response = True
7165 return result , False
7266
7367 elif self .wait_response :
7468 # print(self.node_name, 'running response function:', self.node_funcs['main-response'])
7569 result = self .node_funcs ['main-response' ](* args )
7670 self .wait_response = False
77- elif self .device_related or self .has_response_func :
71+ elif self .device_related or self .need_response :
7872 return None , False
7973 else :
8074 # run(wait_response=True)
@@ -87,10 +81,14 @@ def run(self, *args, wait_response=False):
8781 return result , True
8882
8983class DataNode (TreeNode ):
90- def __init__ (self , feature_name , func_dict , * , node_type = 'one-step' , device_related = False , need_response = False ):
84+ def __init__ (self , feature_name , func_dict , * , node_type = 'one-step' , device_related = False , need_response = False , ** kwargs ):
9185 super ().__init__ (feature_name , func_dict , node_type = node_type , device_related = device_related , need_response = need_response )
86+ self .is_marked = False
9287
9388 def run (self , * args ):
89+ if self .is_marked :
90+ return None , True
91+
9492 # initialize the node when first time entering it
9593 if not self .is_initialized :
9694 self .node_funcs ['init' ]()
@@ -111,19 +109,29 @@ def run(self, *args):
111109
112110
113111class Container :
114- def __init__ (self , root = None ):
112+ def __init__ (self , root = None , cleanup_list = [] ):
115113 self .root = root # root node of the tree
116114 self .curr_node = None # current running node
117115 self .end_flag = False # stop running flag
116+ self .cleanup_list = cleanup_list # a list of nodes containing 'cleanup' functions.
117+ self .is_closed = False
118118
119119 def reset (self ):
120120 self .curr_node = None
121121 self .end_flag = False
122122
123+ def cleanup (self ):
124+ for node in self .cleanup_list :
125+ try :
126+ node .node_funcs ['cleanup' ]()
127+ except :
128+ pass
129+ self .is_closed = True
130+
123131
124132class SignalContainer (Container ):
125- def __init__ (self , root = None , number_of_execution = 1 ):
126- super ().__init__ (root )
133+ def __init__ (self , root = None , cleanup_list = [], number_of_execution = 1 ):
134+ super ().__init__ (root , cleanup_list )
127135 self .number_of_execution = number_of_execution
128136 self .remaining_number_of_execution = number_of_execution
129137
@@ -139,7 +147,12 @@ def run(self, *args, wait_response=False):
139147 self .curr_node = self .root
140148 while self .curr_node :
141149 print ('running signal node:' , self .curr_node .node_name )
142- result , is_end = self .curr_node .run (* args , wait_response = wait_response )
150+ try :
151+ result , is_end = self .curr_node .run (* args , wait_response = wait_response )
152+ except :
153+ self .end_flag = True
154+ self .cleanup ()
155+ return
143156 if not is_end :
144157 return
145158 if self .curr_node .sibling :
@@ -155,14 +168,12 @@ def run(self, *args, wait_response=False):
155168 return
156169
157170 if self .curr_node .device_related :
158- return
159-
160-
171+ return
161172
162173
163174class DataContainer (Container ):
164- def __init__ (self , root = None ):
165- super ().__init__ (root )
175+ def __init__ (self , root = None , cleanup_list = [] ):
176+ super ().__init__ (root , cleanup_list )
166177 self .returned_a_response = False
167178
168179 def run (self , * args ):
@@ -172,7 +183,24 @@ def run(self, *args):
172183 self .curr_node = self .root
173184 self .returned_a_response = False
174185 while self .curr_node :
175- result , is_end = self .curr_node .run (* args )
186+ try :
187+ result , is_end = self .curr_node .run (* args )
188+ except :
189+ if self .curr_node .need_response == False and self .curr_node .node_type == 'one-step' :
190+ try :
191+ self .curr_node .node_funcs .get ('cleanup' , dummy_func )()
192+ except :
193+ print (f'The node({ self .curr_node .node_name } ) is not closed correctly! Please check the cleanup function' )
194+ pass
195+ self .curr_node .is_marked = True
196+ result , is_end = False , True
197+ else :
198+ # terminate the container.
199+ # the signal container may stuck there waiting a response,
200+ # the cleanup function of that node should give it a fake response to make it stop
201+ self .end_flag = True
202+ self .cleanup ()
203+ return
176204 # print('Data running node:', self.curr_node.node_name, 'get result:', result)
177205 if not is_end :
178206 return
@@ -197,11 +225,7 @@ def get_registered_funcs(feature_module, func_type='signal'):
197225 if 'init' not in func_dict :
198226 func_dict ['init' ] = dummy_func
199227 if 'main' not in func_dict :
200- # TODO: keep this now, later might change it after figuring out the meta data thing
201- if hasattr (feature_module , 'generate_meta_data' ) and func_type == 'signal' :
202- func_dict ['main' ] = feature_module .generate_meta_data
203- else :
204- func_dict ['main' ] = dummy_True
228+ func_dict ['main' ] = dummy_True
205229 if 'end' not in func_dict :
206230 func_dict ['end' ] = dummy_True
207231 if func_type == 'data' and 'pre-main' not in func_dict :
@@ -210,6 +234,7 @@ def get_registered_funcs(feature_module, func_type='signal'):
210234
211235def load_features (model , feature_list ):
212236 """ turn list to child-sibling tree"""
237+ signal_cleanup_list , data_cleanup_list = [], []
213238 signal_root , data_root = TreeNode ('none' , None ), TreeNode ('none' , None )
214239 pre_signal = signal_root
215240 pre_data = data_root
@@ -219,18 +244,26 @@ def load_features(model, feature_list):
219244 if 'args' in temp [i ]:
220245 args = temp [i ]['args' ]
221246 feature = temp [i ]['name' ](model , * args )
222- signal_node = SignalNode ( temp [ i ][ 'name' ]. __name__ , get_registered_funcs ( feature , 'signal' ))
223- data_node = DataNode ( temp [ i ][ 'name' ]. __name__ , get_registered_funcs ( feature , 'data' ) )
247+
248+ node_config = feature . config_table . get ( 'node' , {} )
224249 # if signal function has a waiting func, then the nodes are 'need_response' nodes
225250 if 'main-response' in feature .config_table .get ('signal' , {}):
226- signal_node .need_response = True
227- data_node .need_response = True
228- if 'node' in feature .config_table :
229- signal_node .set_property (** feature .config_table ['node' ])
230- data_node .set_property (** feature .config_table ['node' ])
251+ node_config ['need_response' ] = True
231252 if 'node' in temp [i ]:
232- signal_node .set_property (** temp [i ]['node' ])
233- data_node .set_property (** temp [i ]['node' ])
253+ for k , v in temp [i ]['node' ].items ():
254+ node_config [k ] = v
255+ # 'multi-step' must set to be 'device_related'
256+ if node_config .get ('node_type' , '' ) == 'multi-step' :
257+ node_config ['device_related' ] = True
258+
259+ signal_node = SignalNode (temp [i ]['name' ].__name__ , get_registered_funcs (feature , 'signal' ), ** node_config )
260+ data_node = DataNode (temp [i ]['name' ].__name__ , get_registered_funcs (feature , 'data' ), ** node_config )
261+
262+ if 'cleanup' in feature .config_table .get ('signal' , {}):
263+ signal_cleanup_list .append (signal_node )
264+ if 'cleanup' in feature .config_table .get ('data' , {}):
265+ data_cleanup_list .append (data_node )
266+
234267 if i == 0 :
235268 pre_signal .child = signal_node
236269 pre_data .child = data_node
@@ -240,7 +273,7 @@ def load_features(model, feature_list):
240273 pre_signal = signal_node
241274 pre_data = data_node
242275
243- return SignalContainer (signal_root .child ), DataContainer (data_root .child )
276+ return SignalContainer (signal_root .child , signal_cleanup_list ), DataContainer (data_root .child , data_cleanup_list )
244277
245278
246279def dummy_True (* args ):
0 commit comments