def Shutdown_Info(self, request, context: grpc.ServicerContext) \
            -> silaFW_pb2.ExecutionInfo:
        """
        Returns execution information regarding the command call :meth:`~.Shutdown`.

        :param request: A request object with the following properties
            commandId: The UUID of the command executed.
        :param context: gRPC :class:`~grpc.ServicerContext` object providing gRPC-specific information

        :returns: An ExecutionInfo response stream for the command with the following fields:
            commandStatus: Status of the command (enumeration)
            progressInfo: Information on the progress of the command (0 to 1)
            estimatedRemainingTime: Estimate of the remaining time required to run the command
            updatedLifetimeOfExecution: An update on the execution lifetime
        """
        # Get the UUID of the command
        command_uuid = request.value

        if not command_uuid or command_uuid != self.command_uuid:
            raise SiLAFrameworkError(error_type=SiLAFrameworkErrorType.
                                     INVALID_COMMAND_EXECUTION_UUID)

        yield silaFW_pb2.ExecutionInfo(
            commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.running)
        yield silaFW_pb2.ExecutionInfo(commandStatus=silaFW_pb2.ExecutionInfo.
                                       CommandStatus.finishedSuccessfully)
Example #2
0
    def _get_command_state(self, command_uuid: str) -> silaFW_pb2.ExecutionInfo:
        """
        Method to fill an ExecutionInfo message from the SiLA server for observable commands

        :param command_uuid: The uuid of the command for which to return the current state

        :return: An execution info object with the current command state
        """

        #: Enumeration of silaFW_pb2.ExecutionInfo.CommandStatus
        command_status = silaFW_pb2.ExecutionInfo.CommandStatus.waiting
        #: Real silaFW_pb2.Real(0...1)
        command_progress = None
        #: Duration silaFW_pb2.Duration(seconds=<seconds>, nanos=<nanos>)
        command_estimated_remaining = None
        #: Duration silaFW_pb2.Duration(seconds=<seconds>, nanos=<nanos>)
        command_lifetime_of_execution = None

        # TODO: check the state of the command with the given uuid and return the correct information

        # just return a default in this example
        return silaFW_pb2.ExecutionInfo(
            commandStatus=command_status,
            progressInfo=(command_progress if command_progress is not None else None),
            estimatedRemainingTime=(command_estimated_remaining if command_estimated_remaining is not None else None),
            updatedLifetimeOfExecution=(
                command_lifetime_of_execution if command_lifetime_of_execution is not None else None)
        )
    def _get_initialization_state(self) -> silaFW_pb2.ExecutionInfo:
        """
        Method to fill an ExecutionInfo message from the SiLA server for the InitializeContiflow observable command

        :return: An execution info object with the current command state
        """

        #: Enumeration of silaFW_pb2.ExecutionInfo.CommandStatus
        command_status = silaFW_pb2.ExecutionInfo.CommandStatus.waiting
        if self.pump.is_initializing() and not self.timer.is_expired():
            command_status = silaFW_pb2.ExecutionInfo.CommandStatus.running
        elif self.pump.is_initialized():
            command_status = silaFW_pb2.ExecutionInfo.CommandStatus.finishedSuccessfully
        else:
            command_status = silaFW_pb2.ExecutionInfo.CommandStatus.finishedWithError
        #: Duration silaFW_pb2.Duration(seconds=<seconds>, nanos=<nanos>)
        command_estimated_remaining = self.timer.get_msecs_to_expiration(
        ) / 1000

        return silaFW_pb2.ExecutionInfo(
            commandStatus=command_status,
            estimatedRemainingTime=silaFW_pb2.Duration(
                seconds=int(command_estimated_remaining)),
            updatedLifetimeOfExecution=silaFW_pb2.Duration(
                seconds=int(self.timer.period_ms / 1000)))
Example #4
0
    def _wait_calibration_finished(self, timeout_sec):
        """
        The function waits until pump calibration has finished or
        until the timeout occurs.
        """

        if self.pump.is_calibration_finished():
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.
                finishedSuccessfully,
                progressInfo=silaFW_pb2.Real(value=1))
            return

        timer = qmixbus.PollingTimer(timeout_sec * 1000)
        message_timer = qmixbus.PollingTimer(period_ms=500)
        is_calibrating = False
        while not (is_calibrating or timer.is_expired()):
            time.sleep(0.1)
            timeout_sec -= 0.1
            if message_timer.is_expired():
                yield silaFW_pb2.ExecutionInfo(
                    commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.
                    running,
                    progressInfo=silaFW_pb2.Real(
                        value=(self.CALIBRATION_TIMEOUT - timeout_sec) /
                        self.CALIBRATION_TIMEOUT),
                    estimatedRemainingTime=silaFW_pb2.Duration(
                        seconds=int(timeout_sec)))
                message_timer.restart()
            is_calibrating = self.pump.is_calibration_finished()

        if not is_calibrating and not self.pump.is_in_fault_state():
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.
                finishedSuccessfully,
                progressInfo=silaFW_pb2.Real(value=1),
                estimatedRemainingTime=silaFW_pb2.Duration())
        else:
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.
                finishedWithError,
                progressInfo=silaFW_pb2.Real(value=1),
                estimatedRemainingTime=silaFW_pb2.Duration())
            logging.error("An unexpected error occurred: %s",
                          self.pump.read_last_error())
