def MoveToPosition_Result(self, request, context: grpc.ServicerContext) \ -> AxisPositionController_pb2.MoveToPosition_Responses: """ Returns the final result of the command call :meth:`~.MoveToPosition`. :param request: A request object with the following properties CommandExecutionUUID: The UUID of the command executed. :param context: gRPC :class:`~grpc.ServicerContext` object providing gRPC-specific information :returns: The return object defined for the command with the following fields: EmptyResponse (Empty Response): An empty response data type used if no response is required. """ # Get the UUID of the command command_uuid = request.value # catch invalid CommandExecutionUUID: if not command_uuid or command_uuid not in self.uuid_to_axis: raise SiLAFrameworkError( SiLAFrameworkErrorType.INVALID_COMMAND_EXECUTION_UUID) axis = self.uuid_to_axis[command_uuid] # catch premature command call if not axis.is_target_position_reached(): raise SiLAFrameworkError( SiLAFrameworkErrorType.COMMAND_EXECUTION_NOT_FINISHED) logging.info("Finished moving! (UUID: %s)", command_uuid) del self.uuid_to_axis[command_uuid] time.sleep(0.6) return AxisPositionController_pb2.MoveToPosition_Responses()
def GenerateFlow_Result(self, request, context: grpc.ServicerContext) \ -> ContinuousFlowDosingService_pb2.GenerateFlow_Responses: """ Returns the final result of the command call :meth:`~.GenerateFlow`. :param request: A request object with the following properties CommandExecutionUUID: The UUID of the command executed. :param context: gRPC :class:`~grpc.ServicerContext` object providing gRPC-specific information :returns: The return object defined for the command with the following fields: EmptyResponse (Empty Response): An empty response data type used if no response is required. """ # Get the UUID of the command command_uuid = request.value # catch invalid CommandExecutionUUID: if not command_uuid and self.dosage_uuid != command_uuid: raise SiLAFrameworkError( SiLAFrameworkErrorType.INVALID_COMMAND_EXECUTION_UUID ) # catch premature command call if self.pump.is_pumping(): raise SiLAFrameworkError( SiLAFrameworkErrorType.COMMAND_EXECUTION_NOT_FINISHED ) logging.info("Finished dosing! (UUID: %s)", self.dosage_uuid) self.dosage_uuid = "" return ContinuousFlowDosingService_pb2.GenerateFlow_Responses()
def _validate_uuid(self, uuid: silaFW_pb2.CommandExecutionUUID, check_premature_call=False): """ Checks the given UUID for validity (i.e. if this is the UUID of the current movement command) and raises a SiLAFrameworkError if the UUID is not valid. Additionally, if `check_premature_call` is set to `True` then it is also checked that the axis system is not moving (this is only useful for `..._Result` functions). If this check fails, the corresponding FrameworkError will be raised, as well. :param uuid: The UUID to check :param check_premature_call: Whether to check if the axis system is still moving and raise an error if it is. """ # catch invalid CommandExecutionUUID: if not uuid and self.movement_uuid != uuid: raise SiLAFrameworkError( SiLAFrameworkErrorType.INVALID_COMMAND_EXECUTION_UUID) # catch premature command call if check_premature_call and not self.axis_system.is_target_position_reached( ): raise SiLAFrameworkError( SiLAFrameworkErrorType.COMMAND_EXECUTION_NOT_FINISHED)
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 ) return self._wait_dosage_finished()
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_axis(self, invocation_metadata: Dict) -> Axis: """ Retrieves the axis that is requested by the `invocation_metadata` of a RPC. If the axis cannot be found an appropriate error is raised. """ axis_name = self._get_axis_name(invocation_metadata) try: return self.axes[axis_name] except KeyError: raise SiLAFrameworkError(SiLAFrameworkErrorType.INVALID_METADATA, f'Axis {axis_name} is invalid.')
def InitializePumpDrive_Result(self, request, context: grpc.ServicerContext) \ -> PumpDriveControlService_pb2.InitializePumpDrive_Responses: """ Returns the final result of the command call :meth:`~.InitializePumpDrive`. :param request: A request object with the following properties CommandExecutionUUID: The UUID of the command executed. :param context: gRPC :class:`~grpc.ServicerContext` object providing gRPC-specific information :returns: The return object defined for the command with the following fields: EmptyResponse (Empty Response): An empty response data type used if no response is required. """ # Get the UUID of the command command_uuid = request.value # catch invalid CommandExecutionUUID: if not command_uuid or self.calibration_uuid != command_uuid: raise SiLAFrameworkError( SiLAFrameworkErrorType.INVALID_COMMAND_EXECUTION_UUID) # catch premature command call if not self.pump.is_calibration_finished(): raise SiLAFrameworkError( SiLAFrameworkErrorType.COMMAND_EXECUTION_NOT_FINISHED) logging.info("Pump calibrated: %s", self.pump.is_calibration_finished()) last_error = self.pump.read_last_error() if not self.pump.is_calibration_finished() and last_error.code != 0: raise InitializationFailedError( f'The last error that occurred was {last_error}') logging.info("Finished calibration! (UUID: %s)", self.calibration_uuid) self.calibration_uuid = "" return PumpDriveControlService_pb2.InitializePumpDrive_Responses()
def _get_axis_name(self, invocation_metadata: Dict) -> str: """ Retrieves the axis name that is given in the `invocation_metadata` of a RPC. If the metadatum is not present an appropriate error is raised. """ invocation_metadata = { key: value for key, value in invocation_metadata } logging.debug(f"Received invocation metadata: {invocation_metadata}") try: message = AxisPositionController_pb2.Metadata_AxisIdentifier() message.ParseFromString( invocation_metadata[METADATA_AXIS_IDENTIFIER]) return message.AxisIdentifier.value except KeyError: raise SiLAFrameworkError( SiLAFrameworkErrorType.INVALID_METADATA, 'This Command requires the AxisIdentifier metadata!')
def __get_channel_id(self, metadata: Tuple[Tuple[str, str]], type: str) -> str: """ Get the requested channel index from the given `metadata` :param metdata: The metadata of the call. It should contain the requested channel index :param type: Either "Command" or "Property" :return: The channel index if it can be obtained, otherwise a SiLAFrameworkError will be raised """ invocation_metadata = {key: value for key, value in metadata} logging.debug(f"Received invocation metadata: {invocation_metadata}") try: message = pb2.Metadata_ChannelIndex() message.ParseFromString(invocation_metadata[metadata_key]) return message.ChannelIndex.value except KeyError: raise SiLAFrameworkError( SiLAFrameworkErrorType.INVALID_METADATA, f'This {type} requires the ChannelIndex metadata!')
def get_valve(self, metadata: Tuple[Tuple[str, str]], type: str) -> Valve: """ Get the valve that is identified by the valve index given in `metadata` :param metdata: The metadata of the call. It should contain the requested valve index :param type: Either "Command" or "Property" :return: A valid valve object if the valve can be identified, otherwise a SiLAFrameworkError will be raised """ valve_id = self._get_valve_index(metadata, type) logging.debug(f"Requested valve: {valve_id}") try: return self.valves[valve_id] except IndexError: raise SiLAFrameworkError( SiLAFrameworkErrorType.INVALID_METADATA, f"The sent valve index ({valve_id}) is invalid! " f"The index has to be between 0 and {self.num_valves - 1}.")
def _get_valve_index(self, metadata: Tuple[Tuple[str, str]], type: str) -> int: """ Get the requested valve index from the given `metadata` :param metdata: The metadata of the call. It should contain the requested valve index :param type: Either "Command" or "Property" :return: The valve index if it can be obtained, otherwise a SiLAFrameworkError will be raised """ invocation_metadata = {key: value for key, value in metadata} logging.debug(f"Received invocation metadata: {invocation_metadata}") try: message = ValveGatewayService_pb2.Metadata_ValveIndex() message.ParseFromString(invocation_metadata[METADATA_VALVE_INDEX]) return message.ValveIndex.value except KeyError: raise SiLAFrameworkError( SiLAFrameworkErrorType.INVALID_METADATA, f'This {type} requires the ValveIndex metadata!')
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 RunControlLoop_Info(self, request, context: grpc.ServicerContext) \ -> silaFW_pb2.ExecutionInfo: """ Returns execution information regarding the command call :meth:`~.RunControlLoop`. :param request: A request object with the following properties CommandExecutionUUID: 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 """ logging.debug( "RunControlLoop_Info called in {current_mode} mode".format( current_mode=( 'simulation' if self.simulation_mode else 'real'))) # Get the UUID of the command command_uuid = request.value try: if command_uuid not in self.uuid_to_controller: raise SiLAFrameworkError( error_type=SiLAFrameworkErrorType. INVALID_COMMAND_EXECUTION_UUID, msg="There is no command execution with the given UUID!") controller = self.uuid_to_controller[command_uuid] return self.implementation.RunControlLoop_Info( request, controller, context) except (SiLAError, DecoratorInvalidChannelIndex) as err: if isinstance(err, DecoratorInvalidChannelIndex): err = InvalidChannelIndexError( err.invalid_index, f"The index has to be between 0 and {self.num_channels - 1}." ) err.raise_rpc_error(context=context)
def RunControlLoop(self, request, context: grpc.ServicerContext) \ -> silaFW_pb2.CommandConfirmation: """ Executes the observable command "Run Control Loop" Run the Control Loop :param request: gRPC request containing the parameters passed: request.EmptyParameter (Empty Parameter): An empty parameter data type used if no parameter is required. :param context: gRPC :class:`~grpc.ServicerContext` object providing gRPC-specific information :returns: A command confirmation object with the following information: commandId: A command id with which this observable command can be referenced in future calls lifetimeOfExecution: The (maximum) lifetime of this command call. """ logging.debug("RunControlLoop called in {current_mode} mode".format( current_mode=('simulation' if self.simulation_mode else 'real'))) try: controller: ControllerChannel = self._get_channel( context.invocation_metadata(), "Command") if controller.is_control_loop_enabled(): raise SiLAFrameworkError( error_type=SiLAFrameworkErrorType. COMMAND_EXECUTION_NOT_ACCEPTED, msg= "There is a running control loop already. Cannot start a new loop!" ) confirmation = self.implementation.RunControlLoop( request, controller, context) self.uuid_to_controller[ confirmation.commandExecutionUUID.value] = controller return confirmation except (SiLAError, DecoratorInvalidChannelIndex) as err: if isinstance(err, DecoratorInvalidChannelIndex): err = InvalidChannelIndexError( err.invalid_index, f"The index has to be between 0 and {self.num_channels - 1}." ) err.raise_rpc_error(context=context)
def RunControlLoop_Result(self, request, context: grpc.ServicerContext) \ -> ControlLoopService_pb2.RunControlLoop_Responses: """ Returns the final result of the command call :meth:`~.RunControlLoop`. :param request: A request object with the following properties CommandExecutionUUID: The UUID of the command executed. :param context: gRPC :class:`~grpc.ServicerContext` object providing gRPC-specific information :returns: The return object defined for the command with the following fields: EmptyResponse (Empty Response): An empty response data type used if no response is required. """ logging.debug( "RunControlLoop_Result called in {current_mode} mode".format( current_mode=( 'simulation' if self.simulation_mode else 'real'))) # Get the UUID of the command command_uuid = request.value try: if command_uuid not in self.uuid_to_controller: raise SiLAFrameworkError( error_type=SiLAFrameworkErrorType. INVALID_COMMAND_EXECUTION_UUID, msg="There is no command execution with the given UUID!") controller = self.uuid_to_controller[command_uuid] del self.uuid_to_controller[command_uuid] return self.implementation.RunControlLoop_Result( request, controller, context) except (SiLAError, DecoratorInvalidChannelIndex) as err: if isinstance(err, DecoratorInvalidChannelIndex): err = InvalidChannelIndexError( err.invalid_index, f"The index has to be between 0 and {self.num_channels - 1}." ) err.raise_rpc_error(context=context)