@@ -59,16 +59,11 @@ def __init__(self, actions, action_sequence, name, **kwargs):
5959 self .actions [act .name ] = act
6060
6161
62- # Create a graph of the sequence of actions in this task based on action_sequence
63- self .getSequenceGraph (action_sequence )
6462
6563 self .name = name
6664
6765 self .status = 0 # 0, waiting; 1=running; 2=finished
68-
6966 self .actions_ti = {} # relative start time of each action [h]
70- self .t_actions = {} # timing of task's actions, relative to t1 [h]
71- # t_actions is a dict with keys same as action names, and entries of [t1, t2]
7267
7368 self .duration = 0 # duration must be calculated based on lengths of actions
7469 self .cost = 0 # cost must be calculated based on the cost of individual actions.
@@ -77,9 +72,14 @@ def __init__(self, actions, action_sequence, name, **kwargs):
7772
7873 # what else do we need to initialize the task?
7974
80- # internal graph of the actions within this task.
81- self .G = self .getTaskGraph ()
82-
75+ # Create a graph of the sequence of actions in this task based on action_sequence
76+ self .getSequenceGraph (action_sequence , plot = True ) # this also updates duration
77+
78+ self .cost = sum (action .cost for action in self .actions .values ())
79+ 0
80+ print (f"---------------------- Initializing Task '{ self .name } ----------------------" )
81+ print (f"Task '{ self .name } ' initialized with duration = { self .duration :.2f} h." )
82+ print (f"Task '{ self .name } ' initialized with cost = ${ self .cost :.2f} " )
8383
8484 def organizeActions (self ):
8585 '''Organizes the actions to be done by this task into the proper order
@@ -103,7 +103,7 @@ def calcDuration(self):
103103
104104
105105
106- def getSequenceGraph (self , action_sequence ):
106+ def getSequenceGraph (self , action_sequence , plot = True ):
107107 '''Generate a multi-directed graph that visalizes action sequencing within the task.
108108 Build a MultiDiGraph with nodes:
109109 Start -> CP1 -> CP2 -> ... -> End
@@ -164,7 +164,7 @@ def level_of(a: str, b: set[str]) -> int:
164164
165165 pos = nx .shell_layout (H , nlist = shells )
166166
167- xmin , xmax = - 2.0 , 2.0
167+ xmin , xmax = - 2.0 , 2.0 # maybe would need to change those later on.
168168 pos ["Start" ] = (xmin , 0 )
169169 pos ["End" ] = (xmax , 0 )
170170
@@ -186,57 +186,76 @@ def level_of(a: str, b: set[str]) -> int:
186186 else : # lv == max_level
187187 H .add_edge (f"CP{ num_cps } " , "End" , key = action , duration = action .duration , cost = action .cost )
188188
189- fig , ax = plt .subplots ()
190- # pos = nx.shell_layout(G)
191- nx .draw (H , pos , with_labels = True , node_size = 500 , node_color = "lightblue" , edge_color = 'white' )
192-
193- # Group edges by unique (u, v) pairs
194- for (u , v ) in set ((u , v ) for u , v , _ in H .edges (keys = True )):
195- # get all edges between u and v (dict keyed by edge key)
196- edge_dict = H .get_edge_data (u , v ) # {key: {attrdict}, ...}
197- n = len (edge_dict )
198189
199- # curvature values spread between -0.3 and +0.3
200- if n == 1 :
201- rads = [0 ]
202- else :
203- rads = np .linspace (- 0.3 , 0.3 , n )
204-
205- # draw each edge
206- durations = [d .get ("duration" , 0.0 ) for d in edge_dict .values ()]
207- scale = max (max (durations ), 0.0001 )
208- width_scale = 4.0 / scale # normalize largest to ~4px
209-
210- for rad , (k , d ) in zip (rads , edge_dict .items ()):
211- nx .draw_networkx_edges (
212- H , pos , edgelist = [(u , v )], ax = ax ,
213- connectionstyle = f"arc3,rad={ rad } " ,
214- arrows = True , arrowstyle = "-|>" ,
215- edge_color = "gray" ,
216- width = max (0.5 , d .get ("duration" , []) * width_scale ),
217- )
218-
219- # --- after drawing edges ---
220- edge_labels = {}
221- for u , v , k , d in H .edges (keys = True , data = True ):
222- # each edge may have a unique key; include it in the label if desired
223- label = k .name
224- edge_labels [(u , v , k )] = label
225-
226- nx .draw_networkx_edge_labels (
227- H ,
228- pos ,
229- edge_labels = edge_labels ,
230- font_size = 8 ,
231- label_pos = 0.5 , # position along edge (0=start, 0.5=middle, 1=end)
232- rotate = False # keep labels horizontal
233- )
234-
235- ax .axis ("off" )
236- plt .tight_layout ()
237- plt .show ()
190+ # 3. Compute cumulative start time for each level
191+ level_groups = {}
192+ for action , lv in levels .items ():
193+ level_groups .setdefault (lv , []).append (action )
238194
195+ level_durations = {lv : max (self .actions [a ].duration for a in acts )
196+ for lv , acts in level_groups .items ()}
197+
198+ task_duration = sum (level_durations .values ())
199+
200+ level_start_time = {}
201+ elapsed = 0.0
202+ cp_string = []
203+ for lv in range (1 , max_level + 1 ):
204+ level_start_time [lv ] = elapsed
205+ elapsed += level_durations .get (lv , 0.0 )
206+ # also collect all actions at this level for title
207+ acts = [a for a , l in levels .items () if l == lv ]
208+ if acts and lv <= num_cps :
209+ cp_string .append (f"CP{ lv } : { ', ' .join (acts )} " )
210+ elif acts and lv > num_cps :
211+ cp_string .append (f"End: { ', ' .join (acts )} " )
212+
213+ # Assign to self:
214+ self .duration = task_duration
215+ self .actions_ti = {a : level_start_time [lv ] for a , lv in levels .items ()}
239216 self .sequence_graph = H
217+
218+ title_str = f"Task { self .name } . Duration { self .duration :.2f} : " + " | " .join (cp_string )
219+
220+ if plot :
221+ fig , ax = plt .subplots ()
222+ # pos = nx.shell_layout(G)
223+ nx .draw (H , pos , with_labels = True , node_size = 500 , node_color = "lightblue" , edge_color = 'white' )
224+
225+ label_positions = {} # to store label positions for each edge
226+ # Group edges by unique (u, v) pairs
227+ for (u , v ) in set ((u , v ) for u , v , _ in H .edges (keys = True )):
228+ # get all edges between u and v (dict keyed by edge key)
229+ edge_dict = H .get_edge_data (u , v ) # {key: {attrdict}, ...}
230+ n = len (edge_dict )
231+
232+ # curvature values spread between -0.3 and +0.3 [helpful to visualize multiple edges]
233+ if n == 1 :
234+ rads = [0 ]
235+ offsets = [0.5 ]
236+ else :
237+ rads = np .linspace (- 0.3 , 0.3 , n )
238+ offsets = np .linspace (0.2 , 0.8 , n )
239+
240+ # draw each edge
241+ durations = [d .get ("duration" , 0.0 ) for d in edge_dict .values ()]
242+ scale = max (max (durations ), 0.0001 ) # avoid div by zero
243+ width_scale = 4.0 / scale # normalize largest to ~4px
244+
245+ for rad , offset , (k , d ) in zip (rads , offsets , edge_dict .items ()):
246+ nx .draw_networkx_edges (
247+ H , pos , edgelist = [(u , v )], ax = ax ,
248+ connectionstyle = f"arc3,rad={ rad } " ,
249+ arrows = True , arrowstyle = "-|>" ,
250+ edge_color = "gray" ,
251+ width = max (0.5 , d .get ("duration" , []) * width_scale ),
252+ )
253+ label_positions [(u , v , k )] = offset # store position for edge label
254+
255+ ax .set_title (title_str , fontsize = 12 , fontweight = "bold" )
256+ ax .axis ("off" )
257+ plt .tight_layout ()
258+
240259 return H
241260
242261
0 commit comments