# Generated by the protocol buffer compiler.  DO NOT EDIT!
# sources: blickfeld/percept_toolkit/services/geometry.proto, blickfeld/percept_toolkit/services/health.proto
# plugin: python-betterproto
import asyncio
from dataclasses import dataclass
from typing import (
    TYPE_CHECKING,
    AsyncIterable,
    AsyncIterator,
    Dict,
    Iterable,
    Iterator,
    List,
    Optional,
    Union,
)

import blickfeld_qb2.betterproto as betterproto
import blickfeld_qb2.betterproto.lib.google.protobuf as betterproto_lib_google_protobuf
import grpclib
from blickfeld_qb2.betterproto.grpc.grpclib_server import ServiceBase

from ...base import geometry as __base_geometry__
from ...core_processing import data as __core_processing_data__
from .. import data as _data__


if TYPE_CHECKING:
    import grpclib.server
    from blickfeld_qb2.betterproto.grpc.grpclib_client import MetadataLike
    from grpclib.metadata import Deadline


@dataclass(eq=False, repr=False)
class GeometryComputeGroundAlignmentRequest(betterproto.Message):
    """
    Request for estimating alignment to ground/gravity based on imu-data and
    optional point cloud frame.
    """

    accelerometer_data: "__core_processing_data__.Acceleration" = (
        betterproto.message_field(1)
    )
    """
    accelerometer data is assumed to be expressed in the lidar coordinate frame
    """

    lidar_frame: "__core_processing_data__.Frame" = betterproto.message_field(2)
    """
    if a lidar frame is provided, it is used to estimate the z-coordinate of
    the translation as well.
    """


@dataclass(eq=False, repr=False)
class GeometryComputeGroundAlignmentResponse(betterproto.Message):
    """The estimated alignment to the ground/gravity."""

    alignment: "__base_geometry__.Transform" = betterproto.message_field(1)
    """
    Estimated alignment of the sensor to the ground-plane/gravity. If no lidar
    frame is provided only roll, pitch of the rotation are estimated. Otherwise
    the z-component of the translation is additionally aligned with a flat
    ground. The transformation transforms the lidar point clouds such that the
    points are aligned with the ground.
    """


@dataclass(eq=False, repr=False)
class GeometryComputeRegistrationRefinementRequest(betterproto.Message):
    """
    The request for transformation refinement based on 3d point clouds and an
    initial point cloud transformations.
    """

    frame: "__core_processing_data__.Frame" = betterproto.message_field(1)
    """lidar frame used for aligning the 3d point clouds"""

    transform: "__base_geometry__.Transform" = betterproto.message_field(2)
    """
    Transform: the transformation transforming a point in from the local lidar
    coordinate system into a 'combined' reference system the 'combined'
    reference system can also be a lidar.
    """


@dataclass(eq=False, repr=False)
class GeometryComputeRegistrationRefinementResponse(betterproto.Message):
    """The refined lidar transformations."""

    transformations: List["__base_geometry__.Transform"] = betterproto.message_field(1)
    """
    Refined transformations. The array sequence is equal to the sequence in
    which the requests were received. The first element in the array
    corresponds to the lidar frame received in the first client stream message.
    """


@dataclass(eq=False, repr=False)
class HealthWatchResponse(betterproto.Message):
    """Health Stream response of the toolkit module"""

    health: "_data__.Health" = betterproto.message_field(1)
    """The current health state of the module"""


@dataclass(eq=False, repr=False)
class HealthGetResponse(betterproto.Message):
    """Health Get response of the toolkit module"""

    health: "_data__.Health" = betterproto.message_field(1)
    """The current health state of the module"""


