def uses_robot_joints(cls, project: CProject, action_id: str, parameter_id: str, robot_joints_id: str) -> bool: value_id = cls._id_from_value( project.action(action_id).parameter(parameter_id).value) return value_id == robot_joints_id
def parameter_value( cls, type_defs: TypesDict, scene: CScene, project: CProject, action_id: str, parameter_id: str ) -> RelativePose: try: return RelativePose.from_json(project.action(action_id).parameter(parameter_id).value) except ValidationError as e: raise ParameterPluginException(e)
def uses_orientation(cls, project: CProject, action_id: str, parameter_id: str, orientation_id: str) -> bool: action = project.action(action_id) param = action.parameter(parameter_id) for ori_id in cls._param_value_list(param): if ori_id == orientation_id: return True return False
def check_action_params( scene: CachedScene, project: CachedProject, action: common.Action, object_action: ObjectAction ) -> None: _, action_type = action.parse_type() assert action_type == object_action.name if len(object_action.parameters) != len(action.parameters): raise Arcor2Exception("Unexpected number of parameters.") for req_param in object_action.parameters: param = action.parameter(req_param.name) if param.type == common.ActionParameter.TypeEnum.CONSTANT: const = project.constant(param.str_from_value()) param_meta = object_action.parameter(param.name) if param_meta.type != const.type: raise Arcor2Exception("Param type does not match constant type.") elif param.type == common.ActionParameter.TypeEnum.LINK: parsed_link = param.parse_link() if parsed_link.action_id == action.id: raise Arcor2Exception("Can't use own result as a parameter.") outputs = project.action(parsed_link.action_id).flow(parsed_link.flow_name).outputs assert len(outputs) == len(object_action.returns) param_meta = object_action.parameter(param.name) if param_meta.type != object_action.returns[parsed_link.output_index]: raise Arcor2Exception("Param type does not match action output type.") else: if param.type not in known_parameter_types(): raise Arcor2Exception(f"Parameter {param.name} of action {action.name} has unknown type: {param.type}.") try: plugin_from_type_name(param.type).parameter_value( get_types_dict(), scene, project, action.id, param.name ) except ParameterPluginException as e: raise Arcor2Exception(f"Parameter {param.name} of action {action.name} has invalid value. {str(e)}")
def parameter_value( cls, type_defs: TypesDict, scene: CScene, project: CProject, action_id: str, parameter_id: str ) -> Any: action = project.action(action_id) param_value = action.parameter(parameter_id).value try: val = cls._value_from_json(param_value) except ParameterPluginException as e: raise ParameterPluginException( f"Parameter {action.name}/{parameter_id} has invalid value: '{param_value}'." ) from e if not isinstance(val, cls.type()): raise ParameterPluginException(f"Parameter {action.name}/{parameter_id} has invalid type: '{type(val)}'.") return val
def parameter_value(cls, type_defs: TypesDict, scene: CScene, project: CProject, action_id: str, parameter_id: str) -> Enum: action = project.action(action_id) param = action.parameter(parameter_id) obj_id, action_type = action.parse_type() obj_type_name = scene.object(obj_id).type try: obj_type = type_defs[obj_type_name] except KeyError: raise ParameterPluginException( f"Unknown object type {obj_type_name}.") try: method = getattr(obj_type, action_type) except AttributeError: raise ParameterPluginException( f"Object type {obj_type_name} does not have method {action_type}." ) try: ttype = get_type_hints(method)[param.name] except KeyError: raise ParameterPluginException( f"Method {obj_type}/{method.__name__} does not have parameter {param.name}." ) if not issubclass(ttype, cls.type()): raise ParameterPluginException( f"Type {ttype.__name__} is not subclass of {cls.type().__name__}." ) try: return ttype(json.loads(param.value)) except ValueError: raise ParameterPluginException( f"Parameter {parameter_id} of action {action.name} has invalid value." )
def check_action_params(scene: CachedScene, project: CachedProject, action: common.Action, object_action: ObjectAction) -> None: _, action_type = action.parse_type() assert action_type == object_action.name if len(object_action.parameters) != len(action.parameters): raise Arcor2Exception("Unexpected number of parameters.") for req_param in object_action.parameters: param = action.parameter(req_param.name) if param.type == common.ActionParameter.TypeEnum.PROJECT_PARAMETER: pparam = project.parameter(param.str_from_value()) param_meta = object_action.parameter(param.name) if param_meta.type != pparam.type: raise Arcor2Exception( "Action parameter type does not match project parameter type." ) elif param.type == common.ActionParameter.TypeEnum.LINK: parsed_link = param.parse_link() if parsed_link.action_id == action.id: raise Arcor2Exception("Can't use own result as a parameter.") parent_action = project.action(parsed_link.action_id) source_action_pt = parent_action.parse_type() parent_action_meta = glob.OBJECT_TYPES[scene.object( source_action_pt.obj_id).type].actions[ source_action_pt.action_type] if len(parent_action.flow(parsed_link.flow_name).outputs) != len( parent_action_meta.returns): raise Arcor2Exception( "Source action does not have outputs specified.") param_meta = object_action.parameter(param.name) try: if param_meta.type != parent_action_meta.returns[ parsed_link.output_index]: raise Arcor2Exception( "Param type does not match action output type.") except IndexError: raise Arcor2Exception( f"Index {parsed_link.output_index} is invalid for action {object_action.name}," f" which returns {len(object_action.returns)} values.") else: if param.type not in known_parameter_types(): raise Arcor2Exception( f"Parameter {param.name} of action {action.name} has unknown type: {param.type}." ) try: plugin_from_type_name(param.type).parameter_value( get_types_dict(), scene, project, action.id, param.name) except ParameterPluginException as e: raise Arcor2Exception( f"Parameter {param.name} of action {action.name} has invalid value. {str(e)}" )
def add_logic_to_loop(type_defs: TypesDict, tree: Module, scene: CScene, project: CProject) -> None: added_actions: Set[str] = set() def _blocks_to_start(action: Action, depth: int = 0) -> int: inputs, outputs = project.action_io(action.id) for inp in inputs: if inp.start == inp.START: continue parsed_input = inp.parse_start() prev_action = project.action(parsed_input.start_action_id) _, prev_action_outputs = project.action_io(prev_action.id) if len(prev_action_outputs) > 1: depth += 1 _blocks_to_start(prev_action, depth) return depth def _add_logic(container: Container, current_action: Action, super_container: Optional[Container] = None) -> None: # more paths could lead to the same action, so it might be already added # ...this is easier than searching the tree if current_action.id in added_actions: logger.debug( f"Action {current_action.name} already added, skipping.") return inputs, outputs = project.action_io(current_action.id) logger.debug( f"Adding action {current_action.name}, with {len(inputs)} input(s) and {len(outputs)} output(s)." ) act = current_action.parse_type() ac_obj = scene.object(act.obj_id).name args: List[AST] = [] # TODO make sure that the order of parameters is correct / re-order for param in current_action.parameters: if param.type == ActionParameter.TypeEnum.LINK: parsed_link = param.parse_link() parent_action = project.action(parsed_link.action_id) # TODO add support for tuples assert len(parent_action.flow(FlowTypes.DEFAULT).outputs ) == 1, "Only one result is supported atm." assert parsed_link.output_index == 0 res_name = parent_action.flow(FlowTypes.DEFAULT).outputs[0] # make sure that the result already exists if parent_action.id not in added_actions: raise SourceException( f"Action {current_action.name} attempts to use result {res_name} " f"of subsequent action {parent_action.name}.") args.append(Name(id=res_name, ctx=Load())) elif param.type == ActionParameter.TypeEnum.PROJECT_PARAMETER: args.append( Name(id=project.parameter(param.str_from_value()).name, ctx=Load())) else: plugin = plugin_from_type_name(param.type) args.append( plugin.parameter_ast(type_defs, scene, project, current_action.id, param.name)) list_of_imp_tup = plugin.need_to_be_imported( type_defs, scene, project, current_action.id, param.name) if list_of_imp_tup: # TODO what if there are two same names? for imp_tup in list_of_imp_tup: add_import(tree, imp_tup.module_name, imp_tup.class_name, try_to_import=False) add_method_call( container.body, ac_obj, act.action_type, args, [keyword(arg="an", value=Str(s=current_action.name, kind=""))], current_action.flow(FlowTypes.DEFAULT).outputs, ) added_actions.add(current_action.id) if not outputs: raise SourceException( f"Action {current_action.name} has no outputs.") elif len(outputs) == 1: output = outputs[0] if output.end == output.END: # TODO this is just temporary (while there is while loop), should be rather Return() container.body.append(Continue()) return seq_act = project.action(output.end) seq_act_inputs, _ = project.action_io(seq_act.id) if len(seq_act_inputs ) > 1: # the action belongs to a different block if seq_act.id in added_actions: return logger.debug( f"Action {seq_act.name} going to be added to super_container." ) # test if this is the correct super_container -> count distance (number of blocks) to the START blocks_to_start: Dict[str, int] = {} for inp in seq_act_inputs: parsed_start = inp.parse_start() pact = project.action(parsed_start.start_action_id) blocks_to_start[pact.id] = _blocks_to_start(pact) winner = min(blocks_to_start, key=blocks_to_start.get ) # type: ignore # TODO what is wrong with it? # TODO if blocks_to_start is cached somewhere, the second part of the condition is useless # it might happen that there are two different ways with the same distance if winner == current_action.id or all( value == list(blocks_to_start.values())[0] for value in blocks_to_start.values()): assert super_container is not None _add_logic(super_container, seq_act) return logger.debug(f"Sequential action: {seq_act.name}") _add_logic(container, seq_act, super_container) else: root_if: Optional[If] = None # action has more outputs - each output should have condition for idx, output in enumerate(outputs): if not output.condition: raise SourceException("Missing condition.") # TODO use parameter plugin (action metadata will be needed - to get the return types) # TODO support for other countable types # ...this will only work for booleans from arcor2 import json condition_value = json.loads(output.condition.value) comp = NameConstant(value=condition_value, kind=None) what = output.condition.parse_what() output_name = project.action(what.action_id).flow( what.flow_name).outputs[what.output_index] cond = If( test=Compare(left=Name(id=output_name, ctx=Load()), ops=[Eq()], comparators=[comp]), body=[], orelse=[], ) if idx == 0: root_if = cond container.body.append(root_if) logger.debug(f"Adding branch for: {condition_value}") else: assert isinstance(root_if, If) root_if.orelse.append(cond) if output.end == output.END: cond.body.append( Continue()) # TODO should be rather return continue _add_logic(cond, project.action(output.end), container) current_action = project.action(project.first_action_id()) # having 'while True' default loop is temporary solution until there will be support for functions/loops loop = main_loop(tree) _add_logic(loop, current_action) logger.debug( f"Unused actions: {[project.action(act_id).name for act_id in project.action_ids() - added_actions]}" ) if loop and isinstance(loop.body[0], Pass): # pass is not necessary now del loop.body[0] if loop and isinstance(loop.body[-1], Continue): # delete unnecessary continue del loop.body[-1]
def orientation_id(cls, project: CProject, action_id: str, parameter_id: str) -> str: return cls._id_from_value( project.action(action_id).parameter(parameter_id).value)