diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index da77e5c82..9ea12af7e 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -69,7 +69,7 @@ jobs: python-version: [ '3.8', '3.9', '3.10', '3.11' ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index f03623646..b47528604 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -16,7 +16,7 @@ jobs: name: Syncer runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: micnncim/action-label-syncer@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/doc/source/api/analysis.rst b/doc/source/api/analysis.rst index 8fead9dbc..e2828bdc1 100644 --- a/doc/source/api/analysis.rst +++ b/doc/source/api/analysis.rst @@ -11,12 +11,14 @@ The ``analysis`` module contains all analysis capabilities. :toctree: _autosummary ansys.sherlock.core.analysis.Analysis.get_harmonic_vibe_input_fields + ansys.sherlock.core.analysis.Analysis.get_ict_analysis_input_fields ansys.sherlock.core.analysis.Analysis.get_mechanical_shock_input_fields ansys.sherlock.core.analysis.Analysis.get_random_vibe_input_fields ansys.sherlock.core.analysis.Analysis.get_solder_fatigue_input_fields ansys.sherlock.core.analysis.Analysis.run_analysis ansys.sherlock.core.analysis.Analysis.run_strain_map_analysis ansys.sherlock.core.analysis.Analysis.update_harmonic_vibe_props + ansys.sherlock.core.analysis.Analysis.update_ict_analysis_props ansys.sherlock.core.analysis.Analysis.update_mechanical_shock_props ansys.sherlock.core.analysis.Analysis.update_natural_frequency_props ansys.sherlock.core.analysis.Analysis.update_part_modeling_props diff --git a/doc/source/api/parts.rst b/doc/source/api/parts.rst index 43a9ec286..9406106b3 100644 --- a/doc/source/api/parts.rst +++ b/doc/source/api/parts.rst @@ -16,4 +16,5 @@ The ``parts`` module contains all parts management capabilities. ansys.sherlock.core.parts.Parts.update_parts_list ansys.sherlock.core.parts.Parts.update_parts_locations ansys.sherlock.core.parts.Parts.update_parts_locations_by_file - ansys.sherlock.core.parts.Parts.get_part_location \ No newline at end of file + ansys.sherlock.core.parts.Parts.get_part_location + ansys.sherlock.core.parts.Parts.update_parts_from_AVL \ No newline at end of file diff --git a/doc/source/api/parts_types.rst b/doc/source/api/parts_types.rst index 07b52e4fd..2d52a7417 100644 --- a/doc/source/api/parts_types.rst +++ b/doc/source/api/parts_types.rst @@ -11,3 +11,7 @@ Constants and classes used for the Parts API. :toctree: _autosummary PartLocation + PartsListSearchMatchingMode + PartsListSearchDuplicationMode + AVLPartNum + AVLDescription diff --git a/doc/source/getting_started/installation.rst b/doc/source/getting_started/installation.rst index feeb943f1..c0ae80817 100644 --- a/doc/source/getting_started/installation.rst +++ b/doc/source/getting_started/installation.rst @@ -9,7 +9,6 @@ The ``ansys-sherlock-core`` package supports Python 3.8 through Python 3.11 on W To use PySherlock, you must download and install both the ``ansys-api-sherlock`` and ``ansys-sherlock-core`` packages. By using ``pip``, ``ansys-api-sherlock`` is installed as part of ``ansys-sherlock-core``. Run the following to install - the publicly distributed version of the package. .. code:: @@ -17,7 +16,7 @@ the publicly distributed version of the package. pip install ansys-sherlock-core If you want to install the ``ansys-api-sherlock`` and ``ansys-sherlock-core`` packages -from its source code directly, follow the upcoming instructions: +from its source code directly, follow these instructions. #. Download the latest ``ansys-api-sherlock`` package by running this ``git clone`` command: diff --git a/doc/source/user_guide/index.rst b/doc/source/user_guide/index.rst index 25349c760..53b90b6cc 100644 --- a/doc/source/user_guide/index.rst +++ b/doc/source/user_guide/index.rst @@ -117,14 +117,16 @@ method to add a random vibe profile: .. code:: - sherlock.lifecycle.add_random_vibe_profile( + sherlock.lifecycle.add_random_vibe_profiles( "Tutorial", - "Phase 1", - "RVEvent 1", - "Profile 1", - "HZ", - "G2/Hz", - [(30.4, 7.61e-5), (204, 0.1), (296, 0.06), (385, 0.06), (454, 0.03), (497, 0.06)] + [( + "Phase 1", + "RVEvent 1", + "Profile 1", + "HZ", + "G2/Hz", + [(30.4, 7.61e-5), (204, 0.1), (296, 0.06), (385, 0.06), (454, 0.03), (497, 0.06)], + )] ) For information on the ``lifecycle`` module and its methods, see :ref:`ref_lifecycle_module`. @@ -140,7 +142,7 @@ to run a random vibe analysis: "Tutorial", "Main Board", [ - ("RANDOMVIBE", + (RunAnalysisRequestAnalysisType.RANDOM_VIBE, [ ("Phase 1", ["RVEvent 1"]) ] diff --git a/pyproject.toml b/pyproject.toml index 96387ff56..17b0628ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ ] dependencies = [ - "ansys-api-sherlock==0.1.20", + "ansys-api-sherlock==0.1.21", "grpcio>=1.17", "importlib-metadata>=4.0,<5; python_version<='3.8'", "protobuf~=3.20", @@ -32,13 +32,13 @@ dependencies = [ [project.optional-dependencies] tests = [ - "grpcio==1.57.0", + "grpcio==1.58.0", "protobuf==3.20.3", - "pytest==7.4.0", + "pytest==7.4.2", "pytest-cov==4.1.0", ] doc = [ - "ansys-sphinx-theme==0.10.4", + "ansys-sphinx-theme==0.11.2", "numpydoc==1.5.0", "Sphinx==6.2.1", # BLOCKED BY sphinx-design - Cannot upgrade to Sphinx 7 for now "sphinx-copybutton==0.5.2", diff --git a/src/ansys/sherlock/core/analysis.py b/src/ansys/sherlock/core/analysis.py index 7b963587c..b664b8326 100644 --- a/src/ansys/sherlock/core/analysis.py +++ b/src/ansys/sherlock/core/analysis.py @@ -14,6 +14,7 @@ SherlockRunAnalysisError, SherlockRunStrainMapAnalysisError, SherlockUpdateHarmonicVibePropsError, + SherlockUpdateICTAnalysisPropsError, SherlockUpdateMechanicalShockPropsError, SherlockUpdateNaturalFrequencyPropsError, SherlockUpdatePartModelingPropsError, @@ -42,6 +43,10 @@ def __init__(self, channel): "forceModelRebuild": "force_model_rebuild", "harmonicVibeDamping": "harmonic_vibe_damping", "harmonicVibeCount": "harmonic_vibe_count", + "ictApplicationTime": "ict_application_time", + "ictApplicationTimeUnits": "ict_application_time_units", + "ictNumberOfEvents": "ict_number_of_events", + "ictResultCount": "ict_result_count", "modelSource": "model_source", "naturalFreqCount": "natural_freq_count", "naturalFreqMin": "natural_freq_min", @@ -333,7 +338,7 @@ def update_harmonic_vibe_props( if "cca_name" not in harmonic_vibe_props.keys(): raise SherlockUpdateHarmonicVibePropsError( - message=f"CCA name is invalid for harmonic vibe properties {i}." + message=f"CCA name is missing for harmonic vibe properties {i}." ) cca_name = harmonic_vibe_props["cca_name"] @@ -483,6 +488,183 @@ def update_harmonic_vibe_props( LOG.error(str(e)) raise e + def get_ict_analysis_input_fields(self): + """Get ICT analysis property fields based on the user configuration. + + Parameters + ---------- + None + + Returns + ------- + list + List of ICT analysis property fields based on the user configuration. + + Examples + -------- + >>> from ansys.sherlock.core.launcher import launch_sherlock + >>> sherlock = launch_sherlock() + >>> sherlock.analysis.get_ict_analysis_input_fields() + """ + if not self._is_connection_up(): + LOG.error("There is no connection to a gRPC service.") + return + + message = SherlockAnalysisService_pb2.GetICTAnalysisInputFieldsRequest() + response = self.stub.getICTAnalysisInputFields(message) + + fields = self._translate_field_names(response.fieldName) + LOG.info(fields) + + return fields + + def update_ict_analysis_props( + self, + project, + ict_analysis_properties, + ): + """Update properties for an ICT analysis. + + Parameters + ---------- + project : str + Name of the Sherlock project. + ict_analysis_properties : list + List of ICT analysis properties for a CCA consisting of these properties: + + - cca_name : str + Name of the CCA. + - ict_application_time : double + Specifies the amount of time to complete one ICT event. + - ict_application_time_units : str + Application time units. + Options are ``"ms"``, ``"sec"``, ``"min"``, ``"hr"``, ``"day"``, ``"year"``. + - ict_number_of_events: int + Specifies the number of events to apply to the application time when computing + the time to failure for a component. + - part_validation_enabled: bool + Whether to enable part validation. The default is ``None``. + - require_material_assignment_enabled: bool + Whether to require material assignment. The default is ``None``. + - ict_result_count: int + The number of ICT result layers to generate. This parameter is for use with + thermal analysis. + + Returns + ------- + int + Status code of the response. 0 for success. + + Examples + -------- + >>> from ansys.sherlock.core.launcher import launch_sherlock + >>> sherlock = launch_sherlock() + >>> sherlock.project.import_odb_archive( + "ODB++ Tutorial.tgz", + True, + True, + True, + True, + project="Test", + cca_name="Card", + ) + >>> sherlock.analysis.update_ict_analysis_props( + "Test", + [{ + 'cca_name': 'Card', + 'ict_application_time': 2, + 'ict_application_time_units': 'sec', + 'ict_number_of_events': 10, + 'part_validation_enabled': False, + 'require_material_assignment_enabled': False, + }, + ] + ) + + """ + try: + if project == "": + raise SherlockUpdateICTAnalysisPropsError(message="Project name is invalid.") + + if not isinstance(ict_analysis_properties, list): + raise SherlockUpdateICTAnalysisPropsError( + message="ICT analysis properties argument is invalid." + ) + + if len(ict_analysis_properties) == 0: + raise SherlockUpdateICTAnalysisPropsError( + message="One or more ICT analysis properties are required." + ) + + request = SherlockAnalysisService_pb2.UpdateICTAnalysisPropsRequest(project=project) + + for i, ict_analysis_props in enumerate(ict_analysis_properties): + if not isinstance(ict_analysis_props, dict): + raise SherlockUpdateICTAnalysisPropsError( + f"ICT analysis props argument is invalid for ICT analysis properties {i}." + ) + + if "cca_name" not in ict_analysis_props.keys(): + raise SherlockUpdateICTAnalysisPropsError( + message=f"CCA name is missing for ICT analysis properties {i}." + ) + + cca_name = ict_analysis_props["cca_name"] + if cca_name == "": + raise SherlockUpdateICTAnalysisPropsError( + message=f"CCA name is invalid for ICT analysis properties {i}." + ) + + props_request = request.ictAnalysisProperties.add() + props_request.ccaName = cca_name + + if "ict_analysis_count" in ict_analysis_props.keys(): + props_request.ictAnalysisCount = ict_analysis_props["ict_analysis_count"] + + if "ict_application_time" in ict_analysis_props.keys(): + props_request.applicationTime = ict_analysis_props["ict_application_time"] + + if "ict_application_time_units" in ict_analysis_props.keys(): + props_request.applicationTimeUnits = ict_analysis_props[ + "ict_application_time_units" + ] + + if "ict_number_of_events" in ict_analysis_props.keys(): + props_request.numberOfEvents = ict_analysis_props["ict_number_of_events"] + + if "part_validation_enabled" in ict_analysis_props.keys(): + props_request.partValidationEnabled = ict_analysis_props[ + "part_validation_enabled" + ] + + if "require_material_assignment_enabled" in ict_analysis_props.keys(): + props_request.requireMaterialAssignmentEnabled = ict_analysis_props[ + "require_material_assignment_enabled" + ] + + if "force_model_rebuild" in ict_analysis_props.keys(): + props_request.forceModelRebuild = ict_analysis_props["force_model_rebuild"] + + except SherlockUpdateICTAnalysisPropsError as e: + LOG.error(str(e)) + raise e + + if not self._is_connection_up(): + LOG.error("There is no connection to a gRPC service.") + return + + response = self.stub.updateICTAnalysisProps(request) + + try: + if response.value == -1: + raise SherlockUpdateICTAnalysisPropsError(response.message) + else: + LOG.info(response.message) + return response.value + except SherlockUpdateICTAnalysisPropsError as e: + LOG.error(str(e)) + raise e + def get_mechanical_shock_input_fields(self, model_source=None): """Get mechanical shock property fields based on the user configuration. diff --git a/src/ansys/sherlock/core/errors.py b/src/ansys/sherlock/core/errors.py index 96df0dc62..414690bfe 100644 --- a/src/ansys/sherlock/core/errors.py +++ b/src/ansys/sherlock/core/errors.py @@ -810,6 +810,18 @@ def __str__(self): return f"Update harmonic vibe properties error: {self.message}" +class SherlockUpdateICTAnalysisPropsError(Exception): + """Contains the error raised when properties for ICT analysis cannot be updated.""" + + def __init__(self, message): + """Initialize error message.""" + self.message = message + + def __str__(self): + """Format error message.""" + return f"Update ICT analysis properties error: {self.message}" + + class SherlockUpdateMechanicalShockPropsError(Exception): """Contains the error raised when properties for mechanical shock analysis cannot be updated.""" @@ -880,3 +892,20 @@ def __init__(self, message): def __str__(self): """Format error message.""" return f"Update part modeling props error: {self.message}" + + +class SherlockUpdatePartsFromAVLError(Exception): + """Contains the error raised when parts list cannot be updated by AVL.""" + + def __init__(self, message=None, error_array=None): + """Initialize error message.""" + self.message = message + self.error_array = error_array + + def str_itr(self): + """Create list of error messages.""" + if self.message is None: + return [f"Update part from AVL error: {error}" for error in self.error_array] + + assert self.error_array is None + return [f"Update part from AVL error: {self.message}"] diff --git a/src/ansys/sherlock/core/parts.py b/src/ansys/sherlock/core/parts.py index e22d05a14..187ac44ac 100644 --- a/src/ansys/sherlock/core/parts.py +++ b/src/ansys/sherlock/core/parts.py @@ -15,12 +15,19 @@ SherlockExportPartsListError, SherlockGetPartLocationError, SherlockImportPartsListError, + SherlockUpdatePartsFromAVLError, SherlockUpdatePartsListError, SherlockUpdatePartsLocationsByFileError, SherlockUpdatePartsLocationsError, ) from ansys.sherlock.core.grpc_stub import GrpcStub -from ansys.sherlock.core.types.parts_types import PartLocation +from ansys.sherlock.core.types.parts_types import ( + AVLDescription, + AVLPartNum, + PartLocation, + PartsListSearchDuplicationMode, + PartsListSearchMatchingMode, +) class Parts(GrpcStub): @@ -34,6 +41,12 @@ def __init__(self, channel): self.BOARD_SIDES = None self.MATCHING_ARGS = ["Both", "Part"] self.DUPLICATION_ARGS = ["First", "Error", "Ignore"] + self.AVL_PART_NUM_ARGS = [ + "AssignInternalPartNum", + "AssignVendorAndPartNum", + "DoNotChangeVendorOrPartNum", + ] + self.AVL_DESCRIPTION_ARGS = ["AssignApprovedDescription", "DoNotChangeDescription"] @staticmethod def _add_matching_duplication(request, matching, duplication): @@ -155,8 +168,8 @@ def update_parts_list( project, cca_name, part_library, - matching, - duplication, + matching_mode, + duplication_mode, ): """Update a parts list based on matching and duplication preferences. @@ -168,9 +181,9 @@ def update_parts_list( Name of the CCA. part_library : str Name of the parts library. - matching : UpdatesPartsListRequestMatchingMode + matching_mode : PartsListSearchMatchingMode Matching mode for updates. - duplication : UpdatesPartsListRequestDuplicationMode + duplication_mode : PartsListSearchDuplicationMode How to handle duplication during the update. Returns @@ -219,7 +232,7 @@ def update_parts_list( project=project, ccaName=cca_name, partLibrary=part_library ) - self._add_matching_duplication(request, matching, duplication) + self._add_matching_duplication(request, matching_mode, duplication_mode) response = self.stub.updatePartsList(request) @@ -690,3 +703,104 @@ def get_part_location(self, project, cca_name, ref_des, location_units): except SherlockGetPartLocationError as e: LOG.error(str(e)) raise e + + def update_parts_from_AVL( + self, + project: str, + cca_name: str, + matching_mode: PartsListSearchMatchingMode, + duplication_mode: PartsListSearchDuplicationMode, + avl_part_num: AVLPartNum, + avl_description: AVLDescription, + ) -> SherlockPartsService_pb2.UpdatePartsListFromAVLResponse: + r"""Update the parts list from the Approved Vendor List (AVL). + + Parameters + ---------- + project : str + Name of the Sherlock project. + cca_name : str + Name of the CCA. + matching_mode: PartsListSearchMatchingMode + Determines how parts are matched against the AVL + duplication_mode: PartsListSearchDuplicationMode + Determines how duplicate part matches are handled when found + avl_part_num: AVLPartNum + Determines what part number info in the parts list is updated from the AVL + avl_description: AVLDescription + Determines if the part description is updated or not + + Returns + ------- + UpdatePartsListFromAVLResponse + - returnCode : ReturnCode + - value : int + Status code of the response. 0 for success. + - message : str + indicates general errors that occurred while attempting to update parts + - numPartsUpdated : int + Number of parts updated + - updateErrors : list + Errors found when updating part + + Examples + -------- + >>> from ansys.sherlock.core.launcher import launch_sherlock + >>> from ansys.sherlock.core.types.parts_types import ( + AVLDescription, + AVLPartNum, + UpdatesPartsListRequestDuplicationMode, + UpdatesPartsListRequestMatchingMode + ) + >>> sherlock = launch_sherlock() + >>> sherlock.project.import_odb_archive( + "C:\\Program Files\\ANSYS Inc\\v241\\sherlock\\tutorial\\ODB++ Tutorial.tgz", + True, + True, + True, + True, + project="Test", + cca_name="Card", + ) + >>> sherlock.parts.update_parts_from_AVL( + project="Test", + cca_name="Card", + matching_mode=UpdatesPartsListRequestMatchingMode.BOTH, + duplication=UpdatesPartsListRequestDuplicationMode.FIRST, + avl_part_num=AVLPartNum.ASSIGN_INTERNAL_PART_NUM, + avl_description=AVLDescription.ASSIGN_APPROVED_DESCRIPTION + ) + """ + try: + if project == "": + raise SherlockUpdatePartsFromAVLError(message="Project name is invalid.") + if cca_name == "": + raise SherlockUpdatePartsFromAVLError(message="CCA name is invalid.") + + request = SherlockPartsService_pb2.UpdatePartsListFromAVLRequest( + project=project, + ccaName=cca_name, + matching=matching_mode, + duplication=duplication_mode, + avlPartNum=avl_part_num, + avlDesc=avl_description, + ) + + if not self._is_connection_up(): + LOG.error("Not connected to a gRPC service.") + return + + # Call method on server + response = self.stub.updatePartsListFromAVL(request) + + return_code = response.returnCode + + if return_code.value == -1: + if return_code.message == "": + raise SherlockUpdatePartsFromAVLError(error_array=response.updateErrors) + raise SherlockUpdatePartsFromAVLError(message=return_code.message) + + return response + except SherlockUpdatePartsFromAVLError as e: + LOG.error(str(e)) + raise e diff --git a/src/ansys/sherlock/core/types/parts_types.py b/src/ansys/sherlock/core/types/parts_types.py index 5db38c67e..1cdd4e80a 100644 --- a/src/ansys/sherlock/core/types/parts_types.py +++ b/src/ansys/sherlock/core/types/parts_types.py @@ -8,19 +8,36 @@ from ansys.api.sherlock.v0 import SherlockPartsService_pb2 -class UpdatesPartsListRequestMatchingMode: - """Constants for Matching Mode in the Update Parts List request.""" +class PartsListSearchMatchingMode: + """Constants for Matching Mode in the Update Parts List and Update Parts from AVL request.""" - BOTH = SherlockPartsService_pb2.UpdatePartsListRequest.MatchingMode.Both - PART = SherlockPartsService_pb2.UpdatePartsListRequest.MatchingMode.Part + BOTH = SherlockPartsService_pb2.MatchingMode.Both + PART = SherlockPartsService_pb2.MatchingMode.Part -class UpdatesPartsListRequestDuplicationMode: - """Constants for Duplication Mode in the Update Parts List request.""" +class PartsListSearchDuplicationMode: + """Constants for Duplication Mode in the Update Parts List and Update Parts from AVL request.""" - FIRST = SherlockPartsService_pb2.UpdatePartsListRequest.DuplicationMode.First - ERROR = SherlockPartsService_pb2.UpdatePartsListRequest.DuplicationMode.Error - IGNORE = SherlockPartsService_pb2.UpdatePartsListRequest.DuplicationMode.Ignore + FIRST = SherlockPartsService_pb2.DuplicationMode.First + ERROR = SherlockPartsService_pb2.DuplicationMode.Error + IGNORE = SherlockPartsService_pb2.DuplicationMode.Ignore + + +class AVLPartNum: + """Constants for AVLPartNum in the Update Parts List from AVL request.""" + + ASSIGN_INTERNAL_PART_NUM = SherlockPartsService_pb2.AVLPartNum.AssignInternalPartNum + ASSIGN_VENDOR_AND_PART_NUM = SherlockPartsService_pb2.AVLPartNum.AssignVendorAndPartNum + DO_NOT_CHANGE_VENDOR_OR_PART_NUM = ( + SherlockPartsService_pb2.AVLPartNum.DoNotChangeVendorOrPartNum + ) + + +class AVLDescription: + """Constants for AVLDescription in the Update Parts List from AVL request.""" + + ASSIGN_APPROVED_DESCRIPTION = SherlockPartsService_pb2.AVLDescription.AssignApprovedDescription + DO_NOT_CHANGE_DESCRIPTION = SherlockPartsService_pb2.AVLDescription.DoNotChangeDescription class PartLocation: diff --git a/tests/test_analysis.py b/tests/test_analysis.py index 58a2fadbf..3b061c614 100644 --- a/tests/test_analysis.py +++ b/tests/test_analysis.py @@ -14,6 +14,7 @@ SherlockRunAnalysisError, SherlockRunStrainMapAnalysisError, SherlockUpdateHarmonicVibePropsError, + SherlockUpdateICTAnalysisPropsError, SherlockUpdateMechanicalShockPropsError, SherlockUpdateNaturalFrequencyPropsError, SherlockUpdatePartModelingPropsError, @@ -41,11 +42,13 @@ def test_all(): time.sleep(1) helper_test_run_strain_map_analysis(analysis) helper_test_get_harmonic_vibe_input_fields(analysis) + helper_test_get_ict_analysis_input_fields(analysis) helper_test_get_mechanical_shock_input_fields(analysis) helper_test_get_solder_fatigue_input_fields(analysis) helper_test_get_random_vibe_input_fields(analysis) helper_test_translate_field_names(analysis) helper_test_update_harmonic_vibe_props(analysis) + helper_test_update_ict_analysis_props(analysis) helper_test_update_mechanical_shock_props(analysis) helper_test_update_solder_fatigue_props(analysis) helper_test_update_random_vibe_props(analysis) @@ -447,6 +450,16 @@ def helper_test_get_harmonic_vibe_input_fields(analysis): assert "require_material_assignment_enabled" in fields +def helper_test_get_ict_analysis_input_fields(analysis): + if analysis._is_connection_up(): + fields = analysis.get_ict_analysis_input_fields() + assert "ict_application_time" in fields + assert "ict_application_time_units" in fields + assert "ict_number_of_events" in fields + assert "require_material_assignment_enabled" in fields + assert "model_source" not in fields + + def helper_test_get_mechanical_shock_input_fields(analysis): if analysis._is_connection_up(): fields = analysis.get_mechanical_shock_input_fields() @@ -519,6 +532,10 @@ def helper_test_translate_field_names(analysis): "forceModelRebuild", "harmonicVibeDamping", "harmonicVibeCount", + "ictApplicationTime", + "ictApplicationTimeUnits", + "ictNumberOfEvents", + "ictResultCount", "modelSource", "naturalFreqCount", "naturalFreqMin", @@ -543,6 +560,10 @@ def helper_test_translate_field_names(analysis): "force_model_rebuild", "harmonic_vibe_damping", "harmonic_vibe_count", + "ict_application_time", + "ict_application_time_units", + "ict_number_of_events", + "ict_result_count", "model_source", "natural_freq_count", "natural_freq_min", @@ -627,7 +648,7 @@ def helper_test_update_harmonic_vibe_props(analysis): except SherlockUpdateHarmonicVibePropsError as e: assert ( str(e) == "Update harmonic vibe properties error: " - "CCA name is invalid for harmonic vibe properties 0." + "CCA name is missing for harmonic vibe properties 0." ) try: @@ -718,6 +739,135 @@ def helper_test_update_harmonic_vibe_props(analysis): pytest.fail(str(e)) +def helper_test_update_ict_analysis_props(analysis): + try: + analysis.update_ict_analysis_props( + "", + [ + { + "cca_name": "Main Board", + "application_time": 0.22, + "application_time_units": "min", + "ict_number_of_events": 19, + "part_validation_enabled": False, + "require_material_assignment_enabled": False, + }, + ], + ) + assert False + except SherlockUpdateICTAnalysisPropsError as e: + assert str(e) == "Update ICT analysis properties error: Project name is invalid." + + try: + analysis.update_ict_analysis_props("Tutorial Project", "Main Board") + assert False + except SherlockUpdateICTAnalysisPropsError as e: + assert ( + str(e) == "Update ICT analysis properties error: " + "ICT analysis properties argument is invalid." + ) + + try: + analysis.update_ict_analysis_props("Tutorial Project", []) + assert False + except SherlockUpdateICTAnalysisPropsError as e: + assert ( + str(e) == "Update ICT analysis properties error: " + "One or more ICT analysis properties are required." + ) + + try: + analysis.update_ict_analysis_props("Tutorial Project", ["INVALID"]) + assert False + except SherlockUpdateICTAnalysisPropsError as e: + assert ( + str(e) == "Update ICT analysis properties error: " + "ICT analysis props argument is invalid for ICT analysis properties 0." + ) + + try: + analysis.update_ict_analysis_props( + "Tutorial Project", + [ + { + "ict_application_time": 2, + "ict_application_time_units": "sec", + "ict_number_of_events": 5, + "part_validation_enabled": False, + "require_material_assignment_enabled": False, + "force_model_rebuild": "AUTO", + }, + ], + ) + assert False + except SherlockUpdateICTAnalysisPropsError as e: + assert ( + str(e) == "Update ICT analysis properties error: " + "CCA name is missing for ICT analysis properties 0." + ) + + try: + analysis.update_ict_analysis_props( + "Tutorial Project", + [ + { + "cca_name": "", + "ict_application_time": 2, + "ict_application_time_units": "sec", + "ict_number_of_events": 5, + "part_validation_enabled": False, + "require_material_assignment_enabled": False, + "force_model_rebuild": "AUTO", + }, + ], + ) + assert False + except SherlockUpdateICTAnalysisPropsError as e: + assert ( + str(e) == "Update ICT analysis properties error: " + "CCA name is invalid for ICT analysis properties 0." + ) + + if analysis._is_connection_up(): + try: + analysis.update_ict_analysis_props( + "Tutorial Project", + [ + { + "cca_name": "Main Board", + "ict_application_time": -2, + "ict_application_time_units": "sec", + "ict_number_of_events": 5, + "part_validation_enabled": False, + "require_material_assignment_enabled": False, + "force_model_rebuild": "AUTO", + }, + ], + ) + pytest.fail("No exception raised when using an invalid parameter") + except Exception as e: + assert type(e) == SherlockUpdateICTAnalysisPropsError + + try: + result = analysis.update_ict_analysis_props( + "Tutorial Project", + [ + { + "cca_name": "Main Board", + "ict_application_time": 2, + "ict_application_time_units": "sec", + "ict_number_of_events": 5, + "part_validation_enabled": False, + "require_material_assignment_enabled": False, + "force_model_rebuild": "AUTO", + }, + ], + ) + assert result == 0 + except SherlockUpdateICTAnalysisPropsError as e: + pytest.fail(str(e)) + + def helper_test_update_mechanical_shock_props(analysis): try: analysis.update_mechanical_shock_props( diff --git a/tests/test_parts.py b/tests/test_parts.py index a218c6faa..7604a8b70 100644 --- a/tests/test_parts.py +++ b/tests/test_parts.py @@ -12,14 +12,17 @@ SherlockExportPartsListError, SherlockGetPartLocationError, SherlockImportPartsListError, + SherlockUpdatePartsFromAVLError, SherlockUpdatePartsListError, SherlockUpdatePartsLocationsByFileError, SherlockUpdatePartsLocationsError, ) from ansys.sherlock.core.parts import Parts from ansys.sherlock.core.types.parts_types import ( - UpdatesPartsListRequestDuplicationMode, - UpdatesPartsListRequestMatchingMode, + AVLDescription, + AVLPartNum, + PartsListSearchDuplicationMode, + PartsListSearchMatchingMode, ) @@ -30,6 +33,7 @@ def test_all(): parts = Parts(channel) helper_test_update_parts_list(parts) + helper_test_update_parts_from_AVL(parts) helper_test_update_parts_locations(parts) helper_test_update_parts_locations_by_file(parts) helper_test_import_parts_list(parts) @@ -47,8 +51,8 @@ def helper_test_update_parts_list(parts): "Tutorial Project", "Main Board", "Sherlock Part Library", - UpdatesPartsListRequestMatchingMode.BOTH, - UpdatesPartsListRequestDuplicationMode.ERROR, + PartsListSearchMatchingMode.BOTH, + PartsListSearchDuplicationMode.ERROR, ) assert result == 0 # wait for Sherlock to finish updating so subsequent tests don't fail @@ -61,8 +65,8 @@ def helper_test_update_parts_list(parts): "Tutorial Project", "Invalid CCA", "Sherlock Part Library", - UpdatesPartsListRequestMatchingMode.BOTH, - UpdatesPartsListRequestDuplicationMode.ERROR, + PartsListSearchMatchingMode.BOTH, + PartsListSearchDuplicationMode.ERROR, ) pytest.fail("No exception raised when using an invalid parameter") except Exception as e: @@ -73,8 +77,8 @@ def helper_test_update_parts_list(parts): "", "Card", "Sherlock Part Library", - UpdatesPartsListRequestMatchingMode.BOTH, - UpdatesPartsListRequestDuplicationMode.ERROR, + PartsListSearchMatchingMode.BOTH, + PartsListSearchDuplicationMode.ERROR, ) pytest.fail("No exception raised when using an invalid parameter") except SherlockUpdatePartsListError as e: @@ -85,8 +89,8 @@ def helper_test_update_parts_list(parts): "Test", "", "Sherlock Part Library", - UpdatesPartsListRequestMatchingMode.BOTH, - UpdatesPartsListRequestDuplicationMode.ERROR, + PartsListSearchMatchingMode.BOTH, + PartsListSearchDuplicationMode.ERROR, ) pytest.fail("No exception raised when using an invalid parameter") except SherlockUpdatePartsListError as e: @@ -97,14 +101,109 @@ def helper_test_update_parts_list(parts): "Test", "Card", "", - UpdatesPartsListRequestMatchingMode.BOTH, - UpdatesPartsListRequestDuplicationMode.ERROR, + PartsListSearchMatchingMode.BOTH, + PartsListSearchDuplicationMode.ERROR, ) pytest.fail("No exception raised when using an invalid parameter") except SherlockUpdatePartsListError as e: assert str(e.str_itr()) == "['Update parts list error: Parts library is invalid.']" +def helper_test_update_parts_from_AVL(parts): + try: + response = parts.update_parts_from_AVL( + project="", + cca_name="Main Board", + matching_mode=PartsListSearchMatchingMode.BOTH, + duplication_mode=PartsListSearchDuplicationMode.FIRST, + avl_part_num=AVLPartNum.ASSIGN_INTERNAL_PART_NUM, + avl_description=AVLDescription.ASSIGN_APPROVED_DESCRIPTION, + ) + pytest.fail("No exception raised when using an invalid parameter") + except SherlockUpdatePartsFromAVLError as e: + assert e.message == "Project name is invalid." + + try: + response = parts.update_parts_from_AVL( + project="Tutorial Project", + cca_name="", + matching_mode=PartsListSearchMatchingMode.BOTH, + duplication_mode=PartsListSearchDuplicationMode.FIRST, + avl_part_num=AVLPartNum.ASSIGN_INTERNAL_PART_NUM, + avl_description=AVLDescription.ASSIGN_APPROVED_DESCRIPTION, + ) + pytest.fail("No exception raised when using an invalid parameter") + except SherlockUpdatePartsFromAVLError as e: + assert e.message == "CCA name is invalid." + + try: + response = parts.update_parts_from_AVL( + project="Tutorial Project", + cca_name="Main Board", + matching_mode="BOTH", + duplication_mode=PartsListSearchDuplicationMode.ERROR, + avl_part_num=AVLPartNum.ASSIGN_INTERNAL_PART_NUM, + avl_description=AVLDescription.ASSIGN_APPROVED_DESCRIPTION, + ) + except Exception as e: + # The matching_mode should not be a string + assert type(e) == ValueError + + try: + response = parts.update_parts_from_AVL( + project="Tutorial Project", + cca_name="Main Board", + matching_mode=PartsListSearchMatchingMode.BOTH, + duplication_mode="ERROR", + avl_part_num=AVLPartNum.ASSIGN_INTERNAL_PART_NUM, + avl_description=AVLDescription.ASSIGN_APPROVED_DESCRIPTION, + ) + except Exception as e: + # The duplication should not be a string + assert type(e) == ValueError + + try: + response = parts.update_parts_from_AVL( + project="Tutorial Project", + cca_name="Main Board", + matching_mode=PartsListSearchMatchingMode.BOTH, + duplication_mode=PartsListSearchDuplicationMode.ERROR, + avl_part_num="AssignInternalPartNum", + avl_description=AVLDescription.ASSIGN_APPROVED_DESCRIPTION, + ) + except Exception as e: + # The avl_part_num should not be a string + assert type(e) == ValueError + + try: + response = parts.update_parts_from_AVL( + project="Tutorial Project", + cca_name="Main Board", + matching_mode=PartsListSearchMatchingMode.BOTH, + duplication_mode=PartsListSearchDuplicationMode.ERROR, + avl_part_num=AVLPartNum.ASSIGN_INTERNAL_PART_NUM, + avl_description="AssignApprovedDescription", + ) + except Exception as e: + # The avl_description should not be a string + assert type(e) == ValueError + + if parts._is_connection_up(): + try: + response = parts.update_parts_from_AVL( + project="Tutorial Project", + cca_name="Main Board", + matching_mode=PartsListSearchMatchingMode.BOTH, + duplication_mode=PartsListSearchDuplicationMode.FIRST, + avl_part_num=AVLPartNum.ASSIGN_INTERNAL_PART_NUM, + avl_description=AVLDescription.ASSIGN_APPROVED_DESCRIPTION, + ) + + assert response.returnCode.value == 0 + except SherlockUpdatePartsFromAVLError as e: + pytest.fail(e.message) + + def helper_test_update_parts_locations(parts): """Test update_parts_locations API."""