|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +# |
| 3 | +# Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates. |
| 4 | +# SPDX-License-Identifier: MIT |
| 5 | +# |
| 6 | +# |
| 7 | +# Permission is hereby granted, free of charge, to any person obtaining a copy |
| 8 | +# of this software and associated documentation files (the "Software"), to deal |
| 9 | +# in the Software without restriction, including without limitation the rights |
| 10 | +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 11 | +# copies of the Software, and to permit persons to whom the Software is |
| 12 | +# furnished to do so, subject to the following conditions: |
| 13 | +# |
| 14 | +# The above copyright notice and this permission notice shall be included in all |
| 15 | +# copies or substantial portions of the Software. |
| 16 | +# |
| 17 | +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 18 | +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 19 | +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 20 | +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 21 | +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 22 | +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 23 | +# SOFTWARE. |
| 24 | + |
| 25 | + |
| 26 | +from ansys.aedt.core.generic.general_methods import pyaedt_function_handler |
| 27 | + |
| 28 | + |
| 29 | +class EmitSchematic: |
| 30 | + """Represents the EMIT schematic and provides methods to interact with it.""" |
| 31 | + |
| 32 | + def __init__(self, emit_instance): |
| 33 | + """Initialize the EmitSchematic class. |
| 34 | +
|
| 35 | + Parameters |
| 36 | + ---------- |
| 37 | + emit_instance : Emit |
| 38 | + Instance of the Emit class. |
| 39 | + """ |
| 40 | + self.emit_instance = emit_instance |
| 41 | + |
| 42 | + @property |
| 43 | + def _emit_com_module(self): |
| 44 | + """Retrieve the EmitCom module from the Emit instance. |
| 45 | +
|
| 46 | + Returns |
| 47 | + ------- |
| 48 | + object |
| 49 | + The EmitCom module. |
| 50 | +
|
| 51 | + Raises |
| 52 | + ------ |
| 53 | + RuntimeError |
| 54 | + If the EmitCom module cannot be retrieved. |
| 55 | + """ |
| 56 | + if not hasattr(self.emit_instance, "_odesign"): |
| 57 | + raise RuntimeError("Emit instance does not have a valid '_odesign' attribute.") |
| 58 | + try: |
| 59 | + return self.emit_instance._odesign.GetModule("EmitCom") |
| 60 | + except Exception as e: |
| 61 | + raise RuntimeError(f"Failed to retrieve EmitCom module: {e}") |
| 62 | + |
| 63 | + @pyaedt_function_handler |
| 64 | + def create_component(self, component_type: str, name: str = None, library: str = None) -> int: |
| 65 | + """Create a component. |
| 66 | +
|
| 67 | + Parameters |
| 68 | + ---------- |
| 69 | + component_type : str |
| 70 | + Type of the component to create. |
| 71 | + name : str, optional |
| 72 | + Name of the component to create. AEDT defaults used if not provided. |
| 73 | + library : str, optional |
| 74 | + Name of the component library. Defaults to an empty string if not provided. |
| 75 | +
|
| 76 | + Returns |
| 77 | + ------- |
| 78 | + int |
| 79 | + The ID of the created component. |
| 80 | +
|
| 81 | + Raises |
| 82 | + ------ |
| 83 | + ValueError |
| 84 | + If the component type is empty or no matching component is found. |
| 85 | + RuntimeError |
| 86 | + If the component creation fails. |
| 87 | + """ |
| 88 | + if not component_type: |
| 89 | + raise ValueError("The 'component_type' argument is required.") |
| 90 | + |
| 91 | + name = name or "" |
| 92 | + library = library or "" |
| 93 | + |
| 94 | + try: |
| 95 | + # Retrieve matching components from the catalog |
| 96 | + matching_components = self.emit_instance.modeler.components.components_catalog[component_type] |
| 97 | + |
| 98 | + if not matching_components: |
| 99 | + self.emit_instance.logger.error(f"No component found for type '{component_type}'.") |
| 100 | + raise ValueError(f"No component found for type '{component_type}'.") |
| 101 | + |
| 102 | + if len(matching_components) == 1: |
| 103 | + # Use the single matching component |
| 104 | + component = matching_components[0] |
| 105 | + self.emit_instance.logger.info( |
| 106 | + f"Using component '{component.name}' from library '{component.component_library}" |
| 107 | + f"' for type '{component_type}'." |
| 108 | + ) |
| 109 | + else: |
| 110 | + # Attempt to find an exact match |
| 111 | + component = next((comp for comp in matching_components if comp.name == component_type), None) |
| 112 | + if not component: |
| 113 | + self.emit_instance.logger.error( |
| 114 | + f"Multiple components found for type '{component_type}', but no exact match." |
| 115 | + " Please specify a unique component." |
| 116 | + ) |
| 117 | + raise ValueError(f"Multiple components found for type '{component_type}', but no exact match.") |
| 118 | + self.emit_instance.logger.info( |
| 119 | + f"Using exact match component '{component.name}' from library '{component.component_library}" |
| 120 | + "' for type '{component_type}'." |
| 121 | + ) |
| 122 | + |
| 123 | + # Create the component using the EmitCom module |
| 124 | + new_component_id = self._emit_com_module.CreateEmitComponent( |
| 125 | + name, component.name, component.component_library |
| 126 | + ) |
| 127 | + return new_component_id |
| 128 | + except Exception as e: |
| 129 | + self.emit_instance.logger.error(f"Failed to create component '{name}' of type '{component_type}': {e}") |
| 130 | + raise RuntimeError(f"Failed to create component of type '{component_type}': {e}") |
| 131 | + |
| 132 | + @pyaedt_function_handler |
| 133 | + def create_radio_antenna( |
| 134 | + self, radio_type: str, radio_name: str = None, antenna_name: str = None, library: str = None |
| 135 | + ) -> tuple[int, int]: |
| 136 | + """Create a new radio and antenna and connect them. |
| 137 | +
|
| 138 | + Parameters |
| 139 | + ---------- |
| 140 | + radio_type : str |
| 141 | + Type of radio to create. For example, "Bluetooth". Must match |
| 142 | + a radio name in the specified library. |
| 143 | + radio_name : str, optional |
| 144 | + Name to assign to the new radio. If ``None``, then an instance |
| 145 | + name is assigned automatically. The default is ``None``. |
| 146 | + antenna_name : str, optional |
| 147 | + Name to assign to the new antenna. If ``None``, then an instance |
| 148 | + name is assigned automatically. The default is ``None``. |
| 149 | + library : str, optional |
| 150 | + Name of the component library. If ``None``, then the default |
| 151 | + library is used. The default is ``None``. |
| 152 | +
|
| 153 | + Returns |
| 154 | + ------- |
| 155 | + tuple |
| 156 | + A tuple containing the IDs of the created radio and antenna. |
| 157 | +
|
| 158 | + Raises |
| 159 | + ------ |
| 160 | + RuntimeError |
| 161 | + If the radio or antenna creation fails. |
| 162 | + """ |
| 163 | + radio_name = radio_name or "" |
| 164 | + antenna_name = antenna_name or "" |
| 165 | + library = library or "" |
| 166 | + |
| 167 | + try: |
| 168 | + new_radio_id = self.create_component(radio_type, radio_name, library) |
| 169 | + new_antenna_id = self.create_component("Antenna", antenna_name, "Antennas") |
| 170 | + if new_radio_id and new_antenna_id: |
| 171 | + self.connect_components(new_antenna_id, new_radio_id) # Connect antenna to radio |
| 172 | + return new_radio_id, new_antenna_id |
| 173 | + except Exception as e: |
| 174 | + self.emit_instance.logger.error(f"Failed to create radio of type '{radio_type}' or antenna: {e}") |
| 175 | + raise RuntimeError(f"Failed to create radio of type '{radio_type}' or antenna: {e}") |
| 176 | + |
| 177 | + @pyaedt_function_handler |
| 178 | + def connect_components(self, component_id_1: int, component_id_2: int): |
| 179 | + """Connect two components in the schematic. |
| 180 | +
|
| 181 | + Parameters |
| 182 | + ---------- |
| 183 | + component_id_1 : str |
| 184 | + ID of the first component. |
| 185 | + component_id_2 : str |
| 186 | + ID of the second component. |
| 187 | +
|
| 188 | + Raises |
| 189 | + ------ |
| 190 | + RuntimeError |
| 191 | + If the connection fails. |
| 192 | + """ |
| 193 | + try: |
| 194 | + component_name_1 = self.get_component_properties(component_id_1, "Name") |
| 195 | + component_name_2 = self.get_component_properties(component_id_2, "Name") |
| 196 | + self.emit_instance._oeditor.PlaceComponent(component_name_1, component_name_2) |
| 197 | + self.emit_instance.logger.info( |
| 198 | + f"Successfully connected components '{component_name_1}' and '{component_name_2}'." |
| 199 | + ) |
| 200 | + except Exception as e: |
| 201 | + self.emit_instance.logger.error( |
| 202 | + f"Failed to connect components '{component_id_1}' and '{component_id_2}': {e}" |
| 203 | + ) |
| 204 | + raise RuntimeError(f"Failed to connect components '{component_id_1}' and '{component_id_2}': {e}") |
| 205 | + |
| 206 | + @pyaedt_function_handler |
| 207 | + def get_component_properties(self, component_id: int, property_key: str = None) -> dict: |
| 208 | + """Get properties of a component. |
| 209 | +
|
| 210 | + Parameters |
| 211 | + ---------- |
| 212 | + component_id : int |
| 213 | + ID of the component. |
| 214 | + property_key : str, optional |
| 215 | + Specific property key to retrieve. If ``None``, all properties are returned. |
| 216 | +
|
| 217 | + Returns |
| 218 | + ------- |
| 219 | + dict or str |
| 220 | + Dictionary containing all properties of the component if `property_key` is ``None``. |
| 221 | + Otherwise, the value of the specified property key. |
| 222 | +
|
| 223 | + Raises |
| 224 | + ------ |
| 225 | + KeyError |
| 226 | + If the specified property key is not found. |
| 227 | + """ |
| 228 | + try: |
| 229 | + props = self._emit_com_module.GetEmitNodeProperties(0, component_id, True) |
| 230 | + props_dict = {prop.split("=", 1)[0]: prop.split("=", 1)[1] for prop in props} |
| 231 | + if property_key is None: |
| 232 | + return props_dict |
| 233 | + if property_key in props_dict: |
| 234 | + return props_dict[property_key] |
| 235 | + raise KeyError(f"Property key '{property_key}' not found.") |
| 236 | + except Exception as e: |
| 237 | + self.emit_instance.logger.error(f"Failed to retrieve properties for component '{component_id}': {e}") |
| 238 | + raise RuntimeError(f"Failed to retrieve properties for component '{component_id}': {e}") |
0 commit comments