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)
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)))
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())
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())
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 )
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())