Skip to content

Commit f133c51

Browse files
committed
Markdown plaintext in function descriptions for action.py. Adds assignAsset which is called by assignAssets for each {role : asset} pair. Sets up structure for checking different asset combinations for action. Removes requirements from actions.yaml.
1 parent f750c4a commit f133c51

File tree

2 files changed

+130
-94
lines changed

2 files changed

+130
-94
lines changed

famodel/irma/action.py

Lines changed: 129 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ def incrementer(text):
3737
3838
Inputs
3939
------
40-
text : str
40+
`text` : `str`
4141
The input string to increment.
4242
4343
Returns
4444
-------
45-
str
45+
`str`
4646
The incremented string.
4747
'''
4848
split_text = text.split()[::-1]
@@ -61,12 +61,12 @@ def increment_name(name):
6161
6262
Inputs
6363
------
64-
name : str
64+
`name` : `str`
6565
The input name string.
6666
6767
Returns
6868
-------
69-
str
69+
`str`
7070
The incremented name string.
7171
'''
7272
name_parts = name.split(sep='-')
@@ -99,19 +99,19 @@ def __init__(self, actionType, name, **kwargs):
9999
100100
Inputs
101101
----------
102-
actionType : dict
102+
`actionType` : `dict`
103103
Dictionary defining the action type (typically taken from a yaml).
104-
name : string
104+
`name` : `string`
105105
A name for the action. It may be appended with numbers if there
106106
are duplicate names.
107-
kwargs
107+
`kwargs`
108108
Additional arguments may depend on the action type and typically
109109
include a list of FAModel objects that are acted upon, or
110110
a list of dependencies (other action names/objects).
111111
112112
Returns
113113
-------
114-
None
114+
`None`
115115
'''
116116

117117
# list of things that will be controlled during this action
@@ -124,7 +124,8 @@ def __init__(self, actionType, name, **kwargs):
124124
self.name = name
125125
self.status = 0 # 0, waiting; 1=running; 2=finished
126126

127-
self.duration = getFromDict(actionType, 'duration', default=3)
127+
self.duration = getFromDict(actionType, 'duration', default=0) # this will be overwritten by calcDurationAndCost. TODO: or should it overwrite any duration calculation?
128+
self.cost = 0 # this will be overwritten by calcDurationAndCost
128129

129130
self.supported_objects = [] # list of FAModel object types supported by the action
130131

@@ -178,8 +179,6 @@ def __init__(self, actionType, name, **kwargs):
178179
self.dependencies[dep.name] = dep
179180

180181
# Process some optional kwargs depending on the action type
181-
182-
183182

184183

185184
def addDependency(self, dep):
@@ -188,12 +187,12 @@ def addDependency(self, dep):
188187
189188
Inputs
190189
------
191-
dep : Action
190+
`dep` : `Action`
192191
The action to be added as a dependency.
193192
194193
Returns
195194
-------
196-
None
195+
`None`
197196
'''
198197
self.dependencies[dep.name] = dep
199198
# could see if already a dependency and raise a warning if so...
@@ -206,12 +205,12 @@ def assignObjects(self, objects):
206205
207206
Inputs
208207
------
209-
objects : list
208+
`objects` : `list`
210209
A list of FAModel objects to be added to the action.
211210
212211
Returns
213212
-------
214-
None
213+
`None`
215214
'''
216215

217216
for obj in objects:
@@ -231,11 +230,11 @@ def assignObjects(self, objects):
231230
#
232231
# Inputs
233232
# ------
234-
# None
233+
# `None`
235234
#
236235
# Returns
237236
# -------
238-
# None
237+
# `None`
239238
# '''
240239
# # WIP: example of what needs to happen to create a metric
241240

@@ -250,27 +249,28 @@ def assignObjects(self, objects):
250249

251250

