# -*- coding: utf-8 -*-
#
# Copyright (C) 2021 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""Module containing all layer management capabilities."""
import grpc

from ansys.sherlock.core.types.layer_types import (
    CircularShape,
    CopyPottingRegionRequest,
    DeletePottingRegionRequest,
    GetICTFixturesPropertiesRequest,
    GetMountPointsPropertiesRequest,
    GetTestPointPropertiesRequest,
    PCBShape,
    PolygonalShape,
    RectangularShape,
    SlotShape,
    UpdateICTFixturesRequest,
    UpdateMountPointsRequest,
    UpdatePottingRegionRequest,
    UpdateTestPointsRequest,
)

try:
    import SherlockCommonService_pb2
    import SherlockLayerService_pb2
    from SherlockLayerService_pb2 import ModelingRegion
    import SherlockLayerService_pb2_grpc
except ModuleNotFoundError:
    from ansys.api.sherlock.v0 import SherlockCommonService_pb2
    from ansys.api.sherlock.v0 import SherlockLayerService_pb2
    from ansys.api.sherlock.v0 import SherlockLayerService_pb2_grpc
    from ansys.api.sherlock.v0.SherlockLayerService_pb2 import ModelingRegion

from ansys.sherlock.core import LOG
from ansys.sherlock.core.errors import (
    SherlockAddModelingRegionError,
    SherlockAddPottingRegionError,
    SherlockCopyModelingRegionError,
    SherlockDeleteAllICTFixturesError,
    SherlockDeleteAllMountPointsError,
    SherlockDeleteAllTestPointsError,
    SherlockDeleteModelingRegionError,
    SherlockExportAllMountPoints,
    SherlockExportAllTestFixtures,
    SherlockExportAllTestPointsError,
    SherlockExportLayerImageError,
    SherlockListLayersError,
    SherlockNoGrpcConnectionException,
    SherlockUpdateModelingRegionError,
    SherlockUpdateMountPointsByFileError,
    SherlockUpdateTestFixturesByFileError,
    SherlockUpdateTestPointsByFileError,
)
from ansys.sherlock.core.grpc_stub import GrpcStub
from ansys.sherlock.core.utils.version_check import require_version


