Skip to content

Commit 89f8afd

Browse files
committed
New roles/capabilities approach in Actions:
- Updated actions.yaml (half way done) to use a nested roles->capabilities dictionary rather than requirements and capabilities lists. - Actions class now has assets and requirements dicts that correspond to roles/capabilities in the YAML. - Draft outlines of Action methods assignAsset and checkAsset
1 parent 636a8a1 commit 89f8afd

File tree

4 files changed

+116
-37
lines changed

4 files changed

+116
-37
lines changed

famodel/irma/action.py

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ def __init__(self, actionType, name, **kwargs):
8787
'''
8888

8989
# list of things that will be controlled during this action
90-
self.vesselList = [] # all vessels required for the action
90+
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)
9192
self.objectList = [] # all objects that could be acted on
9293
self.dependencies = {} # list of other actions this one depends on
9394

@@ -123,6 +124,7 @@ def __init__(self, actionType, name, **kwargs):
123124
supported_objects = list(actionType['objects'].keys())
124125
else:
125126
supported_objects = []
127+
126128
# Add objects to the action's object list as long as they're supported
127129
if 'objects' in kwargs:
128130
for obj in kwargs['objects']:
@@ -131,7 +133,15 @@ def __init__(self, actionType, name, **kwargs):
131133
self.objectList.append(obj)
132134
else:
133135
raise Exception(f"Object type '{objType}' is not in the action's supported list.")
134-
136+
137+
# Create placeholders for asset roles based on the "requirements"
138+
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] = {}
142+
for cap in caplist:
143+
self.requirements[asset][cap] = 0 # fill in each required metric with zero to start with?
144+
135145

136146
# Process dependencies
137147
if 'dependencies' in kwargs:
@@ -149,22 +159,61 @@ def addDependency(self, dep):
149159
# could see if already a dependency and raise a warning if so...
150160

151161

162+
def checkAsset(self, role_name, asset):
163+
'''Checks if a specified asset has sufficient capabilities to fulfil
164+
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
175+
else:
176+
return False # at least on capability is not met, so return False
177+
178+
return True
152179

153-
def assignAssets(self, assets):
154-
pass
180+
181+
def assignAsset(self, role_name, asset):
182+
'''Assigns a vessel or port to a certain role in the action.
183+
184+
Parameters
185+
----------
186+
role_name : string
187+
Name of the asset role being filled (must be in the action's list)
188+
asset : Vessel or Port object
189+
The asset to be registered with the class.
190+
'''
191+
192+
# Make sure role_name is valid for this action
193+
if not role_name in self.assets.keys():
194+
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
197+
155198

156199
def calcDurationAndCost(self):
157200
pass
158201

159-
def evaluateAsset(self, assets):
202+
203+
def evaluateAssets(self, assets):
160204
'''Check whether an asset can perform the task, and if so calculate
161-
the time and cost associated with using that vessel.
162-
asset : vessel or port object(s)
205+
the time and cost associated with using those assets.
206+
207+
assets : dict
208+
Dictionary of role_name, asset object pairs for assignment to the action.
209+
Each asset is a vessel or port object.
163210
164211
'''
165-
pass
166212

167-
self.assignAssets(assets)
213+
# Assign each specified asset to its respective role
214+
for akey, aval in assets.items():
215+
self.assignAsset(akey, aval)
216+
168217
self.calcDurationAndCost()
169218

170219

@@ -173,7 +222,7 @@ def evaluateAsset(self, assets):
173222

174223

175224
# ----- Below are drafts of methods for use by the engine -----
176-
225+
"""
177226
def begin(self):
178227
'''Take control of all objects'''
179228
for vessel in self.vesselList:
@@ -188,36 +237,36 @@ def end(self):
188237
vessel._detach_from()
189238
for object in self.objectList:
190239
object._detach_from()
191-
240+
"""
192241

193242
def timestep(self):
194243
'''Advance the simulation of this action forward one step in time.'''
195244

196245
# (this is just documenting an idea for possible future implementation)
197246
# Perform the hourly action of the task
198-
'''
247+
199248
if self.type == 'tow':
200249
# controller - make sure things are going in right direction...
201250
# (switch mode if need be)
202251
if self.mode == 0 : # gathering vessels
203-
for ves in self.vesselList:
204-
dr = self.r_start - ves.r
205-
ves.setCourse(dr) # sets vessel velocity
252+
ves = self.assets['vessel']
253+
dr = self.r_start - ves.r
254+
ves.setCourse(dr) # sets vessel velocity
206255

207-
# if all vessels are stopped (at the object), time to move
208-
if all([np.linalg.norm(ves.v) == 0 for ves in self.vesselList]):
256+
# if vessel is stopped (at the object), time to move
257+
if np.linalg.norm(ves.v) == 0:
209258
self.mode = 1
210259

211260
if self.mode == 1: # towing
212-
for ves in self.vesselList:
213-
dr = self.r_finish - ves.r
214-
ves.setCourse(dr) # sets vessel velocity
261+
ves = self.assets['vessel']
262+
dr = self.r_finish - ves.r
263+
ves.setCourse(dr) # sets vessel velocity
215264

216265
# if all vessels are stopped (at the final location), time to end
217-
if all([np.linalg.norm(ves.v) == 0 for ves in self.vesselList]):
266+
if np.linalg.norm(ves.v) == 0:
218267
self.mode = 2
219268

220269
if self.mode == 2: # finished
221270
self.end()
222-
'''
271+
223272