Example #5
0
    def GenerateFlow_Info(self, request, context: grpc.ServicerContext) \
            -> silaFW_pb2.ExecutionInfo:
        """
        Returns execution information regarding the command call :meth:`~.GenerateFlow`.

        :param request: A request object with the following properties
            commandId: The UUID of the command executed.
        :param context: gRPC :class:`~grpc.ServicerContext` object providing gRPC-specific information

        :returns: An ExecutionInfo response stream for the command with the following fields:
            commandStatus: Status of the command (enumeration)
            progressInfo: Information on the progress of the command (0 to 1)
            estimatedRemainingTime: Estimate of the remaining time required to run the command
            updatedLifetimeOfExecution: An update on the execution lifetime
        """
        # Get the UUID of the command
        command_uuid = request.value

        # catch invalid CommandExecutionUUID:
        if not command_uuid or self.dosage_uuid != command_uuid:
            raise SiLAFrameworkError(
                SiLAFrameworkErrorType.INVALID_COMMAND_EXECUTION_UUID
            )

        is_pumping = True
        while is_pumping:
            time.sleep(0.5)
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.running
            )
            is_pumping = self.pump.is_pumping()

        if not is_pumping and not self.pump.is_in_fault_state():
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.finishedSuccessfully
            )
        else:
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.finishedWithError
            )
            raise QmixSDKSiLAError(self.pump.read_last_error())
Example #6
0
    def _wait_movement_finished(self, axis: Axis):
        """
        The function waits until the last movement command for the given axis has finished
        """

        is_moving = True
        while is_moving:
            time.sleep(0.5)
            logging.info("Position: %s", axis.get_actual_position())
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.running)
            is_moving = not axis.is_target_position_reached()

        if not is_moving:
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.
                finishedSuccessfully)
        else:
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.
                finishedWithError)
            logging.error("An unexpected error occurred: %s",
                          axis.read_last_error())
    def RunControlLoop_Info(self, request, controller: ControllerChannel, context: grpc.ServicerContext) \
            -> silaFW_pb2.ExecutionInfo:
        """
        Returns execution information regarding the command call :meth:`~.RunControlLoop`.

        :param request: A request object with the following properties
            commandId: The UUID of the command executed.
        :param controller: The controller to operate on
        :param context: gRPC :class:`~grpc.ServicerContext` object providing gRPC-specific information

        :returns: An ExecutionInfo response stream for the command with the following fields:
            commandStatus: Status of the command (enumeration)
            progressInfo: Information on the progress of the command (0 to 1)
            estimatedRemainingTime: Estimate of the remaining time required to run the command
            updatedLifetimeOfExecution: An update on the execution lifetime
        """

        yield silaFW_pb2.ExecutionInfo(
            commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.waiting
        )

        # we loop only as long as the command is running
        while controller.is_control_loop_enabled():
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.running
            )

            logging.debug(f"Current controller value: {controller.read_actual_value()}")
            logging.debug(f"Device status: {controller.read_status()}")
            # we add a small delay to give the client a chance to keep up.
            time.sleep(1)
        else:
            # one last time yield the status
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.finishedSuccessfully
            )
