|
| 1 | +# Copyright 2025, Battelle Energy Alliance, LLC All Rights Reserved |
| 2 | + |
| 3 | +from dataclasses import dataclass, field |
| 4 | +from typing import Dict, List, Tuple, Optional |
| 5 | +from .equipment import Equipment |
| 6 | +from .spatial import Space, Building, SpaceConstraint |
| 7 | +from .connections import Connection |
| 8 | + |
| 9 | + |
| 10 | +@dataclass |
| 11 | +class Placement: |
| 12 | + """Represents the placement of equipment within a space.""" |
| 13 | + |
| 14 | + equipment_id: str |
| 15 | + space_id: str |
| 16 | + position: Tuple[float, float] # (x, y) relative to space origin |
| 17 | + rotation: float = 0.0 # degrees |
| 18 | + |
| 19 | + |
| 20 | +@dataclass |
| 21 | +class Layout: |
| 22 | + """Complete facility layout solution.""" |
| 23 | + |
| 24 | + buildings: List[Building] |
| 25 | + spaces: List[Space] |
| 26 | + equipment: List[Equipment] |
| 27 | + placements: Dict[str, Placement] # equipment_id -> Placement |
| 28 | + connections: List[Connection] |
| 29 | + space_constraints: List[SpaceConstraint] = field(default_factory=list) |
| 30 | + score: Optional[float] = None |
| 31 | + metadata: Dict = field(default_factory=dict) |
| 32 | + |
| 33 | + def get_equipment_absolute_position( |
| 34 | + self, equipment_id: str |
| 35 | + ) -> Optional[Tuple[float, float]]: |
| 36 | + """Calculate absolute position of equipment considering all hierarchy levels.""" |
| 37 | + if equipment_id not in self.placements: |
| 38 | + return None |
| 39 | + |
| 40 | + placement = self.placements[equipment_id] |
| 41 | + space = next((s for s in self.spaces if s.id == placement.space_id), None) |
| 42 | + if not space: |
| 43 | + return None |
| 44 | + |
| 45 | + # Start with position within space |
| 46 | + x, y = placement.position |
| 47 | + |
| 48 | + # Add space position within building |
| 49 | + if space.position: |
| 50 | + x += space.position[0] |
| 51 | + y += space.position[1] |
| 52 | + |
| 53 | + # Add building position on site |
| 54 | + if space.parent_building: |
| 55 | + building = next( |
| 56 | + (b for b in self.buildings if b.id == space.parent_building), None |
| 57 | + ) |
| 58 | + if building and building.position: |
| 59 | + x += building.position[0] |
| 60 | + y += building.position[1] |
| 61 | + |
| 62 | + return (x, y) |
0 commit comments