# Autogenerated code. DO NOT EDIT. Manual code can be added to the corresponding .template.py file.

from __future__ import annotations
from dlubal.api.common.connection import _ApiKey, check_ssl, init_channel
from dlubal.api.common.packing import pack_message_to_any, pack_object, pack_object_list, unpack_object, unpack_object_list, convert_table_data_to_table
from dlubal.api.common.table_data_pb2 import TableData
from dlubal.api.common.model_id_pb2 import ModelId
from dlubal.api.common.common_messages_pb2 import ConvertObjectsRequest, ImportRequest, ExportRequest, GetObjectListRequest
from dlubal.api.rfem.results import ResultsType
from dlubal.api.rfem.results.results_query_pb2 import ResultsQuery, ResultsFilter
import grpc
from collections.abc import Iterable
import sys
from importlib.metadata import version, PackageNotFoundError
import collections.abc
import dlubal.api.common.common_messages_pb2
import dlubal.api.common.model_id_pb2
import dlubal.api.rfem.application_pb2
import dlubal.api.rfem.application_pb2_grpc
import dlubal.api.rfem.base_data_pb2
import dlubal.api.rfem.design_addons_pb2
import dlubal.api.rfem.mesh.mesh_settings_pb2
import dlubal.api.rfem.object_id_pb2
import dlubal.api.rfem.object_type_pb2
import google.protobuf.empty_pb2
import google.protobuf.wrappers_pb2


