def request_work(self, onlynewwho: bool = False, throttle: bool = False) -> None: """Request work from order manager.""" # Allow requesting work only once every 10 seconds when throttling if self._requested_work_time and throttle is True: time_elapsed = time.time() - self._requested_work_time if time_elapsed < 10: return self._requested_work_time = time.time() if onlynewwho: request = RequestFromRobot(self.lgnum, self.rsrc, requestnewwho=True) else: request = RequestFromRobot(self.lgnum, self.rsrc, requestwork=True) _LOGGER.info('Requesting work from order manager') dtype = create_robcoewmtype_str(request) success = self.send_robot_request(dtype, unstructure(request)) if success is True: _LOGGER.info('Requested work from order manager') else: _LOGGER.error( 'Error requesting work from order manager. Try again') raise ConnectionError
def notify_who_completion(self, who: str) -> None: """ Send a warehouse order completion request to EWM Order Manager. It notifies the robot when the warehouse order was completed. """ request = RequestFromRobot(self.lgnum, self.rsrc, notifywhocompletion=who) _LOGGER.info( 'Requesting warehouse order completion notification for %s from order manager', who) dtype = create_robcoewmtype_str(request) success = self.send_robot_request(dtype, unstructure(request)) if success is True: _LOGGER.info( 'Requested warehouse order completion notification from order manager' ) else: _LOGGER.error( 'Error requesting warehouse order completion notification from order manager. ' 'Try again') raise ConnectionError
def confirm_warehousetask(self, wht: WarehouseTask, enforce_first_conf: bool = False) -> None: """Confirm warehouse task.""" confirmations = [] clear_progress = False if wht.vlpla or enforce_first_conf: confirmation = ConfirmWarehouseTask( lgnum=wht.lgnum, tanum=wht.tanum, rsrc=self.rsrc, who=wht.who, confirmationnumber=ConfirmWarehouseTask.FIRST_CONF, confirmationtype=ConfirmWarehouseTask.CONF_SUCCESS) if enforce_first_conf: _LOGGER.info('First confirmation enforced - confirming') else: _LOGGER.info('Source bin reached - confirming') confirmations.append(confirmation) elif wht.nlpla: confirmation = ConfirmWarehouseTask( lgnum=wht.lgnum, tanum=wht.tanum, rsrc=self.rsrc, who=wht.who, confirmationnumber=ConfirmWarehouseTask.SECOND_CONF, confirmationtype=ConfirmWarehouseTask.CONF_SUCCESS) _LOGGER.info('Target bin reached - confirming') confirmations.append(confirmation) clear_progress = True for confirmation in confirmations: dtype = create_robcoewmtype_str(confirmation) success = self.send_wht_confirmation(dtype, unstructure(confirmation), clear_progress) if success is True: _LOGGER.info( 'Confirmation message for warehouse task "%s" sent to order manager', wht.tanum) else: _LOGGER.error( 'Error sending confirmation message for warehouse task "%s" - Try again', wht.tanum) raise ConnectionError
def send_robot_whos(self, robotident: RobotIdentifier, whos: List[WarehouseOrder]) -> None: """ Send warehouse order from SAP EWM to the robot. Returns True on success and False on fail. """ # Send orders to robot for who in whos: dtype = create_robcoewmtype_str(who) self.ordercontroller.send_who_to_robot(robotident, dtype, unstructure(who)) # Create success log message whos_who = [entry.who for entry in whos] _LOGGER.info( 'Warehouse orders "%s" sent to robot "%s" in warehouse "%s"', whos_who, robotident.rsrc, robotident.lgnum)
def send_warehousetask_error(self, wht: WarehouseTask) -> None: """Send warehouse task error.""" errors = [] if wht.vlpla: error = ConfirmWarehouseTask( lgnum=wht.lgnum, tanum=wht.tanum, rsrc=self.rsrc, who=wht.who, confirmationnumber=ConfirmWarehouseTask.FIRST_CONF, confirmationtype=ConfirmWarehouseTask.CONF_ERROR) _LOGGER.info('Error before first confirmation - sending to EWM') errors.append(error) elif wht.nlpla: error = ConfirmWarehouseTask( lgnum=wht.lgnum, tanum=wht.tanum, rsrc=self.rsrc, who=wht.who, confirmationnumber=ConfirmWarehouseTask.SECOND_CONF, confirmationtype=ConfirmWarehouseTask.CONF_ERROR) _LOGGER.info('Error before second confirmation - sending to EWM') errors.append(error) for error in errors: dtype = create_robcoewmtype_str(error) success = self.send_wht_confirmation(dtype, unstructure(error), True) if success is True: _LOGGER.info( 'Confirmation message for warehouse task "%s" sent to order manager', wht.tanum) else: _LOGGER.error( 'Error sending confirmation message for warehouse task "%s" - Try again', wht.tanum) raise ConnectionError
import sys import traceback import logging from typing import Dict from robcoewmtypes.helper import create_robcoewmtype_str, get_sample_cr from robcoewmtypes.robot import RequestFromRobot, RequestFromRobotStatus from robcoewminterface.exceptions import NoOrderFoundError, RobotHasOrderError from k8scrhandler.k8scrhandler import K8sCRHandler, k8s_cr_callback _LOGGER = logging.getLogger(__name__) ROBOTREQUEST_TYPE = create_robcoewmtype_str(RequestFromRobot('lgnum', 'rsrc')) class RobotRequestController(K8sCRHandler): """Handle K8s custom resources.""" def __init__(self) -> None: """Constructor.""" template_cr = get_sample_cr('robotrequest') labels = {} super().__init__('sap.com', 'v1', 'robotrequests', 'default', template_cr, labels) @k8s_cr_callback def _callback(self, name: str, labels: Dict, operation: str, custom_res: Dict) -> None:
def robotrequest_callback(self, dtype: str, name: str, data: Dict, statusdata: Dict) -> None: """ Handle robotrequest messages. Used for K8S CR handler. """ cls = self.__class__ # Structure the request data robcoewmdata = self._structure_callback_data( dtype, data, cls.ROBOTREQUEST_MSG_TYPES) if not robcoewmdata: return # Get statusdata if available if statusdata: robcoewmstatusdata = self._structure_callback_data( dtype, statusdata, cls.ROBOTREQUEST_MSG_TYPES) else: robcoewmstatusdata = [ RequestFromRobot(robcoewmdata[0].lgnum, robcoewmdata[0].rsrc), ] if len(robcoewmdata) > 1 or len(robcoewmstatusdata) > 1: raise ValueError('Robot request must include only one dataset') # Process the dataset request = robcoewmdata[0] status = robcoewmstatusdata[0] # Return if request was already processed if self.msg_mem.check_robotrequest_processed(name, status): _LOGGER.info('Robot request "%s" already processed before - skip', name) return robotident = RobotIdentifier(request.lgnum, request.rsrc) # Determine if it is the first request request_no = self.msg_mem.request_count[robotident] firstrequest = bool(request_no == 0) # Request work, when robot is asking if request.requestnewwho and not status.requestnewwho: # Get a new warehouse order for the robot success = self.get_and_send_robot_whos(robotident, firstrequest=firstrequest, newwho=True, onlynewwho=True) if success: status.requestnewwho = True status.requestwork = request.requestwork elif request.requestwork and not status.requestwork: # Get existing warehouse orders for the robot. If no exists, get a new warehouse order success = self.get_and_send_robot_whos(robotident, firstrequest=firstrequest, newwho=True, onlynewwho=False) if success: status.requestwork = True # Check if warehouse order was completed if request.notifywhocompletion and not status.notifywhocompletion: try: self.ewmwho.get_robot_warehouseorders(robotident.lgnum, robotident.rsrc) except NoOrderFoundError: status.notifywhocompletion = request.notifywhocompletion _LOGGER.info( 'Warehouse order %s was confirmed, notifying robot "%s"', request.notifywhocompletion, robotident.rsrc) # Raise exception if request was not complete if request == status: self.update_robotrequest_status(name, create_robcoewmtype_str(status), unstructure(status), process_complete=True) self.msg_mem.memorize_robotrequest(name, status) self.msg_mem.delete_robotrequest_from_memory(name, status) else: self.update_robotrequest_status(name, create_robcoewmtype_str(status), unstructure(status), process_complete=False) self.msg_mem.memorize_robotrequest(name, status) if request.notifywhocompletion: raise RobotHasOrderError else: raise NoOrderFoundError
from collections import OrderedDict from typing import Dict from cattr import structure from robcoewmtypes.helper import create_robcoewmtype_str, get_sample_cr from robcoewmtypes.warehouseorder import ( WarehouseOrder, ConfirmWarehouseTask, WarehouseOrderCRDSpec) from k8scrhandler.k8scrhandler import K8sCRHandler, k8s_cr_callback from .manager import RobotIdentifier _LOGGER = logging.getLogger(__name__) WAREHOUSEORDER_TYPE = create_robcoewmtype_str(WarehouseOrder('lgnum', 'who')) WAREHOUSETASKCONF_TYPE = create_robcoewmtype_str(ConfirmWarehouseTask( 'lgnum', 'who', ConfirmWarehouseTask.FIRST_CONF, ConfirmWarehouseTask.CONF_SUCCESS)) class OrderController(K8sCRHandler): """Handle K8s custom resources.""" def __init__(self) -> None: """Constructor.""" # Warehouse order spec dictionary self.warehouse_order_spec = OrderedDict() self.warehouse_order_spec_lock = threading.RLock() template_cr = get_sample_cr('warehouseorder')
import traceback import logging from typing import Dict, Optional from robcoewmtypes.helper import create_robcoewmtype_str, get_sample_cr from robcoewmtypes.warehouseorder import ( WarehouseOrder, ConfirmWarehouseTask, WarehouseOrderCRDSpec) from k8scrhandler.k8scrhandler import K8sCRHandler, k8s_cr_callback from .robot import WarehouseOrderStateRestore _LOGGER = logging.getLogger(__name__) WAREHOUSEORDER_TYPE = create_robcoewmtype_str(WarehouseOrder('lgnum', 'who')) class OrderController(K8sCRHandler): """Handle K8s custom resources.""" def __init__(self) -> None: """Constructor.""" self.init_robot_fromenv() # Last successfully processed spec of warehouse order self.processed_order_spec = {} template_cr = get_sample_cr('warehouseorder') labels = {} labels['cloudrobotics.com/robot-name'] = self.robco_robot_name
def process_robotrequest_cr(self, name: str, custom_res: Dict) -> None: """ Process a robotrequest custom resource. Used for K8S CR handler. """ cls = self.__class__ if custom_res.get( 'status', {}).get('status') == RequestFromRobotStatus.STATE_PROCESSED: return # Structure the request data robcoewmdata = self._structure_callback_data( ROBOTREQUEST_TYPE, custom_res['spec'], cls.ROBOTREQUEST_MSG_TYPES) if not robcoewmdata: return # Get statusdata if available if custom_res.get('status', {}).get('data'): robcoewmstatusdata = self._structure_callback_data( ROBOTREQUEST_TYPE, custom_res['status']['data'], cls.ROBOTREQUEST_MSG_TYPES) else: robcoewmstatusdata = [ RequestFromRobot(robcoewmdata[0].lgnum, robcoewmdata[0].rsrc), ] if len(robcoewmdata) > 1 or len(robcoewmstatusdata) > 1: raise ValueError('Robot request must include only one dataset') # Process the dataset request = robcoewmdata[0] status = robcoewmstatusdata[0] # Return if request was already processed if self.msg_mem.check_robotrequest_processed(name, status): _LOGGER.info('Robot request "%s" already processed before - skip', name) return robotident = RobotIdentifier(request.lgnum, request.rsrc) # Determine if it is the first request self.msg_mem.request_count[name] += 1 firstrequest = bool(self.msg_mem.request_count[name] == 1) # Request work, when robot is asking if request.requestnewwho and not status.requestnewwho: # Get a new warehouse order for the robot success = self.get_and_send_robot_whos(robotident, firstrequest=firstrequest, newwho=True, onlynewwho=True) if success: status.requestnewwho = True status.requestwork = request.requestwork elif request.requestwork and not status.requestwork: # Get existing warehouse orders for the robot. If no exists, get a new warehouse order success = self.get_and_send_robot_whos(robotident, firstrequest=firstrequest, newwho=True, onlynewwho=False) if success: status.requestwork = True # Check if warehouse order was completed if request.notifywhocompletion and not status.notifywhocompletion: notfound = False try: who = self.ewmwho.get_warehouseorder( lgnum=robotident.lgnum, who=request.notifywhocompletion) except NotFoundError: notfound = True else: if who.status not in ['D', '']: notfound = True if notfound: status.notifywhocompletion = request.notifywhocompletion _LOGGER.info( 'Warehouse order %s was confirmed, notifying robot "%s"', request.notifywhocompletion, robotident.rsrc) # Warehouse orders are completed in EWM, thus ensure that they are marked PROCESSED self.cleanup_who( WhoIdentifier(robotident.lgnum, request.notifywhocompletion)) # Check if warehouse task was completed if request.notifywhtcompletion and not status.notifywhtcompletion: try: self.ewmwho.get_openwarehousetask(robotident.lgnum, request.notifywhtcompletion) except NotFoundError: status.notifywhtcompletion = request.notifywhtcompletion _LOGGER.info( 'Warehouse task %s was confirmed, notifying robot "%s"', request.notifywhtcompletion, robotident.rsrc) # Raise exception if request was not complete if request == status: self.robotrequestcontroller.update_status( name, create_robcoewmtype_str(status), unstructure(status), process_complete=True) self.msg_mem.memorize_robotrequest(name, status) self.msg_mem.delete_robotrequest_from_memory(name, status) else: self.robotrequestcontroller.update_status( name, create_robcoewmtype_str(status), unstructure(status), process_complete=False) self.msg_mem.memorize_robotrequest(name, status) if request.notifywhocompletion: raise RobotHasOrderError else: raise NoOrderFoundError
from robcoewminterface.ewm import WarehouseOData, WarehouseOrderOData from robcoewminterface.exceptions import (ODataAPIException, NoOrderFoundError, RobotHasOrderError, WarehouseTaskAlreadyConfirmedError, NotFoundError) from .helper import ProcessedMessageMemory, RobotIdentifier, WhoIdentifier from .ordercontroller import OrderController from .robotrequestcontroller import RobotRequestController _LOGGER = logging.getLogger(__name__) STATE_SUCCEEDED = 'SUCCEEDED' STATE_FAILED = 'FAILED' ROBOTREQUEST_TYPE = create_robcoewmtype_str(RequestFromRobot('lgnum', 'rsrc')) WAREHOUSETASKCONF_TYPE = create_robcoewmtype_str([ ConfirmWarehouseTask('lgnum', 'who', ConfirmWarehouseTask.FIRST_CONF, ConfirmWarehouseTask.CONF_SUCCESS) ]) class EWMOrderManager: """Main order manager class for EWM.""" ROBOTREQUEST_MSG_TYPES = (RequestFromRobot, ) CONFIRMATION_MSG_TYPES = (ConfirmWarehouseTask, ) # Prometheus logging who_counter = Counter('sap_ewm_warehouse_orders', 'Completed EWM Warehouse orders',