|
| 1 | +# calwave_task1.py |
| 2 | +# Build CalWave Task 1 (Anchor installation) following the theory flow: |
| 3 | +# 1) addAction → structure only (type, name, objects, deps) |
| 4 | +# 2) evaluateAssets → assign vessels/roles (+ durations/costs) |
| 5 | +# 3) (schedule/plot handled by your existing tooling) |
| 6 | + |
| 7 | +from famodel.project import Project |
| 8 | +from calwave_irma import Scenario |
| 9 | +import calwave_chart as chart |
| 10 | +from calwave_task import Task |
| 11 | + |
| 12 | +sc = Scenario() # now sc exists in *this* session |
| 13 | + |
| 14 | +def eval_set(a, roles, duration=None, **params): |
| 15 | + """ |
| 16 | + Convenience: call evaluateAssets with roles/params and optionally set .duration. |
| 17 | + Always stores assigned_assets for plotting/scheduling attribution. |
| 18 | + """ |
| 19 | + # Your Action.evaluateAssets may return (duration, cost); we still set explicit duration if passed. |
| 20 | + res = a.evaluateAssets(roles | params) |
| 21 | + if duration is not None: |
| 22 | + a.duration = float(duration) |
| 23 | + elif isinstance(res, tuple) and len(res) > 0 and res[0] is not None: |
| 24 | + try: |
| 25 | + a.duration = float(res[0]) |
| 26 | + except Exception: |
| 27 | + pass |
| 28 | + # keep roles visible on the action |
| 29 | + a.assigned_assets = roles |
| 30 | + return a |
| 31 | + |
| 32 | +# ---------- Core builder ---------- |
| 33 | +def build_task1_calwave(sc: Scenario, project: Project): |
| 34 | + """ |
| 35 | + Creates Task 1 actions + dependencies (no scheduling/plotting here). |
| 36 | + """ |
| 37 | + |
| 38 | + # --- Pre-ops --- |
| 39 | + mob_sd = sc.addAction('mobilize', 'mobilize_SanDiego') |
| 40 | + linehaul_convoy = sc.addAction( |
| 41 | + 'transit_linehaul_tug', 'linehaul_to_site_convoy', |
| 42 | + dependencies=[mob_sd]) |
| 43 | + |
| 44 | + mob_by = sc.addAction( |
| 45 | + 'mobilize', 'mobilize_Beyster', |
| 46 | + #dependencies=[mob_sd] |
| 47 | + ) |
| 48 | + linehaul_by = sc.addAction( |
| 49 | + 'transit_linehaul_self', 'linehaul_to_site_Beyster', |
| 50 | + dependencies=[mob_sd]) |
| 51 | + |
| 52 | + # --- Compute anchor centroid (x,y) for first onsite leg start --- |
| 53 | + anchors_all = list(project.anchorList.values()) |
| 54 | + rs = [getattr(a, 'r', None) for a in anchors_all if getattr(a, 'r', None) is not None] |
| 55 | + xs = [float(r[0]) for r in rs] |
| 56 | + ys = [float(r[1]) for r in rs] |
| 57 | + anchor_centroid = (sum(xs)/len(xs), sum(ys)/len(ys)) if xs and ys else None |
| 58 | + try: |
| 59 | + print('[task1] anchor_centroid =', anchor_centroid) |
| 60 | + except Exception: |
| 61 | + pass |
| 62 | + |
| 63 | + # --- On-site (domain objects REQUIRED) --- |
| 64 | + installs, onsite_tug, onsite_by, monitors = [], [], [], [] |
| 65 | + |
| 66 | + # first convoy leg starts after the linehaul convoy reaches site |
| 67 | + prev_for_next_tug = linehaul_convoy |
| 68 | + # Beyster’s first in-field leg starts after her own linehaul |
| 69 | + prev_for_by = linehaul_by |
| 70 | + |
| 71 | + for i, (key, anchor) in enumerate(project.anchorList.items(), start=1): |
| 72 | + # 1) Onsite convoy (tug + barge) to this anchor |
| 73 | + a_tug = sc.addAction( |
| 74 | + 'transit_onsite_tug', f'transit_convoy-{key}', |
| 75 | + objects=[anchor], |
| 76 | + dependencies=[prev_for_next_tug] # first = linehaul_convoy; then = previous install |
| 77 | + ) |
| 78 | + |
| 79 | + # 2) Beyster to this anchor (after previous monitor), independent of tug |
| 80 | + a_by = sc.addAction( |
| 81 | + 'transit_onsite_self', f'transit_Beyster-{key}', |
| 82 | + objects=[anchor], |
| 83 | + dependencies=[prev_for_by, prev_for_next_tug] |
| 84 | + ) |
| 85 | + |
| 86 | + # Inject centroid for the FIRST onsite legs only (centroid → first anchor) |
| 87 | + if i == 1 and anchor_centroid is not None: |
| 88 | + a_by.meta = getattr(a_by, 'meta', {}) or {} |
| 89 | + a_by.meta['anchor_centroid'] = anchor_centroid |
| 90 | + a_tug.meta = getattr(a_tug, 'meta', {}) or {} |
| 91 | + a_tug.meta['anchor_centroid'] = anchor_centroid |
| 92 | + |
| 93 | + # 3) Install at this anchor (wait for both tug+barge and Beyster on station) |
| 94 | + a_inst = sc.addAction( |
| 95 | + 'install_anchor', f'install_anchor-{key}', |
| 96 | + objects=[anchor], |
| 97 | + dependencies=[a_tug, a_by] |
| 98 | + ) |
| 99 | + |
| 100 | + # 4) Monitor at this anchor (while anchor is installed) |
| 101 | + a_mon = sc.addAction( |
| 102 | + 'monitor_installation', f'monitor_installation-{key}', |
| 103 | + objects=[anchor], |
| 104 | + dependencies=[a_tug] |
| 105 | + ) |
| 106 | + |
| 107 | + # collect handles |
| 108 | + onsite_tug.append(a_tug) |
| 109 | + installs.append(a_inst) |
| 110 | + onsite_by.append(a_by) |
| 111 | + monitors.append(a_mon) |
| 112 | + |
| 113 | + # chain next legs: |
| 114 | + prev_for_next_tug = a_inst # next convoy starts from this installed anchor |
| 115 | + prev_for_by = a_mon # or set to a_inst if you want Beyster to move immediately post-install |
| 116 | + |
| 117 | + |
| 118 | + # --- Post-ops (objectless) --- |
| 119 | + linehome_convoy = sc.addAction( |
| 120 | + 'transit_linehaul_tug', 'linehaul_to_home_convoy', |
| 121 | + dependencies=monitors) |
| 122 | + |
| 123 | + linehome_by = sc.addAction( |
| 124 | + 'transit_linehaul_self', 'transit_to_home_Beyster', |
| 125 | + dependencies=monitors) |
| 126 | + |
| 127 | + # --- Post-ops --- |
| 128 | + demob_sd = sc.addAction( |
| 129 | + 'demobilize', 'demobilize_SanDiego', |
| 130 | + dependencies=[linehome_convoy]) |
| 131 | + |
| 132 | + demob_by = sc.addAction( |
| 133 | + 'demobilize', 'demobilize_Beyster', |
| 134 | + dependencies=[linehome_by]) |
| 135 | + |
| 136 | + # Return a simple list for downstream evaluate/schedule/plot steps |
| 137 | + return { |
| 138 | + 'mobilize': [mob_sd, mob_by], |
| 139 | + 'linehaul_to_site': [linehaul_convoy, linehaul_by], |
| 140 | + 'install': installs, |
| 141 | + 'onsite_tug': onsite_tug, |
| 142 | + 'onsite_by': onsite_by, |
| 143 | + 'monitor': monitors, |
| 144 | + 'linehaul_to_home': [linehome_convoy, linehome_by], |
| 145 | + 'demobilize': [demob_sd, demob_by]} |
| 146 | + |
| 147 | +# ---------- Evaluation step (assign vessels & durations) ---------- |
| 148 | +def evaluate_task1(sc: Scenario, actions: dict): |
| 149 | + """ |
| 150 | + Assign vessels/roles and set durations where the evaluator doesn't. |
| 151 | + Keeps creation and evaluation clearly separated. |
| 152 | + """ |
| 153 | + V = sc.vessels # shorthand |
| 154 | + |
| 155 | + # Mobilize |
| 156 | + eval_set(actions['mobilize'][0], {'operator': V['San_Diego']}, duration=3.0) |
| 157 | + eval_set(actions['mobilize'][1], {'operator': V['Beyster']}, duration=1.0) |
| 158 | + |
| 159 | + # Transit to site |
| 160 | + convoy_to_site, beyster_to_site = actions['linehaul_to_site'] |
| 161 | + eval_set(convoy_to_site, {'carrier': V['Jag'], 'operator': V['San_Diego']}) |
| 162 | + eval_set(beyster_to_site, {'vessel': V['Beyster']}) |
| 163 | + |
| 164 | + # Onsite convoy (tug+barge) |
| 165 | + for a_tug in actions['onsite_tug']: |
| 166 | + eval_set(a_tug, {'carrier': V['Jag'], 'operator': V['San_Diego']}) |
| 167 | + |
| 168 | + # Install (Jag carries, San_Diego operates the install) |
| 169 | + for a_inst in actions['install']: |
| 170 | + eval_set(a_inst, {'carrier': V['Jag'], 'operator': V['San_Diego']}) |
| 171 | + |
| 172 | + # Onsite self-propelled (Beyster) |
| 173 | + for a_by in actions['onsite_by']: |
| 174 | + eval_set(a_by, {'vessel': V['Beyster']}) |
| 175 | + |
| 176 | + # Monitor (Beyster as support) |
| 177 | + for a_mon in actions['monitor']: |
| 178 | + eval_set(a_mon, {'support': V['Beyster']}) |
| 179 | + |
| 180 | + # Transit to home |
| 181 | + convoy_to_home, beyster_to_home = actions['linehaul_to_home'] |
| 182 | + eval_set(convoy_to_home, {'carrier': V['Jag'], 'operator': V['San_Diego']}) |
| 183 | + eval_set(beyster_to_home, {'vessel': V['Beyster']}) |
| 184 | + |
| 185 | + # Demobilize |
| 186 | + eval_set(actions['demobilize'][0], {'operator': V['San_Diego']}, duration=3.0) |
| 187 | + eval_set(actions['demobilize'][1], {'operator': V['Beyster']}, duration=1.0) |
| 188 | + |
| 189 | + |
| 190 | +if __name__ == '__main__': |
| 191 | + # 1) Load ontology that mirrors the sample schema (mooring_systems + mooring_line_configs) |
| 192 | + project = Project(file='calwave_ontology.yaml', raft=False) |
| 193 | + project.getMoorPyArray(cables=1) |
| 194 | + |
| 195 | + # 2) Scenario with CalWave catalogs |
| 196 | + sc = Scenario() |
| 197 | + |
| 198 | + # 3) Build (structure only) |
| 199 | + actions = build_task1_calwave(sc, project) |
| 200 | + |
| 201 | + # 4) Evaluate (assign vessels/roles + durations) |
| 202 | + evaluate_task1(sc, actions) |
| 203 | + |
| 204 | + # 5) schedule once, in the Task |
| 205 | + calwave_task1 = Task.from_scenario( |
| 206 | + sc, |
| 207 | + strategy='levels', # or 'levels' |
| 208 | + enforce_resources=False, # keep single-resource blocking if you want it |
| 209 | + resource_roles=('vessel', 'carrier', 'operator')) |
| 210 | + |
| 211 | + # 6) build the chart input directly from the Task and plot |
| 212 | + chart_view = chart.view_from_task(calwave_task1, sc, title='CalWave Task 1 - Anchor installation plan') |
| 213 | + chart.plot_task(chart_view) |
| 214 | + |
| 215 | + |
| 216 | + |
0 commit comments