def project_problems(scene: CachedScene, project: CachedProject) -> List[str]: scene_objects: Dict[str, str] = {obj.id: obj.type for obj in scene.objects} problems: List[str] = [] unknown_types = {obj.type for obj in scene.objects} - glob.OBJECT_TYPES.keys() if unknown_types: return [f"Scene invalid, contains unknown types: {unknown_types}."] possible_parents = scene.object_ids | project.action_points_ids for ap in project.action_points: # test if all objects exists in scene if ap.parent and ap.parent not in possible_parents: problems.append( f"Action point {ap.name} has non-existing parent: {ap.parent}." ) continue for joints in project.ap_joints(ap.id): if joints.robot_id not in scene.object_ids: problems.append( f"Action point {ap.name} has joints ({joints.name}) for an unknown robot: {joints.robot_id}." ) for action in project.actions: # check if objects have used actions obj_id, action_type = action.parse_type() if obj_id not in scene_objects.keys(): problems.append( f"Object ID {obj_id} which action is used in {action.name} does not exist in scene." ) continue try: os_type = scene_objects[obj_id] # object type except KeyError: os_type = obj_id # service if action_type not in glob.OBJECT_TYPES[os_type].actions: problems.append( f"Object type {scene_objects[obj_id]} does not have action {action_type} used in {action.id}." ) continue try: check_action_params( scene, project, action, glob.OBJECT_TYPES[os_type].actions[action_type]) except Arcor2Exception as e: problems.append(str(e)) return problems
def project_problems(obj_types: ObjectTypeDict, scene: CachedScene, project: CachedProject) -> list[str]: if project.scene_id != scene.id: return ["Project/scene mismatch."] problems: list[str] = scene_problems(obj_types, scene) for proj_param in project.parameters: try: check_project_parameter(project, proj_param) except Arcor2Exception as e: problems.append(str(e)) for ap in project.action_points: try: check_ap_parent(scene, project, ap.parent) except Arcor2Exception: problems.append(f"Action point {ap.name} has invalid parent: {ap.parent}.") for joints in project.ap_joints(ap.id): if joints.robot_id not in scene.object_ids: problems.append( f"Action point {ap.name} has joints ({joints.name}) for an unknown robot: {joints.robot_id}." ) for action in project.actions: # check if objects have used actions obj_id, action_type = action.parse_type() if obj_id not in scene.object_ids: problems.append(f"Object ID {obj_id} which action is used in {action.name} does not exist in scene.") continue scene_obj = scene.object(obj_id) if action_type not in obj_types[scene_obj.type].actions: problems.append( f"ObjectType {scene_obj.type} does not have action {action_type} used in {action.name}." ) continue action_meta = obj_types[scene_obj.type].actions[action_type] try: check_action_params(obj_types, scene, project, action, action_meta) except Arcor2Exception as e: problems.append(str(e)) try: check_flows(project, action, action_meta) except Arcor2Exception as e: problems.append(str(e)) return problems
def project_problems(scene: CachedScene, project: CachedProject) -> List[str]: scene_objects: Dict[str, str] = {obj.id: obj.type for obj in scene.objects} action_ids: Set[str] = set() problems: List[str] = [] unknown_types = {obj.type for obj in scene.objects} - glob.OBJECT_TYPES.keys() if unknown_types: return [f"Scene invalid, contains unknown types: {unknown_types}."] for ap in project.action_points: # test if all objects exists in scene if ap.parent and ap.parent not in scene_objects: problems.append(f"Action point '{ap.name}' has parent '{ap.parent}' that does not exist in the scene.") continue for joints in project.ap_joints(ap.id): if not joints.is_valid: problems.append( f"Action point {ap.name} has invalid joints: {joints.name} " f"(robot {joints.robot_id})." ) for action in project.actions: if action.id in action_ids: problems.append(f"Action {action.name} of the {ap.name} is not unique.") # check if objects have used actions obj_id, action_type = action.parse_type() if obj_id not in scene_objects.keys(): problems.append(f"Object ID {obj_id} which action is used in {action.name} does not exist in scene.") continue try: os_type = scene_objects[obj_id] # object type except KeyError: os_type = obj_id # service if action_type not in glob.OBJECT_TYPES[os_type].actions: problems.append( f"Object type {scene_objects[obj_id]} does not have action {action_type} " f"used in {action.id}." ) continue try: check_action_params(scene, project, action, glob.OBJECT_TYPES[os_type].actions[action_type]) except Arcor2Exception as e: problems.append(str(e)) return problems
def global_action_points_class(project: CachedProject) -> str: tree = Module(body=[]) tree.body.append( ImportFrom( module=arcor2.data.common.__name__, names=[ alias(name=ActionPoint.__name__, asname=None), alias(name=Position.__name__, asname=None), alias(name=Pose.__name__, asname=None), alias(name=ProjectRobotJoints.__name__, asname=None), ], level=0, )) tree.body.append( ImportFrom( module=copy.__name__, names=[alias(name=copy.deepcopy.__name__, asname=None)], level=0, )) tree.body.append( ImportFrom( module=RES_MODULE, names=[alias(name=RES_CLS, asname=None)], level=0, )) aps_init_body: List[Union[Assign, Pass]] = [] for ap in project.action_points: ap_cls_body: List[Assign] = [ Assign( targets=[ Attribute(value=Name(id="self", ctx=Load()), attr="_position", ctx=Store()) ], value=Attribute( value=Call( func=Attribute( value=Attribute(value=Name(id="res", ctx=Load()), attr="project", ctx=Load()), attr=CachedProject.bare_action_point.__name__, ctx=Load(), ), args=[Str(s=ap.id, kind="")], keywords=[], ), attr="position", ctx=Load(), ), type_comment=None, ) ] ap_type_name = humps.pascalize(ap.name) ap_joints_init_body: List[Assign] = [] for joints in project.ap_joints(ap.id): ap_joints_init_body.append( Assign( targets=[ Attribute(value=Name(id="self", ctx=Load()), attr=f"_{joints.name}", ctx=Store()) ], value=Call( func=Attribute( value=Attribute(value=Name(id="res", ctx=Load()), attr="project", ctx=Load()), attr="joints", ctx=Load(), ), args=[Str(s=joints.id, kind="")], keywords=[], ), type_comment=None, )) if ap_joints_init_body: ap_joints_cls_def = ClassDef( name=f"{ap_type_name}Joints", bases=[], keywords=[], body=[ FunctionDef( name="__init__", args=arguments( args=[ arg(arg="self", annotation=None, type_comment=None), arg(arg="res", annotation=Name(id=RES_CLS, ctx=Load()), type_comment=None), ], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=ap_joints_init_body, decorator_list=[], returns=None, type_comment=None, ) ], decorator_list=[], ) for joints in project.ap_joints(ap.id): ap_joints_cls_def.body.append( FunctionDef( name=joints.name, args=arguments( args=[ arg(arg="self", annotation=None, type_comment=None) ], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=[ Return(value=Call( func=Name(id=copy.deepcopy.__name__, ctx=Load()), args=[ Attribute(value=Name(id="self", ctx=Load()), attr=f"_{joints.name}", ctx=Load()) ], keywords=[], )) ], decorator_list=[Name(id="property", ctx=Load())], returns=Name(id=ProjectRobotJoints.__name__, ctx=Load()), type_comment=None, )) tree.body.append(ap_joints_cls_def) ap_cls_body.append( Assign( targets=[ Attribute(value=Name(id="self", ctx=Load()), attr="joints", ctx=Store()) ], value=Call( func=Name(id=f"{ap_type_name}Joints", ctx=Load()), args=[Name(id="res", ctx=Load())], keywords=[], ), type_comment=None, )) ap_orientations_init_body: List[Assign] = [] for ori in project.ap_orientations(ap.id): ap_orientations_init_body.append( Assign( targets=[ Attribute(value=Name(id="self", ctx=Load()), attr=f"_{ori.name}", ctx=Store()) ], value=Call( func=Attribute( value=Attribute(value=Name(id="res", ctx=Load()), attr="project", ctx=Load()), attr="pose", ctx=Load(), ), args=[Str(s=ori.id, kind="")], keywords=[], ), type_comment=None, )) if ap_orientations_init_body: ap_orientations_cls_def = ClassDef( name=f"{ap_type_name}Poses", bases=[], keywords=[], body=[ FunctionDef( name="__init__", args=arguments( args=[ arg(arg="self", annotation=None, type_comment=None), arg(arg="res", annotation=Name(id=RES_CLS, ctx=Load()), type_comment=None), ], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=ap_orientations_init_body, decorator_list=[], returns=None, type_comment=None, ) ], decorator_list=[], ) for ori in project.ap_orientations(ap.id): ap_orientations_cls_def.body.append( FunctionDef( name=ori.name, args=arguments( args=[ arg(arg="self", annotation=None, type_comment=None) ], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=[ Return(value=Call( func=Name(id=copy.deepcopy.__name__, ctx=Load()), args=[ Attribute(value=Name(id="self", ctx=Load()), attr=f"_{ori.name}", ctx=Load()) ], keywords=[], )) ], decorator_list=[Name(id="property", ctx=Load())], returns=Name(id=Pose.__name__, ctx=Load()), type_comment=None, )) tree.body.append(ap_orientations_cls_def) ap_cls_body.append( Assign( targets=[ Attribute(value=Name(id="self", ctx=Load()), attr="poses", ctx=Store()) ], value=Call( func=Name(id=f"{ap_type_name}Poses", ctx=Load()), args=[Name(id="res", ctx=Load())], keywords=[], ), type_comment=None, )) ap_cls_def = ClassDef( name=ap_type_name, bases=[], keywords=[], body=[ FunctionDef( name="__init__", args=arguments( args=[ arg(arg="self", annotation=None, type_comment=None), arg(arg="res", annotation=Name(id=RES_CLS, ctx=Load()), type_comment=None), ], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=ap_cls_body, decorator_list=[], returns=None, type_comment=None, ) ], decorator_list=[], ) # add copy property for position ap_cls_def.body.append( FunctionDef( name="position", args=arguments( args=[arg(arg="self", annotation=None, type_comment=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=[ Return(value=Call( func=Name(id=copy.deepcopy.__name__, ctx=Load()), args=[ Attribute(value=Name(id="self", ctx=Load()), attr="_position", ctx=Load()) ], keywords=[], )) ], decorator_list=[Name(id="property", ctx=Load())], returns=Name(id=Position.__name__, ctx=Load()), type_comment=None, )) tree.body.append(ap_cls_def) aps_init_body.append( Assign( targets=[ Attribute(value=Name(id="self", ctx=Load()), attr=ap.name, ctx=Store()) ], value=Call(func=Name(id=ap_type_name, ctx=Load()), args=[Name(id="res", ctx=Load())], keywords=[]), type_comment=None, )) if not aps_init_body: # there are no action points aps_init_body.append(Pass()) aps_cls_def = ClassDef( name="ActionPoints", bases=[], keywords=[], body=[ FunctionDef( name="__init__", args=arguments( args=[ arg(arg="self", annotation=None, type_comment=None), arg(arg="res", annotation=Name(id=RES_CLS, ctx=Load()), type_comment=None), ], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=aps_init_body, decorator_list=[], returns=None, type_comment=None, ) ], decorator_list=[], ) tree.body.append(aps_cls_def) return tree_to_str(tree)
class Resources: __slots__ = ("project", "scene", "objects", "args", "executor", "_stream_futures") def __init__(self, apply_action_mapping: bool = True) -> None: models: dict[str, Optional[Models]] = {} scene = self.read_project_data(Scene.__name__.lower(), Scene) project = self.read_project_data(Project.__name__.lower(), Project) self.scene = CachedScene(scene) self.project = CachedProject(project) if self.project.scene_id != self.scene.id: raise ResourcesException("Project/scene not consistent!") # make all poses absolute for aps in self.project.action_points_with_parent: # Action point pose is relative to its parent object/AP pose in scene but is absolute during runtime. tr.make_relative_ap_global(self.scene, self.project, aps) for obj_type in self.scene.object_types: try: models[obj_type] = self.read_project_data( "models/" + humps.depascalize(obj_type), ObjectModel).model() except IOError: models[obj_type] = None type_defs: TypesDict = {} for scene_obj_type in self.scene.object_types: # get all type-defs assert scene_obj_type not in type_defs assert scene_obj_type not in built_in_types_names() module = importlib.import_module(CUSTOM_OBJECT_TYPES_MODULE + "." + humps.depascalize(scene_obj_type)) cls = getattr(module, scene_obj_type) patch_object_actions(cls) type_defs[cls.__name__] = cls if apply_action_mapping: patch_with_action_mapping(cls, self.scene, self.project) action.start_paused, action.breakpoints = parse_args() if action.breakpoints: ap_ids = self.project.action_points_ids for bp in action.breakpoints: if bp not in ap_ids: raise ResourcesException(f"Breakpoint ID unknown: {bp}.") # orientations / joints have to be monkey-patched with AP's ID in order to make breakpoints work in @action for ap in self.project.action_points: setattr(ap.position, AP_ID_ATTR, ap.id) for joints in self.project.ap_joints(ap.id): setattr(joints, AP_ID_ATTR, ap.id) package_id = os.path.basename(os.getcwd()) package_meta = package.read_package_meta(package_id) package_info_event = PackageInfo( PackageInfo.Data(package_id, package_meta.name, scene, project)) for model in models.values(): if not model: continue if isinstance(model, Box): package_info_event.data.collision_models.boxes.append(model) elif isinstance(model, Sphere): package_info_event.data.collision_models.spheres.append(model) elif isinstance(model, Cylinder): package_info_event.data.collision_models.cylinders.append( model) elif isinstance(model, Mesh): package_info_event.data.collision_models.meshes.append(model) # following steps might take some time, so let UIs know about the package as a first thing print_event(package_info_event) # in order to prepare a clean environment (clears all configurations and all collisions) scene_service.stop() self.executor = concurrent.futures.ThreadPoolExecutor() futures: list[concurrent.futures.Future] = [] for scene_obj in self.scene.objects: cls = type_defs[scene_obj.type] settings = settings_from_params( cls, scene_obj.parameters, self.project.overrides.get(scene_obj.id, None)) if issubclass(cls, Robot): futures.append( self.executor.submit(cls, scene_obj.id, scene_obj.name, scene_obj.pose, settings)) elif issubclass(cls, CollisionObject): futures.append( self.executor.submit(cls, scene_obj.id, scene_obj.name, scene_obj.pose, models[scene_obj.type], settings)) elif issubclass(cls, GenericWithPose): futures.append( self.executor.submit(cls, scene_obj.id, scene_obj.name, scene_obj.pose, settings)) elif issubclass(cls, Generic): futures.append( self.executor.submit(cls, scene_obj.id, scene_obj.name, settings)) else: raise Arcor2Exception( f"{cls.__name__} has unknown base class.") exceptions: list[Arcor2Exception] = [] self.objects: dict[str, Generic] = {} for f in concurrent.futures.as_completed(futures): try: inst = f.result( ) # if an object creation resulted in exception, it will be raised here except Arcor2Exception as e: print_exception(e) exceptions.append(e) else: self.objects[ inst.id] = inst # successfully initialized objects if exceptions: # if something failed, tear down those that succeeded and stop self.cleanup_all_objects() raise ResourcesException(" ".join([str(e) for e in exceptions]), exceptions) scene_service.start() self._stream_futures: list[concurrent.futures.Future] = [] def read_project_data(self, file_name: str, cls: type[T]) -> T: try: with open(os.path.join("data", file_name + ".json")) as scene_file: return cls.from_dict( humps.decamelize(json.loads_type(scene_file.read(), dict))) except JsonSchemaValidationError as e: raise ResourcesException(f"Invalid project/scene: {e}") def cleanup_all_objects(self) -> None: """Calls cleanup method of all objects in parallel. Errors are just logged. """ futures: list[concurrent.futures.Future] = [] with concurrent.futures.ThreadPoolExecutor() as executor: for obj in self.objects.values(): futures.append(executor.submit(obj.cleanup)) for future in concurrent.futures.as_completed(futures): try: future.result() except Arcor2Exception as e: print_exception(e) def __enter__(self: R) -> R: if _streaming_period > 0: # TODO could be also controlled by script argument (RunPackage flag) for inst in self.objects.values(): if not isinstance(inst, Robot): continue self._stream_futures.append( self.executor.submit(stream_pose, inst)) self._stream_futures.append( self.executor.submit(stream_joints, inst)) return self def __exit__( self, ex_type: Optional[type[BaseException]], ex_value: Optional[BaseException], traceback: Optional[TracebackType], ) -> bool: _shutting_down.set() try: concurrent.futures.wait(self._stream_futures, 1.0) except concurrent.futures.TimeoutError: pass if isinstance(ex_value, Exception): # this intentionally ignores KeyboardInterrupt (derived from BaseException) print_exception(ex_value) scene_service.stop() self.cleanup_all_objects() return True