252251
def checkAsset(self, role_name, asset):
253-
'''Checks if a specified asset has sufficient capabilities to fulfil
252+
'''
253+
Checks if a specified asset has sufficient capabilities to fulfil
254254
a specified role in this action.
255255
256256
Inputs
257257
------
258-
role_name : string
258+
`role_name` : `string`
259259
The name of the role to check.
260-
asset : dict
260+
`asset` : `dict`
261261
The asset to check against the role's requirements.
262262
263263
Returns
264264
-------
265-
bool
265+
`bool`
266266
True if the asset meets the role's requirements, False otherwise.
267-
str
267+
`str`
268268
A message providing additional information about the check.
269269
'''
270270

271271
# Make sure role_name is valid for this action
272272
if not role_name in self.assets.keys():
273-
raise Exception(f"The specified role name '{role_name}' is not a named asset role in this action.")
273+
raise Exception(f"The specified role '{role_name}' is not a named in this action.")
274274

275275
for capability in self.requirements[role_name].keys():
276276

@@ -289,52 +289,27 @@ def checkAsset(self, role_name, asset):
289289

290290
else:
291291
return False, f"The asset does not have the '{capability}' capability for '{role_name}' role of '{self.name}' action." # a capability is not met
292-
293-
294-
def assignAsset(self, role_name, asset):
295-
'''
296-
Assigns a vessel or port to a certain role in the action.
297-
298-
Inputs
299-
------
300-
role_name : string
301-
Name of the asset role being filled (must be in the action's list)
302-
asset : Vessel or Port object
303-
The asset to be registered with the class.
304-
305-
Returns
306-
-------
307-
None
308-
'''
309-
310-
# Make sure role_name is valid for this action
311-
if not role_name in self.assets.keys():
312-
raise Exception(f"The specified role name '{role_name}' is not a named asset role in this action.")
313-
314-
assignable, message = self.checkAsset(role_name, asset)
315-
if assignable:
316-
self.assets[role_name] = asset
317-
else:
318-
raise Exception(message) # throw error message
319292

320293

321294
def calcDurationAndCost(self):
322295
'''
323-
Calculates duration and cost for the action. The structure here is dependent on actions.yaml.
296+
Calculates duration and cost for the action. The structure here is dependent on `actions.yaml`.
324297
TODO: finish description
325298
326299
Inputs
327300
------
328-
None
301+
`None`
329302
330303
Returns
331304
-------
332-
None
305+
`None`
333306
'''
334307

335-
print('Calculating duration and cost for action:', self.name)
336-
# print(self.type)
337-
308+
# Check that all roles in the action are filled
309+
for role_name in self.requirements.keys():
310+
if self.assets[role_name] is None:
311+
raise Exception(f"Role '{role_name}' is not filled in action '{self.name}'. Cannot calculate duration and cost.")
312+
338313
# --- Towing & Transport ---
339314
if self.type == 'tow':
340315
pass
@@ -382,35 +357,113 @@ def calcDurationAndCost(self):
382357
pass
383358
else:
384359
raise ValueError(f"Action type '{self.type}' not recognized.")
385-
386-
360+
361+
return self.duration, self.cost
362+
363+
387364
def evaluateAssets(self, assets):
388365
'''
389-
Check whether an asset can perform the task, and if so calculate
390-
the time and cost associated with using those assets.
366+
Checks assets for all the roles in the action. This calls `checkAsset()`
367+
for each role/asset pair and then calculates the duration and
368+
cost for the action as if the assets were assigned. Does not assign
369+
the asset(s) to the action. WARNING: this function will clear the values
370+
(but not keys) in `self.assets`.
391371
392372
Inputs
393373
------
394-
assets : dict
374+
`assets` : `dict`
395375
Dictionary of {role_name: asset} pairs for assignment of the
396376
assets to the roles in the action.
397377
398378
Returns
399379
-------
400-
None
380+
`cost` : `float`
381+
Estimated cost of using the asset.
382+
`duration` : `float`
383+
Estimated duration of the action when performed by asset.
401384
'''
402-
403-
# error check that assets is a dict of {role_name, asset dict}, and not just an asset dict?
404385

405-
# Assign each specified asset to its respective role
406-
for akey, aval in assets.items():
407-
self.assignAsset(akey, aval)
408-
409-
self.calcDurationAndCost()
386+
# Check each specified asset for its respective role
387+
for role_name, asset in assets.items():
388+
assignable, message = self.checkAsset(role_name, asset)
389+
if assignable:
390+
self.assets[role_name] = asset # Assignment required for calcDurationAndCost(), will be cleared later
391+
else:
392+
print('INFO: '+message+' Action cannot be completed by provided asset list.')
393+
return -1, -1 # return negative values to indicate incompatibility. Loop is terminated becasue assets not compatible for roles.
410394

395+
# Check that all roles in the action are filled
396+
for role_name in self.requirements.keys():
397+
if self.assets[role_name] is None:
398+
399+
for role_name in assets.keys(): # Clear the assets dictionary
400+
assets[role_name] = None
401+
raise Exception(f"Role '{role_name}' is not filled in action '{self.name}'. Cannot calculate duration and cost.") # possibly just a warning and not an exception?
402+
403+
404+
duration, cost = self.calcDurationAndCost()
405+
406+
for role_name in assets.keys(): # Clear the assets dictionary
407+
assets[role_name] = None
408+
409+
return duration, cost # values returned here rather than set because will be used to check compatibility and not set properties of action
410+
411+
412+
def assignAsset(self, role_name, asset):
413+
'''
414+
Checks if asset can be assigned to an action.
415+
If yes, assigns asset to role in the action.
416+
417+
Inputs
418+
------
419+
`role_name` : `str`
420+
The name of the role to which the asset will be assigned.
421+
`asset` : `dict`
422+
The asset to be assigned to the role.
423+
424+
Returns
425+
-------
426+
`None`
427+
'''
428+
# Make sure role_name is valid for this action
429+
if not role_name in self.assets.keys():
430+
raise Exception(f"The specified role name '{role_name}' is not in this action.")
431+
432+
assignable, message = self.checkAsset(role_name, asset)
433+
if assignable:
434+
self.assets[role_name] = asset
435+
else:
436+
raise Exception(message) # throw error message
437+
438+
def assignAssets(self, assets):
439+
'''
440+
Assigns assets to all the roles in the action. This calls
441+
`assignAsset()` for each role/asset pair and then calculates the
442+
duration and cost for the action. Similar to `evaluateAssets()`
443+
however here assets are assigned and duration and cost are
444+
set after evaluation.
445+
446+
Inputs
447+
------
448+
`assets` : `dict`
449+
Dictionary of {role_name: asset} pairs for assignment of the
450+
assets to the roles in the action.
451+
452+
Returns
453+
-------
454+
`None`
455+
'''
411456

412-
# can store the cost and duration in self as well
457+
# Assign each specified asset to its respective role
458+
for role_name, asset in assets.items():
459+
self.assignAsset(role_name, asset)
413460

461+
# Check that all roles in the action are filled
462+
for role_name in self.requirements.keys():
463+
if self.assets[role_name] is None:
464+
raise Exception(f"Role '{role_name}' is not filled in action '{self.name}'. Cannot calculate duration and cost.") # possibly just a warning and not an exception?
465+
466+
self.calcDurationAndCost()
414467

415468

416469
# ----- Below are drafts of methods for use by the engine -----
@@ -421,11 +474,11 @@ def begin(self):
421474
422475
Inputs
423476
------
424-
None
477+
`None`
425478
426479
Returns
427480
-------
428-
None
481+
`None`
429482
'''
430483
for vessel in self.vesselList:
431484
vessel._attach_to(self)
@@ -439,11 +492,11 @@ def end(self):
439492
440493
Inputs
441494
------
442-
None
495+
`None`
443496
444497
Returns
445498
-------
446-
None
499+
`None`
447500
'''
448501
for vessel in self.vesselList:
449502
vessel._detach_from()
@@ -457,11 +510,11 @@ def timestep(self):
457510
458511
Inputs
459512
------
460-
None
513+
`None`
461514
462515
Returns
463516
-------
464-
None
517+
`None`
465518
'''
466519

467520
# (this is just documenting an idea for possible future implementation)

0 commit comments

Comments
 (0)