Example #8
0
    def InitializeContiflow_Info(self, request, context: grpc.ServicerContext) \
            -> silaFW_pb2.ExecutionInfo:
        """
        Returns execution information regarding the command call :meth:`~.InitializeContiflow`.

        :param request: A request object with the following properties
            commandId: The UUID of the command executed.
        :param context: gRPC :class:`~grpc.ServicerContext` object providing gRPC-specific information

        :returns: An ExecutionInfo response stream for the command with the following fields:
            commandStatus: Status of the command (enumeration)
            progressInfo: Information on the progress of the command (0 to 1)
            estimatedRemainingTime: Estimate of the remaining time required to run the command
            updatedLifetimeOfExecution: An update on the execution lifetime
        """
        # Get the UUID of the command
        command_uuid = request.value

        # Get the current state
        execution_info = self._get_command_state(command_uuid=command_uuid)

        # construct the initial return dictionary in case while is not executed
        return_values = {'commandStatus': execution_info.commandStatus}
        if execution_info.HasField('progressInfo'):
            return_values['progressInfo'] = execution_info.progressInfo
        if execution_info.HasField('estimatedRemainingTime'):
            return_values[
                'estimatedRemainingTime'] = execution_info.estimatedRemainingTime
        if execution_info.HasField('updatedLifetimeOfExecution'):
            return_values[
                'updatedLifetimeOfExecution'] = execution_info.updatedLifetimeOfExecution

        # we loop only as long as the command is running
        while execution_info.commandStatus == silaFW_pb2.ExecutionInfo.CommandStatus.waiting \
                or execution_info.commandStatus == silaFW_pb2.ExecutionInfo.CommandStatus.running:
            # TODO:
            #   Evaluate the command status --> command_status. Options:
            #       command_stats = silaFW_pb2.ExecutionInfo.CommandStatus.waiting
            #       command_stats = silaFW_pb2.ExecutionInfo.CommandStatus.running
            #       command_stats = silaFW_pb2.ExecutionInfo.CommandStatus.finishedSuccessfully
            #       command_stats = silaFW_pb2.ExecutionInfo.CommandStatus.finishedWithError
            #   Optional:
            #       * Determine the progress (progressInfo)
            #       * Determine the estimated remaining time
            #       * Update the Lifetime of execution

            # Update all values
            execution_info = self._get_command_state(command_uuid=command_uuid)

            # construct the return dictionary
            return_values = {'commandStatus': execution_info.commandStatus}
            if execution_info.HasField('progressInfo'):
                return_values['progressInfo'] = execution_info.progressInfo
            if execution_info.HasField('estimatedRemainingTime'):
                return_values[
                    'estimatedRemainingTime'] = execution_info.estimatedRemainingTime
            if execution_info.HasField('updatedLifetimeOfExecution'):
                return_values[
                    'updatedLifetimeOfExecution'] = execution_info.updatedLifetimeOfExecution

            yield silaFW_pb2.ExecutionInfo(**return_values)

            # we add a small delay to give the client a chance to keep up.
            time.sleep(0.5)
        else:
            # one last time yield the status
            yield silaFW_pb2.ExecutionInfo(**return_values)
    def _wait_dosage_finished(self):
        """
        The function waits until the last dosage command has finished or
        until a timeout occurs. The timeout is estimated from the dosage's flow
        and target volume
        """

        if not self.pump.is_pumping():
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.finishedSuccessfully,
                progressInfo=silaFW_pb2.Real(value=1)
            )
            return

        target_volume = self.pump.get_target_volume()
        logging.debug("target volume: %f", target_volume)
        flow_in_sec = self.pump.get_flow_is() / self.pump.get_flow_unit().time_unitid.value
        if flow_in_sec == 0:
            # try again, maybe the pump didn't start pumping yet
            time.sleep(0.5)
            flow_in_sec = self.pump.get_flow_is() / self.pump.get_flow_unit().time_unitid.value
        if flow_in_sec == 0:
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.finishedWithError,
                progressInfo=silaFW_pb2.Real(value=1)
            )
            logging.error("The pump didn't start pumping. Last error: %s", self.pump.read_last_error())

        logging.debug("flow_in_sec: %f", flow_in_sec)
        dosing_time_s = self.pump.get_target_volume() / flow_in_sec + 2 # +2 sec buffer
        logging.debug("dosing_time_s: %fs", dosing_time_s)

        timer = qmixbus.PollingTimer(period_ms=dosing_time_s * 1000)
        message_timer = qmixbus.PollingTimer(period_ms=500)
        is_pumping = True
        while is_pumping and not timer.is_expired():
            time.sleep(0.1)
            dosing_time_s -= 0.1
            if message_timer.is_expired():
                logging.info("Fill level: %s", self.pump.get_fill_level())
                yield silaFW_pb2.ExecutionInfo(
                    commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.running,
                    progressInfo=silaFW_pb2.Real(value=self.pump.get_dosed_volume()/target_volume),
                    estimatedRemainingTime=silaFW_pb2.Duration(
                        seconds=int(dosing_time_s),
                        nanos=int(dosing_time_s-int(dosing_time_s)) * 1000000000
                    )
                )
                message_timer.restart()
            is_pumping = self.pump.is_pumping()

        if not is_pumping and not self.pump.is_in_fault_state():
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.finishedSuccessfully,
                progressInfo=silaFW_pb2.Real(value=1),
                estimatedRemainingTime=silaFW_pb2.Duration()
            )
        else:
            yield silaFW_pb2.ExecutionInfo(
                commandStatus=silaFW_pb2.ExecutionInfo.CommandStatus.finishedWithError,
                progressInfo=silaFW_pb2.Real(value=1),
                estimatedRemainingTime=silaFW_pb2.Duration()
            )
            logging.error("An unexpected error occurred: %s", self.pump.read_last_error())