@@ -88,7 +88,7 @@ def __init__(self, actionType, name, **kwargs):
8888
8989 # list of things that will be controlled during this action
9090 self .assets = {} # dict of named roles for the vessel(s) or port required to perform the action
91- self .requirements = {} # the capabilities required of each asset role assets (same keys as self.assets)
91+ self .requirements = {} # the capabilities required of each role (same keys as self.assets)
9292 self .objectList = [] # all objects that could be acted on
9393 self .dependencies = {} # list of other actions this one depends on
9494
@@ -97,6 +97,8 @@ def __init__(self, actionType, name, **kwargs):
9797 self .status = 0 # 0, waiting; 1=running; 2=finished
9898
9999 self .duration = getFromDict (actionType , 'duration' , default = 3 )
100+
101+ self .supported_objects = [] # list of FAModel object types supported by the action
100102
101103 '''
102104 # Create a dictionary of supported object types (with empty entries)
@@ -119,30 +121,29 @@ def __init__(self, actionType, name, **kwargs):
119121 # make list of supported object type names
120122 if 'objects' in actionType :
121123 if isinstance (actionType ['objects' ], list ):
122- supported_objects = actionType ['objects' ]
124+ self . supported_objects = actionType ['objects' ]
123125 elif isinstance (actionType ['objects' ], dict ):
124- supported_objects = list (actionType ['objects' ].keys ())
125- else :
126- supported_objects = []
126+ self .supported_objects = list (actionType ['objects' ].keys ())
127127
128128 # Add objects to the action's object list as long as they're supported
129129 if 'objects' in kwargs :
130130 for obj in kwargs ['objects' ]:
131131 objType = obj .__class__ .__name__ .lower () # object class name
132- if objType in supported_objects :
132+ if objType in self . supported_objects :
133133 self .objectList .append (obj )
134134 else :
135135 raise Exception (f"Object type '{ objType } ' is not in the action's supported list." )
136136
137137 # Create placeholders for asset roles based on the "requirements"
138138 if 'roles' in actionType :
139- for asset , caplist in actionType ['roles' ].items ():
140- self .assets [asset ] = None # each asset role holds a None value until assigned
141- self .requirements [asset ] = {}
139+ for role , caplist in actionType ['roles' ].items ():
140+ self .requirements [role ] = {key : None for key in caplist } # each role requirment holds a dict of capabilities with values set to None for now
142141 for cap in caplist :
143- self .requirements [asset ][cap ] = 0 # fill in each required metric with zero to start with?
144-
145-
142+ # self.requirements[role][cap] = {} # fill in each required capacity with {'metric': 0.0}
143+ self .requirements [role ][cap ] = {'area_m2' : 1000 , 'max_load_t' : 1600 } # dummy values for now, just larger than MPSV_01 values to trigger failure
144+
145+ self .assets [role ] = None # placeholder for the asset assigned to this role
146+
146147 # Process dependencies
147148 if 'dependencies' in kwargs :
148149 for dep in kwargs ['dependencies' ]:
@@ -154,26 +155,69 @@ def __init__(self, actionType, name, **kwargs):
154155
155156
156157 def addDependency (self , dep ):
157- '''Registers other action as a dependency of this one.'''
158+ '''Registers other action as a dependency of this one.
159+
160+ Inputs
161+ ------
162+ dep : Action
163+ The action to be added as a dependency.
164+ '''
158165 self .dependencies [dep .name ] = dep
159166 # could see if already a dependency and raise a warning if so...
160167
168+
169+ def assignObjects (self , objects ):
170+ '''
171+ Adds a list of objects to the actions objects list,
172+ checking they are valid for the actions supported objects
173+
174+ Inputs
175+ ------
176+ objects : list
177+ A list of FAModel objects to be added to the action.
178+ '''
179+
180+ for obj in objects :
181+ objType = obj .__class__ .__name__ .lower () # object class name
182+ if objType in self .supported_objects :
183+ if obj in self .objectList :
184+ print (f"Warning: Object '{ obj } ' is already in the action's object list." )
185+ self .objectList .append (obj )
186+ else :
187+ raise Exception (f"Object type '{ objType } ' is not in the action's supported list." )
188+
189+
190+
191+ # def setUpCapability(self):
192+ # # WIP: example of what needs to happen to create a metric
193+
194+ # # figure out how to assign required metrics to capabilies in the roles based on the objects
195+ # for role, caps in self.requirements.items():
196+ # for cap, metrics in caps.items():
197+ # for obj in self.objectList:
198+ # # this is for the deck_space capability
199+ # metrics = {'area_m2': obj.area, 'max_load_t': obj.mass / 1000} # / 1000 to convert kg to T
200+ # metrics.update(obj.get_capability_metrics(cap))
201+ # pass
202+
161203
162204 def checkAsset (self , role_name , asset ):
163205 '''Checks if a specified asset has sufficient capabilities to fulfil
164206 a specified role in this action.
165- '''
166-
167- # Make sure role_name is valid for this action
168- if not role_name in self .assets .keys ():
169- raise Exception (f"The specified role name '{ role_name } ' is not a named asset role in this action." )
170-
171- for cap , req in self .requirements ['role_name' ]:
172- if asset .capabilities [cap ] >= req : # <<< this is pseudocode. Needs to look at capability numbers! <<<
173- pass
174- # so far so good
207+ '''
208+
209+ for capability in self .requirements [role_name ].keys ():
210+
211+ if capability in asset ['capabilities' ].keys (): # check capability
212+ # does this work if there are no metrics in a capability? This should be possible, as not all capabilities will require a constraint.
213+ for metric in self .requirements [role_name ][capability ].keys (): # loop over the capacity requirements for the capability (if more than one)
214+ if self .requirements [role_name ][capability ][metric ] > asset ['capabilities' ][capability ][metric ]: # check capacity
215+ # TODO: can we throw a message here that explains what metric is violated?
216+ return False # the asset does not have the capacity for this capability
217+ return True
218+
175219 else :
176- return False # at least on capability is not met, so return False
220+ return False # at least one capability is not met
177221
178222 return True
179223
@@ -192,12 +236,68 @@ def assignAsset(self, role_name, asset):
192236 # Make sure role_name is valid for this action
193237 if not role_name in self .assets .keys ():
194238 raise Exception (f"The specified role name '{ role_name } ' is not a named asset role in this action." )
195-
196- self .assets [role_name ] = asset
239+
240+ if self .checkAsset (role_name , asset ):
241+ self .assets [role_name ] = asset
242+ else :
243+ raise Exception (f"The asset does not have the capabilities for role '{ role_name } '." )
197244
198245
199246 def calcDurationAndCost (self ):
200- pass
247+ '''The structure here is dependent on actions.yaml.
248+ TODO: finish description
249+ '''
250+
251+ print ('Calculating duration and cost for action:' , self .name )
252+ # print(self.type)
253+
254+ # --- Towing & Transport ---
255+ if self .type == 'tow' :
256+ pass
257+ elif self .type == 'transport_components' :
258+ pass
259+
260+ # --- Mooring & Anchors ---
261+ elif self .type == 'install_anchor' :
262+ pass
263+ elif self .type == 'retrieve_anchor' :
264+ pass
265+ elif self .type == 'load_mooring' :
266+ pass
267+ elif self .type == 'lay_mooring' :
268+ pass
269+ elif self .type == 'mooring_hookup' :
270+ pass
271+
272+ # --- Heavy Lift & Installation ---
273+ elif self .type == 'install_wec' :
274+ pass
275+ elif self .type == 'install_semisub' :
276+ pass
277+ elif self .type == 'install_spar' :
278+ pass
279+ elif self .type == 'install_tlp' :
280+ pass
281+ elif self .type == 'install_wtg' :
282+ pass
283+
284+ # --- Cable Operations ---
285+ elif self .type == 'lay_cable' :
286+ pass
287+ elif self .type == 'retrieve_cable' :
288+ pass
289+ elif self .type == 'lay_and_bury_cable' :
290+ pass
291+ elif self .type == 'backfill_rockdump' :
292+ pass
293+
294+ # --- Survey & Monitoring ---
295+ elif self .type == 'site_survey' :
296+ pass
297+ elif self .type == 'monitor_installation' :
298+ pass
299+ else :
300+ raise ValueError (f"Action type '{ self .type } ' not recognized." )
201301
202302
203303 def evaluateAssets (self , assets ):
@@ -209,6 +309,8 @@ def evaluateAssets(self, assets):
209309 Each asset is a vessel or port object.
210310
211311 '''
312+
313+ # error check that assets is a dict of {role_name, asset dict}, and not just an asset dict?
212314
213315 # Assign each specified asset to its respective role
214316 for akey , aval in assets .items ():
0 commit comments