class Layer(GrpcStub):
    """Module containing all the layer management capabilities."""

    def __init__(self, channel: grpc.Channel, server_version: int):
        """Initialize a gRPC stub for SherlockLayerService."""
        super().__init__(channel, server_version)
        self.stub = SherlockLayerService_pb2_grpc.SherlockLayerServiceStub(channel)

    @require_version(241)
    def add_potting_region(
        self,
        project: str,
        potting_regions: list[
            dict[
                str,
                float
                | str
                | PolygonalShape
                | RectangularShape
                | SlotShape
                | CircularShape
                | PCBShape,
            ]
        ],
    ) -> int:
        """Add one or more potting regions to a given project.

        Available Since: 2024R1

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        potting_regions: list[dict[str, float | str | PolygonalShape | RectangularShape | SlotShape\
 | CircularShape | PCBShape]]
            Potting region properties consisting of these properties:

            - cca_name: str
                Name of the CCA.
            - potting_id: str
                Potting ID. The default is ``None``.
            - side: str
                The side to add the potting region to. The default is ``None``.
                Options are ``"TOP"``, ``"BOTTOM"``, and ``"BOT"``.
            - material: str
                The potting material. The default is ``None``.
            - potting_units: str
                The potting region units. The default is ``None``.
            - thickness: float
                The potting thickness. The default is ``None``.
            - standoff: float
                The potting standoff. The default is ``None``.
            - shape: PolygonalShape|RectangularShape|SlotShape|CircularShape|PCBShape
                The shape of the potting region.


        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core.types.layer_types import PolygonalShape
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Test",
        >>>     cca_name="Card",
        >>> )
        >>> polygonal_shape = PolygonalShape(points=[
        >>>     (0, 0),
        >>>     (0, 6.35),
        >>>     (9.77, 0)
        >>> ], rotation=87.8)
        >>> sherlock.layer.add_potting_region(
        >>> "Test",
        >>> [{
        >>>     'cca_name': 'Card',
        >>>     'potting_id': 'Test Region',
        >>>     'side': 'TOP',
        >>>     'material': 'epoxyencapsulant',
        >>>     'potting_units': 'in',
        >>>     'thickness': 0.1,
        >>>     'standoff': 0.2,
        >>>     'shape': polygonal_shape
        >>> },
        >>> ])
        """
        try:
            if project == "":
                raise SherlockAddPottingRegionError(message="Project name is invalid.")

            if not isinstance(potting_regions, list):
                raise SherlockAddPottingRegionError(message="Potting regions argument is invalid.")

            if len(potting_regions) == 0:
                raise SherlockAddPottingRegionError(
                    message="One or more potting regions are required."
                )

            request = SherlockLayerService_pb2.AddPottingRegionRequest(project=project)

            for i, potting_region in enumerate(potting_regions):
                if not isinstance(potting_region, dict):
                    raise SherlockAddPottingRegionError(
                        message=f"Potting region argument is invalid for potting region {i}."
                    )

                if "cca_name" not in potting_region.keys():
                    raise SherlockAddPottingRegionError(
                        message=f"CCA name is missing for potting region {i}."
                    )

                region_request = request.pottingRegions.add()
                region_request.ccaName = potting_region["cca_name"]

                if region_request.ccaName == "":
                    raise SherlockAddPottingRegionError(
                        message=f"CCA name is invalid for potting region {i}."
                    )

                if "potting_id" in potting_region.keys():
                    region_request.pottingID = potting_region["potting_id"]
                if "side" in potting_region.keys():
                    region_request.pottingSide = potting_region["side"]
                if "material" in potting_region.keys():
                    region_request.pottingMaterial = potting_region["material"]
                if "potting_units" in potting_region.keys():
                    region_request.pottingUnits = potting_region["potting_units"]
                if "thickness" in potting_region.keys():
                    region_request.pottingThickness = potting_region["thickness"]
                if "standoff" in potting_region.keys():
                    region_request.pottingStandoff = potting_region["standoff"]
                if "shape" not in potting_region.keys():
                    raise SherlockAddPottingRegionError(
                        message=f"Shape missing for potting region {i}."
                    )

                shape = potting_region["shape"]

                if isinstance(shape, PolygonalShape):
                    if not isinstance(shape.points, list):
                        raise SherlockAddPottingRegionError(
                            message=f"Invalid points argument for potting region {i}."
                        )

                    for j, point in enumerate(shape.points):
                        point_message = region_request.polygonalShape.points.add()

                        if not isinstance(point, tuple) or len(point) != 2:
                            raise SherlockAddPottingRegionError(
                                message=f"Point {j} invalid for potting region {i}."
                            )
                        point_message.x = point[0]
                        point_message.y = point[1]
                    region_request.polygonalShape.rotation = shape.rotation
                elif isinstance(shape, RectangularShape):
                    region_request.rectangularShape.length = shape.length
                    region_request.rectangularShape.width = shape.width
                    region_request.rectangularShape.centerX = shape.center_x
                    region_request.rectangularShape.centerY = shape.center_y
                    region_request.rectangularShape.rotation = shape.rotation
                elif isinstance(shape, SlotShape):
                    region_request.slotShape.length = shape.length
                    region_request.slotShape.width = shape.width
                    region_request.slotShape.nodeCount = shape.node_count
                    region_request.slotShape.centerX = shape.center_x
                    region_request.slotShape.centerY = shape.center_y
                    region_request.slotShape.rotation = shape.rotation
                elif isinstance(shape, CircularShape):
                    region_request.circularShape.diameter = shape.diameter
                    region_request.circularShape.nodeCount = shape.node_count
                    region_request.circularShape.centerX = shape.center_x
                    region_request.circularShape.centerY = shape.center_y
                    region_request.circularShape.rotation = shape.rotation
                elif isinstance(shape, PCBShape):
                    region_request.pCBShape.CopyFrom(SherlockLayerService_pb2.PCBShape())
                else:
                    raise SherlockAddPottingRegionError(
                        message=f"Shape invalid for potting region {i}."
                    )

        except SherlockAddPottingRegionError as e:
            LOG.error(str(e))
            raise e

        if not self._is_connection_up():
            raise SherlockNoGrpcConnectionException()

        response = self.stub.addPottingRegion(request)

        try:
            if response.value == -1:
                raise SherlockAddPottingRegionError(response.message)
            else:
                LOG.info(response.message)
                return response.value
        except SherlockAddPottingRegionError as e:
            LOG.error(str(e))
            raise e

    @require_version(251)
    def update_potting_region(
        self, request: UpdatePottingRegionRequest
    ) -> list[SherlockCommonService_pb2.ReturnCode]:
        """Update one or more potting regions in a specific project.

        Available Since: 2025R1

        Parameters
        ----------
        request: UpdatePottingRegionRequest
            Contains all the information needed to update one or more potting regions per project.

        Returns
        -------
        list[SherlockCommonService_pb2.ReturnCode]
            Return codes for each request.

        Examples
        --------
        >>> from ansys.sherlock.core.types.layer_types import PolygonalShape
        >>> from ansys.sherlock.core.types.layer_types import PottingRegionUpdateData
        >>> from ansys.sherlock.core.types.layer_types import PottingRegion
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>>
        >>> update1 = PottingRegionUpdateData(
        >>> potting_region_id_to_update=potting_id,
        >>> potting_region=PottingRegionData(
        >>>     cca_name=cca_name,
        >>>     potting_id=potting_id,
        >>>     potting_side=potting_side,
        >>>     potting_material=potting_material,
        >>>     potting_units=potting_units,
        >>>     potting_thickness=potting_thickness,
        >>>     potting_standoff=potting_standoff,
        >>>     shape=PolygonalShape(
        >>>         points=[(0, 1), (5, 1), (5, 5), (1, 5)],
        >>>         rotation=45.0
        >>>     )
        >>> )
        >>> )
        >>> update2 = PottingRegionUpdateData(
        >>> potting_region_id_to_update=potting_id,
        >>> potting_region=PottingRegionData(
        >>>     cca_name=cca_name,
        >>>     potting_id=potting_id,
        >>>     potting_side=potting_side,
        >>>     potting_material=potting_material,
        >>>     potting_units=potting_units,
        >>>     potting_thickness=potting_thickness,
        >>>     potting_standoff=potting_standoff,
        >>>     shape=PolygonalShape(
        >>>         points=[(0, 1), (5, 1), (5, 5), (1, 5)],
        >>>         rotation=0.0
        >>>     )
        >>> )
        >>> )
        >>> example_request = UpdatePottingRegionRequest(
        >>>     "project_name",
        >>>     [
        >>>         update1,
        >>>         update2
        >>>     ]
        >>> )
        >>> return_codes = sherlock.layer.update_potting_region(example_request)
        """
        update_request = request._convert_to_grpc()

        responses = []
        for grpc_return_code in self.stub.updatePottingRegion(update_request):
            responses.append(grpc_return_code)
        return responses

    @require_version(251)
    def copy_potting_region(
        self, request: CopyPottingRegionRequest
    ) -> list[SherlockCommonService_pb2.ReturnCode]:
        """Copy one or more potting regions in a specific project.

        Available Since: 2025R1

        Parameters
        ----------
        request: CopyPottingRegionRequest
             Contains all the information needed to copy one or more potting regions per project.

        Returns
        -------
        list[SherlockCommonService_pb2.ReturnCode]
            Return codes for each request.

        Examples
        --------
        >>> from ansys.sherlock.core.types.layer_types import CopyPottingRegionRequest
        >>> from ansys.sherlock.core.types.layer_types import PottingRegionCopyData
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>>
        >>> copy_request_example = CopyPottingRegionRequest(
        >>> project=project,
        >>> potting_region_copy_data=[
        >>>     PottingRegionCopyData(
        >>>         cca_name=cca_name,
        >>>         potting_id=potting_id,
        >>>         copy_potting_id=new_id,
        >>>         center_x=center_x,
        >>>         center_y=center_y
        >>>     ),
        >>>     PottingRegionCopyData(
        >>>         cca_name=cca_name,
        >>>         potting_id=new_id,
        >>>         copy_potting_id=new_id+"1",
        >>>         center_x=center_x,
        >>>         center_y=center_y
        >>>     )
        >>> ]
        >>> )
        >>> responses_example = sherlock.layer.copy_potting_region(copy_request_example)
        """
        copy_request = request._convert_to_grpc()

        responses = []
        for grpc_return_code in self.stub.copyPottingRegion(copy_request):
            responses.append(grpc_return_code)
        return responses

    @require_version(251)
    def delete_potting_region(
        self, request: DeletePottingRegionRequest
    ) -> list[SherlockCommonService_pb2.ReturnCode]:
        """Delete on or more potting regions in a specific project.

        Available Since: 2025R1

        Parameters
        ----------
        request: DeletePottingRegionRequest
             Contains all the information needed to delete one or more potting regions per project.

        Returns
        -------
        list[SherlockCommonService_pb2.ReturnCode]
            Return codes for each request.

        Examples
        --------
        >>> from ansys.sherlock.core.types.layer_types import DeletePottingRegionRequest
        >>> from ansys.sherlock.core.types.layer_types import PottingRegionDeleteData
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>>
        >>> delete_request_example = DeletePottingRegionRequest(
        >>> project=project,
        >>> potting_region_delete_data=[
        >>>     PottingRegionDeleteData(
        >>>         cca_name=cca_name,
        >>>         potting_id=potting_id
        >>>     )
        >>> ]
        >>> )
        >>> responses_example = sherlock.layer.delete_potting_region(delete_request_example)
        """
        delete_request = request._convert_to_grpc()

        responses = []
        for grpc_return_code in self.stub.deletePottingRegion(delete_request):
            responses.append(grpc_return_code)
        return responses

    @require_version()
    def update_mount_points_by_file(
        self,
        project: str,
        cca_name: str,
        file_path: str,
    ) -> int:
        """Update mount point properties of a CCA from a CSV file.

        Available Since: 2023R1

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        cca_name: str
            Name of the CCA.
        file_path: str
            Path for the CSV file with the mount point properties.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Test",
        >>>     cca_name="Card",
        >>> )
        >>> sherlock.layer.update_mount_points_by_file(
        >>>     "Test",
        >>>     "Card",
        >>>     "MountPointImport.csv"
        >>> )
        """
        try:
            if project == "":
                raise SherlockUpdateMountPointsByFileError(message="Project name is invalid.")
            if cca_name == "":
                raise SherlockUpdateMountPointsByFileError(message="CCA name is invalid.")
            if file_path == "":
                raise SherlockUpdateMountPointsByFileError(message="File path is required.")
        except SherlockUpdateMountPointsByFileError as e:
            for error in e.str_itr():
                LOG.error(error)
            raise e

        if not self._is_connection_up():
            raise SherlockNoGrpcConnectionException()

        request = SherlockLayerService_pb2.UpdateMountPointsByFileRequest(
            project=project,
            ccaName=cca_name,
            filePath=file_path,
        )

        response = self.stub.updateMountPointsByFile(request)

        return_code = response.returnCode

        try:
            if return_code.value == -1:
                if return_code.message == "":
                    raise SherlockUpdateMountPointsByFileError(error_array=response.updateError)

                raise SherlockUpdateMountPointsByFileError(message=return_code.message)
            else:
                LOG.info(return_code.message)
                return return_code.value
        except SherlockUpdateMountPointsByFileError as e:
            for error in e.str_itr():
                LOG.error(error)
            raise e

    @require_version(222)
    def delete_all_mount_points(self, project: str, cca_name: str) -> int:
        """Delete all mount points for a CCA.

        Available Since: 2022R2

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        cca_name: str
            Name of the CCA.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Test",
        >>>     cca_name="Card",
        >>> )
        >>> sherlock.layer.update_mount_points_by_file(
        >>>     "Test",
        >>>     "Card",
        >>>     "MountPointImport.csv",
        >>> )
        >>> sherlock.layer.delete_all_mount_points("Test", "Card")
        """
        try:
            if project == "":
                raise SherlockDeleteAllMountPointsError(message="Project name is invalid.")
            if cca_name == "":
                raise SherlockDeleteAllMountPointsError(message="CCA name is invalid.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            request = SherlockLayerService_pb2.DeleteAllMountPointsRequest(
                project=project,
                ccaName=cca_name,
            )

            response = self.stub.deleteAllMountPoints(request)

            if response.value == -1:
                raise SherlockDeleteAllMountPointsError(message=response.message)

        except SherlockDeleteAllMountPointsError as e:
            LOG.error(str(e))
            raise e

        return response.value

    @require_version(231)
    def delete_all_ict_fixtures(self, project: str, cca_name: str) -> int:
        """Delete all ICT fixtures for a CCA.

        Available Since: 2023R1

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        cca_name: str
            Name of the CCA.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Test",
        >>>     cca_name="Card",
        >>> )
        >>> sherlock.layer.update_ict_fixtures_by_file(
        >>>     "Test",
        >>>     "Card",
        >>>     "ICTFixturesImport.csv",
        >>> )
        >>> sherlock.layer.delete_all_ict_fixtures("Test", "Card")
        """
        try:
            if project == "":
                raise SherlockDeleteAllICTFixturesError(message="Project name is invalid.")
            if cca_name == "":
                raise SherlockDeleteAllICTFixturesError(message="CCA name is invalid.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            request = SherlockLayerService_pb2.DeleteAllICTFixturesRequest(
                project=project,
                ccaName=cca_name,
            )

            response = self.stub.deleteAllICTFixtures(request)

            if response.value == -1:
                raise SherlockDeleteAllICTFixturesError(message=response.message)

        except SherlockDeleteAllICTFixturesError as e:
            LOG.error(str(e))
            raise e

        return response.value

    @require_version(231)
    def delete_all_test_points(self, project: str, cca_name: str) -> int:
        """Delete all test points for a CCA.

        Available Since: 2023R1

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        cca_name: str
            Name of the CCA.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Test",
        >>>     cca_name="Card",
        >>> )
        >>> sherlock.layer.update_test_points_by_file(
        >>>     "Test",
        >>>     "Card",
        >>>     "TestPointsImport.csv",
        >>> )
        >>> sherlock.layer.delete_all_test_points("Test", "Card")
        """
        try:
            if project == "":
                raise SherlockDeleteAllTestPointsError(message="Project name is invalid.")
            if cca_name == "":
                raise SherlockDeleteAllTestPointsError(message="CCA name is invalid.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            request = SherlockLayerService_pb2.DeleteAllTestPointsRequest(
                project=project,
                ccaName=cca_name,
            )

            response = self.stub.deleteAllTestPoints(request)

            if response.value == -1:
                raise SherlockDeleteAllTestPointsError(message=response.message)

        except SherlockDeleteAllTestPointsError as e:
            LOG.error(str(e))
            raise e

        return response.value

    @require_version(231)
    def update_test_points_by_file(
        self,
        project: str,
        cca_name: str,
        file_path: str,
    ) -> int:
        """Update test point properties of a CCA from a CSV file.

        Available Since: 2023R1

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        cca_name: str
            Name of the CCA.
        file_path: str
            Path for the CSV file with the test point properties.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Test",
        >>>     cca_name="Card",
        >>> )
        >>> sherlock.layer.update_test_points_by_file(
        >>>     "Test",
        >>>     "Card",
        >>>     "TestPointsImport.csv"
        >>> )
        """
        try:
            if project == "":
                raise SherlockUpdateTestPointsByFileError(message="Project name is invalid.")
            if cca_name == "":
                raise SherlockUpdateTestPointsByFileError(message="CCA name is invalid.")
            if file_path == "":
                raise SherlockUpdateTestPointsByFileError(message="File path is required.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            request = SherlockLayerService_pb2.UpdateTestPointsByFileRequest(
                project=project,
                ccaName=cca_name,
                filePath=file_path,
            )

            response = self.stub.updateTestPointsByFile(request)

            return_code = response.returnCode

            if return_code.value == -1:
                if return_code.message == "":
                    raise SherlockUpdateTestPointsByFileError(error_array=response.updateError)

                raise SherlockUpdateTestPointsByFileError(message=return_code.message)

            else:
                LOG.info(return_code.message)
                return return_code.value

        except SherlockUpdateTestPointsByFileError as e:
            for error in e.str_itr():
                LOG.error(error)
            raise e

    @require_version(231)
    def update_test_fixtures_by_file(
        self,
        project: str,
        cca_name: str,
        file_path: str,
    ) -> int:
        """Update test fixture properties of a CCA from a CSV file.

        Available Since: 2023R1

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        cca_name: str
            Name of the CCA.
        file_path: str
            Path for the CSV file with the test fixture properties.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Test",
        >>>     cca_name="Card",
        >>> )
        >>> sherlock.layer.update_test_fixtures_by_file(
        >>>     "Test",
        >>>     "Card",
        >>>     "TestFixturesImport.csv"
        >>> )
        """
        try:
            if project == "":
                raise SherlockUpdateTestFixturesByFileError(message="Project name is invalid.")
            if cca_name == "":
                raise SherlockUpdateTestFixturesByFileError(message="CCA name is invalid.")
            if file_path == "":
                raise SherlockUpdateTestFixturesByFileError(message="File path is required.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            request = SherlockLayerService_pb2.UpdateICTFixturesByFileRequest(
                project=project,
                ccaName=cca_name,
                filePath=file_path,
            )

            response = self.stub.updateICTFixturesByFile(request)

            return_code = response.returnCode

            if return_code.value == -1:
                if return_code.message == "":
                    raise SherlockUpdateTestFixturesByFileError(error_array=response.updateError)

                raise SherlockUpdateTestFixturesByFileError(message=return_code.message)

            else:
                LOG.info(return_code.message)
                return return_code.value

        except SherlockUpdateTestFixturesByFileError as e:
            for error in e.str_itr():
                LOG.error(error)
            raise e

    @require_version(231)
    def export_all_test_points(
        self,
        project: str,
        cca_name: str,
        export_file: str,
        length_units: str = "DEFAULT",
        displacement_units: str = "DEFAULT",
        force_units: str = "DEFAULT",
    ) -> int:
        """Export the test point properties for a CCA.

        Available Since: 2023R1

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        cca_name: str
            Name of the CCA.
        export_file: str
            Full path for the CSV file to export the test points list to.
        length_units: str, optional
            Length units to use when exporting the test points.
            The default is ``DEFAULT``.
        displacement_units: str, optional
            Displacement units to use when exporting the test points.
            The default is ``DEFAULT``.
        force_units: str, optional
            Force units to use when exporting the test points.
            The default is ``DEFAULT``.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Tutorial Project",
        >>>     cca_name="Card",
        >>> )
        >>> sherlock.layer.export_all_test_points(
        >>>     "Tutorial Project",
        >>>     "Card",
        >>>     "TestPointsExport.csv",
        >>>     "DEFAULT",
        >>>     "DEFAULT",
        >>>     "DEFAULT"
        >>> )
        """
        try:
            if project == "":
                raise SherlockExportAllTestPointsError(message="Project name is invalid.")
            if cca_name == "":
                raise SherlockExportAllTestPointsError(message="CCA name is invalid.")
            if export_file == "":
                raise SherlockExportAllTestPointsError(message="File path is required.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            request = SherlockLayerService_pb2.ExportAllTestPointsRequest(
                project=project,
                ccaName=cca_name,
                filePath=export_file,
                lengthUnits=length_units,
                displacementUnits=displacement_units,
                forceUnits=force_units,
            )

            response = self.stub.exportAllTestPoints(request)

            if response.value == -1:
                raise SherlockExportAllTestPointsError(message=response.message)

        except SherlockExportAllTestPointsError as e:
            LOG.error(str(e))
            raise e

        return response.value

    @require_version(231)
    def export_all_test_fixtures(
        self,
        project: str,
        cca_name: str,
        export_file: str,
        units: str = "DEFAULT",
    ) -> int:
        """Export the test fixture properties for a CCA.

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        cca_name: str
            Name of the CCA.
        export_file: str
            Full path for the CSV file to export the text fixtures list to.
        units: str, optional
            Units to use when exporting the test fixtures.
            The default is ``DEFAULT``.


        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Tutorial Project",
        >>>     cca_name="Card",
        >>> )
        >>> sherlock.layer.export_all_test_fixtures(
        >>>     "Tutorial Project",
        >>>     "Card",
        >>>     "TestFixturesExport.csv",
        >>>     "DEFAULT"
        >>> )
        """
        try:
            if project == "":
                raise SherlockExportAllTestFixtures(message="Project name is invalid.")
            if cca_name == "":
                raise SherlockExportAllTestFixtures(message="CCA name is invalid.")
            if export_file == "":
                raise SherlockExportAllTestFixtures(message="File path is required.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            request = SherlockLayerService_pb2.ExportAllICTFixturesRequest(
                project=project,
                ccaName=cca_name,
                filePath=export_file,
                units=units,
            )

            response = self.stub.exportAllICTFixtures(request)

            if response.value == -1:
                raise SherlockExportAllTestFixtures(message=response.message)

        except SherlockExportAllTestFixtures as e:
            LOG.error(str(e))
            raise e

        return response.value

    @require_version(222)
    def export_all_mount_points(
        self,
        project: str,
        cca_name: str,
        export_file: str,
        units: str = "DEFAULT",
    ) -> int:
        """Export the mount point properties for a CCA.

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        cca_name: str
            Name of the CCA.
        export_file: str
            Full path for the CSV file to export the mount points list to.
        units: str, optional
            Units to use when exporting the mount points.
            The default is ``DEFAULT``.


        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Tutorial Project",
        >>>     cca_name="Card",
        >>> )
        >>> sherlock.layer.export_all_mount_points(
        >>>     "Tutorial Project",
        >>>     "Card",
        >>>     "MountPointsExport.csv",
        >>>     "DEFAULT"
        >>> )
        """
        try:
            if project == "":
                raise SherlockExportAllMountPoints(message="Project name is invalid.")
            if cca_name == "":
                raise SherlockExportAllMountPoints(message="CCA name is invalid.")
            if export_file == "":
                raise SherlockExportAllMountPoints(message="File path is required.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            request = SherlockLayerService_pb2.ExportAllMountPointsRequest(
                project=project,
                ccaName=cca_name,
                filePath=export_file,
                units=units,
            )

            response = self.stub.exportAllMountPoints(request)

            if response.value == -1:
                raise SherlockExportAllMountPoints(message=response.message)

        except SherlockExportAllMountPoints as e:
            LOG.error(str(e))
            raise e

        return response.value

    @require_version(251)
    def add_modeling_region(
        self,
        project: str,
        modeling_regions: list[
            dict[str, bool | float | str | dict[str, bool | float | str] | dict[str, float | str]]
        ],
    ) -> int:
        """
        Add one or more modeling regions to a specific project.

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        modeling_regions: list[dict[str, bool | float | str | dict[str, bool | float | str]\
                | dict[str, float | str]]]
            Modeling regions to add. Each dictionary should contain:

            - cca_name: str
                Name of the CCA.
            - region_id: str
                Unique region ID of the modeling region.
            - region_units: str
                Units of the modeling region.
            - model_mode: str
                Mode that specifies how the region is used. Valid values are ``Enabled``,
                ``Disabled`` and ``Excluded``.
            - shape: PolygonalShape|RectangularShape|SlotShape|CircularShape|PCBShape
                The shape of the modeling region.
            - pcb_model_props: dict[str, bool | float | str]
                PCB model parameters consisting of these properties:

                    - export_model_type: str
                        The type of model to be generated for a given modeling region.
                        Valid values are ``Default``, ``Sherlock``, ``Sweep`` and ``None``.
                    - elem_order: str
                        The type of 3D elements to be created for the PCB in the modeling region.
                        Valid values are ``First_Order``, ``Second_Order`` and ``Solid_Shell``.
                    - max_mesh_size: float
                        The maximum size of the mesh to be used in the region.
                    - max_mesh_size_units: str
                        Units for the maximum mesh size.
                    - quads_preferred: bool
                        Whether to generate quad-shaped elements when creating the mesh if true.
            - trace_model_props: dict[str, float | str]
                Trace model parameters consisting of these properties:

                    - trace_model_type : str
                        The specification of whether trace modeling should be performed
                        within the region. Valid values are ``Default``, ``Enabled`` and
                        ``Disabled``.
                    - elem_order: str, optional
                        The type of 3D elements to be created for the PCB in the modeling region.
                        Valid values are ``First_Order``, ``Second_Order`` and ``Solid_Shell``.
                    - trace_mesh_size : float, optional
                        The maximum mesh size to be used in the region when trace modeling
                        is enabled.
                    - trace_mesh_size_units: str, optional
                        Units for the maximum mesh size when trace modeling is enabled.


        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Tutorial Project",
        >>>     cca_name="Card",
        >>> )
        >>> modeling_regions = [
        >>> {
        >>>     "cca_name": "Card",
        >>>     "region_id": "Region001",
        >>>     "region_units": "mm",
        >>>     "model_mode": "Enabled",
        >>>     "shape": PolygonalShape(points=[
        >>>         (0, 0),
        >>>         (0, 6.35),
        >>>         (9.77, 0)
        >>>     ], rotation=87.8),
        >>>     "pcb_model_props": {
        >>>         "export_model_type": "Sherlock",
        >>>         "elem_order": "First_Order",
        >>>         "max_mesh_size": 0.5,
        >>>         "max_mesh_size_units": "mm",
        >>>         "quads_preferred": True
        >>>     },
        >>>     "trace_model_props": {
        >>>         "trace_model_type": "Enabled",
        >>>         "elem_order": "Second_Order",
        >>>         "trace_mesh_size": 0.3,
        >>>         "trace_mesh_size_units": "mm"
        >>>     }
        >>> }
        >>> ]
        >>> result = sherlock.layer.add_modeling_region("Tutorial Project", modeling_regions)
        """
        try:
            if not project:
                raise SherlockAddModelingRegionError(message="Project name is invalid.")

            if not modeling_regions:
                raise SherlockAddModelingRegionError(message="Modeling regions list is empty.")

            for region in modeling_regions:
                if "cca_name" not in region or not region["cca_name"]:
                    raise SherlockAddModelingRegionError(message="CCA name is invalid.")
                if "region_id" not in region or not region["region_id"]:
                    raise SherlockAddModelingRegionError(message="Region ID is invalid.")
                if "region_units" not in region or not region["region_units"]:
                    raise SherlockAddModelingRegionError(message="Region units are invalid.")
                if "shape" not in region:
                    raise SherlockAddModelingRegionError(message="Shape is missing.")
                elif not isinstance(
                    region["shape"],
                    (
                        PolygonalShape,
                        RectangularShape,
                        SlotShape,
                        CircularShape,
                        PCBShape,
                    ),
                ):
                    raise SherlockAddModelingRegionError(message="Shape is not of a valid type.")

                pcb_model_props = region.get("pcb_model_props", {})
                if pcb_model_props:
                    if (
                        "export_model_type" not in pcb_model_props
                        or pcb_model_props["export_model_type"] == ""
                    ):
                        raise SherlockAddModelingRegionError(
                            message="PCB model export type is invalid."
                        )
                    if "elem_order" not in pcb_model_props or pcb_model_props["elem_order"] == "":
                        raise SherlockAddModelingRegionError(
                            message="PCB element order is invalid."
                        )
                    if "max_mesh_size" not in pcb_model_props or not isinstance(
                        pcb_model_props["max_mesh_size"], float
                    ):
                        raise SherlockAddModelingRegionError(
                            message="PCB max mesh size is invalid."
                        )
                    if "quads_preferred" not in pcb_model_props or not isinstance(
                        pcb_model_props["quads_preferred"], bool
                    ):
                        raise SherlockAddModelingRegionError(
                            message="PCB quads preferred is invalid."
                        )

                trace_model_props = region.get("trace_model_props", {})
                if trace_model_props:
                    if (
                        "trace_model_type" not in trace_model_props
                        or trace_model_props["trace_model_type"] == ""
                    ):
                        raise SherlockAddModelingRegionError(message="Trace model type is invalid.")

                if not self._is_connection_up():
                    raise SherlockNoGrpcConnectionException()

                add_modeling_region_request = SherlockLayerService_pb2.AddModelingRegionRequest()
                add_modeling_region_request.project = project

                for region_request in modeling_regions:
                    modeling_region = add_modeling_region_request.modelingRegions.add()
                    modeling_region.ccaName = region_request["cca_name"]
                    modeling_region.regionId = region_request["region_id"]
                    modeling_region.regionUnits = region_request["region_units"]
                    modeling_region.modelMode = ModelingRegion.ModelingMode.Value(
                        region_request["model_mode"]
                    )

                    shape = region_request["shape"]
                    if isinstance(shape, PolygonalShape):
                        polygonal_shape = modeling_region.polygonalShape
                        for point in shape.points:
                            polygonal_point = polygonal_shape.points.add()
                            polygonal_point.x = point[0]
                            polygonal_point.y = point[1]
                        polygonal_shape.rotation = shape.rotation
                    elif isinstance(shape, RectangularShape):
                        rectangular_shape = modeling_region.rectangularShape
                        rectangular_shape.length = shape.length
                        rectangular_shape.width = shape.width
                        rectangular_shape.centerX = shape.center_x
                        rectangular_shape.centerY = shape.center_y
                        rectangular_shape.rotation = shape.rotation
                    elif isinstance(shape, SlotShape):
                        slot_shape = modeling_region.slotShape
                        slot_shape.length = shape.length
                        slot_shape.width = shape.width
                        slot_shape.nodeCount = shape.node_count
                        slot_shape.centerX = shape.center_x
                        slot_shape.centerY = shape.center_y
                        slot_shape.rotation = shape.rotation
                    elif isinstance(shape, CircularShape):
                        circular_shape = modeling_region.circularShape
                        circular_shape.diameter = shape.diameter
                        circular_shape.nodeCount = shape.node_count
                        circular_shape.centerX = shape.center_x
                        circular_shape.centerY = shape.center_y
                        circular_shape.rotation = shape.rotation
                    else:
                        raise SherlockAddModelingRegionError(
                            message="Shape is not of a valid type."
                        )

                    export_model_type = ModelingRegion.PCBModelingProperties.ExportModelType
                    pcb_model_props = region_request.get("pcb_model_props", {})
                    modeling_region.pcbModelProps.exportModelType = getattr(
                        export_model_type,
                        pcb_model_props["export_model_type"],
                    )
                    modeling_region.pcbModelProps.elemOrder = getattr(
                        ModelingRegion.ElementOrder,
                        pcb_model_props["elem_order"],
                    )
                    modeling_region.pcbModelProps.maxMeshSize = pcb_model_props["max_mesh_size"]
                    modeling_region.pcbModelProps.maxMeshSizeUnits = pcb_model_props[
                        "max_mesh_size_units"
                    ]
                    modeling_region.pcbModelProps.quadsPreferred = pcb_model_props[
                        "quads_preferred"
                    ]

                    trace_modeling_type = ModelingRegion.TraceModelingProperties.TraceModelingType
                    trace_model_props = region_request.get("trace_model_props", {})
                    modeling_region.traceModelProps.traceModelType = getattr(
                        trace_modeling_type,
                        trace_model_props["trace_model_type"],
                    )
                    if "elem_order" in trace_model_props:
                        modeling_region.traceModelProps.elemOrder = getattr(
                            ModelingRegion.ElementOrder,
                            trace_model_props["elem_order"],
                        )
                    if "trace_mesh_size" in trace_model_props:
                        modeling_region.traceModelProps.traceMeshSize = trace_model_props[
                            "trace_mesh_size"
                        ]
                    if "trace_mesh_size_units" in trace_model_props:
                        modeling_region.traceModelProps.traceMeshSizeUnits = trace_model_props[
                            "trace_mesh_size_units"
                        ]

                return_code = self.stub.addModelingRegion(add_modeling_region_request)
                if return_code.value != 0:
                    raise SherlockAddModelingRegionError(message=return_code.message)

                return return_code.value

        except SherlockAddModelingRegionError as e:
            for error in e.str_itr():
                LOG.error(error)
            raise e

    @require_version(251)
    def update_modeling_region(
        self,
        project: str,
        modeling_regions: list[
            dict[
                str,
                bool | float | str | dict[str, bool | float | str] | dict[str, bool | float | str],
            ]
        ],
    ) -> int:
        """
        Update one or more modeling regions in a specific project.

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        modeling_regions : list[dict]
            Modeling regions to update. Each dictionary should contain:

            - cca_name: str
                Name of the CCA.
            - region_id: str
                Unique region ID of the modeling region.
            - region_units: str
                Units of the modeling region.
            - model_mode: str
                Mode that specifies how the region is used. Valid values are ``Enabled``,
                ``Disabled`` and ``Excluded``.
            - shape: PolygonalShape|RectangularShape|SlotShape|CircularShape|PCBShape
                The shape of the modeling region.
            - pcb_model_props: dict[str, bool | float | str]
                PCB model parameters consisting of these properties:

                    - export_model_type: str
                        The type of model to be generated for a given modeling region.
                        Valid values are ``Default``, ``Sherlock``, ``Sweep`` and ``None``.
                    - elem_order: str
                        The type of 3D elements to be created for the PCB in the modeling region.
                        Valid values are ``First_Order``, ``Second_Order`` and ``Solid_Shell``.
                    - max_mesh_size: float
                        The maximum size of the mesh to be used in the region.
                    - max_mesh_size_units: str
                        Units for the maximum mesh size.
                    - quads_preferred: bool
                        Whether to generate quad-shaped elements when creating the mesh if true.
            - trace_model_props: dict[str, float | str]
                Trace model parameters consisting of these properties:

                    - trace_model_type: str
                        The specification of whether trace modeling should be performed
                        within the region. Valid values are ``Default``, ``Enabled`` and
                        ``Disabled``.
                    - elem_order: str, optional
                        The type of 3D elements to be created for the PCB in the modeling region.
                        Valid values are ``First_Order``, ``Second_Order`` and ``Solid_Shell``.
                    - trace_mesh_size: float, optional
                        The maximum mesh size to be used in the region when trace modeling
                        is enabled.
                    - trace_mesh_size_units: str, optional
                        Units for the maximum mesh size when trace modeling is enabled.
            - region_id_replacement: str, optional
                Represents a unique region id that will replace the existing regionId value during
                a modeling region update if a value exists.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Tutorial Project",
        >>>     cca_name="Card",
        >>> )
        >>> modeling_regions = [
        >>>     {
        >>>         "cca_name": "Card",
        >>>         "region_id": "Region001",
        >>>         "region_units": "mm",
        >>>         "model_mode": "Enabled",
        >>>         "shape": PolygonalShape(points=[(0, 0), (1, 1)], rotation=0),
        >>>         "pcb_model_props": {
        >>>             "export_model_type": "Sherlock",
        >>>             "elem_order": "Second_Order",
        >>>             "max_mesh_size": 0.5,
        >>>             "max_mesh_size_units": "mm",
        >>>             "quads_preferred": True,
        >>>         },
        >>>         "trace_model_props": {
        >>>             "trace_model_type": "Enabled",
        >>>             "elem_order": "Second_Order",
        >>>             "trace_mesh_size": 0.1,
        >>>             "trace_mesh_size_units": "mm",
        >>>         },
        >>>         "region_id_replacement": "NewRegion001",
        >>>     }
        >>> ]
        >>> result = sherlock.layer.update_modeling_region("Tutorial Project", modeling_regions)
        """
        try:
            if not project:
                raise SherlockUpdateModelingRegionError(message="Project name is invalid.")

            if not modeling_regions:
                raise SherlockUpdateModelingRegionError(message="Modeling regions list is empty.")

            for region in modeling_regions:
                if "cca_name" not in region:
                    raise SherlockUpdateModelingRegionError(message="CCA name is invalid.")
                if "region_id" not in region:
                    raise SherlockUpdateModelingRegionError(message="Region ID is invalid.")
                if "region_units" not in region:
                    raise SherlockUpdateModelingRegionError(message="Region units are invalid.")
                if "shape" not in region:
                    raise SherlockUpdateModelingRegionError(message="Shape is missing.")
                elif not isinstance(
                    region["shape"],
                    (
                        PolygonalShape,
                        RectangularShape,
                        SlotShape,
                        CircularShape,
                        PCBShape,
                    ),
                ):
                    raise SherlockUpdateModelingRegionError(message="Shape is not of a valid type.")

                pcb_model_props = region.get("pcb_model_props", {})
                if pcb_model_props:
                    if (
                        "export_model_type" not in pcb_model_props
                        or pcb_model_props["export_model_type"] == ""
                    ):
                        raise SherlockUpdateModelingRegionError(
                            message="PCB model export type is invalid."
                        )
                    if "elem_order" not in pcb_model_props or pcb_model_props["elem_order"] == "":
                        raise SherlockUpdateModelingRegionError(
                            message="PCB element order is invalid."
                        )
                    if "max_mesh_size" not in pcb_model_props or not isinstance(
                        pcb_model_props["max_mesh_size"], float
                    ):
                        raise SherlockUpdateModelingRegionError(
                            message="PCB max mesh size is invalid."
                        )
                    if "quads_preferred" not in pcb_model_props or not isinstance(
                        pcb_model_props["quads_preferred"], bool
                    ):
                        raise SherlockUpdateModelingRegionError(
                            message="PCB quads preferred is invalid."
                        )

                trace_model_props = region.get("trace_model_props", {})
                if trace_model_props:
                    if (
                        "trace_model_type" not in trace_model_props
                        or trace_model_props["trace_model_type"] == ""
                    ):
                        raise SherlockUpdateModelingRegionError(
                            message="Trace model type is invalid."
                        )

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            update_modeling_region_request = SherlockLayerService_pb2.UpdateModelingRegionRequest()
            update_modeling_region_request.project = project

            for region_request in modeling_regions:
                modeling_region = update_modeling_region_request.modelingRegions.add()
                modeling_region.ccaName = region_request["cca_name"]
                modeling_region.regionId = region_request["region_id"]
                modeling_region.regionUnits = region_request["region_units"]
                modeling_region.modelMode = ModelingRegion.ModelingMode.Value(
                    region_request["model_mode"]
                )

                shape = region_request["shape"]
                if isinstance(shape, PolygonalShape):
                    polygonal_shape = modeling_region.polygonalShape
                    for point in shape.points:
                        polygonal_point = polygonal_shape.points.add()
                        polygonal_point.x = point[0]
                        polygonal_point.y = point[1]
                    polygonal_shape.rotation = shape.rotation
                elif isinstance(shape, RectangularShape):
                    rectangular_shape = modeling_region.rectangularShape
                    rectangular_shape.length = shape.length
                    rectangular_shape.width = shape.width
                    rectangular_shape.centerX = shape.center_x
                    rectangular_shape.centerY = shape.center_y
                    rectangular_shape.rotation = shape.rotation
                elif isinstance(shape, SlotShape):
                    slot_shape = modeling_region.slotShape
                    slot_shape.length = shape.length
                    slot_shape.width = shape.width
                    slot_shape.nodeCount = shape.node_count
                    slot_shape.centerX = shape.center_x
                    slot_shape.centerY = shape.center_y
                    slot_shape.rotation = shape.rotation
                elif isinstance(shape, CircularShape):
                    circular_shape = modeling_region.circularShape
                    circular_shape.diameter = shape.diameter
                    circular_shape.nodeCount = shape.node_count
                    circular_shape.centerX = shape.center_x
                    circular_shape.centerY = shape.center_y
                    circular_shape.rotation = shape.rotation

                export_model_type = ModelingRegion.PCBModelingProperties.ExportModelType
                pcb_model_props = region_request.get("pcb_model_props", {})
                modeling_region.pcbModelProps.exportModelType = getattr(
                    export_model_type,
                    pcb_model_props["export_model_type"],
                )
                modeling_region.pcbModelProps.elemOrder = getattr(
                    ModelingRegion.ElementOrder,
                    pcb_model_props["elem_order"],
                )
                modeling_region.pcbModelProps.maxMeshSize = pcb_model_props["max_mesh_size"]
                modeling_region.pcbModelProps.maxMeshSizeUnits = pcb_model_props[
                    "max_mesh_size_units"
                ]
                modeling_region.pcbModelProps.quadsPreferred = pcb_model_props["quads_preferred"]

                trace_modeling_type = ModelingRegion.TraceModelingProperties.TraceModelingType
                trace_model_props = region_request.get("trace_model_props", {})
                modeling_region.traceModelProps.traceModelType = getattr(
                    trace_modeling_type,
                    trace_model_props["trace_model_type"],
                )
                if "elem_order" in trace_model_props:
                    modeling_region.traceModelProps.elemOrder = getattr(
                        ModelingRegion.ElementOrder,
                        trace_model_props["elem_order"],
                    )
                if "trace_mesh_size" in trace_model_props:
                    modeling_region.traceModelProps.traceMeshSize = trace_model_props[
                        "trace_mesh_size"
                    ]
                if "trace_mesh_size_units" in trace_model_props:
                    modeling_region.traceModelProps.traceMeshSizeUnits = trace_model_props[
                        "trace_mesh_size_units"
                    ]

                if "region_id_replacement" in region_request:
                    modeling_region.regionIdReplacement = region_request["region_id_replacement"]

            return_code = self.stub.updateModelingRegion(update_modeling_region_request)
            if return_code.value != 0:
                raise SherlockUpdateModelingRegionError(message=return_code.message)

            return return_code.value

        except SherlockUpdateModelingRegionError as e:
            for error in e.str_itr():
                LOG.error(error)
            raise e

    @require_version(251)
    def copy_modeling_region(
        self,
        project: str,
        copy_regions: list[dict[str, float | str]],
    ) -> int:
        """
        Copy one or more modeling regions in a specific project.

        Parameters
        ----------
        project : str
            Name of the Sherlock project.
        copy_regions : list[dict[str, float | str]]
            Modeling regions to copy along with their corresponding "copy to" parameters.
            Each dictionary should contain:

            - cca_name : str
                Name of the CCA.
            - region_id : str
                Region ID of the existing modeling region to copy.
            - region_id_copy : str
                Region ID of the modeling region copy. Must be unique.
            - center_x : float
                The center x coordinate of the modeling region copy. Used for location placement in
                the Layer Viewer.
            - center_y : float
                The center y coordinate of the modeling region copy. Used for location placement in
                the Layer Viewer.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Tutorial Project",
        >>>     cca_name="Card",
        >>> )
        >>> modeling_regions = [
        >>>     {
        >>>         "cca_name": "Card",
        >>>         "region_id": "Region001",
        >>>         "region_id_copy": "RegionCopy001",
        >>>         "center_x": 10.0,
        >>>         "center_y": 20.0,
        >>>     }
        >>> ]
        >>> result = sherlock.layer.copy_modeling_region("Tutorial Project", modeling_regions)
        """
        try:
            if not project:
                raise SherlockCopyModelingRegionError(message="Project name is invalid.")

            if not copy_regions:
                raise SherlockCopyModelingRegionError(message="Copy regions list is empty.")

            for region in copy_regions:
                if "cca_name" not in region or region["cca_name"] == "":
                    raise SherlockCopyModelingRegionError(message="CCA name is invalid.")
                if "region_id" not in region or region["region_id"] == "":
                    raise SherlockCopyModelingRegionError(message="Region ID is invalid.")
                if "region_id_copy" not in region or region["region_id_copy"] == "":
                    raise SherlockCopyModelingRegionError(message="Region ID copy is invalid.")
                if "center_x" not in region or not isinstance(region["center_x"], float):
                    raise SherlockCopyModelingRegionError(message="Center X coordinate is invalid.")
                if "center_y" not in region or not isinstance(region["center_y"], float):
                    raise SherlockCopyModelingRegionError(message="Center Y coordinate is invalid.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            copy_regions_request = []

            for region in copy_regions:
                copy_region = (
                    SherlockLayerService_pb2.CopyModelingRegionRequest.CopyModelingRegionInfo(
                        ccaName=region["cca_name"],
                        regionId=region["region_id"],
                        regionIdCopy=region["region_id_copy"],
                        centerX=region["center_x"],
                        centerY=region["center_y"],
                    )
                )
                copy_regions_request.append(copy_region)

            request = SherlockLayerService_pb2.CopyModelingRegionRequest(
                project=project,
                copyRegions=copy_regions_request,
            )

            response = self.stub.copyModelingRegion(request)

            if response.value == -1:
                raise SherlockCopyModelingRegionError(message=response.message)

            return response.value

        except SherlockCopyModelingRegionError as e:
            for error in e.str_itr():
                LOG.error(error)
            raise e

    @require_version(251)
    def delete_modeling_region(self, project: str, delete_regions: list[dict[str, str]]) -> int:
        """Delete one or more modeling regions for a specific project.

        Parameters
        ----------
        project: str
            Name of the Sherlock project.
        delete_regions: list[dict[str, str]]
            Modeling regions to delete. Each dictionary should contain:
            - "cca_name": str, Name of the CCA.
            - "region_id": str, Unique region ID of the modeling region to delete.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Test",
        >>>     cca_name="Card",
        >>> )
        >>> modeling_regions = [{"cca_name": "Card", "region_id": "12345"}]
        >>> sherlock.layer.delete_modeling_region("Test", modeling_regions)
        """
        try:
            if project == "":
                raise SherlockDeleteModelingRegionError(message="Project name is invalid.")
            if not isinstance(delete_regions, list):
                raise SherlockDeleteModelingRegionError(message="Delete regions should be a list.")
            if not delete_regions:
                raise SherlockDeleteModelingRegionError(message="Delete regions list is empty.")

            for region in delete_regions:
                if not isinstance(region, dict):
                    raise SherlockDeleteModelingRegionError(
                        message="Each region should be a dictionary."
                    )
                if "cca_name" not in region or region["cca_name"] == "":
                    raise SherlockDeleteModelingRegionError(message="CCA name is invalid.")
                if "region_id" not in region or region["region_id"] == "":
                    raise SherlockDeleteModelingRegionError(message="Region ID is invalid.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            delete_regions_info = [
                SherlockLayerService_pb2.DeleteModelingRegionRequest.DeleteModelingRegionInfo(
                    ccaName=region["cca_name"], regionId=region["region_id"]
                )
                for region in delete_regions
            ]

            request = SherlockLayerService_pb2.DeleteModelingRegionRequest(
                project=project, deleteRegions=delete_regions_info
            )

            response = self.stub.deleteModelingRegion(request)

            if response.value == -1:
                raise SherlockDeleteModelingRegionError(message=response.message)

        except SherlockDeleteModelingRegionError as e:
            LOG.error(str(e))
            raise e

        return response.value

    @require_version(252)
    def list_layers(self, project, cca_name) -> list:
        """List all layers as seen in the Layer Viewer for a specific project CCA.

        Parameters
        ----------
        project : str
            Name of the Sherlock project.
        cca_name: str
            Name of the CCA.

        Returns
        -------
        layer_infos: list[dict[str, list]]
            The layers as seen in the Layer Viewer for the given project CCA.
            Each dictionary should contain:

            - "layer_folder": str, name of layer_folder enum.
            - "layers": list, list of names of layers under this folder

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Tutorial Project",
        >>>     cca_name="Card"
        >>> )
        >>> sherlock.layer.list_layers(
        >>>     project="Tutorial Project",
        >>>     cca_name="Card"
        >>> )
        """
        try:
            if project == "":
                raise SherlockListLayersError(message="Project name is invalid.")

            if cca_name == "":
                raise SherlockListLayersError(message="CCA name is invalid.")

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            request = SherlockLayerService_pb2.ListLayersRequest(project=project, ccaName=cca_name)

            response = self.stub.listLayers(request)
            if response.returnCode.value == -1:
                raise SherlockListLayersError(response.returnCode.message)

            return response.layerInfos

        except SherlockListLayersError as e:
            LOG.error(str(e))
            raise e

    @require_version(252)
    def export_layer_image(
        self, project: str, cca_name: str, export_layers: list[dict[str, bool | int | str | list]]
    ) -> int:
        r"""
        Export one or more 2D Layer Viewer images from a project CCA.

        Parameters
        ----------
        project : str
            Name of the Sherlock project.
        cca_name : str
            Name of the CCA.
        export_layers : export_layer_image_info
            List of parameters for the export image specified.
            Each dictionary should contain:

            - components_enabled: bool, optional
                Displays the components in the export image. Default to true when not provided.
            - labels_enabled: bool, optional
                Displays the component reference designators in the export image. Default to true
                when not provided.
            - leads_enabled : bool, optional
                Displays the component leads and solder balls in the export image. Default to true
                when not provided.
            - axes_enabled : bool
                Displays the x and y axes in the export image.
            - grid_enabled : bool
                Displays a grid in the export image.
            - layer_infos: list[dict[enum, list]]
                The layers as seen in the Layer Viewer for the given project CCA.
                Each dictionary should contain:

                - "layer_folder": enum, layer_folder enum.
                - "layers": list, list of names of layers under this folder
            - file_path : str
                Full file path of the export image.
            - image_height : int
                The export image height.
            - image_width : int
                The export image width.
            - overwrite_existing_file : bool
                If the file path already exists, overwrite the file if this is set to true.

        Returns
        -------
        int
            Status code of the response. 0 for success.

        Examples
        --------
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> sherlock.project.import_odb_archive(
        >>>     "ODB++ Tutorial.tgz",
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     True,
        >>>     project="Tutorial Project",
        >>>     cca_name="Card"
        >>> )
        >>> layer_infos = [
        >>> { "layer_folder": "Components",
        >>>   "layers": ["comp-top"]},
        >>> { "layer_folder": "Harmonic_Vibe",
        >>>   "layers":["HV Disp @ 203.39 Hz"]}
        >>> ]
        >>> export_layers = [
        >>> {
        >>>     "components_enabled": True,
        >>>     "labels_enabled": True,
        >>>     "leads_enabled": True,
        >>>     "axes_enabled": True,
        >>>     "grid_enabled": True,
        >>>     "layer_infos": layer_infos,
        >>>     "file_path": "C:\\Users\\user_id\\Downloads\\SH-image.jpg",
        >>>     "image_height": 600,
        >>>     "image_width": 800,
        >>>     "overwrite_existing_file": True
        >>> }
        >>> ]
        >>> sherlock.layer.export_layer_image("Tutorial Project", "Card", export_layers)
        """
        try:
            if project == "":
                raise SherlockExportLayerImageError(message="Project name is invalid.")

            if cca_name == "":
                raise SherlockExportLayerImageError(message="CCA name is invalid.")

            for export_layer in export_layers:
                if "axes_enabled" not in export_layer:
                    raise SherlockExportLayerImageError(message="Axes Enabled is invalid.")
                if "grid_enabled" not in export_layer:
                    raise SherlockExportLayerImageError(message="Grid Enabled is invalid.")
                if "layer_infos" not in export_layer or len(export_layer["layer_infos"]) == 0:
                    raise SherlockExportLayerImageError(message="Layer info is invalid.")
                if "file_path" not in export_layer or export_layer["file_path"] == "":
                    raise SherlockExportLayerImageError(message="File Path is invalid.")
                if "image_height" not in export_layer or not isinstance(
                    export_layer["image_height"], int
                ):
                    raise SherlockExportLayerImageError(message="Image Height is invalid.")
                if "image_width" not in export_layer or not isinstance(
                    export_layer["image_width"], int
                ):
                    raise SherlockExportLayerImageError(message="Image Width is invalid.")
                if "overwrite_existing_file" not in export_layer:
                    raise SherlockExportLayerImageError(
                        message="Overwrite Existing File is " "invalid."
                    )

            if not self._is_connection_up():
                raise SherlockNoGrpcConnectionException()

            export_layer_image_request = []

            for export_layer in export_layers:
                if "components_enabled" in export_layer:
                    components_enabled_val = export_layer["components_enabled"]
                else:
                    components_enabled_val = None

                if "labels_enabled" in export_layer:
                    labels_enabled_val = export_layer["labels_enabled"]
                else:
                    labels_enabled_val = None

                if "leads_enabled" in export_layer:
                    leads_enabled_val = export_layer["leads_enabled"]
                else:
                    leads_enabled_val = None

                layer_info_request = []
                for layer in export_layer["layer_infos"]:
                    layer_info = SherlockLayerService_pb2.LayerInfo(
                        layerFolder=layer["layer_folder"], layers=layer["layers"]
                    )
                    layer_info_request.append(layer_info)

                export_layer_info = (
                    SherlockLayerService_pb2.ExportLayerImageRequest.ExportLayerImageInfo(
                        componentsEnabled=components_enabled_val,
                        labelsEnabled=labels_enabled_val,
                        leadsEnabled=leads_enabled_val,
                        axesEnabled=export_layer["axes_enabled"],
                        gridEnabled=export_layer["grid_enabled"],
                        layerInfos=layer_info_request,
                        filePath=export_layer["file_path"],
                        imageHeight=export_layer["image_height"],
                        imageWidth=export_layer["image_width"],
                        overwriteExistingFile=export_layer["overwrite_existing_file"],
                    )
                )
                export_layer_image_request.append(export_layer_info)

            request = SherlockLayerService_pb2.ExportLayerImageRequest(
                project=project, ccaName=cca_name, exportLayers=export_layer_image_request
            )

            responses = []
            for response in self.stub.exportLayerImage(request):
                responses.append(response)
            return responses

        except SherlockExportLayerImageError as e:
            LOG.error(str(e))
            raise e

    @require_version(261)
    def get_test_point_props(
        self, request: GetTestPointPropertiesRequest
    ) -> list[SherlockLayerService_pb2.GetTestPointPropertiesResponse]:
        """Return the properties for each test point given a comma-separated list of test point IDs.

        Available Since: 2026R1

        Parameters
        ----------
        request: GetTestPointPropertiesRequest
             Contains all the information needed to return the properties for one or more test
             points.

        Returns
        -------
        list[SherlockCommonService_pb2.GetTestPointPropertiesResponse]
            Properties for each test point that correspond to the reference designators.

        Examples
        --------
        >>> from ansys.sherlock.core.types.layer_types import GetTestPointPropertiesRequest
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> request = layer_types.GetTestPointPropertiesRequest(
        >>>    project = "Test Point Test Project"
        >>>    cca_name = "Main Board"
        >>>    test_point_ids = "TP1,TP2"
        >>> )
        >>> responses = layer.get_test_point_props(request)
        """
        get_test_point_props_request = request._convert_to_grpc()
        responses = []
        for get_test_point_props_response in self.stub.getTestPointProperties(
            get_test_point_props_request
        ):
            responses.append(get_test_point_props_response)
        return responses

    @require_version(261)
    def get_ict_fixtures_props(
        self, request: GetICTFixturesPropertiesRequest
    ) -> SherlockLayerService_pb2.GetICTFixturesPropertiesResponse:
        """Return the properties for each ICT fixture given a comma-separated list of fixture IDs.

        Available Since: 2026R1

        Parameters
        ----------
        request: GetICTFixturesPropertiesRequest
             Contains all the information needed to return the properties for one or more ICT
             fixtures.

        Returns
        -------
        SherlockCommonService_pb2.GetICTFixturesPropertiesResponse
            Properties for each ICT fixture that correspond to the reference designators.

        Examples
        --------
        >>> from ansys.sherlock.core.types.layer_types import GetICTFixturesPropertiesRequest
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> request = layer_types.GetICTFixturesPropertiesRequest(
        >>>    project = "Tutorial Project"
        >>>    cca_name = "Main Board"
        >>>    ict_fixtures_ids = "F1,F2"
        >>> )
        >>> response = layer.get_ict_fixtures_props(request)
        """
        get_ict_fixture_props_request = request._convert_to_grpc()
        response = self.stub.getICTFixturesProperties(get_ict_fixture_props_request)

        return response

    @require_version(261)
    def update_test_points(
        self, request: UpdateTestPointsRequest
    ) -> SherlockLayerService_pb2.UpdateTestPointsResponse:
        """Update test point properties of a CCA from input parameters.

        Available Since: 2026R1

        Parameters
        ----------
        request: UpdateTestPointsRequest
            Contains all the information needed to update the properties for one or more
            test points.

        Returns
        -------
        SherlockCommonService_pb2.UpdateTestPointsResponse
            A status code and message for the update test points request.

        Examples
        --------
        >>> from ansys.sherlock.core.types.layer_types import UpdateTestPointsRequest,
        >>> TestPointProperties
        >>> from ansys.api.sherlock.v0 import SherlockLayerService_pb2
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> test_point = TestPointProperties(
        >>>     id="TP1",
        >>>     side="BOTTOM",
        >>>     units="in",
        >>>     center_x=1.0,
        >>>     center_y=0.5,
        >>>     radius=0.2,
        >>>     load_type=SherlockLayerService_pb2.TestPointProperties.LoadType.Force,
        >>>     load_value=3.0,
        >>>     load_units="ozf",
        >>> )
        >>> response = sherlock.layer.update_test_points(UpdateTestPointsRequest(
        >>> project="Tutorial Project",
        >>> cca_name="Main Board",
        >>> update_test_points=[test_point],
        >>> ))
        """
        update_request = request._convert_to_grpc()
        response = self.stub.updateTestPoints(update_request)
        return response

    @require_version(261)
    def update_ict_fixtures(
        self, request: UpdateICTFixturesRequest
    ) -> SherlockLayerService_pb2.UpdateICTFixturesResponse:
        """Update ict fixture properties of a CCA from input parameters.

        Available Since: 2026R1

        Parameters
        ----------
        request: UpdateICTFixturesRequest
            Contains all the information needed to update the properties for one or more
            ict fixtures.

        Returns
        -------
        SherlockCommonService_pb2.UpdateICTFixturesResponse
            A status code and message for the update ict fixtures request.

        Examples
        --------
        >>> from ansys.sherlock.core.types.layer_types import UpdateICTFixturesRequest,
        >>> ICTFixtureProperties
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> fixture = ICTFixtureProperties(
        >>>     id="F1",
        >>>     type="Mount Hole",
        >>>     units="in",
        >>>     side="TOP",
        >>>     height="0.0",
        >>>     material="GOLD",
        >>>     state="DISABLED",
        >>>     shape="Slot",
        >>>     x="0.3",
        >>>     y="-0.4",
        >>>     length="1.0",
        >>>     width="0.2",
        >>>     diameter="0.0",
        >>>     nodes="10",
        >>>     rotation="15",
        >>>     polygon="",
        >>>     boundary="Outline",
        >>>     constraints="X-axis translation|Z-axis translation",
        >>>     chassis_material="SILVER",
        >>> )
        >>> response = sherlock.layer.update_ict_fixtures(UpdateICTFixturesRequest(
        >>> project="Tutorial Project",
        >>> cca_name="Main Board",
        >>> update_fixtures=[fixture],
        >>> ))
        """
        update_request = request._convert_to_grpc()
        response = self.stub.updateICTFixtures(update_request)
        return response

    @require_version(261)
    def get_mount_point_props(
        self, request: GetMountPointsPropertiesRequest
    ) -> SherlockLayerService_pb2.GetMountPointsPropertiesResponse:
        """
        Return the properties for each mount point given a comma-separated list of mount point IDs.

        Available Since: 2026R1

        Parameters
        ----------
        request: GetmountPointsPropertiesRequest
             Contains all the information needed to return the properties for one or more mount
             points.

        Returns
        -------
        SherlockCommonService_pb2.GetMountPointsPropertiesResponse
            Properties for each mount point that correspond to the reference designators.

        Examples
        --------
        >>> from ansys.sherlock.core.types.layer_types import GetMountPointsPropertiesRequest
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> request = layer_types.GetMountPointsPropertiesRequest(
        >>>    project = "Tutorial Project"
        >>>    cca_name = "Main Board"
        >>>    mount_point_ids = "MP1,MP2"
        >>> )
        >>> response = layer.get_mount_point_props(request)
        """
        if not self._is_connection_up():
            raise SherlockNoGrpcConnectionException()

        return self.stub.getMountPointsProperties(request._convert_to_grpc())

    @require_version(261)
    def update_mount_points(
        self, request: UpdateMountPointsRequest
    ) -> SherlockLayerService_pb2.UpdateMountPointsResponse:
        """Update mount point properties of a CCA from input parameters.

        Available Since: 2026R1

        Parameters
        ----------
        request: UpdateMountPointsRequest
            Contains all the information needed to update the properties for one or more
            mount points.

        Returns
        -------
        SherlockCommonService_pb2.UpdateMountPointsResponse
            A status code and message for the update mount points request.

        Examples
        --------
        >>> from ansys.sherlock.core.types.layer_types import UpdateMountPointsRequest,
        >>> MountPointProperties
        >>> from ansys.sherlock.core import launcher
        >>> sherlock, install_dir = launcher.launch_and_connect(transport_mode="wnua")
        >>> mount_point = MountPointProperties(
        >>>     id="MP1",
        >>>     type="Mount Pad",
        >>>     shape="Rectangular",
        >>>     units="mm",
        >>>     side="BOTTOM",
        >>>     height=1.0,
        >>>     material="GOLD",
        >>>     state="DISABLED",
        >>>     x=0.3,
        >>>     y=-0.4,
        >>>     length=1.0,
        >>>     width=0.2,
        >>>     diameter=0.0,
        >>>     nodes="",
        >>>     rotation=45,
        >>>     polygon="",
        >>>     boundary="Outline",
        >>>     constraints="X-axis translation|Z-axis translation",
        >>>     chassis_material="SILVER",
        >>> )
        >>> response = sherlock.layer.update_mount_points(UpdateMountPointsRequest(
        >>>     project="Tutorial Project",
        >>>     cca_name="Main Board",
        >>>     update_mount_points=[mount_point],
        >>> ))
        """
        if not self._is_connection_up():
            raise SherlockNoGrpcConnectionException()

        return self.stub.updateMountPoints(request._convert_to_grpc())
