|
22 | 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | 23 | # SOFTWARE. |
24 | 24 |
|
| 25 | +from collections import defaultdict |
25 | 26 | import copy |
26 | 27 | from datetime import datetime |
27 | 28 | import json |
@@ -2166,3 +2167,222 @@ def apply_operations_to_native_components(obj, operation_dict, native_dict): # |
2166 | 2167 | native_dict["Instances"], |
2167 | 2168 | ) |
2168 | 2169 | return True |
| 2170 | + |
| 2171 | + |
| 2172 | +class ConfigurationsNexxim(Configurations): |
| 2173 | + """Enables export and import configuration options to be applied to a new or existing Nexxim design.""" |
| 2174 | + |
| 2175 | + @pyaedt_function_handler() |
| 2176 | + def export_config(self, config_file=None, overwrite=False): |
| 2177 | + """Export current design properties to a JSON or TOML file. |
| 2178 | +
|
| 2179 | + Parameters |
| 2180 | + ---------- |
| 2181 | + config_file : str, optional |
| 2182 | + Full path to json file. If ``None``, then the config file will be saved in working directory. |
| 2183 | + overwrite : bool, optional |
| 2184 | + If ``True`` the json file will be overwritten if already existing. |
| 2185 | + If ``False`` and the version is compatible, the data in the existing file will be updated. |
| 2186 | + Default is ``False``. |
| 2187 | +
|
| 2188 | + Returns |
| 2189 | + ------- |
| 2190 | + str |
| 2191 | + Exported config file. |
| 2192 | + """ |
| 2193 | + |
| 2194 | + if not config_file: |
| 2195 | + config_file = os.path.join( |
| 2196 | + self._app.working_directory, generate_unique_name(self._app.design_name) + ".json" |
| 2197 | + ) |
| 2198 | + # dict_out = {} |
| 2199 | + # self._export_general(dict_out) |
| 2200 | + pin_mapping = defaultdict(list) |
| 2201 | + data_refdes = {} |
| 2202 | + data_models = {} |
| 2203 | + pin_nets = {} |
| 2204 | + skip_list = [ |
| 2205 | + "LabelID", |
| 2206 | + "ADD_NOISE", |
| 2207 | + "DTEMP", |
| 2208 | + "ModelName", |
| 2209 | + "CosimDefinition", |
| 2210 | + "CoSimulator", |
| 2211 | + "InstanceName", |
| 2212 | + "NexximNetlist", |
| 2213 | + "Name", |
| 2214 | + "COMPONENT", |
| 2215 | + "EyeMeasurementFunctions", |
| 2216 | + "ACMAG", |
| 2217 | + ] |
| 2218 | + for comp in list(self._app.modeler.schematic.components.values()): |
| 2219 | + properties = {} |
| 2220 | + name = [] |
| 2221 | + num_terminals = None |
| 2222 | + refdes = comp.refdes |
| 2223 | + position = comp.location |
| 2224 | + angle = comp.angle |
| 2225 | + parameters = comp.parameters |
| 2226 | + if not comp.component_info: |
| 2227 | + continue |
| 2228 | + else: |
| 2229 | + component = comp.component_info["Component"] |
| 2230 | + path = comp.component_path |
| 2231 | + if path == False: |
| 2232 | + type = "Nexxim Component" |
| 2233 | + path = "" |
| 2234 | + for param, value in parameters.items(): |
| 2235 | + if param in skip_list: |
| 2236 | + continue |
| 2237 | + elif value and value[-1] == "'" and value[1] == "'": |
| 2238 | + value = value[-1:1] |
| 2239 | + properties[param] = value |
| 2240 | + elif path[-4:] == ".ibs": |
| 2241 | + if "AMI_Version" in parameters: |
| 2242 | + type = "ami" |
| 2243 | + else: |
| 2244 | + type = "ibis" |
| 2245 | + for prop, value in parameters.items(): |
| 2246 | + if value and value[-1] == '"' and value[0] == '"': |
| 2247 | + value = value[1:-1] |
| 2248 | + properties[prop] = value |
| 2249 | + elif path[-4:] in [".LIB", ".lib"] or path[-3:] == ".sp": |
| 2250 | + type = "spice" |
| 2251 | + elif path[-1:] == "p" and path[-2:-1].isdigit(): |
| 2252 | + type = "touchstone" |
| 2253 | + elif path[-4:] == ".sss": |
| 2254 | + type = "nexxim state space" |
| 2255 | + num_terminals = comp.model_data.props["numberofports"] |
| 2256 | + |
| 2257 | + for pin in comp.pins: |
| 2258 | + if pin.net == "0": |
| 2259 | + net = "gnd" |
| 2260 | + else: |
| 2261 | + net = pin.net |
| 2262 | + temp_dict = {pin: net} |
| 2263 | + pin_nets.update(temp_dict) |
| 2264 | + |
| 2265 | + temp_dict2 = { |
| 2266 | + refdes: {"component": component, "properties": properties, "position": position, "angle": angle} |
| 2267 | + } |
| 2268 | + data_refdes.update(temp_dict2) |
| 2269 | + if num_terminals: |
| 2270 | + model = {component: {"type": type, "file_path": path, "num_terminals": num_terminals}} |
| 2271 | + num_terminals = None |
| 2272 | + else: |
| 2273 | + model = {component: {"type": type, "file_path": path}} |
| 2274 | + data_models.update(model) |
| 2275 | + print(data_refdes) |
| 2276 | + |
| 2277 | + for k, v in pin_nets.items(): |
| 2278 | + pin_mapping[v].append(k) |
| 2279 | + |
| 2280 | + if "" in pin_mapping: |
| 2281 | + del pin_mapping[""] |
| 2282 | + for k, l in pin_mapping.items(): |
| 2283 | + temp_dict3 = {} |
| 2284 | + for i in l: |
| 2285 | + temp_dict3.update({i._circuit_comp.refdes: i.name}) |
| 2286 | + pin_mapping[k] = temp_dict3 |
| 2287 | + |
| 2288 | + dict_out = { |
| 2289 | + "models": data_models, |
| 2290 | + "refdes": data_refdes, |
| 2291 | + "pin_mapping": pin_mapping, |
| 2292 | + } # Call private export method to update dict_out. |
| 2293 | + |
| 2294 | + # update the json if it exists already |
| 2295 | + |
| 2296 | + if os.path.exists(config_file) and not overwrite: |
| 2297 | + dict_in = read_configuration_file(config_file) |
| 2298 | + try: # TODO: Allow import of config created with other versions of pyaedt. |
| 2299 | + if dict_in["general"]["pyaedt_version"] == __version__: |
| 2300 | + for k, v in dict_in.items(): |
| 2301 | + if k not in dict_out: |
| 2302 | + dict_out[k] = v |
| 2303 | + elif isinstance(v, dict): |
| 2304 | + for i, j in v.items(): |
| 2305 | + if i not in dict_out[k]: |
| 2306 | + dict_out[k][i] = j |
| 2307 | + except KeyError as e: |
| 2308 | + self._app.logger.error(str(e)) |
| 2309 | + |
| 2310 | + # write the updated dict to file |
| 2311 | + if write_configuration_file(dict_out, config_file): |
| 2312 | + self._app.logger.info(f"Json file {config_file} created correctly.") |
| 2313 | + return config_file |
| 2314 | + self._app.logger.error(f"Error creating json file {config_file}.") |
| 2315 | + return False |
| 2316 | + |
| 2317 | + @pyaedt_function_handler() |
| 2318 | + def import_config(self, config_file, *args): |
| 2319 | + """Import configuration settings from a JSON or TOML file and apply it to the current design. |
| 2320 | +
|
| 2321 | +
|
| 2322 | + Parameters |
| 2323 | + ---------- |
| 2324 | + config_file : str |
| 2325 | + Full path to json file. |
| 2326 | +
|
| 2327 | + Returns |
| 2328 | + ------- |
| 2329 | + dict, bool |
| 2330 | + Config dictionary. |
| 2331 | + """ |
| 2332 | + if len(args) > 0: # pragma: no cover |
| 2333 | + raise TypeError("import_config expected at most 1 arguments, got %d" % (len(args) + 1)) |
| 2334 | + self.results._reset_results() |
| 2335 | + |
| 2336 | + data = read_configuration_file(config_file) |
| 2337 | + for i, j in data["refdes"].items(): |
| 2338 | + for k, l in data["models"].items(): |
| 2339 | + if k == j["component"]: |
| 2340 | + type = l["type"] |
| 2341 | + if type == "Nexxim Component": |
| 2342 | + new_comp = self._app.modeler.components.create_component( |
| 2343 | + name=i, |
| 2344 | + component_library="", |
| 2345 | + component_name=j["component"], |
| 2346 | + location=j["position"], |
| 2347 | + angle=j["angle"], |
| 2348 | + ) |
| 2349 | + elif type in ["ibis", "ami"]: |
| 2350 | + if type == "ami": |
| 2351 | + ami = True |
| 2352 | + else: |
| 2353 | + ami = False |
| 2354 | + comp_set = self._app.get_ibis_model_from_file(l["file_path"], ami).components.values() |
| 2355 | + for comp in comp_set: |
| 2356 | + for pin in comp.pins.values(): |
| 2357 | + if pin.buffer_name == k: |
| 2358 | + new_comp = pin.insert(j["position"][0], j["position"][1]) |
| 2359 | + elif type == "touchstone": |
| 2360 | + new_comp = self._app.modeler.schematic.create_touchstone_component( |
| 2361 | + l["file_path"], location=j["position"], angle=j["angle"] |
| 2362 | + ) |
| 2363 | + elif type == "spice": |
| 2364 | + new_comp = self._app.modeler.schematic.create_component_from_spicemodel( |
| 2365 | + input_file=l["file_path"], location=j["position"] |
| 2366 | + ) |
| 2367 | + elif type == "nexxim state space": |
| 2368 | + new_comp = self._app.modeler.schematic.create_nexxim_state_space_component( |
| 2369 | + l["file_path"], l["num_terminals"], location=j["position"], angle=j["angle"] |
| 2370 | + ) |
| 2371 | + for name, parameter in j["properties"].items(): |
| 2372 | + new_comp.parameters[name] = parameter |
| 2373 | + |
| 2374 | + for i, j in data["pin_mapping"].items(): |
| 2375 | + pins = [] |
| 2376 | + for k, l in j.items(): |
| 2377 | + for comp in list(self._app.modeler.schematic.components.values()): |
| 2378 | + if not comp.refdes: |
| 2379 | + continue |
| 2380 | + elif comp.refdes == k: |
| 2381 | + for pin in comp.pins: |
| 2382 | + if pin.name == l: |
| 2383 | + pins.append(pin) |
| 2384 | + if i == "gnd": |
| 2385 | + for gnd_pin in pins: |
| 2386 | + self._app.modeler.schematic.create_gnd(gnd_pin.location, gnd_pin.angle, page=i) |
| 2387 | + else: |
| 2388 | + pins[0].connect_to_component(pins[1:], page_name=i) |
0 commit comments