famodel/irma/actions.yaml

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
# This file defines standardized marine operations actions.
22
# Each entry needs numeric values per specific asset in vessels.yaml.
33
# Vessel actions will be checked against capabilities/actions for validation.
4+
#
5+
# Old format: requirements and capabilities
6+
# New format: roles, which lists asset roles, each with associated required capabilities
47

58
# --- Towing & Transport ---
69

710
tow:
811
objects: [platform]
912
requirements: [TUG]
10-
capabilities:
13+
roles: # The asset roles involved and the capabilities required of each role
14+
vessel:
1115
- deck_space
1216
- bollard_pull
1317
- winch
@@ -19,7 +23,8 @@ tow:
1923
transport_components:
2024
objects: [component]
2125
requirements: [HL, WTIV, CSV]
22-
capabilities:
26+
roles: # The asset roles involved and the capabilities required of each role
27+
carrier: # vessel carrying things
2328
- deck_space
2429
- crane
2530
- positioning_system
@@ -32,8 +37,10 @@ transport_components:
3237
install_anchor:
3338
objects: [anchor]
3439
requirements: [AHTS, MSV]
35-
capabilities:
40+
roles: # The asset roles involved and the capabilities required of each role
41+
carrier: # vessl that has been carrying the anchor
3642
- deck_space
43+
operator: # vessel that lowers and installs the anchor
3744
- winch
3845
- bollard_pull
3946
- crane
@@ -47,8 +54,10 @@ install_anchor:
4754
retrieve_anchor:
4855
objects: [anchor]
4956
requirements: [AHTS, MSV]
50-
capabilities:
57+
roles: # The asset roles involved and the capabilities required of each role
58+
carrier:
5159
- deck_space
60+
operator:
5261
- winch
5362
- bollard_pull
5463
- crane
@@ -57,25 +66,31 @@ retrieve_anchor:
5766
duration_h: 4
5867
Hs_m:: 2.5
5968
description: "Anchor retrieval, including break-out and recovery to deck."
60-
69+
70+
6171
load_mooring:
6272
objects: [mooring]
63-
requirements: [port, vessel]
64-
capabilities:
73+
roles: # The asset roles involved and the capabilities required of each role
74+
carrier1: [] # the port or vessel where the moorings begin
75+
# (no requirements)
76+
carrier2: # the vessel things will be loaded onto
6577
- deck_space
6678
- winch
67-
- bollard_pull
68-
- mooring_work
6979
- positioning_system
80+
operator: # the entity with the crane (like the port or the new vessel)
81+
- crane
7082
duration_h: 2
7183
Hs_m:: 2.5
72-
description: "Load-out of mooring lines and components from port or vessel onto vessel."
84+
description: "Load-out of mooring lines and components from port or vessel onto vessel."
85+
7386

7487
lay_mooring:
7588
objects: [mooring]
7689
requirements: [AHTS, CSV]
77-
capabilities:
90+
roles: # The asset roles involved and the capabilities required of each role
91+
carrier: # vessel carrying the mooring
7892
- deck_space
93+
operator: # vessel laying the mooring
7994
- winch
8095
- bollard_pull
8196
- mooring_work
@@ -84,13 +99,16 @@ lay_mooring:
8499
Hs_m:: 2.5
85100
description: "Laying mooring lines, tensioning and connection to anchors and floaters."
86101

102+
87103
mooring_hookup:
88104
objects:
89105
mooring: [pretension]
90106
platform: [wec]
91107
requirements: [AHTS, CSV]
92-
capabilities:
108+
roles: # The asset roles involved and the capabilities required of each role
109+
carrier:
93110
- deck_space
111+
operator:
94112
- winch
95113
- bollard_pull
96114
- mooring_work

famodel/irma/capabilities.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
# Each entry needs numeric values per specific asset in vessels.yaml.
33
# Vessel actions will be checked against capabilities/actions for validation.
44

5+
# >>> Units to be converted to standard values, with optional converter script
6+
# for allowing conventional unit inputs. <<<
7+
58
# --- Vessel (on-board) ---
69

710
- name: deck_space

famodel/irma/irma.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,12 @@ def __init__(self):
145145
# raise Exception(f"Action key ({key}) contradicts its name ({act['name']})")
146146

147147
# Check capabilities
148-
if not 'capabilities' in act:
149-
raise Exception(f"Action '{key}' is missing a capabilities list.")
148+
#if 'capabilities' in act:
149+
# raise Exception(f"Action '{key}' is missing a capabilities list.")
150+
151+
if 'capabilities' in act:
150152

151-
for cap in act['capabilities']:
153+
for cap in act['capabilities']:
152154
if not cap in capabilities:
153155
raise Exception(f"Action '{key}' capability '{cap}' is not in the global capability list.")
154156

@@ -157,6 +159,13 @@ def __init__(self):
157159
# if not cap_param in capabilities[cap['name']]:
158160
# raise Exception(f"Action '{key}' capability '{cap['name']}' parameter '{cap_param}' is not in the global capability's parameter list.")
159161

162+
if 'roles' in act: # look through capabilities listed under each role
163+
for caps in act['roles'].values():
164+
for cap in caps:
165+
if not cap in capabilities:
166+
raise Exception(f"Action '{key}' capability '{cap}' is not in the global capability list.")
167+
168+
160169
# Check objects
161170
if not 'objects' in act:
162171
raise Exception(f"Action '{key}' is missing an objects list.")

0 commit comments

Comments
 (0)