class Geometry(betterproto.ServiceStub):
    """Service offering on-demand geometric processing tasks."""

    async def async_compute_ground_alignment(
        self,
        *,
        accelerometer_data: "__core_processing_data__.Acceleration" = None,
        lidar_frame: "__core_processing_data__.Frame" = None,
        timeout: Optional[float] = None,
        deadline: Optional["Deadline"] = None,
        metadata: Optional["MetadataLike"] = None
    ) -> "GeometryComputeGroundAlignmentResponse":
        """
        Computes the transformation aligning the lidar point cloud to the
        ground plane/gravity.
        """

        request = GeometryComputeGroundAlignmentRequest()
        if accelerometer_data is not None:
            request.accelerometer_data = accelerometer_data
        if lidar_frame is not None:
            request.lidar_frame = lidar_frame

        return await self._unary_unary(
            "/blickfeld.percept_toolkit.services.Geometry/ComputeGroundAlignment",
            request,
            GeometryComputeGroundAlignmentResponse,
            timeout=timeout,
            deadline=deadline,
            metadata=metadata,
        )

    def compute_ground_alignment(
        self,
        *,
        accelerometer_data: "__core_processing_data__.Acceleration" = None,
        lidar_frame: "__core_processing_data__.Frame" = None,
        timeout: Optional[float] = None,
        deadline: Optional["Deadline"] = None,
        metadata: Optional["MetadataLike"] = None
    ) -> "GeometryComputeGroundAlignmentResponse":
        """
        Computes the transformation aligning the lidar point cloud to the
        ground plane/gravity.
        """

        loop = asyncio.get_event_loop()
        return loop.run_until_complete(
            self.async_compute_ground_alignment(
                accelerometer_data=accelerometer_data,
                lidar_frame=lidar_frame,
                timeout=timeout,
                deadline=deadline,
                metadata=metadata,
            )
        )

    async def async_compute_registration_refinement(
        self,
        request_iterator: Union[
            AsyncIterable["GeometryComputeRegistrationRefinementRequest"],
            Iterable["GeometryComputeRegistrationRefinementRequest"],
        ],
        timeout: Optional[float] = None,
        deadline: Optional["Deadline"] = None,
        metadata: Optional["MetadataLike"] = None,
    ) -> "GeometryComputeRegistrationRefinementResponse":
        """
        Refines the point cloud registration based on provided initial
        transformations. If the method succeeds, the refined transformation is
        returned. Otherwise an exception will be thrown.
        """

        return await self._stream_unary(
            "/blickfeld.percept_toolkit.services.Geometry/ComputeRegistrationRefinement",
            request_iterator,
            GeometryComputeRegistrationRefinementRequest,
            GeometryComputeRegistrationRefinementResponse,
            timeout=timeout,
            deadline=deadline,
            metadata=metadata,
        )

    def compute_registration_refinement(
        self,
        timeout: Optional[float] = None,
        deadline: Optional["Deadline"] = None,
        metadata: Optional["MetadataLike"] = None,
    ) -> "GeometryComputeRegistrationRefinementResponse":
        """
        Refines the point cloud registration based on provided initial
        transformations. If the method succeeds, the refined transformation is
        returned. Otherwise an exception will be thrown.
        """

        loop = asyncio.get_event_loop()
        return loop.run_until_complete(
            self.async_compute_registration_refinement(
                timeout=timeout,
                deadline=deadline,
                metadata=metadata,
            )
        )


class Health(betterproto.ServiceStub):
    """An gRPC service to request the health of toolkit"""

    async def async_watch(
        self,
        timeout: Optional[float] = None,
        deadline: Optional["Deadline"] = None,
        metadata: Optional["MetadataLike"] = None,
    ) -> AsyncIterator["HealthWatchResponse"]:
        """
        Streams health messages in regular intervals containing the state of
        toolkit
        """

        request = betterproto_lib_google_protobuf.Empty()

        async for response in self._unary_stream(
            "/blickfeld.percept_toolkit.services.Health/Watch",
            request,
            HealthWatchResponse,
            timeout=timeout,
            deadline=deadline,
            metadata=metadata,
        ):
            yield response

    def watch(
        self,
        timeout: Optional[float] = None,
        deadline: Optional["Deadline"] = None,
        metadata: Optional["MetadataLike"] = None,
    ) -> Iterator["HealthWatchResponse"]:
        """
        Streams health messages in regular intervals containing the state of
        toolkit
        """

        loop = asyncio.get_event_loop()
        ait = self.async_watch(
            timeout=timeout,
            deadline=deadline,
            metadata=metadata,
        ).__aiter__()

        async def get_next():
            try:
                obj = await ait.__anext__()
                return False, obj
            except StopAsyncIteration:
                return True, None

        while True:
            done, obj = loop.run_until_complete(get_next())
            if done:
                break
            yield obj

    async def async_get(
        self,
        timeout: Optional[float] = None,
        deadline: Optional["Deadline"] = None,
        metadata: Optional["MetadataLike"] = None,
    ) -> "HealthGetResponse":
        """Return health messages containing the state of toolkit"""

        request = betterproto_lib_google_protobuf.Empty()

        return await self._unary_unary(
            "/blickfeld.percept_toolkit.services.Health/Get",
            request,
            HealthGetResponse,
            timeout=timeout,
            deadline=deadline,
            metadata=metadata,
        )

    def get(
        self,
        timeout: Optional[float] = None,
        deadline: Optional["Deadline"] = None,
        metadata: Optional["MetadataLike"] = None,
    ) -> "HealthGetResponse":
        """Return health messages containing the state of toolkit"""

        loop = asyncio.get_event_loop()
        return loop.run_until_complete(
            self.async_get(
                timeout=timeout,
                deadline=deadline,
                metadata=metadata,
            )
        )