class Application:

    def __init__(self,
                 api_key_value=None,
                 api_key_name=None,
                 url='127.0.0.1',
                 port=9000,
                 ssl=False,
                 ssl_file=''):
        '''
        Initialize the RFEM client and connect to gRPC server.

        Args:
            api_key_value (str | None):
                The API key for authentication, obtained from the Extranet > My Data.
                Can be skipped if an API key is stored in ``config.ini`` file.
            api_key_name (str | None):
                The name of a stored API key in the ``config.ini`` file.
                Used to retrieve the corresponding ``api_key_value``.
                If not provided, the default API key will be used.
            url (str | 127.0.0.1):
                The gRPC server's URL. Use this to connect to a remote server by specifying its IP.
            port (int | 9000):
                The server's port number. Adjust if the server uses a different port.
            ssl (bool | False):
                Enables SSL encryption for secure communication.
            ssl_file (str | None):
                Path to the SSL certificate file (e.g., .crt).
                Used only if SSL encryption is enabled.
        '''
        self.url = url
        self.port = port
        check_ssl(ssl, ssl_file)
        self.ssl = ssl
        self.ssl_file = ssl_file
        self.api_key = _ApiKey(api_key_name, api_key_value)
        self.channel = init_channel(self.api_key.value, self.url, self.port, self.ssl, self.ssl_file)
        self.stub = dlubal.api.rfem.application_pb2_grpc.ApplicationStub(self.channel) if self.channel else None
        self.__check_connection()

    def __enter__(self):
        '''
        Start a session by entering the context manager
        '''
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        '''
        Ensure the session is finished and channel is closed properly before handling exceptions.
        '''

        # Attempt to close the connection first to prevent delays
        try:
            self.close_connection()
        except Exception as e:
            pass

        # Now handle exceptions that occurred in the `with` block
        if exc_type is not None:
            if issubclass(exc_type, grpc.RpcError):
                raise RuntimeError(f"gRPC Error: {exc_value.code().name} - {exc_value.details()}") from None
            elif issubclass(exc_type, (SystemExit, KeyboardInterrupt)):
                return False
            raise RuntimeError(f"Unexpected error: {str(exc_value)}") from None

    def __check_connection(self):
        '''
        Check connection and package version compatibility.
        '''

        # Check connection
        try:
            application_info = self.get_application_info()
        except grpc._channel._InactiveRpcError as e:
            sys.tracebacklimit = 0
            raise RuntimeError(f"gRPC Connection error: {e.code().name} - {e.details()}") from None

        # Check package version compatibility
        try:
            client_version = version('dlubal.api')
            client_major, client_minor = map(int, client_version.split('.')[1:3])
            app_major, app_minor = map(int, application_info.version.split('.')[1:3])

            # If the versions differ, print a warning
            if app_major != client_major or app_minor != client_minor:
                print('\033[93mWARNING:\033[0m')
                print(f'Version mismatch between dlubal.api client package \033[91m{client_version}\033[0m and server application {application_info.name}.')
                print(f'To ensure full compatibility, please use the client version 2.{app_major}.{app_minor}.')
                print(f'To update, run: \033[92mpython.exe -m pip install --upgrade dlubal.api==2.{app_major}.{app_minor}\033[0m\n')

        except PackageNotFoundError:
            return

    # Functions using packing
    def get_object(self, obj, model_id: ModelId | None = None):
        """
        Retrieves a single object from the model.

        Args:
            obj (obj):
                An object to be retrieved.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            :ref:`rfem_objects`: A retrieved object.
        """
        result = self.stub.get_object_impl(pack_object(obj, model_id))
        return unpack_object(result, type(obj))

    def get_object_list(self, objs: list, only_selected: bool = False, model_id: ModelId | None = None):
        """
        Retrieves a list of objects from the model.

        Args:
            objs (list[obj]):
                A list of model objects to retrieve.
            only_selected (bool, optional):
                If True, only returns objects from the list that are currently selected in the model.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            list[obj]: A list of the retrieved objects.
        """
        result = self.stub.get_object_list_impl(
            GetObjectListRequest(
                objects=pack_object_list(objs, model_id),
                return_only_selected_objects=only_selected
            )
        )
        return unpack_object_list(result, objs)

    def create_object(self, obj, model_id: ModelId | None = None):
        """
        Creates a single object in the model.

        Args:
            obj (obj):
                An object to be created.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        return self.stub.create_object_impl(pack_object(obj, model_id))

    def create_object_list(self, objs: list, model_id: ModelId | None = None):
        """
        Creates a list of objects in the model.

        Args:
            objs (list[obj]):
                A list of objects to be created.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        return self.stub.create_object_list_impl(pack_object_list(objs, model_id))

    def update_object(self, obj, model_id: ModelId | None = None):
        """
        Updates a single object in the model.

        Args:
            obj (obj):
                An object to be updated.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        self.stub.update_object_impl(pack_object(obj, model_id))

    def update_object_list(self, objs: list, model_id: ModelId | None = None):
        """
        Updates a list of objects in the model.

        Args:
            objs (list[obj]):
                A list of objects to be updated.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        self.stub.update_object_list_impl(pack_object_list(objs, model_id))

    def delete_object(self, obj, model_id: ModelId | None = None):
        """
        Deletes a single object from the model.

        Args:
            obj (obj):
                An object to be deleted.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        self.stub.delete_object_impl(pack_object(obj, model_id))

    def delete_object_list(self, objs: list, model_id: ModelId | None = None):
        """
        Deletes a list of objects from the model.

        Args:
            objs (list[obj]):
                A list of objects to be deleted.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        self.stub.delete_object_list_impl(pack_object_list(objs, model_id))

    def select_objects(self, objs: list, model_id: ModelId | None = None):
        """
        Selects a list of objects in the model.

        Args:
            objs (list[obj]):
                A list of objects to be selected.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        self.stub.select_objects_impl(pack_object_list(objs, model_id))

    def get_results(self, results_type: ResultsType, filters: Iterable[ResultsFilter] | None = None,
                    member_axes_system : MemberAxesSystem | None = None,
                    support_coordinate_system : CoordinateSystem | None = None,
                    model_id: ModelId | None = None, **keyword_filters):
        '''
        Retrieves complete, unprocessed results from the database wrapped in a DataFrame for more efficient analysis.

        Args:
            results_type (:ref:`rfem_results_results_type_ResultsType`):
                Unique identifier for the result category type.
            filters (:ref:`rfem_results_results_query_ResultsFilter` | None):
                One or more filters to return only relevant results.
            member_axes_system (:ref:`rfem_results_settings_result_settings_MemberAxesSystem` | None):
                The axes system to use for member results. If not provided, it defaults to ``MEMBER_AXES_SYSTEM_MEMBER_AXES_X_Y_Z``.
            support_coordinate_system (:ref:`rfem_results_settings_result_settings_CoordinateSystem` | None):
                The coordinate system to use for support results. If not provided, it defaults to ``COORDINATE_SYSTEM_LOCAL``.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            A :ref:`common_table_Table` object containing the results as a DataFrame.
        '''

        all_filters = list(filters) if filters else []

        for key, value in keyword_filters.items():
            all_filters.append(ResultsFilter(column_id=key, filter_expression=str(value)))

        results, call = self.stub.get_results_impl.with_call(
            ResultsQuery(results_type=results_type,
                         filters=all_filters,
                         member_axes_system=member_axes_system,
                         support_coordinate_system=support_coordinate_system,
                         model_id=model_id))

        warning: str = ""
        # trailing_metadata() returns a sequence of (key, value) tuples
        # Some gRPC implementations return bytes for values, decode if possible
        if call is not None:
            for key, value in call.trailing_metadata():
                if key == "warning":
                    try:
                        warning = value.decode("utf-8")
                    except Exception:
                        warning = value

        return convert_table_data_to_table(table_data=results, warning=warning)

    def get_result_table(self, table: ResultTable, loading: ObjectId,
                         member_axes_system : MemberAxesSystem | None = None,
                         support_coordinate_system : CoordinateSystem | None = None,
                         model_id: ModelId | None = None):
        """
        Retrieves a pre-processed result table wrapped in a DataFrame for more efficient analysis,
        containing only the most relevant values as displayed in the desktop application.

        Args:
            table (:ref:`rfem_results_result_table_ResultTable`):
                Unique identifier for the result table type.
            loading (:ref:`rfem_object_id_ObjectId`):
                Reference to the loading (e.g., load case, combination, etc.).
            member_axes_system (:ref:`rfem_results_settings_result_settings_MemberAxesSystem` | None):
                The axes system to use for member results. If not provided, it defaults to ``MEMBER_AXES_SYSTEM_MEMBER_AXES_X_Y_Z``.
            support_coordinate_system (:ref:`rfem_results_settings_result_settings_CoordinateSystem` | None):
                The coordinate system to use for support results. If not provided, it defaults to ``COORDINATE_SYSTEM_LOCAL``.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            A :ref:`common_table_Table` object containing the result table as a DataFrame.
        """
        # Use with_call to access trailing metadata (warnings) from the gRPC call
        results, call = self.stub.get_result_table_impl.with_call(
            dlubal.api.rfem.GetResultTableRequest(
                table=table,
                loading=loading,
                model_id=model_id,
                member_axes_system=member_axes_system,
                support_coordinate_system=support_coordinate_system)
        )

        warning: str = ""
        # trailing_metadata() returns a sequence of (key, value) tuples
        # Some gRPC implementations return bytes for values, decode if possible
        if call is not None:
            for key, value in call.trailing_metadata():
                if key == "warning":
                    try:
                        warning = value.decode("utf-8")
                    except Exception:
                        warning = value

        return convert_table_data_to_table(table_data=results, warning=warning)

    def convert_objects(self, *, convert_into: ConvertObjectInto, objs: list, model_id: ModelId | None = None):
        """
        Converts specified objects into a different type.

        Args:
            convert_into (:ref:`common_common_messages_ConvertObjectInto`):
                Specifies the target conversion type.
            objs (list):
                Objects to be converted.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        request = ConvertObjectsRequest(convert_into=convert_into, objects=pack_object_list(objs, model_id))
        self.stub.convert_objects_impl(request)

    def import_from(self, *, filepath: str, import_attributes: google.protobuf.any_pb2.Any, model_id: ModelId | None = None):
        """
        Imports data into the model from the specified file format.

        Args:
            filepath (str):
                The full path of the file to be imported.
            import_attributes (Any):
                Specifies the format and attributes of the import.
                Supported message types:
                :ref:`common_import_export_import_attributes_IfcImportAttributes`
                :ref:`common_import_export_import_attributes_XmlImportAttributes`
                :ref:`common_import_export_import_attributes_RsectionImportAttributes`
            model_id (:ref:`common_model_id_ModelId` | None):
                The unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        request = ImportRequest(filepath=filepath, import_attributes=pack_message_to_any(import_attributes), model_id=model_id)
        self.stub.import_from_impl(request)

    def export_to(self, *, filepath: str, export_attributes: google.protobuf.any_pb2.Any, model_id: ModelId | None = None):
        """
        Exports the model to the specified file format.

        Args:
            filepath (str):
                The full path of the file where the model will be exported.
            export_attributes (Any):
                Specifies the format and attributes for the export.
                Supported message types:
                :ref:`common_import_export_export_attributes_PythonGrpcExportAttributes`
                :ref:`common_import_export_export_attributes_IfcExportAttributes`
            model_id (:ref:`common_model_id_ModelId` | None):
                The unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        request = ExportRequest(filepath=filepath, export_attributes=pack_message_to_any(export_attributes), model_id=model_id)
        self.stub.export_to_impl(request)

    def move_objects(self, objs: list,
                          direction_through: DirectionThrough,
                          coordinate_system: int = 1,
                          displacement_vector: common.Vector3d | None = None,
                          axis: CoordinateAxis | None = None,
                          create_copy: bool = False,
                          number_of_steps: int | None = None,
                          spacing: float | None = None,
                          copy_including_loading: bool = False,
                          copy_including_imperfections: bool = False,
                          connect_lines_members_surfaces: bool = False,
                          model_id: ModelId | None = None):
        """
        Moves or copies the specified objects in a defined direction.

        Args:
            objs (list):
                The list of specified model objects.
            direction_through (:ref:`rfem_manipulation_manipulation_DirectionThrough`):
                Specifies the type of direction through which the objects will be moved.
            coordinate_system (int | 1):
                Specifies the coordination system to use.
            displacement_vector (:ref:`common_common_Vector3d` | None):
                Specifies the displacement vector for vector-based movement.
            axis (:ref:`rfem_manipulation_manipulation_CoordinateAxis` | None):
                Specifies the axis for parallel movement.
            create_copy (bool | False):
                Specifies whether copies of the specified objects should be created.
            number_of_steps (int | None):
                Specifies the number of copies to create.
            spacing (double | None):
                Specifies the spacing between copies for parallel movement.
            copy_including_loading (bool | False):
                Specifies whether to include the loading when copying the objects.
            copy_including_imperfections (bool | False):
                Specifies whether to include imperfections when copying the objects.
            connect_lines_members_surfaces (bool | False):
                Specifies whether to connect lines, members, or surfaces when they overlap in the copy.
            model_id (:ref:`common_model_id_ModelId` | None):
                The unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        # Build request
        request = dlubal.api.rfem.MoveObjectsRequest()

        # objects field is a common.ObjectList; use pack_object_list to build it
        request.objects.CopyFrom(pack_object_list(objs, model_id))
        request.direction_through = direction_through

        if direction_through == dlubal.api.rfem.manipulation.DIRECTION_THROUGH_DISPLACEMENT_VECTOR:
            if displacement_vector is None:
                raise ValueError("Missing required parameter `displacement_vector` for `direction_through` set to `DIRECTION_THROUGH_DISPLACEMENT_VECTOR`")
        if direction_through == dlubal.api.rfem.manipulation.DIRECTION_THROUGH_PARALLEL_TO_AXIS:
            if spacing is None:
                raise ValueError("Missing required parameter `spacing` for `direction_through` set to `DIRECTION_THROUGH_PARALLEL_TO_AXIS`")
            if axis is None:
                raise ValueError("Missing required parameter `axis` for `direction_through` set to `DIRECTION_THROUGH_PARALLEL_TO_AXIS`")

        # Optional fields: set only when provided to avoid overriding defaults
        if displacement_vector is not None:
            # displacement_vector is a message, copy it
            request.displacement_vector.CopyFrom(displacement_vector)
        if axis is not None:
            request.axis = axis
        if coordinate_system != 1:
            request.coordinate_system = coordinate_system
        if spacing is not None:
            request.spacing = spacing
        if create_copy:
            request.create_copy = create_copy
            if number_of_steps is not None:
                request.number_of_steps = number_of_steps
            else:
                raise ValueError("Missing required parameter `number_of_steps` for `create_copy` set to `True`")
            if copy_including_loading:
                request.copy_including_loading = copy_including_loading
            if copy_including_imperfections:
                request.copy_including_imperfections = copy_including_imperfections
        if connect_lines_members_surfaces:
            request.connect_lines_members_surfaces = connect_lines_members_surfaces

        # Call RPC
        self.stub.move_objects_impl(request)

    def rotate_objects(self, objs: list,
                         rotation_angle: float,
                         rotation_axis: RotationAxisSpecificationType,
                         coordinate_system: int = 1,
                         point_1: common.Vector3d | None = None,
                         point_2: common.Vector3d | None = None,
                         axis: CoordinateAxis | None = None,
                         create_copy: bool = False,
                         number_of_steps: int | None = None,
                         copy_including_loading: bool = False,
                         copy_including_imperfections: bool = False,
                         connect_lines_members_surfaces: bool = False,
                         rotate_local_coordinate_systems_of_lines_members: bool = False,
                         adjust_loading_nodal_loads: bool = False,
                         model_id: ModelId | None = None):
        """
        Rotate or copies the specified objects about defined axis.

        Args:
            objs (list):
                The list of specified model objects.
            rotation_angle (float):
                Specifies the angle for rotation.
            rotation_axis (:ref:`rfem_manipulation_manipulation_RotationAxisSpecificationType`):
                Specifies the type of rotation axis type for the objects rotation.
            coordinate_system (int | 1):
                Specifies the coordination system to use.
            point_1 (:ref:`common_common_Vector3d` | None):
                Specifies the point 1 for vector-based rotation.
            point_2 (:ref:`common_common_Vector3d` | None):
                Specifies the point 2 for vector-based rotation.
            axis (:ref:`rfem_manipulation_manipulation_CoordinateAxis` | None):
                Specifies the axis for parallel rotation.
            create_copy (bool | False):
                Specifies whether copies of the specified objects should be created.
            number_of_steps (int | None):
                Specifies the number of copies to create.
            copy_including_loading (bool | False):
                Specifies whether to include the loading when copying the objects.
            copy_including_imperfections (bool | False):
                Specifies whether to include imperfections when copying the objects.
            connect_lines_members_surfaces (bool | False):
                Specifies whether to connect lines, members, or surfaces when they overlap in the copy.
            rotate_local_coordinate_systems_of_lines_members (bool | False):
                Specifies whether the objects' local coordinate systems (LCS) should be rotated together with the lines, members.
            adjust_loading_nodal_loads (bool | False):
                Specifies whether to also update nodal loads so their force and moment vectors remain correct in world coordinates after the transform.
            model_id (:ref:`common_model_id_ModelId` | None):
                The unique identifier of the model. If None, the active model is used.

        Returns:
            None
        """
        # Build request
        request = dlubal.api.rfem.RotateObjectsRequest()

        # objects field is a common.ObjectList; use pack_object_list to build it
        request.objects.CopyFrom(pack_object_list(objs, model_id))
        request.rotation_angle = rotation_angle
        request.rotation_axis = rotation_axis

        if rotation_axis == dlubal.api.rfem.manipulation.ROTATION_AXIS_SPECIFICATION_TYPE_TWO_POINTS:
            if point_1 is None:
                raise ValueError("Missing required parameter `point_1` for `rotation_axis` set to `ROTATION_AXIS_SPECIFICATION_TYPE_TWO_POINTS`")
            if point_2 is None:
                raise ValueError("Missing required parameter `point_2` for `rotation_axis` set to `ROTATION_AXIS_SPECIFICATION_TYPE_TWO_POINTS`")
        elif rotation_axis == dlubal.api.rfem.manipulation.ROTATION_AXIS_SPECIFICATION_TYPE_POINT_AND_PARALLEL_AXIS:
            if point_1 is None:
                raise ValueError("Missing required parameter `point_1` for `rotation_axis` set to `ROTATION_AXIS_SPECIFICATION_TYPE_POINT_AND_PARALLEL_AXIS`")
            if axis is None:
                raise ValueError("Missing required parameter `axis` for `rotation_axis` set to `ROTATION_AXIS_SPECIFICATION_TYPE_POINT_AND_PARALLEL_AXIS`")

        # Optional fields: set only when provided to avoid overriding defaults
        if point_1 is not None:
            # point_1 is a message, copy it
            request.point_1.CopyFrom(point_1)
        if point_2 is not None:
            # point_2 is a message, copy it
            request.point_2.CopyFrom(point_2)
        if axis is not None:
            request.axis = axis
        if coordinate_system != 1:
            request.coordinate_system = coordinate_system
        if create_copy:
            request.create_copy = create_copy
            if number_of_steps is not None:
                request.number_of_steps = number_of_steps
            else:
                raise ValueError("Missing required parameter `number_of_steps` for `create_copy` set to `True`")
            if copy_including_loading:
                request.copy_including_loading = copy_including_loading
            if copy_including_imperfections:
                request.copy_including_imperfections = copy_including_imperfections
        if connect_lines_members_surfaces:
            request.connect_lines_members_surfaces = connect_lines_members_surfaces
        if rotate_local_coordinate_systems_of_lines_members:
            request.rotate_local_coordinate_systems_of_lines_members = rotate_local_coordinate_systems_of_lines_members
        if adjust_loading_nodal_loads:
            request.adjust_loading_nodal_loads = adjust_loading_nodal_loads

        # Call RPC
        self.stub.rotate_objects_impl(request)



    def create_model(self, *, name: str, template_path: str | None = None) -> dlubal.api.common.model_id_pb2.ModelId:
        """
        Creates new model with the name specified in request.
        Returns model id of created model

        Args:
            name (str):
                Name of the new model
            template_path (str | None):
                Path to the existing template to be opened

        Returns:
            :ref:`common_model_id_ModelId`
        """
        request = dlubal.api.common.common_messages_pb2.CreateModelRequest(name=name, template_path=template_path)
        return self.stub.create_model(request)

    def open_model(self, *, path: str) -> dlubal.api.common.model_id_pb2.ModelId:
        """
        Opens model from path specified in 'name' field of the request
        Returns model id of opened model

        Args:
            path (str):
                Path to the existing model to be opened

        Returns:
            :ref:`common_model_id_ModelId`
        """
        request = dlubal.api.common.common_messages_pb2.OpenModelRequest(path=path)
        return self.stub.open_model(request)

    def save_model(self, *, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None, path: str | None = None, results: bool | None = None, fe_mesh: bool | None = None, printout_reports: bool | None = None):
        """
        Saves model specified by model id to optional specified path

        Args:
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model Active model is used if this field is not set.
            path (str | None):
                Path to the model
            results (bool | None):
                When enabled, the model is saved together with the calculated results
            fe_mesh (bool | None):
                When enabled, the model is saved together with the FE Mesh
            printout_reports (bool | None):
                When enabled, the printout reports are saved
        """
        request = dlubal.api.rfem.application_pb2.SaveModelAsRequest(model_id=model_id, path=path, results=results, fe_mesh=fe_mesh, printout_reports=printout_reports)
        self.stub.save_model(request)

    def close_model(self, *, save_changes: bool, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None):
        """
        Closes model specified by model id

        Args:
            save_changes (bool):
                Specifies whether to save changes before closing the model
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model Active model is used if this field is not set.
        """
        request = dlubal.api.common.common_messages_pb2.CloseModelRequest(save_changes=save_changes, model_id=model_id)
        self.stub.close_model(request)

    def close_all_models(self, *, save_changes: bool):
        """
        Closes all open models

        Args:
            save_changes (bool):
                Specifies whether to save changes before closing the models
        """
        request = dlubal.api.common.common_messages_pb2.CloseAllModelsRequest(save_changes=save_changes)
        self.stub.close_all_models(request)

    def get_active_model(self) -> dlubal.api.common.model_id_pb2.ModelId:
        """
        Returns model id of an active model

        Returns:
            :ref:`common_model_id_ModelId`
        """
        return self.stub.get_active_model(google.protobuf.empty_pb2.Empty())

    def set_active_model(self, *, model_id: dlubal.api.common.model_id_pb2.ModelId):
        """
        Sets active model specified by model id

        Args:
            model_id (:ref:`common_model_id_ModelId`):
                
        """
        self.stub.set_active_model(model_id)

    def close_application(self):
        """
        Closes the whole application
        """
        self.stub.close_application(google.protobuf.empty_pb2.Empty())

    def get_model_list(self) -> dlubal.api.common.common_messages_pb2.ModelList:
        """
        Returns list of models and information about them

        Returns:
            :ref:`common_common_messages_ModelList`
        """
        return self.stub.get_model_list(google.protobuf.empty_pb2.Empty())

    def get_application_info(self) -> dlubal.api.common.common_messages_pb2.ApplicationInfo:
        """
        Returns information about the application

        Returns:
            :ref:`common_common_messages_ApplicationInfo`
        """
        return self.stub.get_application_info(google.protobuf.empty_pb2.Empty())

    def get_subscription_info(self) -> dlubal.api.common.common_messages_pb2.SubscriptionInfo:
        """
        Returns current company's API subscription information

        Returns:
            :ref:`common_common_messages_SubscriptionInfo`
        """
        return self.stub.get_subscription_info(google.protobuf.empty_pb2.Empty())

    def close_connection(self):
        """
        Closes connection to API server
        """
        self.stub.close_connection(google.protobuf.empty_pb2.Empty())

    def delete_all_objects(self, *, optional_model_id: dlubal.api.common.model_id_pb2.OptionalModelId = dlubal.api.common.model_id_pb2.OptionalModelId()):
        """
        Deletes all objects

        Args:
            optional_model_id (:ref:`common_model_id_OptionalModelId`):
                
        """
        self.stub.delete_all_objects(optional_model_id)

    def calculate_all(self, *, skip_warnings: bool, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None) -> dlubal.api.common.common_messages_pb2.OperationResult:
        """
        Performs a full calculation for all objects.
        Returns result of the operation, description of the result and possibly additional information about an error.

        Args:
            skip_warnings (bool):
                Specifies whether to skip warnings during the calculation.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. Active model is used if this field is not set.

        Returns:
            :ref:`common_common_messages_OperationResult`
        """
        request = dlubal.api.common.common_messages_pb2.CalculateAllRequest(skip_warnings=skip_warnings, model_id=model_id)
        return self.stub.calculate_all(request)

    def calculate_specific(self, *, loadings: collections.abc.Iterable[dlubal.api.rfem.object_id_pb2.ObjectId] | None = None, skip_warnings: bool, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None) -> dlubal.api.common.common_messages_pb2.OperationResult:
        """
        Performs a calculation for the specific objects.
        Returns result of the operation, description of the result and possibly additional information about an error.

        Args:
            loadings (list[:ref:`rfem_object_id_ObjectId`] | None):
                Unique identifier of the loading.
            skip_warnings (bool):
                Specifies whether to skip warnings during the calculation.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. Active model is used if this field is not set.

        Returns:
            :ref:`common_common_messages_OperationResult`
        """
        request = dlubal.api.rfem.application_pb2.CalculateSpecificRequest(loadings=loadings, skip_warnings=skip_warnings, model_id=model_id)
        return self.stub.calculate_specific(request)

    def has_results(self, *, loading: dlubal.api.rfem.object_id_pb2.ObjectId | None = None, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None) -> google.protobuf.wrappers_pb2.BoolValue:
        """
        Checks whether the model contains calculation results for the selected loading, loading type, or any loading.
        Returns True if results are available.

        Args:
            loading (:ref:`rfem_object_id_ObjectId` | None):
                Allowed types: OBJECT_TYPE_CONSTRUCTION_STAGE, OBJECT_TYPE_DESIGN_SITUATION, OBJECT_TYPE_LOAD_CASE, OBJECT_TYPE_LOAD_COMBINATION, OBJECT_TYPE_RESULT_COMBINATION
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. Active model is used if this field is not set.

        Returns:
            google.protobuf.BoolValue
        """
        request = dlubal.api.rfem.application_pb2.HasResultsRequest(loading=loading, model_id=model_id)
        return self.stub.has_results(request)

    def plausibility_check(self, *, type: dlubal.api.common.common_messages_pb2.PlausibilityCheckType, skip_warnings: bool, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None) -> dlubal.api.common.common_messages_pb2.OperationResult:
        """
        Performs validation based on the specified validation type.
        Returns result of the operation, description of the result and possibly additional information about an error.

        Args:
            type (:ref:`common_common_messages_PlausibilityCheckType`):
                Specifies various validation checks that can be performed before generating a mesh, running a calculation, or verifying plausibility.
            skip_warnings (bool):
                Specifies whether to skip warnings during the calculation.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. Active model is used if this field is not set.

        Returns:
            :ref:`common_common_messages_OperationResult`
        """
        request = dlubal.api.common.common_messages_pb2.PlausibilityCheckRequest(type=type, skip_warnings=skip_warnings, model_id=model_id)
        return self.stub.plausibility_check(request)

    def get_object_id_list(self, *, object_type: dlubal.api.rfem.object_type_pb2.ObjectType | None = None, parent_no: int | None = None, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None) -> dlubal.api.rfem.object_id_pb2.ObjectIdList:
        """
        Retrieves object ids filtered by a specific type.
        If you want to retrieve all object ids, do not specify the object_type.

        Args:
            object_type (:ref:`rfem_object_type_ObjectType` | None):
                The type of objects to retrieve. To get all object ids, omit it.
            parent_no (int | None):
                Unique identifier of the parent object. Omit this parameter if the object type does not have a parent or you want to get result for all parent objects.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If not specified, the active model is used.

        Returns:
            :ref:`rfem_object_id_ObjectIdList`
        """
        request = dlubal.api.rfem.application_pb2.GetObjectIdListRequest(object_type=object_type, parent_no=parent_no, model_id=model_id)
        return self.stub.get_object_id_list(request)

    def get_model_main_parameters(self, *, optional_model_id: dlubal.api.common.model_id_pb2.OptionalModelId = dlubal.api.common.model_id_pb2.OptionalModelId()) -> dlubal.api.common.common_messages_pb2.ModelMainParameters:
        """
        Retrieves the main parameters of a model, including its ID, name, description,
        comments, file path, and associated project details.

        Args:
            optional_model_id (:ref:`common_model_id_OptionalModelId`):
                

        Returns:
            :ref:`common_common_messages_ModelMainParameters`
        """
        return self.stub.get_model_main_parameters(optional_model_id)

    def generate_combinations(self, *, optional_model_id: dlubal.api.common.model_id_pb2.OptionalModelId = dlubal.api.common.model_id_pb2.OptionalModelId()):
        """
        Generates combinations.

        Args:
            optional_model_id (:ref:`common_model_id_OptionalModelId`):
                
        """
        self.stub.generate_combinations(optional_model_id)

    def get_base_data(self, *, optional_model_id: dlubal.api.common.model_id_pb2.OptionalModelId = dlubal.api.common.model_id_pb2.OptionalModelId()) -> dlubal.api.rfem.base_data_pb2.BaseData:
        """
        Get the base data.
        This method returns the complete set of data stored for a particular model,
        including add-ons, standards, general and combinations settings, etc.

        Args:
            optional_model_id (:ref:`common_model_id_OptionalModelId`):
                

        Returns:
            :ref:`rfem_base_data_BaseData`
        """
        return self.stub.get_base_data(optional_model_id)

    def set_base_data(self, *, base_data: dlubal.api.rfem.base_data_pb2.BaseData | None = None, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None):
        """
        Set the base data.
        This method updates the complete set of data for a particular model,
        including add-ons, standards, general and combinations settings, and other parameters.

        Args:
            base_data (:ref:`rfem_base_data_BaseData` | None):
                
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. If not specified, the active model is used.
        """
        request = dlubal.api.rfem.application_pb2.BaseDataRequest(base_data=base_data, model_id=model_id)
        self.stub.set_base_data(request)

    def get_mesh_settings(self, *, optional_model_id: dlubal.api.common.model_id_pb2.OptionalModelId = dlubal.api.common.model_id_pb2.OptionalModelId()) -> dlubal.api.rfem.mesh.mesh_settings_pb2.MeshSettings:
        """
        Returns mesh settings.

        Args:
            optional_model_id (:ref:`common_model_id_OptionalModelId`):
                

        Returns:
            :ref:`rfem_mesh_mesh_settings_MeshSettings`
        """
        return self.stub.get_mesh_settings(optional_model_id)

    def set_mesh_settings(self, *, mesh_settings: dlubal.api.rfem.mesh.mesh_settings_pb2.MeshSettings | None = None, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None):
        """
        Sets mesh settings.

        Args:
            mesh_settings (:ref:`rfem_mesh_mesh_settings_MeshSettings` | None):
                
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. Active model is used if this field is not set.
        """
        request = dlubal.api.rfem.application_pb2.SetMeshSettingsRequest(mesh_settings=mesh_settings, model_id=model_id)
        self.stub.set_mesh_settings(request)

    def generate_mesh(self, *, skip_warnings: bool, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None) -> dlubal.api.common.common_messages_pb2.OperationResult:
        """
        Generates mesh.
        Returns result of the operation.

        Args:
            skip_warnings (bool):
                Specifies whether to skip warnings during the generation mesh.
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. Active model is used if this field is not set.

        Returns:
            :ref:`common_common_messages_OperationResult`
        """
        request = dlubal.api.rfem.application_pb2.GenerateMeshRequest(skip_warnings=skip_warnings, model_id=model_id)
        return self.stub.generate_mesh(request)

    def get_mesh_statistics(self, *, optional_model_id: dlubal.api.common.model_id_pb2.OptionalModelId = dlubal.api.common.model_id_pb2.OptionalModelId()) -> dlubal.api.rfem.mesh.mesh_settings_pb2.MeshStatistics:
        """
        Returns mesh statistics.

        Args:
            optional_model_id (:ref:`common_model_id_OptionalModelId`):
                

        Returns:
            :ref:`rfem_mesh_mesh_settings_MeshStatistics`
        """
        return self.stub.get_mesh_statistics(optional_model_id)

    def delete_mesh(self, *, optional_model_id: dlubal.api.common.model_id_pb2.OptionalModelId = dlubal.api.common.model_id_pb2.OptionalModelId()):
        """
        Delete mesh

        Args:
            optional_model_id (:ref:`common_model_id_OptionalModelId`):
                
        """
        self.stub.delete_mesh(optional_model_id)

    def get_design_settings(self, *, addon: dlubal.api.rfem.design_addons_pb2.DesignAddons, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None) -> dlubal.api.rfem.application_pb2.GlobalSettingsTreeTable:
        """
        Get global settings for a design addon.

        Args:
            addon (:ref:`rfem_design_addons_DesignAddons`):
                
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. Active model is used if this field is not set.

        Returns:
            :ref:`rfem_application_GlobalSettingsTreeTable`
        """
        request = dlubal.api.rfem.application_pb2.GetDesignSettingsRequest(addon=addon, model_id=model_id)
        return self.stub.get_design_settings(request)

    def set_design_settings(self, *, addon: dlubal.api.rfem.design_addons_pb2.DesignAddons, global_settings_tree_table: dlubal.api.rfem.application_pb2.GlobalSettingsTreeTable | None = None, model_id: dlubal.api.common.model_id_pb2.ModelId | None = None):
        """
        Set global settings for a design addon.

        Args:
            addon (:ref:`rfem_design_addons_DesignAddons`):
                
            global_settings_tree_table (:ref:`rfem_application_GlobalSettingsTreeTable` | None):
                
            model_id (:ref:`common_model_id_ModelId` | None):
                Unique identifier of the model. Active model is used if this field is not set.
        """
        request = dlubal.api.rfem.application_pb2.SetDesignSettingsRequest(addon=addon, global_settings_tree_table=global_settings_tree_table, model_id=model_id)
        self.stub.set_design_settings(request)
